CIRCT 22.0.0git
Loading...
Searching...
No Matches
FIRRTLOps.cpp
Go to the documentation of this file.
1//===- FIRRTLOps.cpp - Implement the FIRRTL operations --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implement the FIRRTL ops.
10//
11//===----------------------------------------------------------------------===//
12
27#include "circt/Support/Utils.h"
28#include "mlir/IR/BuiltinTypes.h"
29#include "mlir/IR/Diagnostics.h"
30#include "mlir/IR/DialectImplementation.h"
31#include "mlir/IR/PatternMatch.h"
32#include "mlir/IR/SymbolTable.h"
33#include "mlir/Interfaces/FunctionImplementation.h"
34#include "llvm/ADT/BitVector.h"
35#include "llvm/ADT/DenseMap.h"
36#include "llvm/ADT/DenseSet.h"
37#include "llvm/ADT/STLExtras.h"
38#include "llvm/ADT/SmallSet.h"
39#include "llvm/ADT/StringExtras.h"
40#include "llvm/ADT/TypeSwitch.h"
41#include "llvm/Support/Casting.h"
42#include "llvm/Support/FormatVariadic.h"
43
44using llvm::SmallDenseSet;
45using mlir::RegionRange;
46using namespace circt;
47using namespace firrtl;
48using namespace chirrtl;
49
50//===----------------------------------------------------------------------===//
51// Utilities
52//===----------------------------------------------------------------------===//
53
54/// Remove elements from the input array corresponding to set bits in
55/// `indicesToDrop`, returning the elements not mentioned.
56template <typename T>
57static SmallVector<T>
58removeElementsAtIndices(ArrayRef<T> input,
59 const llvm::BitVector &indicesToDrop) {
60#ifndef NDEBUG
61 if (!input.empty()) {
62 int lastIndex = indicesToDrop.find_last();
63 if (lastIndex >= 0)
64 assert((size_t)lastIndex < input.size() && "index out of range");
65 }
66#endif
67
68 // If the input is empty (which is an optimization we do for certain array
69 // attributes), simply return an empty vector.
70 if (input.empty())
71 return {};
72
73 // Copy over the live chunks.
74 size_t lastCopied = 0;
75 SmallVector<T> result;
76 result.reserve(input.size() - indicesToDrop.count());
77
78 for (unsigned indexToDrop : indicesToDrop.set_bits()) {
79 // If we skipped over some valid elements, copy them over.
80 if (indexToDrop > lastCopied) {
81 result.append(input.begin() + lastCopied, input.begin() + indexToDrop);
82 lastCopied = indexToDrop;
83 }
84 // Ignore this value so we don't copy it in the next iteration.
85 ++lastCopied;
86 }
87
88 // If there are live elements at the end, copy them over.
89 if (lastCopied < input.size())
90 result.append(input.begin() + lastCopied, input.end());
91
92 return result;
93}
94
95/// Emit an error if optional location is non-null, return null of return type.
96template <typename RetTy = FIRRTLType, typename... Args>
97static RetTy emitInferRetTypeError(std::optional<Location> loc,
98 const Twine &message, Args &&...args) {
99 if (loc)
100 (mlir::emitError(*loc, message) << ... << std::forward<Args>(args));
101 return {};
102}
103
104bool firrtl::isDuplexValue(Value val) {
105 // Block arguments are not duplex values.
106 while (Operation *op = val.getDefiningOp()) {
107 auto isDuplex =
108 TypeSwitch<Operation *, std::optional<bool>>(op)
109 .Case<SubfieldOp, SubindexOp, SubaccessOp>([&val](auto op) {
110 val = op.getInput();
111 return std::nullopt;
112 })
113 .Case<RegOp, RegResetOp, WireOp>([](auto) { return true; })
114 .Default([](auto) { return false; });
115 if (isDuplex)
116 return *isDuplex;
117 }
118 return false;
119}
120
121SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
122MemOp::computeDataFlow() {
123 // If read result has non-zero latency, then no combinational dependency
124 // exists.
125 if (getReadLatency() > 0)
126 return {};
127 SmallVector<std::pair<circt::FieldRef, circt::FieldRef>> deps;
128 // Add a dependency from the enable and address fields to the data field.
129 for (auto memPort : getResults())
130 if (auto type = type_dyn_cast<BundleType>(memPort.getType())) {
131 auto enableFieldId = type.getFieldID((unsigned)ReadPortSubfield::en);
132 auto addressFieldId = type.getFieldID((unsigned)ReadPortSubfield::addr);
133 auto dataFieldId = type.getFieldID((unsigned)ReadPortSubfield::data);
134 deps.emplace_back(
135 FieldRef(memPort, static_cast<unsigned>(dataFieldId)),
136 FieldRef(memPort, static_cast<unsigned>(enableFieldId)));
137 deps.emplace_back(
138 FieldRef(memPort, static_cast<unsigned>(dataFieldId)),
139 FieldRef(memPort, static_cast<unsigned>(addressFieldId)));
140 }
141 return deps;
142}
143
144/// Return the kind of port this is given the port type from a 'mem' decl.
145static MemOp::PortKind getMemPortKindFromType(FIRRTLType type) {
146 constexpr unsigned int addr = 1 << 0;
147 constexpr unsigned int en = 1 << 1;
148 constexpr unsigned int clk = 1 << 2;
149 constexpr unsigned int data = 1 << 3;
150 constexpr unsigned int mask = 1 << 4;
151 constexpr unsigned int rdata = 1 << 5;
152 constexpr unsigned int wdata = 1 << 6;
153 constexpr unsigned int wmask = 1 << 7;
154 constexpr unsigned int wmode = 1 << 8;
155 constexpr unsigned int def = 1 << 9;
156 // Get the kind of port based on the fields of the Bundle.
157 auto portType = type_dyn_cast<BundleType>(type);
158 if (!portType)
159 return MemOp::PortKind::Debug;
160 unsigned fields = 0;
161 // Get the kind of port based on the fields of the Bundle.
162 for (auto elem : portType.getElements()) {
163 fields |= llvm::StringSwitch<unsigned>(elem.name.getValue())
164 .Case("addr", addr)
165 .Case("en", en)
166 .Case("clk", clk)
167 .Case("data", data)
168 .Case("mask", mask)
169 .Case("rdata", rdata)
170 .Case("wdata", wdata)
171 .Case("wmask", wmask)
172 .Case("wmode", wmode)
173 .Default(def);
174 }
175 if (fields == (addr | en | clk | data))
176 return MemOp::PortKind::Read;
177 if (fields == (addr | en | clk | data | mask))
178 return MemOp::PortKind::Write;
179 if (fields == (addr | en | clk | wdata | wmask | rdata | wmode))
180 return MemOp::PortKind::ReadWrite;
181 return MemOp::PortKind::Debug;
182}
183
185 switch (flow) {
186 case Flow::None:
187 return Flow::None;
188 case Flow::Source:
189 return Flow::Sink;
190 case Flow::Sink:
191 return Flow::Source;
192 case Flow::Duplex:
193 return Flow::Duplex;
194 }
195 // Unreachable but silences warning
196 llvm_unreachable("Unsupported Flow type.");
197}
198
199const char *toString(Flow flow) {
200 switch (flow) {
201 case Flow::None:
202 return "no flow";
203 case Flow::Source:
204 return "source flow";
205 case Flow::Sink:
206 return "sink flow";
207 case Flow::Duplex:
208 return "duplex flow";
209 }
210 // Unreachable but silences warning
211 llvm_unreachable("Unsupported Flow type.");
212}
213
214Flow firrtl::foldFlow(Value val, Flow accumulatedFlow) {
215
216 if (auto blockArg = dyn_cast<BlockArgument>(val)) {
217 auto *op = val.getParentBlock()->getParentOp();
218 if (auto moduleLike = dyn_cast<FModuleLike>(op)) {
219 auto direction = moduleLike.getPortDirection(blockArg.getArgNumber());
220 if (direction == Direction::Out)
221 return swapFlow(accumulatedFlow);
222 }
223 return accumulatedFlow;
224 }
225
226 Operation *op = val.getDefiningOp();
227
228 return TypeSwitch<Operation *, Flow>(op)
229 .Case<SubfieldOp, OpenSubfieldOp>([&](auto op) {
230 return foldFlow(op.getInput(), op.isFieldFlipped()
231 ? swapFlow(accumulatedFlow)
232 : accumulatedFlow);
233 })
234 .Case<SubindexOp, SubaccessOp, OpenSubindexOp, RefSubOp>(
235 [&](auto op) { return foldFlow(op.getInput(), accumulatedFlow); })
236 // Registers, Wires, and behavioral memory ports are always Duplex.
237 .Case<RegOp, RegResetOp, WireOp, MemoryPortOp>(
238 [](auto) { return Flow::Duplex; })
239 .Case<InstanceOp, InstanceChoiceOp>([&](auto inst) {
240 auto resultNo = cast<OpResult>(val).getResultNumber();
241 if (inst.getPortDirection(resultNo) == Direction::Out)
242 return accumulatedFlow;
243 return swapFlow(accumulatedFlow);
244 })
245 .Case<MemOp>([&](auto op) {
246 // only debug ports with RefType have source flow.
247 if (type_isa<RefType>(val.getType()))
248 return Flow::Source;
249 return swapFlow(accumulatedFlow);
250 })
251 .Case<ObjectSubfieldOp>([&](ObjectSubfieldOp op) {
252 auto input = op.getInput();
253 auto *inputOp = input.getDefiningOp();
254
255 // We are directly accessing a port on a local declaration.
256 if (auto objectOp = dyn_cast_or_null<ObjectOp>(inputOp)) {
257 auto classType = input.getType();
258 auto direction = classType.getElement(op.getIndex()).direction;
259 if (direction == Direction::In)
260 return Flow::Sink;
261 return Flow::Source;
262 }
263
264 // We are accessing a remote object. Input ports on remote objects are
265 // inaccessible, and thus have Flow::None. Walk backwards through the
266 // chain of subindexes, to detect if we have indexed through an input
267 // port. At the end, either we did index through an input port, or the
268 // entire path was through output ports with source flow.
269 while (true) {
270 auto classType = input.getType();
271 auto direction = classType.getElement(op.getIndex()).direction;
272 if (direction == Direction::In)
273 return Flow::None;
274
275 op = dyn_cast_or_null<ObjectSubfieldOp>(inputOp);
276 if (op) {
277 input = op.getInput();
278 inputOp = input.getDefiningOp();
279 continue;
280 }
281
282 return accumulatedFlow;
283 };
284 })
285 // Anything else acts like a universal source.
286 .Default([&](auto) { return accumulatedFlow; });
287}
288
289// TODO: This is doing the same walk as foldFlow. These two functions can be
290// combined and return a (flow, kind) product.
292 Operation *op = val.getDefiningOp();
293 if (!op)
294 return DeclKind::Port;
295
296 return TypeSwitch<Operation *, DeclKind>(op)
297 .Case<InstanceOp>([](auto) { return DeclKind::Instance; })
298 .Case<SubfieldOp, SubindexOp, SubaccessOp, OpenSubfieldOp, OpenSubindexOp,
299 RefSubOp>([](auto op) { return getDeclarationKind(op.getInput()); })
300 .Default([](auto) { return DeclKind::Other; });
301}
302
303size_t firrtl::getNumPorts(Operation *op) {
304 if (auto module = dyn_cast<FModuleLike>(op))
305 return module.getNumPorts();
306 return op->getNumResults();
307}
308
309/// Check whether an operation has a `DontTouch` annotation, or a symbol that
310/// should prevent certain types of canonicalizations.
311bool firrtl::hasDontTouch(Operation *op) {
312 return op->getAttr(hw::InnerSymbolTable::getInnerSymbolAttrName()) ||
314}
315
316/// Check whether a block argument ("port") or the operation defining a value
317/// has a `DontTouch` annotation, or a symbol that should prevent certain types
318/// of canonicalizations.
319bool firrtl::hasDontTouch(Value value) {
320 if (auto *op = value.getDefiningOp())
321 return hasDontTouch(op);
322 auto arg = dyn_cast<BlockArgument>(value);
323 auto module = dyn_cast<FModuleOp>(arg.getOwner()->getParentOp());
324 if (!module)
325 return false;
326 return (module.getPortSymbolAttr(arg.getArgNumber())) ||
327 AnnotationSet::forPort(module, arg.getArgNumber()).hasDontTouch();
328}
329
330/// Get a special name to use when printing the entry block arguments of the
331/// region contained by an operation in this dialect.
332void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region &region,
333 OpAsmSetValueNameFn setNameFn) {
334 if (region.empty())
335 return;
336 auto *parentOp = op;
337 auto *block = &region.front();
338 // Check to see if the operation containing the arguments has 'firrtl.name'
339 // attributes for them. If so, use that as the name.
340 auto argAttr = parentOp->getAttrOfType<ArrayAttr>("portNames");
341 // Do not crash on invalid IR.
342 if (!argAttr || argAttr.size() != block->getNumArguments())
343 return;
344
345 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
346 auto str = cast<StringAttr>(argAttr[i]).getValue();
347 if (!str.empty())
348 setNameFn(block->getArgument(i), str);
349 }
350}
351
352/// A forward declaration for `NameKind` attribute parser.
353static ParseResult parseNameKind(OpAsmParser &parser,
354 firrtl::NameKindEnumAttr &result);
355
356//===----------------------------------------------------------------------===//
357// Layer Verification Utilities
358//===----------------------------------------------------------------------===//
359
360/// Get the ambient layers active at the given op.
361static LayerSet getAmbientLayersAt(Operation *op) {
362 // Crawl through the parent ops, accumulating all ambient layers at the given
363 // operation.
364 LayerSet result;
365 for (; op != nullptr; op = op->getParentOp()) {
366 if (auto module = dyn_cast<FModuleLike>(op)) {
367 auto layers = module.getLayersAttr().getAsRange<SymbolRefAttr>();
368 result.insert(layers.begin(), layers.end());
369 break;
370 }
371 if (auto layerblock = dyn_cast<LayerBlockOp>(op)) {
372 result.insert(layerblock.getLayerName());
373 continue;
374 }
375 }
376 return result;
377}
378
379/// Get the ambient layer requirements at the definition site of the value.
380static LayerSet getAmbientLayersFor(Value value) {
381 return getAmbientLayersAt(getFieldRefFromValue(value).getDefiningOp());
382}
383
384/// Get the effective layer requirements for the given value.
385/// The effective layers for a value is the union of
386/// - the ambient layers for the cannonical storage location.
387/// - any explicit layer annotations in the value's type.
388static LayerSet getLayersFor(Value value) {
389 auto result = getAmbientLayersFor(value);
390 if (auto type = dyn_cast<RefType>(value.getType()))
391 if (auto layer = type.getLayer())
392 result.insert(type.getLayer());
393 return result;
394}
395
396/// Check that the source layer is compatible with the destination layer.
397/// Either the source and destination are identical, or the source-layer
398/// is a parent of the destination. For example `A` is compatible with `A.B.C`,
399/// because any definition valid in `A` is also valid in `A.B.C`.
400static bool isLayerCompatibleWith(mlir::SymbolRefAttr srcLayer,
401 mlir::SymbolRefAttr dstLayer) {
402 // A non-colored probe may be cast to any colored probe.
403 if (!srcLayer)
404 return true;
405
406 // A colored probe cannot be cast to an uncolored probe.
407 if (!dstLayer)
408 return false;
409
410 // Return true if the srcLayer is a prefix of the dstLayer.
411 if (srcLayer.getRootReference() != dstLayer.getRootReference())
412 return false;
413
414 auto srcNames = srcLayer.getNestedReferences();
415 auto dstNames = dstLayer.getNestedReferences();
416 if (dstNames.size() < srcNames.size())
417 return false;
418
419 return llvm::all_of(llvm::zip_first(srcNames, dstNames),
420 [](auto x) { return std::get<0>(x) == std::get<1>(x); });
421}
422
423/// Check that the source layer is present in the destination layers.
424static bool isLayerCompatibleWith(SymbolRefAttr srcLayer,
425 const LayerSet &dstLayers) {
426 // fast path: the required layer is directly listed in the provided layers.
427 if (dstLayers.contains(srcLayer))
428 return true;
429
430 // Slow path: the required layer is not directly listed in the provided
431 // layers, but the layer may still be provided by a nested layer.
432 return any_of(dstLayers, [=](SymbolRefAttr dstLayer) {
433 return isLayerCompatibleWith(srcLayer, dstLayer);
434 });
435}
436
437/// Check that the source layers are all present in the destination layers.
438/// True if all source layers are present in the destination.
439/// Outputs the set of source layers that are missing in the destination.
440static bool isLayerSetCompatibleWith(const LayerSet &src, const LayerSet &dst,
441 SmallVectorImpl<SymbolRefAttr> &missing) {
442 for (auto srcLayer : src)
443 if (!isLayerCompatibleWith(srcLayer, dst))
444 missing.push_back(srcLayer);
445
446 llvm::sort(missing, LayerSetCompare());
447 return missing.empty();
448}
449
450static LogicalResult checkLayerCompatibility(
451 Operation *op, const LayerSet &src, const LayerSet &dst,
452 const Twine &errorMsg,
453 const Twine &noteMsg = Twine("missing layer requirements")) {
454 SmallVector<SymbolRefAttr> missing;
455 if (isLayerSetCompatibleWith(src, dst, missing))
456 return success();
457 interleaveComma(missing, op->emitOpError(errorMsg).attachNote()
458 << noteMsg << ": ");
459 return failure();
460}
461
462//===----------------------------------------------------------------------===//
463// CircuitOp
464//===----------------------------------------------------------------------===//
465
466void CircuitOp::build(OpBuilder &builder, OperationState &result,
467 StringAttr name, ArrayAttr annotations) {
468 // Add an attribute for the name.
469 result.getOrAddProperties<Properties>().setName(name);
470
471 if (!annotations)
472 annotations = builder.getArrayAttr({});
473 result.getOrAddProperties<Properties>().setAnnotations(annotations);
474
475 // Create a region and a block for the body.
476 Region *bodyRegion = result.addRegion();
477 Block *body = new Block();
478 bodyRegion->push_back(body);
479}
480
481static ParseResult parseCircuitOpAttrs(OpAsmParser &parser,
482 NamedAttrList &resultAttrs) {
483 auto result = parser.parseOptionalAttrDictWithKeyword(resultAttrs);
484 if (!resultAttrs.get("annotations"))
485 resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
486
487 return result;
488}
489
490static void printCircuitOpAttrs(OpAsmPrinter &p, Operation *op,
491 DictionaryAttr attr) {
492 // "name" is always elided.
493 SmallVector<StringRef> elidedAttrs = {"name"};
494 // Elide "annotations" if it doesn't exist or if it is empty
495 auto annotationsAttr = op->getAttrOfType<ArrayAttr>("annotations");
496 if (annotationsAttr.empty())
497 elidedAttrs.push_back("annotations");
498
499 p.printOptionalAttrDictWithKeyword(op->getAttrs(), elidedAttrs);
500}
501
502LogicalResult CircuitOp::verifyRegions() {
503 StringRef main = getName();
504
505 // Check that the circuit has a non-empty name.
506 if (main.empty()) {
507 emitOpError("must have a non-empty name");
508 return failure();
509 }
510
511 mlir::SymbolTable symtbl(getOperation());
512
513 auto *mainModule = symtbl.lookup(main);
514 if (!mainModule)
515 return emitOpError().append(
516 "does not contain module with same name as circuit");
517 if (!isa<FModuleLike>(mainModule))
518 return mainModule->emitError(
519 "entity with name of circuit must be a module");
520 if (symtbl.getSymbolVisibility(mainModule) !=
521 mlir::SymbolTable::Visibility::Public)
522 return mainModule->emitError("main module must be public");
523
524 // Store a mapping of defname to either the first external module
525 // that defines it or, preferentially, the first external module
526 // that defines it and has no parameters.
527 llvm::DenseMap<Attribute, FExtModuleOp> defnameMap;
528
529 auto verifyExtModule = [&](FExtModuleOp extModule) -> LogicalResult {
530 if (!extModule)
531 return success();
532
533 auto defname = extModule.getDefnameAttr();
534 if (!defname)
535 return success();
536
537 // Check that this extmodule's defname does not conflict with
538 // the symbol name of any module.
539 if (auto collidingModule = symtbl.lookup<FModuleOp>(defname.getValue()))
540 return extModule.emitOpError()
541 .append("attribute 'defname' with value ", defname,
542 " conflicts with the name of another module in the circuit")
543 .attachNote(collidingModule.getLoc())
544 .append("previous module declared here");
545
546 // Find an optional extmodule with a defname collision. Update
547 // the defnameMap if this is the first extmodule with that
548 // defname or if the current extmodule takes no parameters and
549 // the collision does. The latter condition improves later
550 // extmodule verification as checking against a parameterless
551 // module is stricter.
552 FExtModuleOp collidingExtModule;
553 if (auto &value = defnameMap[defname]) {
554 collidingExtModule = value;
555 if (!value.getParameters().empty() && extModule.getParameters().empty())
556 value = extModule;
557 } else {
558 value = extModule;
559 // Go to the next extmodule if no extmodule with the same
560 // defname was found.
561 return success();
562 }
563
564 // Check that the number of ports is exactly the same.
565 SmallVector<PortInfo> ports = extModule.getPorts();
566 SmallVector<PortInfo> collidingPorts = collidingExtModule.getPorts();
567
568 if (ports.size() != collidingPorts.size())
569 return extModule.emitOpError()
570 .append("with 'defname' attribute ", defname, " has ", ports.size(),
571 " ports which is different from a previously defined "
572 "extmodule with the same 'defname' which has ",
573 collidingPorts.size(), " ports")
574 .attachNote(collidingExtModule.getLoc())
575 .append("previous extmodule definition occurred here");
576
577 // Check that ports match for name and type. Since parameters
578 // *might* affect widths, ignore widths if either module has
579 // parameters. Note that this allows for misdetections, but
580 // has zero false positives.
581 for (auto p : llvm::zip(ports, collidingPorts)) {
582 StringAttr aName = std::get<0>(p).name, bName = std::get<1>(p).name;
583 Type aType = std::get<0>(p).type, bType = std::get<1>(p).type;
584
585 if (aName != bName)
586 return extModule.emitOpError()
587 .append("with 'defname' attribute ", defname,
588 " has a port with name ", aName,
589 " which does not match the name of the port in the same "
590 "position of a previously defined extmodule with the same "
591 "'defname', expected port to have name ",
592 bName)
593 .attachNote(collidingExtModule.getLoc())
594 .append("previous extmodule definition occurred here");
595
596 if (!extModule.getParameters().empty() ||
597 !collidingExtModule.getParameters().empty()) {
598 // Compare base types as widthless, others must match.
599 if (auto base = type_dyn_cast<FIRRTLBaseType>(aType))
600 aType = base.getWidthlessType();
601 if (auto base = type_dyn_cast<FIRRTLBaseType>(bType))
602 bType = base.getWidthlessType();
603 }
604 if (aType != bType)
605 return extModule.emitOpError()
606 .append("with 'defname' attribute ", defname,
607 " has a port with name ", aName,
608 " which has a different type ", aType,
609 " which does not match the type of the port in the same "
610 "position of a previously defined extmodule with the same "
611 "'defname', expected port to have type ",
612 bType)
613 .attachNote(collidingExtModule.getLoc())
614 .append("previous extmodule definition occurred here");
615 }
616 return success();
617 };
618
619 SmallVector<FModuleOp, 1> dutModules;
620 for (auto &op : *getBodyBlock()) {
621 // Verify modules.
622 if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
623 if (AnnotationSet(moduleOp).hasAnnotation(dutAnnoClass))
624 dutModules.push_back(moduleOp);
625 continue;
626 }
627
628 // Verify external modules.
629 if (auto extModule = dyn_cast<FExtModuleOp>(op)) {
630 if (verifyExtModule(extModule).failed())
631 return failure();
632 }
633 }
634
635 // Error if there is more than one design-under-test.
636 if (dutModules.size() > 1) {
637 auto diag = dutModules[0]->emitOpError()
638 << "is annotated as the design-under-test (DUT), but other "
639 "modules are also annotated";
640 for (auto moduleOp : ArrayRef(dutModules).drop_front())
641 diag.attachNote(moduleOp.getLoc()) << "is also annotated as the DUT";
642 return failure();
643 }
644
645 return success();
646}
647
648Block *CircuitOp::getBodyBlock() { return &getBody().front(); }
649
650//===----------------------------------------------------------------------===//
651// FExtModuleOp and FModuleOp
652//===----------------------------------------------------------------------===//
653
654static SmallVector<PortInfo> getPortImpl(FModuleLike module) {
655 SmallVector<PortInfo> results;
656 ArrayRef<Attribute> domains = module.getDomainInfo();
657 for (unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
658 results.push_back({module.getPortNameAttr(i), module.getPortType(i),
659 module.getPortDirection(i), module.getPortSymbolAttr(i),
660 module.getPortLocation(i),
661 AnnotationSet::forPort(module, i),
662 domains.empty() ? Attribute{} : domains[i]});
663 }
664 return results;
665}
666
667SmallVector<PortInfo> FModuleOp::getPorts() { return ::getPortImpl(*this); }
668
669SmallVector<PortInfo> FExtModuleOp::getPorts() { return ::getPortImpl(*this); }
670
671SmallVector<PortInfo> FIntModuleOp::getPorts() { return ::getPortImpl(*this); }
672
673SmallVector<PortInfo> FMemModuleOp::getPorts() { return ::getPortImpl(*this); }
674
676 if (dir == Direction::In)
677 return hw::ModulePort::Direction::Input;
678 if (dir == Direction::Out)
679 return hw::ModulePort::Direction::Output;
680 assert(0 && "invalid direction");
681 abort();
682}
683
684static SmallVector<hw::PortInfo> getPortListImpl(FModuleLike module) {
685 SmallVector<hw::PortInfo> results;
686 auto aname = StringAttr::get(module.getContext(),
687 hw::HWModuleLike::getPortSymbolAttrName());
688 auto emptyDict = DictionaryAttr::get(module.getContext());
689 for (unsigned i = 0, e = getNumPorts(module); i < e; ++i) {
690 auto sym = module.getPortSymbolAttr(i);
691 results.push_back(
692 {{module.getPortNameAttr(i), module.getPortType(i),
693 dirFtoH(module.getPortDirection(i))},
694 i,
695 sym ? DictionaryAttr::get(
696 module.getContext(),
697 ArrayRef<mlir::NamedAttribute>{NamedAttribute{aname, sym}})
698 : emptyDict,
699 module.getPortLocation(i)});
700 }
701 return results;
702}
703
704SmallVector<::circt::hw::PortInfo> FModuleOp::getPortList() {
705 return ::getPortListImpl(*this);
706}
707
708SmallVector<::circt::hw::PortInfo> FExtModuleOp::getPortList() {
709 return ::getPortListImpl(*this);
710}
711
712SmallVector<::circt::hw::PortInfo> FIntModuleOp::getPortList() {
713 return ::getPortListImpl(*this);
714}
715
716SmallVector<::circt::hw::PortInfo> FMemModuleOp::getPortList() {
717 return ::getPortListImpl(*this);
718}
719
720static hw::PortInfo getPortImpl(FModuleLike module, size_t idx) {
721 return {{module.getPortNameAttr(idx), module.getPortType(idx),
722 dirFtoH(module.getPortDirection(idx))},
723 idx,
724 DictionaryAttr::get(
725 module.getContext(),
726 ArrayRef<mlir::NamedAttribute>{NamedAttribute{
727 StringAttr::get(module.getContext(),
728 hw::HWModuleLike::getPortSymbolAttrName()),
729 module.getPortSymbolAttr(idx)}}),
730 module.getPortLocation(idx)};
731}
732
733::circt::hw::PortInfo FModuleOp::getPort(size_t idx) {
734 return ::getPortImpl(*this, idx);
735}
736
737::circt::hw::PortInfo FExtModuleOp::getPort(size_t idx) {
738 return ::getPortImpl(*this, idx);
739}
740
741::circt::hw::PortInfo FIntModuleOp::getPort(size_t idx) {
742 return ::getPortImpl(*this, idx);
743}
744
745::circt::hw::PortInfo FMemModuleOp::getPort(size_t idx) {
746 return ::getPortImpl(*this, idx);
747}
748
749// Return the port with the specified name.
750BlockArgument FModuleOp::getArgument(size_t portNumber) {
751 return getBodyBlock()->getArgument(portNumber);
752}
753
754/// Return an updated domain info Attribute with domain indices updated based on
755/// port insertions.
756static Attribute
757fixDomainInfoInsertions(MLIRContext *context, Attribute domainInfoAttr,
758 const SmallVectorImpl<unsigned> &numInserted) {
759 // This is a domain type port. Return the original domain info unmodified.
760 auto di = dyn_cast_or_null<ArrayAttr>(domainInfoAttr);
761 if (!di)
762 return domainInfoAttr;
763 // This is a non-domain type port. Update any indeices referenced.
764 SmallVector<Attribute> domainInfo;
765 for (auto attr : di) {
766 auto oldIdx = cast<IntegerAttr>(attr).getUInt();
767 auto newIdx = oldIdx + numInserted[oldIdx];
768 if (oldIdx == newIdx)
769 domainInfo.push_back(attr);
770 else
771 domainInfo.push_back(IntegerAttr::get(
772 IntegerType::get(context, 32, IntegerType::Unsigned), newIdx));
773 }
774 return ArrayAttr::get(context, domainInfo);
775}
776
777/// Inserts the given ports. The insertion indices are expected to be in order.
778/// Insertion occurs in-order, such that ports with the same insertion index
779/// appear in the module in the same order they appeared in the list.
780static void insertPorts(FModuleLike op,
781 ArrayRef<std::pair<unsigned, PortInfo>> ports,
782 bool supportsInternalPaths = false) {
783 if (ports.empty())
784 return;
785 unsigned oldNumArgs = op.getNumPorts();
786 unsigned newNumArgs = oldNumArgs + ports.size();
787
788 // Add direction markers and names for new ports.
789 auto existingDirections = op.getPortDirectionsAttr();
790 ArrayRef<Attribute> existingNames = op.getPortNames();
791 ArrayRef<Attribute> existingTypes = op.getPortTypes();
792 ArrayRef<Attribute> existingLocs = op.getPortLocations();
793 assert(existingDirections.size() == oldNumArgs);
794 assert(existingNames.size() == oldNumArgs);
795 assert(existingTypes.size() == oldNumArgs);
796 assert(existingLocs.size() == oldNumArgs);
797 SmallVector<Attribute> internalPaths;
798 auto emptyInternalPath = InternalPathAttr::get(op.getContext());
799 if (supportsInternalPaths) {
800 if (auto internalPathsAttr = op->getAttrOfType<ArrayAttr>("internalPaths"))
801 llvm::append_range(internalPaths, internalPathsAttr);
802 else
803 internalPaths.resize(oldNumArgs, emptyInternalPath);
804 assert(internalPaths.size() == oldNumArgs);
805 }
806
807 SmallVector<bool> newDirections;
808 SmallVector<Attribute> newNames, newTypes, newDomains, newAnnos, newSyms,
809 newLocs, newInternalPaths;
810 SmallVector<unsigned> numInserted;
811 newDirections.reserve(newNumArgs);
812 newNames.reserve(newNumArgs);
813 newTypes.reserve(newNumArgs);
814 newDomains.reserve(newNumArgs);
815 newAnnos.reserve(newNumArgs);
816 newSyms.reserve(newNumArgs);
817 newLocs.reserve(newNumArgs);
818 newInternalPaths.reserve(newNumArgs);
819 numInserted.resize(op.getNumPorts());
820
821 auto emptyArray = ArrayAttr::get(op.getContext(), {});
822
823 unsigned oldIdx = 0;
824 auto migrateOldPorts = [&](unsigned untilOldIdx) {
825 while (oldIdx < oldNumArgs && oldIdx < untilOldIdx) {
826 newDirections.push_back(existingDirections[oldIdx]);
827 newNames.push_back(existingNames[oldIdx]);
828 newTypes.push_back(existingTypes[oldIdx]);
829 newDomains.push_back(fixDomainInfoInsertions(
830 op.getContext(), op.getDomainInfoAttrForPort(oldIdx), numInserted));
831 newAnnos.push_back(op.getAnnotationsAttrForPort(oldIdx));
832 newSyms.push_back(op.getPortSymbolAttr(oldIdx));
833 newLocs.push_back(existingLocs[oldIdx]);
834 if (supportsInternalPaths)
835 newInternalPaths.push_back(internalPaths[oldIdx]);
836 if (oldIdx == 0)
837 numInserted[0] = 0;
838 else
839 numInserted[oldIdx] = numInserted[oldIdx - 1];
840 ++oldIdx;
841 }
842 };
843 for (auto pair : llvm::enumerate(ports)) {
844 auto idx = pair.value().first;
845 auto &port = pair.value().second;
846 migrateOldPorts(idx);
847 newDirections.push_back(direction::unGet(port.direction));
848 newNames.push_back(port.name);
849 newTypes.push_back(TypeAttr::get(port.type));
850 newDomains.push_back(fixDomainInfoInsertions(
851 op.getContext(),
852 port.domains ? port.domains : ArrayAttr::get(op.getContext(), {}),
853 numInserted));
854 auto annos = port.annotations.getArrayAttr();
855 newAnnos.push_back(annos ? annos : emptyArray);
856 newSyms.push_back(port.sym);
857 newLocs.push_back(port.loc);
858 if (supportsInternalPaths)
859 newInternalPaths.push_back(emptyInternalPath);
860 if (idx < oldNumArgs)
861 numInserted[idx] += 1;
862 }
863 migrateOldPorts(oldNumArgs);
864
865 // The lack of *any* port annotations is represented by an empty
866 // `portAnnotations` array as a shorthand.
867 if (llvm::all_of(newAnnos, [](Attribute attr) {
868 return cast<ArrayAttr>(attr).empty();
869 }))
870 newAnnos.clear();
871
872 // The lack of *any* domains is also represented by an empty `domainInfo`
873 // attribute.
874 if (llvm::all_of(newDomains, [](Attribute attr) {
875 if (!attr)
876 return true;
877 if (auto arrayAttr = dyn_cast<ArrayAttr>(attr))
878 return arrayAttr.empty();
879 return false;
880 }))
881 newDomains.clear();
882
883 // Apply these changed markers.
884 op->setAttr("portDirections",
885 direction::packAttribute(op.getContext(), newDirections));
886 op->setAttr("portNames", ArrayAttr::get(op.getContext(), newNames));
887 op->setAttr("portTypes", ArrayAttr::get(op.getContext(), newTypes));
888 op->setAttr("domainInfo", ArrayAttr::get(op.getContext(), newDomains));
889 op->setAttr("portAnnotations", ArrayAttr::get(op.getContext(), newAnnos));
890 FModuleLike::fixupPortSymsArray(newSyms, op.getContext());
891 op.setPortSymbols(newSyms);
892 op->setAttr("portLocations", ArrayAttr::get(op.getContext(), newLocs));
893 if (supportsInternalPaths) {
894 // Drop if all-empty, otherwise set to new array.
895 auto empty = llvm::all_of(newInternalPaths, [](Attribute attr) {
896 return !cast<InternalPathAttr>(attr).getPath();
897 });
898 if (empty)
899 op->removeAttr("internalPaths");
900 else
901 op->setAttr("internalPaths",
902 ArrayAttr::get(op.getContext(), newInternalPaths));
903 }
904}
905
906// Return an Attribute with updated port domain information based on information
907// about which ports have been deleted. This is necessary because the port
908// domain storage uses an integer to indicate the index of a domain port with
909// which it is associated.
910//
911// Note: this will _always_ return one-entry-per-port. While this is not
912// required for FModuleLike, InstanceOps have this restriction.
913static ArrayAttr fixDomainInfoDeletions(MLIRContext *context,
914 ArrayAttr domainInfoAttr,
915 const llvm::BitVector &portIndices,
916 bool supportsEmptyAttr) {
917 if (supportsEmptyAttr && domainInfoAttr.empty())
918 return domainInfoAttr;
919
920 // Compute an array where each entry is the number of ports before that
921 // index that have been deleted. This is used to update domain information.
922 SmallVector<unsigned> numDeleted;
923 numDeleted.resize(portIndices.size());
924 size_t deletionIndex = portIndices.find_first();
925 for (size_t i = 0, e = portIndices.size(); i != e; ++i) {
926 if (i == deletionIndex) {
927 if (i == 0)
928 numDeleted[i] = 1;
929 else
930 numDeleted[i] = numDeleted[i - 1] + 1;
931 deletionIndex = portIndices.find_next(i);
932 continue;
933 }
934 if (i == 0)
935 numDeleted[i] = 0;
936 else
937 numDeleted[i] = numDeleted[i - 1];
938 }
939
940 // Return a cached empty ArrayAttr.
941 ArrayAttr eEmpty;
942 auto getEmpty = [&]() {
943 if (!eEmpty)
944 eEmpty = ArrayAttr::get(context, {});
945 return eEmpty;
946 };
947
948 // Update the domain indices.
949 SmallVector<Attribute> newDomainInfo;
950 newDomainInfo.reserve(portIndices.size() - portIndices.count());
951 for (size_t i = 0, e = portIndices.size(); i != e; ++i) {
952 // If this port is deleted, then do nothing.
953 if (portIndices.test(i))
954 continue;
955 // If there is no domain info, then add an empty attribute.
956 if (domainInfoAttr.empty()) {
957 newDomainInfo.push_back(getEmpty());
958 continue;
959 }
960 auto attr = domainInfoAttr[i];
961 // If this is a Domain Type (indicating domain kind) or an empty domain.
962 auto domains = dyn_cast<ArrayAttr>(attr);
963 if (!domains || domains.empty()) {
964 newDomainInfo.push_back(attr);
965 continue;
966 }
967 // This contains indexes to domain ports. Update them.
968 SmallVector<Attribute> newDomains;
969 for (auto domain : domains) {
970 // If the domain port was deleted, drop the association.
971 auto oldIdx = cast<IntegerAttr>(domain).getUInt();
972 if (portIndices.test(oldIdx))
973 continue;
974 // If the new index is the same, do nothing.
975 auto newIdx = oldIdx - numDeleted[oldIdx];
976 if (oldIdx == newIdx) {
977 newDomainInfo.push_back(attr);
978 continue;
979 }
980 // Update the index.
981 newDomains.push_back(IntegerAttr::get(
982 IntegerType::get(context, 32, IntegerType::Unsigned), newIdx));
983 }
984 newDomainInfo.push_back(ArrayAttr::get(context, newDomains));
985 }
986
987 return ArrayAttr::get(context, newDomainInfo);
988}
989
990/// Erases the ports that have their corresponding bit set in `portIndices`.
991static void erasePorts(FModuleLike op, const llvm::BitVector &portIndices) {
992 if (portIndices.none())
993 return;
994
995 // Drop the direction markers for dead ports.
996 ArrayRef<bool> portDirections = op.getPortDirectionsAttr().asArrayRef();
997 ArrayRef<Attribute> portNames = op.getPortNames();
998 ArrayRef<Attribute> portTypes = op.getPortTypes();
999 ArrayRef<Attribute> portAnnos = op.getPortAnnotations();
1000 ArrayRef<Attribute> portSyms = op.getPortSymbols();
1001 ArrayRef<Attribute> portLocs = op.getPortLocations();
1002 ArrayRef<Attribute> portDomains = op.getDomainInfo();
1003 auto numPorts = op.getNumPorts();
1004 (void)numPorts;
1005 assert(portDirections.size() == numPorts);
1006 assert(portNames.size() == numPorts);
1007 assert(portAnnos.size() == numPorts || portAnnos.empty());
1008 assert(portTypes.size() == numPorts);
1009 assert(portSyms.size() == numPorts || portSyms.empty());
1010 assert(portLocs.size() == numPorts);
1011 assert(portDomains.size() == numPorts || portDomains.empty());
1012
1013 SmallVector<bool> newPortDirections =
1014 removeElementsAtIndices<bool>(portDirections, portIndices);
1015 SmallVector<Attribute> newPortNames, newPortTypes, newPortAnnos, newPortSyms,
1016 newPortLocs;
1017 newPortNames = removeElementsAtIndices(portNames, portIndices);
1018 newPortTypes = removeElementsAtIndices(portTypes, portIndices);
1019 newPortAnnos = removeElementsAtIndices(portAnnos, portIndices);
1020 newPortSyms = removeElementsAtIndices(portSyms, portIndices);
1021 newPortLocs = removeElementsAtIndices(portLocs, portIndices);
1022
1023 op->setAttr("portDirections",
1024 direction::packAttribute(op.getContext(), newPortDirections));
1025 op->setAttr("portNames", ArrayAttr::get(op.getContext(), newPortNames));
1026 op->setAttr("portAnnotations", ArrayAttr::get(op.getContext(), newPortAnnos));
1027 op->setAttr("portTypes", ArrayAttr::get(op.getContext(), newPortTypes));
1028 FModuleLike::fixupPortSymsArray(newPortSyms, op.getContext());
1029 op->setAttr("portSymbols", ArrayAttr::get(op.getContext(), newPortSyms));
1030 op->setAttr("portLocations", ArrayAttr::get(op.getContext(), newPortLocs));
1031 op->setAttr("domainInfo",
1032 fixDomainInfoDeletions(op.getContext(), op.getDomainInfoAttr(),
1033 portIndices, /*supportsEmptyAttr=*/true));
1034}
1035
1036template <typename T>
1037static void eraseInternalPaths(T op, const llvm::BitVector &portIndices) {
1038 // Fixup internalPaths array.
1039 auto internalPaths = op.getInternalPaths();
1040 if (!internalPaths)
1041 return;
1042
1043 auto newPaths =
1044 removeElementsAtIndices(internalPaths->getValue(), portIndices);
1045
1046 // Drop if all-empty, otherwise set to new array.
1047 auto empty = llvm::all_of(newPaths, [](Attribute attr) {
1048 return !cast<InternalPathAttr>(attr).getPath();
1049 });
1050 if (empty)
1051 op.removeInternalPathsAttr();
1052 else
1053 op.setInternalPathsAttr(ArrayAttr::get(op.getContext(), newPaths));
1054}
1055
1056void FExtModuleOp::erasePorts(const llvm::BitVector &portIndices) {
1057 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1058 eraseInternalPaths(*this, portIndices);
1059}
1060
1061void FIntModuleOp::erasePorts(const llvm::BitVector &portIndices) {
1062 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1063 eraseInternalPaths(*this, portIndices);
1064}
1065
1066void FMemModuleOp::erasePorts(const llvm::BitVector &portIndices) {
1067 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1068}
1069
1070void FModuleOp::erasePorts(const llvm::BitVector &portIndices) {
1071 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
1072 getBodyBlock()->eraseArguments(portIndices);
1073}
1074
1075/// Inserts the given ports. The insertion indices are expected to be in order.
1076/// Insertion occurs in-order, such that ports with the same insertion index
1077/// appear in the module in the same order they appeared in the list.
1078void FModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1079 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
1080
1081 // Insert the block arguments.
1082 auto *body = getBodyBlock();
1083 for (size_t i = 0, e = ports.size(); i < e; ++i) {
1084 // Block arguments are inserted one at a time, so for each argument we
1085 // insert we have to increase the index by 1.
1086 auto &[index, port] = ports[i];
1087 body->insertArgument(index + i, port.type, port.loc);
1088 }
1089}
1090
1091void FExtModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1092 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports,
1093 /*supportsInternalPaths=*/true);
1094}
1095
1096void FIntModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1097 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports,
1098 /*supportsInternalPaths=*/true);
1099}
1100
1101/// Inserts the given ports. The insertion indices are expected to be in order.
1102/// Insertion occurs in-order, such that ports with the same insertion index
1103/// appear in the module in the same order they appeared in the list.
1104void FMemModuleOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
1105 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
1106}
1107
1108template <typename OpTy>
1109void buildModuleLike(OpBuilder &builder, OperationState &result,
1110 StringAttr name, ArrayRef<PortInfo> ports) {
1111 // Add an attribute for the name.
1112 auto &properties = result.getOrAddProperties<typename OpTy::Properties>();
1113 properties.setSymName(name);
1114
1115 // Record the names of the arguments if present.
1116 SmallVector<Direction, 4> portDirections;
1117 SmallVector<Attribute, 4> portNames;
1118 SmallVector<Attribute, 4> portTypes;
1119 SmallVector<Attribute, 4> portSyms;
1120 SmallVector<Attribute, 4> portLocs;
1121 SmallVector<Attribute, 4> portDomains;
1122 for (const auto &port : ports) {
1123 portDirections.push_back(port.direction);
1124 portNames.push_back(port.name);
1125 portTypes.push_back(TypeAttr::get(port.type));
1126 portSyms.push_back(port.sym);
1127 portLocs.push_back(port.loc);
1128 portDomains.push_back(port.domains);
1129 }
1130 if (llvm::all_of(portDomains, [](Attribute attr) {
1131 if (!attr)
1132 return true;
1133 if (auto arrayAttr = dyn_cast<ArrayAttr>(attr))
1134 return arrayAttr.empty();
1135 return false;
1136 }))
1137 portDomains.clear();
1138
1139 FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
1140
1141 // Both attributes are added, even if the module has no ports.
1142 properties.setPortDirections(
1143 direction::packAttribute(builder.getContext(), portDirections));
1144 properties.setPortNames(builder.getArrayAttr(portNames));
1145 properties.setPortTypes(builder.getArrayAttr(portTypes));
1146 properties.setPortSymbols(builder.getArrayAttr(portSyms));
1147 properties.setPortLocations(builder.getArrayAttr(portLocs));
1148 properties.setDomainInfo(builder.getArrayAttr(portDomains));
1149
1150 result.addRegion();
1151}
1152
1153template <typename OpTy>
1154static void buildModule(OpBuilder &builder, OperationState &result,
1155 StringAttr name, ArrayRef<PortInfo> ports,
1156 ArrayAttr annotations, ArrayAttr layers) {
1157 buildModuleLike<OpTy>(builder, result, name, ports);
1158 auto &properties = result.getOrAddProperties<typename OpTy::Properties>();
1159 // Annotations.
1160 if (!annotations)
1161 annotations = builder.getArrayAttr({});
1162 properties.setAnnotations(annotations);
1163
1164 // Port annotations. lack of *any* port annotations is represented by an empty
1165 // `portAnnotations` array as a shorthand.
1166 SmallVector<Attribute, 4> portAnnotations;
1167 for (const auto &port : ports)
1168 portAnnotations.push_back(port.annotations.getArrayAttr());
1169 if (llvm::all_of(portAnnotations, [](Attribute attr) {
1170 return cast<ArrayAttr>(attr).empty();
1171 }))
1172 portAnnotations.clear();
1173 properties.setPortAnnotations(builder.getArrayAttr(portAnnotations));
1174
1175 // Layers.
1176 if (!layers)
1177 layers = builder.getArrayAttr({});
1178 properties.setLayers(layers);
1179}
1180
1181template <typename OpTy>
1182static void buildClass(OpBuilder &builder, OperationState &result,
1183 StringAttr name, ArrayRef<PortInfo> ports) {
1184 return buildModuleLike<OpTy>(builder, result, name, ports);
1185}
1186
1187void FModuleOp::build(OpBuilder &builder, OperationState &result,
1188 StringAttr name, ConventionAttr convention,
1189 ArrayRef<PortInfo> ports, ArrayAttr annotations,
1190 ArrayAttr layers) {
1191 buildModule<FModuleOp>(builder, result, name, ports, annotations, layers);
1192 auto &properties = result.getOrAddProperties<Properties>();
1193 properties.setConvention(convention);
1194
1195 // Create a region and a block for the body.
1196 auto *bodyRegion = result.regions[0].get();
1197 Block *body = new Block();
1198 bodyRegion->push_back(body);
1199
1200 // Add arguments to the body block.
1201 for (auto &elt : ports)
1202 body->addArgument(elt.type, elt.loc);
1203}
1204
1205void FExtModuleOp::build(OpBuilder &builder, OperationState &result,
1206 StringAttr name, ConventionAttr convention,
1207 ArrayRef<PortInfo> ports, ArrayAttr knownLayers,
1208 StringRef defnameAttr, ArrayAttr annotations,
1209 ArrayAttr parameters, ArrayAttr internalPaths,
1210 ArrayAttr layers) {
1211 buildModule<FExtModuleOp>(builder, result, name, ports, annotations, layers);
1212 auto &properties = result.getOrAddProperties<Properties>();
1213 properties.setConvention(convention);
1214 if (!knownLayers)
1215 knownLayers = builder.getArrayAttr({});
1216 properties.setKnownLayers(knownLayers);
1217 if (!defnameAttr.empty())
1218 properties.setDefname(builder.getStringAttr(defnameAttr));
1219 if (!parameters)
1220 parameters = builder.getArrayAttr({});
1221 properties.setParameters(parameters);
1222 if (internalPaths && !internalPaths.empty())
1223 properties.setInternalPaths(internalPaths);
1224}
1225
1226void FIntModuleOp::build(OpBuilder &builder, OperationState &result,
1227 StringAttr name, ArrayRef<PortInfo> ports,
1228 StringRef intrinsicNameStr, ArrayAttr annotations,
1229 ArrayAttr parameters, ArrayAttr internalPaths,
1230 ArrayAttr layers) {
1231 buildModule<FIntModuleOp>(builder, result, name, ports, annotations, layers);
1232 auto &properties = result.getOrAddProperties<Properties>();
1233 properties.setIntrinsic(builder.getStringAttr(intrinsicNameStr));
1234 if (!parameters)
1235 parameters = builder.getArrayAttr({});
1236 properties.setParameters(parameters);
1237 if (internalPaths && !internalPaths.empty())
1238 properties.setInternalPaths(internalPaths);
1239}
1240
1241void FMemModuleOp::build(OpBuilder &builder, OperationState &result,
1242 StringAttr name, ArrayRef<PortInfo> ports,
1243 uint32_t numReadPorts, uint32_t numWritePorts,
1244 uint32_t numReadWritePorts, uint32_t dataWidth,
1245 uint32_t maskBits, uint32_t readLatency,
1246 uint32_t writeLatency, uint64_t depth, RUWBehavior ruw,
1247 ArrayAttr annotations, ArrayAttr layers) {
1248 auto *context = builder.getContext();
1249 buildModule<FMemModuleOp>(builder, result, name, ports, annotations, layers);
1250 auto ui32Type = IntegerType::get(context, 32, IntegerType::Unsigned);
1251 auto ui64Type = IntegerType::get(context, 64, IntegerType::Unsigned);
1252 auto &properties = result.getOrAddProperties<Properties>();
1253 properties.setNumReadPorts(IntegerAttr::get(ui32Type, numReadPorts));
1254 properties.setNumWritePorts(IntegerAttr::get(ui32Type, numWritePorts));
1255 properties.setNumReadWritePorts(
1256 IntegerAttr::get(ui32Type, numReadWritePorts));
1257 properties.setDataWidth(IntegerAttr::get(ui32Type, dataWidth));
1258 properties.setMaskBits(IntegerAttr::get(ui32Type, maskBits));
1259 properties.setReadLatency(IntegerAttr::get(ui32Type, readLatency));
1260 properties.setWriteLatency(IntegerAttr::get(ui32Type, writeLatency));
1261 properties.setDepth(IntegerAttr::get(ui64Type, depth));
1262 properties.setExtraPorts(ArrayAttr::get(context, {}));
1263 properties.setRuw(RUWBehaviorAttr::get(context, ruw));
1264}
1265
1266/// Print a list of module ports in the following form:
1267/// in x: !firrtl.uint<1> [{class = "DontTouch}], out "_port": !firrtl.uint<2>
1268///
1269/// When there is no block specified, the port names print as MLIR identifiers,
1270/// wrapping in quotes if not legal to print as-is. When there is no block
1271/// specified, this function always return false, indicating that there was no
1272/// issue printing port names.
1273///
1274/// If there is a block specified, then port names will be printed as SSA
1275/// values. If there is a reason the printed SSA values can't match the true
1276/// port name, then this function will return true. When this happens, the
1277/// caller should print the port names as a part of the `attr-dict`.
1278static bool
1279printModulePorts(OpAsmPrinter &p, Block *block, ArrayRef<bool> portDirections,
1280 ArrayRef<Attribute> portNames, ArrayRef<Attribute> portTypes,
1281 ArrayRef<Attribute> portAnnotations,
1282 ArrayRef<Attribute> portSyms, ArrayRef<Attribute> portLocs,
1283 ArrayRef<Attribute> domainInfo) {
1284 // When printing port names as SSA values, we can fail to print them
1285 // identically.
1286 bool printedNamesDontMatch = false;
1287
1288 mlir::OpPrintingFlags flags;
1289
1290 // If we are printing the ports as block arguments the op must have a first
1291 // block.
1292 SmallString<32> resultNameStr;
1293 DenseMap<unsigned, std::string> domainPortNames;
1294 p << '(';
1295 for (unsigned i = 0, e = portTypes.size(); i < e; ++i) {
1296 if (i > 0)
1297 p << ", ";
1298
1299 // Print the port direction.
1300 p << direction::get(portDirections[i]) << " ";
1301
1302 // Print the port name. If there is a valid block, we print it as a block
1303 // argument.
1304 auto portType = cast<TypeAttr>(portTypes[i]).getValue();
1305 if (block) {
1306 // Get the printed format for the argument name.
1307 resultNameStr.clear();
1308 llvm::raw_svector_ostream tmpStream(resultNameStr);
1309 p.printOperand(block->getArgument(i), tmpStream);
1310 // If the name wasn't printable in a way that agreed with portName, make
1311 // sure to print out an explicit portNames attribute.
1312 auto portName = cast<StringAttr>(portNames[i]).getValue();
1313 if (tmpStream.str().drop_front() != portName)
1314 printedNamesDontMatch = true;
1315 p << tmpStream.str();
1316 if (isa<DomainType>(portType))
1317 domainPortNames[i] = tmpStream.str();
1318 } else {
1319 auto name = cast<StringAttr>(portNames[i]).getValue();
1320 p.printKeywordOrString(name);
1321 if (isa<DomainType>(portType))
1322 domainPortNames[i] = name.str();
1323 }
1324
1325 // Print the port type.
1326 p << ": ";
1327 p.printType(portType);
1328
1329 // Print the optional port symbol.
1330 if (!portSyms.empty()) {
1331 if (!cast<hw::InnerSymAttr>(portSyms[i]).empty()) {
1332 p << " sym ";
1333 cast<hw::InnerSymAttr>(portSyms[i]).print(p);
1334 }
1335 }
1336
1337 // Print domain information.
1338 if (!domainInfo.empty()) {
1339 if (auto domainKind = dyn_cast<FlatSymbolRefAttr>(domainInfo[i])) {
1340 p << " of " << domainKind;
1341 } else {
1342 auto domains = cast<ArrayAttr>(domainInfo[i]);
1343 if (!domains.empty()) {
1344 p << " domains [";
1345 llvm::interleaveComma(domains, p, [&](Attribute attr) {
1346 p << domainPortNames[cast<IntegerAttr>(attr).getUInt()];
1347 });
1348 p << "]";
1349 }
1350 }
1351 }
1352
1353 // Print the port specific annotations. The port annotations array will be
1354 // empty if there are none.
1355 if (!portAnnotations.empty() &&
1356 !cast<ArrayAttr>(portAnnotations[i]).empty()) {
1357 p << " ";
1358 p.printAttribute(portAnnotations[i]);
1359 }
1360
1361 // Print the port location.
1362 // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
1363 // even if they are not printed. This will have to be fixed upstream. For
1364 // now, use what was specified on the command line.
1365 if (flags.shouldPrintDebugInfo() && !portLocs.empty())
1366 p.printOptionalLocationSpecifier(cast<LocationAttr>(portLocs[i]));
1367 }
1368
1369 p << ')';
1370 return printedNamesDontMatch;
1371}
1372
1373/// Parse a list of module ports. If port names are SSA identifiers, then this
1374/// will populate `entryArgs`.
1375static ParseResult parseModulePorts(
1376 OpAsmParser &parser, bool hasSSAIdentifiers, bool supportsSymbols,
1377 bool supportsDomains, SmallVectorImpl<OpAsmParser::Argument> &entryArgs,
1378 SmallVectorImpl<Direction> &portDirections,
1379 SmallVectorImpl<Attribute> &portNames,
1380 SmallVectorImpl<Attribute> &portTypes,
1381 SmallVectorImpl<Attribute> &portAnnotations,
1382 SmallVectorImpl<Attribute> &portSyms, SmallVectorImpl<Attribute> &portLocs,
1383 SmallVectorImpl<Attribute> &domains) {
1384 auto *context = parser.getContext();
1385
1386 // Mapping of domain name to port index.
1387 DenseMap<Attribute, size_t> domainIndex;
1388
1389 auto parseArgument = [&]() -> ParseResult {
1390 // Parse port direction.
1391 if (succeeded(parser.parseOptionalKeyword("out")))
1392 portDirections.push_back(Direction::Out);
1393 else if (succeeded(parser.parseKeyword("in", " or 'out'")))
1394 portDirections.push_back(Direction::In);
1395 else
1396 return failure();
1397
1398 // This is the location or the port declaration in the IR. If there is no
1399 // other location information, we use this to point to the MLIR.
1400 llvm::SMLoc irLoc;
1401
1402 if (hasSSAIdentifiers) {
1403 OpAsmParser::Argument arg;
1404 if (parser.parseArgument(arg))
1405 return failure();
1406 entryArgs.push_back(arg);
1407
1408 // The name of an argument is of the form "%42" or "%id", and since
1409 // parsing succeeded, we know it always has one character.
1410 assert(arg.ssaName.name.size() > 1 && arg.ssaName.name[0] == '%' &&
1411 "Unknown MLIR name");
1412 if (isdigit(arg.ssaName.name[1]))
1413 portNames.push_back(StringAttr::get(context, ""));
1414 else
1415 portNames.push_back(
1416 StringAttr::get(context, arg.ssaName.name.drop_front()));
1417
1418 // Store the location of the SSA name.
1419 irLoc = arg.ssaName.location;
1420
1421 } else {
1422 // Parse the port name.
1423 irLoc = parser.getCurrentLocation();
1424 std::string portName;
1425 if (parser.parseKeywordOrString(&portName))
1426 return failure();
1427 portNames.push_back(StringAttr::get(context, portName));
1428 }
1429
1430 // Parse the port type.
1431 Type portType;
1432 if (parser.parseColonType(portType))
1433 return failure();
1434 portTypes.push_back(TypeAttr::get(portType));
1435 if (isa<DomainType>(portType))
1436 domainIndex[portNames.back()] = portNames.size() - 1;
1437
1438 if (hasSSAIdentifiers)
1439 entryArgs.back().type = portType;
1440
1441 // Parse the optional port symbol.
1442 if (supportsSymbols) {
1443 hw::InnerSymAttr innerSymAttr;
1444 if (succeeded(parser.parseOptionalKeyword("sym"))) {
1445 NamedAttrList dummyAttrs;
1446 if (parser.parseCustomAttributeWithFallback(
1447 innerSymAttr, ::mlir::Type{},
1449 return ::mlir::failure();
1450 }
1451 }
1452 portSyms.push_back(innerSymAttr);
1453 }
1454
1455 // Parse optional port domain information if it exists.
1456 Attribute domainsAttr;
1457 SmallVector<Attribute> portDomains;
1458 if (supportsDomains) {
1459 if (isa<DomainType>(portType)) {
1460 if (parser.parseKeyword("of"))
1461 return failure();
1462 StringAttr domainKind;
1463 if (parser.parseSymbolName(domainKind))
1464 return failure();
1465 domainsAttr = FlatSymbolRefAttr::get(context, domainKind);
1466 } else if (succeeded(parser.parseOptionalKeyword("domains"))) {
1467 auto result = parser.parseCommaSeparatedList(
1468 OpAsmParser::Delimiter::Square, [&]() -> ParseResult {
1469 StringAttr argName;
1470 if (hasSSAIdentifiers) {
1471 OpAsmParser::Argument arg;
1472 if (parser.parseArgument(arg))
1473 return failure();
1474 argName =
1475 StringAttr::get(context, arg.ssaName.name.drop_front());
1476 } else {
1477 std::string portName;
1478 if (parser.parseKeywordOrString(&portName))
1479 return failure();
1480 argName = StringAttr::get(context, portName);
1481 }
1482
1483 auto index = domainIndex.find(argName);
1484 if (index == domainIndex.end()) {
1485 parser.emitError(irLoc)
1486 << "domain name '" << argName << "' not found";
1487 return failure();
1488 }
1489 portDomains.push_back(IntegerAttr::get(
1490 IntegerType::get(context, 32, IntegerType::Unsigned),
1491 index->second));
1492 return success();
1493 });
1494 if (failed(result))
1495 return failure();
1496 domainsAttr = parser.getBuilder().getArrayAttr(portDomains);
1497 }
1498 }
1499 if (!domainsAttr)
1500 domainsAttr = parser.getBuilder().getArrayAttr({});
1501 domains.push_back(domainsAttr);
1502
1503 // Parse the port annotations.
1504 ArrayAttr annos;
1505 auto parseResult = parser.parseOptionalAttribute(annos);
1506 if (!parseResult.has_value())
1507 annos = parser.getBuilder().getArrayAttr({});
1508 else if (failed(*parseResult))
1509 return failure();
1510 portAnnotations.push_back(annos);
1511
1512 // Parse the optional port location.
1513 std::optional<Location> maybeLoc;
1514 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
1515 return failure();
1516 Location loc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
1517 portLocs.push_back(loc);
1518 if (hasSSAIdentifiers)
1519 entryArgs.back().sourceLoc = loc;
1520
1521 return success();
1522 };
1523
1524 // Parse all ports.
1525 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1526 parseArgument);
1527}
1528
1529/// Print a paramter list for a module or instance.
1530static void printParameterList(OpAsmPrinter &p, Operation *op,
1531 ArrayAttr parameters) {
1532 if (!parameters || parameters.empty())
1533 return;
1534
1535 p << '<';
1536 llvm::interleaveComma(parameters, p, [&](Attribute param) {
1537 auto paramAttr = cast<ParamDeclAttr>(param);
1538 p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1539 if (auto value = paramAttr.getValue()) {
1540 p << " = ";
1541 p.printAttributeWithoutType(value);
1542 }
1543 });
1544 p << '>';
1545}
1546
1547static void printFModuleLikeOp(OpAsmPrinter &p, FModuleLike op) {
1548 p << " ";
1549
1550 // Print the visibility of the module.
1551 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
1552 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
1553 p << visibility.getValue() << ' ';
1554
1555 // Print the operation and the function name.
1556 p.printSymbolName(op.getModuleName());
1557
1558 // Print the parameter list (if non-empty).
1559 printParameterList(p, op, op->getAttrOfType<ArrayAttr>("parameters"));
1560
1561 // Both modules and external modules have a body, but it is always empty for
1562 // external modules.
1563 Block *body = nullptr;
1564 if (!op->getRegion(0).empty())
1565 body = &op->getRegion(0).front();
1566
1567 auto needPortNamesAttr = printModulePorts(
1568 p, body, op.getPortDirectionsAttr(), op.getPortNames(), op.getPortTypes(),
1569 op.getPortAnnotations(), op.getPortSymbols(), op.getPortLocations(),
1570 op.getDomainInfo());
1571
1572 SmallVector<StringRef, 13> omittedAttrs = {
1573 "sym_name", "portDirections", "portTypes",
1574 "portAnnotations", "portSymbols", "portLocations",
1575 "parameters", visibilityAttrName, "domainInfo"};
1576
1577 if (op.getConvention() == Convention::Internal)
1578 omittedAttrs.push_back("convention");
1579
1580 // We can omit the portNames if they were able to be printed as properly as
1581 // block arguments.
1582 if (!needPortNamesAttr)
1583 omittedAttrs.push_back("portNames");
1584
1585 // If there are no annotations we can omit the empty array.
1586 if (op->getAttrOfType<ArrayAttr>("annotations").empty())
1587 omittedAttrs.push_back("annotations");
1588
1589 // If there are no known layers, then omit the empty array.
1590 if (auto knownLayers = op->getAttrOfType<ArrayAttr>("knownLayers"))
1591 if (knownLayers.empty())
1592 omittedAttrs.push_back("knownLayers");
1593
1594 // If there are no enabled layers, then omit the empty array.
1595 if (auto layers = op->getAttrOfType<ArrayAttr>("layers"))
1596 if (layers.empty())
1597 omittedAttrs.push_back("layers");
1598
1599 p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
1600}
1601
1602void FExtModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1603
1604void FIntModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1605
1606void FMemModuleOp::print(OpAsmPrinter &p) { printFModuleLikeOp(p, *this); }
1607
1608void FModuleOp::print(OpAsmPrinter &p) {
1609 printFModuleLikeOp(p, *this);
1610
1611 // Print the body if this is not an external function. Since this block does
1612 // not have terminators, printing the terminator actually just prints the last
1613 // operation.
1614 Region &fbody = getBody();
1615 if (!fbody.empty()) {
1616 p << " ";
1617 p.printRegion(fbody, /*printEntryBlockArgs=*/false,
1618 /*printBlockTerminators=*/true);
1619 }
1620}
1621
1622/// Parse an parameter list if present.
1623/// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1624/// parameter-decl ::= identifier `:` type
1625/// parameter-decl ::= identifier `:` type `=` attribute
1626///
1627static ParseResult
1628parseOptionalParameters(OpAsmParser &parser,
1629 SmallVectorImpl<Attribute> &parameters) {
1630
1631 return parser.parseCommaSeparatedList(
1632 OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1633 std::string name;
1634 Type type;
1635 Attribute value;
1636
1637 if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1638 return failure();
1639
1640 // Parse the default value if present.
1641 if (succeeded(parser.parseOptionalEqual())) {
1642 if (parser.parseAttribute(value, type))
1643 return failure();
1644 }
1645
1646 auto &builder = parser.getBuilder();
1647 parameters.push_back(ParamDeclAttr::get(
1648 builder.getContext(), builder.getStringAttr(name), type, value));
1649 return success();
1650 });
1651}
1652
1653/// Shim to use with assemblyFormat, custom<ParameterList>.
1654static ParseResult parseParameterList(OpAsmParser &parser,
1655 ArrayAttr &parameters) {
1656 SmallVector<Attribute> parseParameters;
1657 if (failed(parseOptionalParameters(parser, parseParameters)))
1658 return failure();
1659
1660 parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1661
1662 return success();
1663}
1664
1665template <typename Properties, typename = void>
1666struct HasParameters : std::false_type {};
1667
1668template <typename Properties>
1670 Properties, std::void_t<decltype(std::declval<Properties>().parameters)>>
1671 : std::true_type {};
1672
1673template <typename OpTy>
1674static ParseResult parseFModuleLikeOp(OpAsmParser &parser,
1675 OperationState &result,
1676 bool hasSSAIdentifiers) {
1677 auto *context = result.getContext();
1678 auto &builder = parser.getBuilder();
1679 using Properties = typename OpTy::Properties;
1680 auto &properties = result.getOrAddProperties<Properties>();
1681
1682 // TODO: this should be using properties.
1683 // Parse the visibility attribute.
1684 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
1685
1686 // Parse the name as a symbol.
1687 StringAttr nameAttr;
1688 if (parser.parseSymbolName(nameAttr))
1689 return failure();
1690 properties.setSymName(nameAttr);
1691
1692 // Parse optional parameters.
1693 if constexpr (HasParameters<Properties>::value) {
1694 SmallVector<Attribute, 4> parameters;
1695 if (parseOptionalParameters(parser, parameters))
1696 return failure();
1697 properties.setParameters(builder.getArrayAttr(parameters));
1698 }
1699
1700 // Parse the module ports.
1701 SmallVector<OpAsmParser::Argument> entryArgs;
1702 SmallVector<Direction, 4> portDirections;
1703 SmallVector<Attribute, 4> portNames;
1704 SmallVector<Attribute, 4> portTypes;
1705 SmallVector<Attribute, 4> portAnnotations;
1706 SmallVector<Attribute, 4> portSyms;
1707 SmallVector<Attribute, 4> portLocs;
1708 SmallVector<Attribute, 4> domains;
1709 if (parseModulePorts(parser, hasSSAIdentifiers, /*supportsSymbols=*/true,
1710 /*supportsDomains=*/true, entryArgs, portDirections,
1711 portNames, portTypes, portAnnotations, portSyms,
1712 portLocs, domains))
1713 return failure();
1714
1715 // If module attributes are present, parse them.
1716 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1717 return failure();
1718
1719 assert(portNames.size() == portTypes.size());
1720
1721 // Record the argument and result types as an attribute. This is necessary
1722 // for external modules.
1723
1724 // Add port directions.
1725 properties.setPortDirections(
1726 direction::packAttribute(context, portDirections));
1727
1728 // Add port names.
1729 properties.setPortNames(builder.getArrayAttr(portNames));
1730
1731 // Add the port types.
1732 properties.setPortTypes(ArrayAttr::get(context, portTypes));
1733
1734 // Add the port annotations.
1735 // If there are no portAnnotations, don't add the attribute.
1736 if (llvm::any_of(portAnnotations, [&](Attribute anno) {
1737 return !cast<ArrayAttr>(anno).empty();
1738 }))
1739 properties.setPortAnnotations(ArrayAttr::get(context, portAnnotations));
1740 else
1741 properties.setPortAnnotations(builder.getArrayAttr({}));
1742
1743 // Add port symbols.
1744 FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
1745 properties.setPortSymbols(builder.getArrayAttr(portSyms));
1746
1747 // Add port locations.
1748 properties.setPortLocations(ArrayAttr::get(context, portLocs));
1749
1750 // The annotations attribute is always present, but not printed when empty.
1751 properties.setAnnotations(builder.getArrayAttr({}));
1752
1753 // Add domains.
1754 properties.setDomainInfo(ArrayAttr::get(context, domains));
1755
1756 // Parse the optional function body.
1757 auto *body = result.addRegion();
1758
1759 if (hasSSAIdentifiers) {
1760 if (parser.parseRegion(*body, entryArgs))
1761 return failure();
1762 if (body->empty())
1763 body->push_back(new Block());
1764 }
1765 return success();
1766}
1767
1768ParseResult FModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1769 if (parseFModuleLikeOp<FModuleOp>(parser, result,
1770 /*hasSSAIdentifiers=*/true))
1771 return failure();
1772 auto &properties = result.getOrAddProperties<Properties>();
1773 properties.setConvention(
1774 ConventionAttr::get(result.getContext(), Convention::Internal));
1775 properties.setLayers(ArrayAttr::get(parser.getContext(), {}));
1776 return success();
1777}
1778
1779ParseResult FExtModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1780 if (parseFModuleLikeOp<FExtModuleOp>(parser, result,
1781 /*hasSSAIdentifiers=*/false))
1782 return failure();
1783 auto &properties = result.getOrAddProperties<Properties>();
1784 properties.setConvention(
1785 ConventionAttr::get(result.getContext(), Convention::Internal));
1786 properties.setKnownLayers(ArrayAttr::get(result.getContext(), {}));
1787 return success();
1788}
1789
1790ParseResult FIntModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1791 return parseFModuleLikeOp<FIntModuleOp>(parser, result,
1792 /*hasSSAIdentifiers=*/false);
1793}
1794
1795ParseResult FMemModuleOp::parse(OpAsmParser &parser, OperationState &result) {
1796 return parseFModuleLikeOp<FMemModuleOp>(parser, result,
1797 /*hasSSAIdentifiers=*/false);
1798}
1799
1800LogicalResult FModuleOp::verify() {
1801 // Verify the block arguments.
1802 auto *body = getBodyBlock();
1803 auto portTypes = getPortTypes();
1804 auto portLocs = getPortLocations();
1805 auto numPorts = portTypes.size();
1806
1807 // Verify that we have the correct number of block arguments.
1808 if (body->getNumArguments() != numPorts)
1809 return emitOpError("entry block must have ")
1810 << numPorts << " arguments to match module signature";
1811
1812 // Verify the block arguments' types and locations match our attributes.
1813 for (auto [arg, type, loc] : zip(body->getArguments(), portTypes, portLocs)) {
1814 if (arg.getType() != cast<TypeAttr>(type).getValue())
1815 return emitOpError("block argument types should match signature types");
1816 if (arg.getLoc() != cast<LocationAttr>(loc))
1817 return emitOpError(
1818 "block argument locations should match signature locations");
1819 }
1820
1821 return success();
1822}
1823
1824static LogicalResult
1825verifyInternalPaths(FModuleLike op,
1826 std::optional<::mlir::ArrayAttr> internalPaths) {
1827 if (!internalPaths)
1828 return success();
1829
1830 // If internal paths are present, should cover all ports.
1831 if (internalPaths->size() != op.getNumPorts())
1832 return op.emitError("module has inconsistent internal path array with ")
1833 << internalPaths->size() << " entries for " << op.getNumPorts()
1834 << " ports";
1835
1836 // No internal paths for non-ref-type ports.
1837 for (auto [idx, path, typeattr] : llvm::enumerate(
1838 internalPaths->getAsRange<InternalPathAttr>(), op.getPortTypes())) {
1839 if (path.getPath() &&
1840 !type_isa<RefType>(cast<TypeAttr>(typeattr).getValue())) {
1841 auto diag =
1842 op.emitError("module has internal path for non-ref-type port ")
1843 << op.getPortNameAttr(idx);
1844 return diag.attachNote(op.getPortLocation(idx)) << "this port";
1845 }
1846 }
1847
1848 return success();
1849}
1850
1851LogicalResult FExtModuleOp::verify() {
1852 if (failed(verifyInternalPaths(*this, getInternalPaths())))
1853 return failure();
1854
1855 auto params = getParameters();
1856
1857 auto checkParmValue = [&](Attribute elt) -> bool {
1858 auto param = cast<ParamDeclAttr>(elt);
1859 auto value = param.getValue();
1860 if (isa<IntegerAttr, StringAttr, FloatAttr, hw::ParamVerbatimAttr>(value))
1861 return true;
1862 emitError() << "has unknown extmodule parameter value '"
1863 << param.getName().getValue() << "' = " << value;
1864 return false;
1865 };
1866
1867 if (!llvm::all_of(params, checkParmValue))
1868 return failure();
1869
1870 // Verify that any mentioned layers are marked as known.
1871 LayerSet known;
1872 known.insert_range(getKnownLayersAttr().getAsRange<SymbolRefAttr>());
1873
1874 LayerSet referenced;
1875 referenced.insert_range(getLayersAttr().getAsRange<SymbolRefAttr>());
1876 for (auto attr : getPortTypes()) {
1877 auto type = cast<TypeAttr>(attr).getValue();
1878 if (auto refType = type_dyn_cast<RefType>(type))
1879 if (auto layer = refType.getLayer())
1880 referenced.insert(layer);
1881 }
1882
1883 return checkLayerCompatibility(getOperation(), referenced, known,
1884 "references unknown layers", "unknown layers");
1885}
1886
1887LogicalResult FIntModuleOp::verify() {
1888 if (failed(verifyInternalPaths(*this, getInternalPaths())))
1889 return failure();
1890
1891 auto params = getParameters();
1892 if (params.empty())
1893 return success();
1894
1895 auto checkParmValue = [&](Attribute elt) -> bool {
1896 auto param = cast<ParamDeclAttr>(elt);
1897 auto value = param.getValue();
1898 if (isa<IntegerAttr, StringAttr, FloatAttr>(value))
1899 return true;
1900 emitError() << "has unknown intmodule parameter value '"
1901 << param.getName().getValue() << "' = " << value;
1902 return false;
1903 };
1904
1905 if (!llvm::all_of(params, checkParmValue))
1906 return failure();
1907
1908 return success();
1909}
1910
1911static LogicalResult verifyProbeType(RefType refType, Location loc,
1912 CircuitOp circuitOp,
1913 SymbolTableCollection &symbolTable,
1914 Twine start) {
1915 auto layer = refType.getLayer();
1916 if (!layer)
1917 return success();
1918 auto *layerOp = symbolTable.lookupSymbolIn(circuitOp, layer);
1919 if (!layerOp)
1920 return emitError(loc) << start << " associated with layer '" << layer
1921 << "', but this layer was not defined";
1922 if (!isa<LayerOp>(layerOp)) {
1923 auto diag = emitError(loc)
1924 << start << " associated with layer '" << layer
1925 << "', but symbol '" << layer << "' does not refer to a '"
1926 << LayerOp::getOperationName() << "' op";
1927 return diag.attachNote(layerOp->getLoc()) << "symbol refers to this op";
1928 }
1929 return success();
1930}
1931
1932static LogicalResult verifyPortSymbolUses(FModuleLike module,
1933 SymbolTableCollection &symbolTable) {
1934 // verify types in ports.
1935 auto circuitOp = module->getParentOfType<CircuitOp>();
1936 for (size_t i = 0, e = module.getNumPorts(); i < e; ++i) {
1937 auto type = module.getPortType(i);
1938
1939 if (auto refType = type_dyn_cast<RefType>(type)) {
1940 if (failed(verifyProbeType(
1941 refType, module.getPortLocation(i), circuitOp, symbolTable,
1942 Twine("probe port '") + module.getPortName(i) + "' is")))
1943 return failure();
1944 continue;
1945 }
1946
1947 if (auto classType = dyn_cast<ClassType>(type)) {
1948 auto className = classType.getNameAttr();
1949 auto classOp = dyn_cast_or_null<ClassLike>(
1950 symbolTable.lookupSymbolIn(circuitOp, className));
1951 if (!classOp)
1952 return module.emitOpError() << "references unknown class " << className;
1953
1954 // verify that the result type agrees with the class definition.
1955 if (failed(classOp.verifyType(classType,
1956 [&]() { return module.emitOpError(); })))
1957 return failure();
1958 continue;
1959 }
1960
1961 if (isa<DomainType>(type)) {
1962 auto domainInfo = module.getDomainInfoAttrForPort(i);
1963 if (auto kind = dyn_cast<FlatSymbolRefAttr>(domainInfo))
1964 if (!dyn_cast_or_null<DomainOp>(
1965 symbolTable.lookupSymbolIn(circuitOp, kind)))
1966 return mlir::emitError(module.getPortLocation(i))
1967 << "domain port '" << module.getPortName(i)
1968 << "' has undefined domain kind '" << kind.getValue() << "'";
1969 }
1970 }
1971
1972 return success();
1973}
1974
1975LogicalResult FModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1976 if (failed(verifyPortSymbolUses(*this, symbolTable)))
1977 return failure();
1978
1979 auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
1980 for (auto layer : getLayers()) {
1981 if (!symbolTable.lookupSymbolIn(circuitOp, cast<SymbolRefAttr>(layer)))
1982 return emitOpError() << "enables undefined layer '" << layer << "'";
1983 }
1984
1985 return success();
1986}
1987
1988LogicalResult
1989FExtModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1990 if (failed(verifyPortSymbolUses(*this, symbolTable)))
1991 return failure();
1992
1993 auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
1994 for (auto layer : getKnownLayersAttr().getAsRange<SymbolRefAttr>()) {
1995 if (!symbolTable.lookupSymbolIn(circuitOp, layer))
1996 return emitOpError() << "knows undefined layer '" << layer << "'";
1997 }
1998 for (auto layer : getLayersAttr().getAsRange<SymbolRefAttr>()) {
1999 if (!symbolTable.lookupSymbolIn(circuitOp, layer))
2000 return emitOpError() << "enables undefined layer '" << layer << "'";
2001 }
2002
2003 return success();
2004}
2005
2006LogicalResult
2007FIntModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2008 return verifyPortSymbolUses(*this, symbolTable);
2009}
2010
2011LogicalResult
2012FMemModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2013 return verifyPortSymbolUses(*this, symbolTable);
2014}
2015
2016void FModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
2017 mlir::OpAsmSetValueNameFn setNameFn) {
2018 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2019}
2020
2021void FExtModuleOp::getAsmBlockArgumentNames(
2022 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
2023 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2024}
2025
2026void FIntModuleOp::getAsmBlockArgumentNames(
2027 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
2028 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2029}
2030
2031void FMemModuleOp::getAsmBlockArgumentNames(
2032 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
2033 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2034}
2035
2036ArrayAttr FMemModuleOp::getParameters() { return {}; }
2037
2038ArrayAttr FModuleOp::getParameters() { return {}; }
2039
2040Convention FIntModuleOp::getConvention() { return Convention::Internal; }
2041
2042ConventionAttr FIntModuleOp::getConventionAttr() {
2043 return ConventionAttr::get(getContext(), getConvention());
2044}
2045
2046Convention FMemModuleOp::getConvention() { return Convention::Internal; }
2047
2048ConventionAttr FMemModuleOp::getConventionAttr() {
2049 return ConventionAttr::get(getContext(), getConvention());
2050}
2051
2052//===----------------------------------------------------------------------===//
2053// ClassLike Helpers
2054//===----------------------------------------------------------------------===//
2055
2057 ClassLike classOp, ClassType type,
2058 function_ref<InFlightDiagnostic()> emitError) {
2059 // This check is probably not required, but done for sanity.
2060 auto name = type.getNameAttr().getAttr();
2061 auto expectedName = classOp.getModuleNameAttr();
2062 if (name != expectedName)
2063 return emitError() << "type has wrong name, got " << name << ", expected "
2064 << expectedName;
2065
2066 auto elements = type.getElements();
2067 auto numElements = elements.size();
2068 auto expectedNumElements = classOp.getNumPorts();
2069 if (numElements != expectedNumElements)
2070 return emitError() << "has wrong number of ports, got " << numElements
2071 << ", expected " << expectedNumElements;
2072
2073 auto portNames = classOp.getPortNames();
2074 auto portDirections = classOp.getPortDirections();
2075 auto portTypes = classOp.getPortTypes();
2076
2077 for (unsigned i = 0; i < numElements; ++i) {
2078 auto element = elements[i];
2079
2080 auto name = element.name;
2081 auto expectedName = portNames[i];
2082 if (name != expectedName)
2083 return emitError() << "port #" << i << " has wrong name, got " << name
2084 << ", expected " << expectedName;
2085
2086 auto direction = element.direction;
2087 auto expectedDirection = Direction(portDirections[i]);
2088 if (direction != expectedDirection)
2089 return emitError() << "port " << name << " has wrong direction, got "
2090 << direction::toString(direction) << ", expected "
2091 << direction::toString(expectedDirection);
2092
2093 auto type = element.type;
2094 auto expectedType = cast<TypeAttr>(portTypes[i]).getValue();
2095 if (type != expectedType)
2096 return emitError() << "port " << name << " has wrong type, got " << type
2097 << ", expected " << expectedType;
2098 }
2099
2100 return success();
2101}
2102
2104 auto n = classOp.getNumPorts();
2105 SmallVector<ClassElement> elements;
2106 elements.reserve(n);
2107 for (size_t i = 0; i < n; ++i)
2108 elements.push_back({classOp.getPortNameAttr(i), classOp.getPortType(i),
2109 classOp.getPortDirection(i)});
2110 auto name = FlatSymbolRefAttr::get(classOp.getNameAttr());
2111 return ClassType::get(name, elements);
2112}
2113
2114template <typename OpTy>
2115ParseResult parseClassLike(OpAsmParser &parser, OperationState &result,
2116 bool hasSSAIdentifiers) {
2117 auto *context = result.getContext();
2118 auto &builder = parser.getBuilder();
2119 auto &properties = result.getOrAddProperties<typename OpTy::Properties>();
2120
2121 // TODO: this should use properties.
2122 // Parse the visibility attribute.
2123 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2124
2125 // Parse the name as a symbol.
2126 StringAttr nameAttr;
2127 if (parser.parseSymbolName(nameAttr))
2128 return failure();
2129 properties.setSymName(nameAttr);
2130
2131 // Parse the module ports.
2132 SmallVector<OpAsmParser::Argument> entryArgs;
2133 SmallVector<Direction, 4> portDirections;
2134 SmallVector<Attribute, 4> portNames;
2135 SmallVector<Attribute, 4> portTypes;
2136 SmallVector<Attribute, 4> portAnnotations;
2137 SmallVector<Attribute, 4> portSyms;
2138 SmallVector<Attribute, 4> portLocs;
2139 SmallVector<Attribute, 4> domains;
2140 if (parseModulePorts(parser, hasSSAIdentifiers,
2141 /*supportsSymbols=*/false, /*supportsDomains=*/false,
2142 entryArgs, portDirections, portNames, portTypes,
2143 portAnnotations, portSyms, portLocs, domains))
2144 return failure();
2145
2146 // Ports on ClassLike ops cannot have annotations
2147 for (auto annos : portAnnotations)
2148 if (!cast<ArrayAttr>(annos).empty())
2149 return failure();
2150
2151 // If attributes are present, parse them.
2152 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
2153 return failure();
2154
2155 assert(portNames.size() == portTypes.size());
2156
2157 // Record the argument and result types as an attribute. This is necessary
2158 // for external modules.
2159
2160 // Add port directions.
2161 properties.setPortDirections(
2162 direction::packAttribute(context, portDirections));
2163
2164 // Add port names.
2165 properties.setPortNames(builder.getArrayAttr(portNames));
2166
2167 // Add the port types.
2168 properties.setPortTypes(builder.getArrayAttr(portTypes));
2169
2170 // Add the port symbols.
2171 FModuleLike::fixupPortSymsArray(portSyms, builder.getContext());
2172 properties.setPortSymbols(builder.getArrayAttr(portSyms));
2173
2174 // Add port locations.
2175 properties.setPortLocations(ArrayAttr::get(context, portLocs));
2176
2177 // Notably missing compared to other FModuleLike, we do not track port
2178 // annotations, nor port symbols, on classes.
2179
2180 // Add the region (unused by extclass).
2181 auto *bodyRegion = result.addRegion();
2182
2183 if (hasSSAIdentifiers) {
2184 if (parser.parseRegion(*bodyRegion, entryArgs))
2185 return failure();
2186 if (bodyRegion->empty())
2187 bodyRegion->push_back(new Block());
2188 }
2189
2190 return success();
2191}
2192
2193static void printClassLike(OpAsmPrinter &p, ClassLike op) {
2194 p << ' ';
2195
2196 // Print the visibility of the class.
2197 StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
2198 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
2199 p << visibility.getValue() << ' ';
2200
2201 // Print the class name.
2202 p.printSymbolName(op.getName());
2203
2204 // Both classes and external classes have a body, but it is always empty for
2205 // external classes.
2206 Region &region = op->getRegion(0);
2207 Block *body = nullptr;
2208 if (!region.empty())
2209 body = &region.front();
2210
2211 auto needPortNamesAttr = printModulePorts(
2212 p, body, op.getPortDirectionsAttr(), op.getPortNames(), op.getPortTypes(),
2213 {}, op.getPortSymbols(), op.getPortLocations(), {});
2214
2215 // Print the attr-dict.
2216 SmallVector<StringRef, 8> omittedAttrs = {
2217 "sym_name", "portNames", "portTypes", "portDirections",
2218 "portSymbols", "portLocations", visibilityAttrName, "domainInfo"};
2219
2220 // We can omit the portNames if they were able to be printed as properly as
2221 // block arguments.
2222 if (!needPortNamesAttr)
2223 omittedAttrs.push_back("portNames");
2224
2225 p.printOptionalAttrDictWithKeyword(op->getAttrs(), omittedAttrs);
2226
2227 // print the body if it exists.
2228 if (!region.empty()) {
2229 p << " ";
2230 auto printEntryBlockArgs = false;
2231 auto printBlockTerminators = false;
2232 p.printRegion(region, printEntryBlockArgs, printBlockTerminators);
2233 }
2234}
2235
2236//===----------------------------------------------------------------------===//
2237// ClassOp
2238//===----------------------------------------------------------------------===//
2239
2240void ClassOp::build(OpBuilder &builder, OperationState &result, StringAttr name,
2241 ArrayRef<PortInfo> ports) {
2242 assert(
2243 llvm::all_of(ports,
2244 [](const auto &port) { return port.annotations.empty(); }) &&
2245 "class ports may not have annotations");
2246
2247 buildClass<ClassOp>(builder, result, name, ports);
2248
2249 // Create a region and a block for the body.
2250 auto *bodyRegion = result.regions[0].get();
2251 Block *body = new Block();
2252 bodyRegion->push_back(body);
2253
2254 // Add arguments to the body block.
2255 for (auto &elt : ports)
2256 body->addArgument(elt.type, elt.loc);
2257}
2258
2259void ClassOp::build(::mlir::OpBuilder &odsBuilder,
2260 ::mlir::OperationState &odsState, Twine name,
2261 mlir::ArrayRef<mlir::StringRef> fieldNames,
2262 mlir::ArrayRef<mlir::Type> fieldTypes) {
2263
2264 SmallVector<PortInfo, 10> ports;
2265 for (auto [fieldName, fieldType] : llvm::zip(fieldNames, fieldTypes)) {
2266 ports.emplace_back(odsBuilder.getStringAttr(fieldName + "_in"), fieldType,
2267 Direction::In);
2268 ports.emplace_back(odsBuilder.getStringAttr(fieldName), fieldType,
2269 Direction::Out);
2270 }
2271 build(odsBuilder, odsState, odsBuilder.getStringAttr(name), ports);
2272 // Create a region and a block for the body.
2273 auto &body = odsState.regions[0]->getBlocks().front();
2274 auto prevLoc = odsBuilder.saveInsertionPoint();
2275 odsBuilder.setInsertionPointToEnd(&body);
2276 auto args = body.getArguments();
2277 auto loc = odsState.location;
2278 for (unsigned i = 0, e = ports.size(); i != e; i += 2)
2279 PropAssignOp::create(odsBuilder, loc, args[i + 1], args[i]);
2280
2281 odsBuilder.restoreInsertionPoint(prevLoc);
2282}
2283void ClassOp::print(OpAsmPrinter &p) {
2284 printClassLike(p, cast<ClassLike>(getOperation()));
2285}
2286
2287ParseResult ClassOp::parse(OpAsmParser &parser, OperationState &result) {
2288 auto hasSSAIdentifiers = true;
2289 return parseClassLike<ClassOp>(parser, result, hasSSAIdentifiers);
2290}
2291
2292LogicalResult ClassOp::verify() {
2293 for (auto operand : getBodyBlock()->getArguments()) {
2294 auto type = operand.getType();
2295 if (!isa<PropertyType>(type)) {
2296 emitOpError("ports on a class must be properties");
2297 return failure();
2298 }
2299 }
2300
2301 return success();
2302}
2303
2304LogicalResult
2305ClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
2306 return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
2307}
2308
2309void ClassOp::getAsmBlockArgumentNames(mlir::Region &region,
2310 mlir::OpAsmSetValueNameFn setNameFn) {
2311 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2312}
2313
2314SmallVector<PortInfo> ClassOp::getPorts() {
2315 return ::getPortImpl(cast<FModuleLike>((Operation *)*this));
2316}
2317
2318void ClassOp::erasePorts(const llvm::BitVector &portIndices) {
2319 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
2320 getBodyBlock()->eraseArguments(portIndices);
2321}
2322
2323void ClassOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2324 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
2325}
2326
2327Convention ClassOp::getConvention() { return Convention::Internal; }
2328
2329ConventionAttr ClassOp::getConventionAttr() {
2330 return ConventionAttr::get(getContext(), getConvention());
2331}
2332
2333ArrayAttr ClassOp::getParameters() { return {}; }
2334
2335ArrayAttr ClassOp::getPortAnnotationsAttr() {
2336 return ArrayAttr::get(getContext(), {});
2337}
2338
2339ArrayRef<Attribute> ClassOp::getPortAnnotations() { return {}; }
2340
2341void ClassOp::setPortAnnotationsAttr(ArrayAttr annotations) {
2342 llvm_unreachable("classes do not support annotations");
2343}
2344
2345ArrayAttr ClassOp::getLayersAttr() { return ArrayAttr::get(getContext(), {}); }
2346
2347ArrayRef<Attribute> ClassOp::getLayers() { return {}; }
2348
2349SmallVector<::circt::hw::PortInfo> ClassOp::getPortList() {
2350 return ::getPortListImpl(*this);
2351}
2352
2353::circt::hw::PortInfo ClassOp::getPort(size_t idx) {
2354 return ::getPortImpl(*this, idx);
2355}
2356
2357BlockArgument ClassOp::getArgument(size_t portNumber) {
2358 return getBodyBlock()->getArgument(portNumber);
2359}
2360
2361bool ClassOp::canDiscardOnUseEmpty() {
2362 // ClassOps are referenced by ClassTypes, and these uses are not
2363 // discoverable by the symbol infrastructure. Return false here to prevent
2364 // passes like symbolDCE from removing our classes.
2365 return false;
2366}
2367
2368//===----------------------------------------------------------------------===//
2369// ExtClassOp
2370//===----------------------------------------------------------------------===//
2371
2372void ExtClassOp::build(OpBuilder &builder, OperationState &result,
2373 StringAttr name, ArrayRef<PortInfo> ports) {
2374 assert(
2375 llvm::all_of(ports,
2376 [](const auto &port) { return port.annotations.empty(); }) &&
2377 "class ports may not have annotations");
2378 buildClass<ClassOp>(builder, result, name, ports);
2379}
2380
2381void ExtClassOp::print(OpAsmPrinter &p) {
2382 printClassLike(p, cast<ClassLike>(getOperation()));
2383}
2384
2385ParseResult ExtClassOp::parse(OpAsmParser &parser, OperationState &result) {
2386 auto hasSSAIdentifiers = false;
2387 return parseClassLike<ExtClassOp>(parser, result, hasSSAIdentifiers);
2388}
2389
2390LogicalResult
2391ExtClassOp::verifySymbolUses(::mlir::SymbolTableCollection &symbolTable) {
2392 return verifyPortSymbolUses(cast<FModuleLike>(getOperation()), symbolTable);
2393}
2394
2395void ExtClassOp::getAsmBlockArgumentNames(mlir::Region &region,
2396 mlir::OpAsmSetValueNameFn setNameFn) {
2397 getAsmBlockArgumentNamesImpl(getOperation(), region, setNameFn);
2398}
2399
2400SmallVector<PortInfo> ExtClassOp::getPorts() {
2401 return ::getPortImpl(cast<FModuleLike>((Operation *)*this));
2402}
2403
2404void ExtClassOp::erasePorts(const llvm::BitVector &portIndices) {
2405 ::erasePorts(cast<FModuleLike>((Operation *)*this), portIndices);
2406}
2407
2408void ExtClassOp::insertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2409 ::insertPorts(cast<FModuleLike>((Operation *)*this), ports);
2410}
2411
2412Convention ExtClassOp::getConvention() { return Convention::Internal; }
2413
2414ConventionAttr ExtClassOp::getConventionAttr() {
2415 return ConventionAttr::get(getContext(), getConvention());
2416}
2417
2418ArrayAttr ExtClassOp::getLayersAttr() {
2419 return ArrayAttr::get(getContext(), {});
2420}
2421
2422ArrayRef<Attribute> ExtClassOp::getLayers() { return {}; }
2423
2424ArrayAttr ExtClassOp::getParameters() { return {}; }
2425
2426ArrayAttr ExtClassOp::getPortAnnotationsAttr() {
2427 return ArrayAttr::get(getContext(), {});
2428}
2429
2430ArrayRef<Attribute> ExtClassOp::getPortAnnotations() { return {}; }
2431
2432void ExtClassOp::setPortAnnotationsAttr(ArrayAttr annotations) {
2433 llvm_unreachable("classes do not support annotations");
2434}
2435
2436SmallVector<::circt::hw::PortInfo> ExtClassOp::getPortList() {
2437 return ::getPortListImpl(*this);
2438}
2439
2440::circt::hw::PortInfo ExtClassOp::getPort(size_t idx) {
2441 return ::getPortImpl(*this, idx);
2442}
2443
2444bool ExtClassOp::canDiscardOnUseEmpty() {
2445 // ClassOps are referenced by ClassTypes, and these uses are not
2446 // discovereable by the symbol infrastructure. Return false here to prevent
2447 // passes like symbolDCE from removing our classes.
2448 return false;
2449}
2450
2451//===----------------------------------------------------------------------===//
2452// InstanceOp
2453//===----------------------------------------------------------------------===//
2454
2455void InstanceOp::build(
2456 OpBuilder &builder, OperationState &result, TypeRange resultTypes,
2457 StringRef moduleName, StringRef name, NameKindEnum nameKind,
2458 ArrayRef<Direction> portDirections, ArrayRef<Attribute> portNames,
2459 ArrayRef<Attribute> domainInfo, ArrayRef<Attribute> annotations,
2460 ArrayRef<Attribute> portAnnotations, ArrayRef<Attribute> layers,
2461 bool lowerToBind, bool doNotPrint, StringAttr innerSym) {
2462 build(builder, result, resultTypes, moduleName, name, nameKind,
2463 portDirections, portNames, domainInfo, annotations, portAnnotations,
2464 layers, lowerToBind, doNotPrint,
2465 innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
2466}
2467
2468void InstanceOp::build(
2469 OpBuilder &builder, OperationState &result, TypeRange resultTypes,
2470 StringRef moduleName, StringRef name, NameKindEnum nameKind,
2471 ArrayRef<Direction> portDirections, ArrayRef<Attribute> portNames,
2472 ArrayRef<Attribute> domainInfo, ArrayRef<Attribute> annotations,
2473 ArrayRef<Attribute> portAnnotations, ArrayRef<Attribute> layers,
2474 bool lowerToBind, bool doNotPrint, hw::InnerSymAttr innerSym) {
2475 result.addTypes(resultTypes);
2476 result.getOrAddProperties<Properties>().setModuleName(
2477 SymbolRefAttr::get(builder.getContext(), moduleName));
2478 result.getOrAddProperties<Properties>().setName(builder.getStringAttr(name));
2479 result.getOrAddProperties<Properties>().setPortDirections(
2480 direction::packAttribute(builder.getContext(), portDirections));
2481 result.getOrAddProperties<Properties>().setPortNames(
2482 builder.getArrayAttr(portNames));
2483
2484 if (domainInfo.empty()) {
2485 SmallVector<Attribute, 16> domainInfoVec(resultTypes.size(),
2486 builder.getArrayAttr({}));
2487 result.getOrAddProperties<Properties>().setDomainInfo(
2488 builder.getArrayAttr(domainInfoVec));
2489 } else {
2490 assert(domainInfo.size() == resultTypes.size());
2491 result.getOrAddProperties<Properties>().setDomainInfo(
2492 builder.getArrayAttr(domainInfo));
2493 }
2494
2495 result.getOrAddProperties<Properties>().setAnnotations(
2496 builder.getArrayAttr(annotations));
2497 result.getOrAddProperties<Properties>().setLayers(
2498 builder.getArrayAttr(layers));
2499 if (lowerToBind)
2500 result.getOrAddProperties<Properties>().setLowerToBind(
2501 builder.getUnitAttr());
2502 if (doNotPrint)
2503 result.getOrAddProperties<Properties>().setDoNotPrint(
2504 builder.getUnitAttr());
2505 if (innerSym)
2506 result.getOrAddProperties<Properties>().setInnerSym(innerSym);
2507
2508 result.getOrAddProperties<Properties>().setNameKind(
2509 NameKindEnumAttr::get(builder.getContext(), nameKind));
2510
2511 if (portAnnotations.empty()) {
2512 SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
2513 builder.getArrayAttr({}));
2514 result.getOrAddProperties<Properties>().setPortAnnotations(
2515 builder.getArrayAttr(portAnnotationsVec));
2516 } else {
2517 assert(portAnnotations.size() == resultTypes.size());
2518 result.getOrAddProperties<Properties>().setPortAnnotations(
2519 builder.getArrayAttr(portAnnotations));
2520 }
2521}
2522
2523void InstanceOp::build(OpBuilder &builder, OperationState &result,
2524 FModuleLike module, StringRef name,
2525 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2526 ArrayRef<Attribute> portAnnotations, bool lowerToBind,
2527 bool doNotPrint, hw::InnerSymAttr innerSym) {
2528
2529 // Gather the result types.
2530 SmallVector<Type> resultTypes;
2531 resultTypes.reserve(module.getNumPorts());
2532 llvm::transform(
2533 module.getPortTypes(), std::back_inserter(resultTypes),
2534 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2535
2536 // The storage for annotations and domains on the module uses an empty array
2537 // if there are no annotations or domains. However, the instance op always
2538 // stores one-array-per-port. Do this massaging here.
2539 ArrayAttr portAnnotationsAttr;
2540 if (portAnnotations.empty()) {
2541 portAnnotationsAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
2542 resultTypes.size(), builder.getArrayAttr({})));
2543 } else {
2544 portAnnotationsAttr = builder.getArrayAttr(portAnnotations);
2545 }
2546 ArrayAttr domainInfoAttr = module.getDomainInfoAttr();
2547 if (domainInfoAttr.empty()) {
2548 domainInfoAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
2549 resultTypes.size(), builder.getArrayAttr({})));
2550 }
2551
2552 return build(
2553 builder, result, resultTypes,
2554 SymbolRefAttr::get(builder.getContext(), module.getModuleNameAttr()),
2555 builder.getStringAttr(name),
2556 NameKindEnumAttr::get(builder.getContext(), nameKind),
2557 module.getPortDirectionsAttr(), module.getPortNamesAttr(), domainInfoAttr,
2558 builder.getArrayAttr(annotations), portAnnotationsAttr,
2559 module.getLayersAttr(), lowerToBind ? builder.getUnitAttr() : UnitAttr(),
2560 doNotPrint ? builder.getUnitAttr() : UnitAttr(), innerSym);
2561}
2562
2563void InstanceOp::build(OpBuilder &builder, OperationState &odsState,
2564 ArrayRef<PortInfo> ports, StringRef moduleName,
2565 StringRef name, NameKindEnum nameKind,
2566 ArrayRef<Attribute> annotations,
2567 ArrayRef<Attribute> layers, bool lowerToBind,
2568 bool doNotPrint, hw::InnerSymAttr innerSym) {
2569 // Gather the result types.
2570 SmallVector<Type> newResultTypes;
2571 SmallVector<Direction> newPortDirections;
2572 SmallVector<Attribute> newPortNames;
2573 SmallVector<Attribute> newPortAnnotations;
2574 SmallVector<Attribute> newDomainInfo;
2575 for (auto &p : ports) {
2576 newResultTypes.push_back(p.type);
2577 newPortDirections.push_back(p.direction);
2578 newPortNames.push_back(p.name);
2579 newPortAnnotations.push_back(p.annotations.getArrayAttr());
2580 if (p.domains)
2581 newDomainInfo.push_back(p.domains);
2582 else
2583 newDomainInfo.push_back(builder.getArrayAttr({}));
2584 }
2585
2586 return build(builder, odsState, newResultTypes, moduleName, name, nameKind,
2587 newPortDirections, newPortNames, newDomainInfo, annotations,
2588 newPortAnnotations, layers, lowerToBind, doNotPrint, innerSym);
2589}
2590
2591LogicalResult InstanceOp::verify() {
2592 // The instance may only be instantiated under its required layers.
2593 auto ambientLayers = getAmbientLayersAt(getOperation());
2594 SmallVector<SymbolRefAttr> missingLayers;
2595 for (auto layer : getLayersAttr().getAsRange<SymbolRefAttr>())
2596 if (!isLayerCompatibleWith(layer, ambientLayers))
2597 missingLayers.push_back(layer);
2598
2599 if (missingLayers.empty())
2600 return success();
2601
2602 auto diag =
2603 emitOpError("ambient layers are insufficient to instantiate module");
2604 auto &note = diag.attachNote();
2605 note << "missing layer requirements: ";
2606 interleaveComma(missingLayers, note);
2607 return failure();
2608}
2609
2610/// Builds a new `InstanceOp` with the ports listed in `portIndices` erased, and
2611/// updates any users of the remaining ports to point at the new instance.
2612InstanceOp InstanceOp::erasePorts(OpBuilder &builder,
2613 const llvm::BitVector &portIndices) {
2614 assert(portIndices.size() >= getNumResults() &&
2615 "portIndices is not at least as large as getNumResults()");
2616
2617 if (portIndices.none())
2618 return *this;
2619
2620 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
2621 SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
2622 SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
2623 direction::unpackAttribute(getPortDirectionsAttr()), portIndices);
2624 SmallVector<Attribute> newPortNames =
2625 removeElementsAtIndices(getPortNames().getValue(), portIndices);
2626 SmallVector<Attribute> newPortAnnotations =
2627 removeElementsAtIndices(getPortAnnotations().getValue(), portIndices);
2628 ArrayAttr newDomainInfo =
2629 fixDomainInfoDeletions(getContext(), getDomainInfoAttr(), portIndices,
2630 /*supportsEmptyAttr=*/false);
2631
2632 auto newOp = InstanceOp::create(
2633 builder, getLoc(), newResultTypes, getModuleName(), getName(),
2634 getNameKind(), newPortDirections, newPortNames, newDomainInfo.getValue(),
2635 getAnnotations().getValue(), newPortAnnotations, getLayers(),
2636 getLowerToBind(), getDoNotPrint(), getInnerSymAttr());
2637
2638 for (unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
2639 oldIdx != numOldPorts; ++oldIdx) {
2640 if (portIndices.test(oldIdx)) {
2641 assert(getResult(oldIdx).use_empty() && "removed instance port has uses");
2642 continue;
2643 }
2644 getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
2645 ++newIdx;
2646 }
2647
2648 // Compy over "output_file" information so that this is not lost when ports
2649 // are erased.
2650 //
2651 // TODO: Other attributes may need to be copied over.
2652 if (auto outputFile = (*this)->getAttr("output_file"))
2653 newOp->setAttr("output_file", outputFile);
2654
2655 return newOp;
2656}
2657
2658ArrayAttr InstanceOp::getPortAnnotation(unsigned portIdx) {
2659 assert(portIdx < getNumResults() &&
2660 "index should be smaller than result number");
2661 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2662}
2663
2664void InstanceOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2665 assert(annotations.size() == getNumResults() &&
2666 "number of annotations is not equal to result number");
2667 (*this)->setAttr("portAnnotations",
2668 ArrayAttr::get(getContext(), annotations));
2669}
2670
2671ArrayAttr InstanceOp::getPortDomain(unsigned portIdx) {
2672 assert(portIdx < getNumResults() &&
2673 "index should be smaller than result number");
2674 return cast<ArrayAttr>(getDomainInfo()[portIdx]);
2675}
2676
2677InstanceOp
2678InstanceOp::cloneAndInsertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2679 auto portSize = ports.size();
2680 auto newPortCount = getNumResults() + portSize;
2681 SmallVector<Direction> newPortDirections;
2682 newPortDirections.reserve(newPortCount);
2683 SmallVector<Attribute> newPortNames;
2684 newPortNames.reserve(newPortCount);
2685 SmallVector<Type> newPortTypes;
2686 newPortTypes.reserve(newPortCount);
2687 SmallVector<Attribute> newPortAnnos;
2688 newPortAnnos.reserve(newPortCount);
2689 SmallVector<Attribute> newDomainInfo;
2690 newDomainInfo.reserve(newPortCount);
2691 SmallVector<unsigned> numInserted;
2692 numInserted.resize(getNumResults());
2693
2694 unsigned oldIndex = 0;
2695 unsigned newIndex = 0;
2696 while (oldIndex + newIndex < newPortCount) {
2697 // Check if we should insert a port here.
2698 if (newIndex < portSize && ports[newIndex].first == oldIndex) {
2699 auto &newPort = ports[newIndex].second;
2700 newPortDirections.push_back(newPort.direction);
2701 newPortNames.push_back(newPort.name);
2702 newPortTypes.push_back(newPort.type);
2703 newPortAnnos.push_back(newPort.annotations.getArrayAttr());
2704 newDomainInfo.push_back(fixDomainInfoInsertions(
2705 getContext(),
2706 newPort.domains ? newPort.domains : ArrayAttr::get(getContext(), {}),
2707 numInserted));
2708 if (oldIndex < getNumResults())
2709 numInserted[oldIndex] += 1;
2710 ++newIndex;
2711 } else {
2712 // Copy the next old port.
2713 newPortDirections.push_back(getPortDirection(oldIndex));
2714 newPortNames.push_back(getPortName(oldIndex));
2715 newPortTypes.push_back(getType(oldIndex));
2716 newPortAnnos.push_back(getPortAnnotation(oldIndex));
2717 newDomainInfo.push_back(getDomainInfo()[oldIndex]);
2718 if (oldIndex == 0)
2719 numInserted[oldIndex] = 0;
2720 else
2721 numInserted[oldIndex] += numInserted[oldIndex - 1];
2722 ++oldIndex;
2723 }
2724 }
2725
2726 // Create a new instance op with the reset inserted.
2727 OpBuilder builder(*this);
2728 return InstanceOp::create(
2729 builder, getLoc(), newPortTypes, getModuleName(), getName(),
2730 getNameKind(), newPortDirections, newPortNames, newDomainInfo,
2731 getAnnotations().getValue(), newPortAnnos, getLayers(), getLowerToBind(),
2732 getDoNotPrint(), getInnerSymAttr());
2733}
2734
2735LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2736 return instance_like_impl::verifyReferencedModule(*this, symbolTable,
2737 getModuleNameAttr());
2738}
2739
2740StringRef InstanceOp::getInstanceName() { return getName(); }
2741
2742StringAttr InstanceOp::getInstanceNameAttr() { return getNameAttr(); }
2743
2744void InstanceOp::print(OpAsmPrinter &p) {
2745 // Print the instance name.
2746 p << " ";
2747 p.printKeywordOrString(getName());
2748 if (auto attr = getInnerSymAttr()) {
2749 p << " sym ";
2750 p.printSymbolName(attr.getSymName());
2751 }
2752 if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2753 p << ' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2754
2755 // Print the attr-dict.
2756 SmallVector<StringRef, 10> omittedAttrs = {
2757 "moduleName", "name", "portDirections",
2758 "portNames", "portTypes", "portAnnotations",
2759 "inner_sym", "nameKind", "domainInfo"};
2760 if (getAnnotations().empty())
2761 omittedAttrs.push_back("annotations");
2762 if (getLayers().empty())
2763 omittedAttrs.push_back("layers");
2764 p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2765
2766 // Print the module name.
2767 p << " ";
2768 p.printSymbolName(getModuleName());
2769
2770 // Collect all the result types as TypeAttrs for printing.
2771 SmallVector<Attribute> portTypes;
2772 portTypes.reserve(getNumResults());
2773 llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2774 &TypeAttr::get);
2775 // This needs to be passed domain information.
2776 printModulePorts(p, /*block=*/nullptr, getPortDirectionsAttr(),
2777 getPortNames().getValue(), portTypes,
2778 getPortAnnotations().getValue(), {}, {},
2779 getDomainInfo().getValue());
2780}
2781
2782ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
2783 auto *context = parser.getContext();
2784 auto &properties = result.getOrAddProperties<Properties>();
2785
2786 std::string name;
2787 hw::InnerSymAttr innerSymAttr;
2788 FlatSymbolRefAttr moduleName;
2789 SmallVector<OpAsmParser::Argument> entryArgs;
2790 SmallVector<Direction, 4> portDirections;
2791 SmallVector<Attribute, 4> portNames;
2792 SmallVector<Attribute, 4> portTypes;
2793 SmallVector<Attribute, 4> portAnnotations;
2794 SmallVector<Attribute, 4> portSyms;
2795 SmallVector<Attribute, 4> portLocs;
2796 SmallVector<Attribute, 4> domains;
2797 NameKindEnumAttr nameKind;
2798
2799 if (parser.parseKeywordOrString(&name))
2800 return failure();
2801 if (succeeded(parser.parseOptionalKeyword("sym"))) {
2802 if (parser.parseCustomAttributeWithFallback(
2803 innerSymAttr, ::mlir::Type{},
2805 result.attributes)) {
2806 return ::mlir::failure();
2807 }
2808 }
2809 if (parseNameKind(parser, nameKind) ||
2810 parser.parseOptionalAttrDict(result.attributes) ||
2811 parser.parseAttribute(moduleName) ||
2812 parseModulePorts(parser, /*hasSSAIdentifiers=*/false,
2813 /*supportsSymbols=*/false, /*supportsDomains=*/true,
2814 entryArgs, portDirections, portNames, portTypes,
2815 portAnnotations, portSyms, portLocs, domains))
2816 return failure();
2817
2818 // Add the attributes. We let attributes defined in the attr-dict override
2819 // attributes parsed out of the module signature.
2820
2821 properties.setModuleName(moduleName);
2822 properties.setName(StringAttr::get(context, name));
2823 properties.setNameKind(nameKind);
2824 properties.setPortDirections(
2825 direction::packAttribute(context, portDirections));
2826 properties.setPortNames(ArrayAttr::get(context, portNames));
2827 properties.setPortAnnotations(ArrayAttr::get(context, portAnnotations));
2828
2829 // Annotations, layers, and LowerToBind are omitted in the printed format
2830 // if they are empty, empty, and false (respectively).
2831 properties.setAnnotations(parser.getBuilder().getArrayAttr({}));
2832 properties.setLayers(parser.getBuilder().getArrayAttr({}));
2833
2834 // Add domain information.
2835 properties.setDomainInfo(ArrayAttr::get(context, domains));
2836
2837 // Add result types.
2838 result.types.reserve(portTypes.size());
2839 llvm::transform(
2840 portTypes, std::back_inserter(result.types),
2841 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2842
2843 return success();
2844}
2845
2846void InstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2847 StringRef base = getName();
2848 if (base.empty())
2849 base = "inst";
2850
2851 for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2852 setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
2853 }
2854}
2855
2856std::optional<size_t> InstanceOp::getTargetResultIndex() {
2857 // Inner symbols on instance operations target the op not any result.
2858 return std::nullopt;
2859}
2860
2861// -----------------------------------------------------------------------------
2862// InstanceChoiceOp
2863// -----------------------------------------------------------------------------
2864
2865void InstanceChoiceOp::build(
2866 OpBuilder &builder, OperationState &result, FModuleLike defaultModule,
2867 ArrayRef<std::pair<OptionCaseOp, FModuleLike>> cases, StringRef name,
2868 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2869 ArrayRef<Attribute> portAnnotations, StringAttr innerSym) {
2870 // Gather the result types.
2871 SmallVector<Type> resultTypes;
2872 for (Attribute portType : defaultModule.getPortTypes())
2873 resultTypes.push_back(cast<TypeAttr>(portType).getValue());
2874
2875 // Create the port annotations.
2876 ArrayAttr portAnnotationsAttr;
2877 if (portAnnotations.empty()) {
2878 portAnnotationsAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
2879 resultTypes.size(), builder.getArrayAttr({})));
2880 } else {
2881 portAnnotationsAttr = builder.getArrayAttr(portAnnotations);
2882 }
2883
2884 // Gather the module & case names.
2885 SmallVector<Attribute> moduleNames, caseNames;
2886 moduleNames.push_back(SymbolRefAttr::get(defaultModule.getModuleNameAttr()));
2887 for (auto [caseOption, caseModule] : cases) {
2888 auto caseGroup = caseOption->getParentOfType<OptionOp>();
2889 caseNames.push_back(SymbolRefAttr::get(caseGroup.getSymNameAttr(),
2890 {SymbolRefAttr::get(caseOption)}));
2891 moduleNames.push_back(SymbolRefAttr::get(caseModule.getModuleNameAttr()));
2892 }
2893
2894 return build(
2895 builder, result, resultTypes, builder.getArrayAttr(moduleNames),
2896 builder.getArrayAttr(caseNames), builder.getStringAttr(name),
2897 NameKindEnumAttr::get(builder.getContext(), nameKind),
2898 defaultModule.getPortDirectionsAttr(), defaultModule.getPortNamesAttr(),
2899 defaultModule.getDomainInfoAttr(), builder.getArrayAttr(annotations),
2900 portAnnotationsAttr, defaultModule.getLayersAttr(),
2901 innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
2902}
2903
2904std::optional<size_t> InstanceChoiceOp::getTargetResultIndex() {
2905 return std::nullopt;
2906}
2907
2908void InstanceChoiceOp::print(OpAsmPrinter &p) {
2909 // Print the instance name.
2910 p << " ";
2911 p.printKeywordOrString(getName());
2912 if (auto attr = getInnerSymAttr()) {
2913 p << " sym ";
2914 p.printSymbolName(attr.getSymName());
2915 }
2916 if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2917 p << ' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2918
2919 // Print the attr-dict.
2920 SmallVector<StringRef, 11> omittedAttrs = {
2921 "moduleNames", "caseNames", "name",
2922 "portDirections", "portNames", "portTypes",
2923 "portAnnotations", "inner_sym", "nameKind",
2924 "domainInfo"};
2925 if (getAnnotations().empty())
2926 omittedAttrs.push_back("annotations");
2927 if (getLayers().empty())
2928 omittedAttrs.push_back("layers");
2929 p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2930
2931 // Print the module name.
2932 p << ' ';
2933
2934 auto moduleNames = getModuleNamesAttr();
2935 auto caseNames = getCaseNamesAttr();
2936
2937 p.printSymbolName(cast<FlatSymbolRefAttr>(moduleNames[0]).getValue());
2938
2939 p << " alternatives ";
2940 p.printSymbolName(
2941 cast<SymbolRefAttr>(caseNames[0]).getRootReference().getValue());
2942 p << " { ";
2943 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
2944 if (i != 0)
2945 p << ", ";
2946
2947 auto symbol = cast<SymbolRefAttr>(caseNames[i]);
2948 p.printSymbolName(symbol.getNestedReferences()[0].getValue());
2949 p << " -> ";
2950 p.printSymbolName(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getValue());
2951 }
2952
2953 p << " } ";
2954
2955 // Collect all the result types as TypeAttrs for printing.
2956 SmallVector<Attribute> portTypes;
2957 portTypes.reserve(getNumResults());
2958 llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2959 &TypeAttr::get);
2960 printModulePorts(p, /*block=*/nullptr, getPortDirectionsAttr(),
2961 getPortNames().getValue(), portTypes,
2962 getPortAnnotations().getValue(), {}, {},
2963 getDomainInfo().getValue());
2964}
2965
2966ParseResult InstanceChoiceOp::parse(OpAsmParser &parser,
2967 OperationState &result) {
2968 auto *context = parser.getContext();
2969 auto &properties = result.getOrAddProperties<Properties>();
2970
2971 std::string name;
2972 hw::InnerSymAttr innerSymAttr;
2973 SmallVector<Attribute> moduleNames;
2974 SmallVector<Attribute> caseNames;
2975 SmallVector<OpAsmParser::Argument> entryArgs;
2976 SmallVector<Direction, 4> portDirections;
2977 SmallVector<Attribute, 4> portNames;
2978 SmallVector<Attribute, 4> portTypes;
2979 SmallVector<Attribute, 4> portAnnotations;
2980 SmallVector<Attribute, 4> portSyms;
2981 SmallVector<Attribute, 4> portLocs;
2982 SmallVector<Attribute, 4> domains;
2983 NameKindEnumAttr nameKind;
2984
2985 if (parser.parseKeywordOrString(&name))
2986 return failure();
2987 if (succeeded(parser.parseOptionalKeyword("sym"))) {
2988 if (parser.parseCustomAttributeWithFallback(
2989 innerSymAttr, Type{},
2991 result.attributes)) {
2992 return failure();
2993 }
2994 }
2995 if (parseNameKind(parser, nameKind) ||
2996 parser.parseOptionalAttrDict(result.attributes))
2997 return failure();
2998
2999 FlatSymbolRefAttr defaultModuleName;
3000 if (parser.parseAttribute(defaultModuleName))
3001 return failure();
3002 moduleNames.push_back(defaultModuleName);
3003
3004 // alternatives { @opt::@case -> @target, ... }
3005 {
3006 FlatSymbolRefAttr optionName;
3007 if (parser.parseKeyword("alternatives") ||
3008 parser.parseAttribute(optionName) || parser.parseLBrace())
3009 return failure();
3010
3011 FlatSymbolRefAttr moduleName;
3012 StringAttr caseName;
3013 while (succeeded(parser.parseOptionalSymbolName(caseName))) {
3014 if (parser.parseArrow() || parser.parseAttribute(moduleName))
3015 return failure();
3016 moduleNames.push_back(moduleName);
3017 caseNames.push_back(SymbolRefAttr::get(
3018 optionName.getAttr(), {FlatSymbolRefAttr::get(caseName)}));
3019 if (failed(parser.parseOptionalComma()))
3020 break;
3021 }
3022 if (parser.parseRBrace())
3023 return failure();
3024 }
3025
3026 if (parseModulePorts(parser, /*hasSSAIdentifiers=*/false,
3027 /*supportsSymbols=*/false, /*supportsDomains=*/false,
3028 entryArgs, portDirections, portNames, portTypes,
3029 portAnnotations, portSyms, portLocs, domains))
3030 return failure();
3031
3032 // Add the attributes. We let attributes defined in the attr-dict override
3033 // attributes parsed out of the module signature.
3034 properties.setModuleNames(ArrayAttr::get(context, moduleNames));
3035 properties.setCaseNames(ArrayAttr::get(context, caseNames));
3036 properties.setName(StringAttr::get(context, name));
3037 properties.setNameKind(nameKind);
3038 properties.setPortDirections(
3039 direction::packAttribute(context, portDirections));
3040 properties.setPortNames(ArrayAttr::get(context, portNames));
3041 properties.setDomainInfo(ArrayAttr::get(context, domains));
3042 properties.setPortAnnotations(ArrayAttr::get(context, portAnnotations));
3043
3044 // Annotations, layers, and LowerToBind are omitted in the printed format if
3045 // they are empty, empty, and false (respectively).
3046 properties.setAnnotations(parser.getBuilder().getArrayAttr({}));
3047 properties.setLayers(parser.getBuilder().getArrayAttr({}));
3048
3049 // Add result types.
3050 result.types.reserve(portTypes.size());
3051 llvm::transform(
3052 portTypes, std::back_inserter(result.types),
3053 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
3054
3055 return success();
3056}
3057
3058void InstanceChoiceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3059 StringRef base = getName().empty() ? "inst" : getName();
3060 for (auto [result, name] : llvm::zip(getResults(), getPortNames()))
3061 setNameFn(result, (base + "_" + cast<StringAttr>(name).getValue()).str());
3062}
3063
3064LogicalResult InstanceChoiceOp::verify() {
3065 if (getCaseNamesAttr().empty())
3066 return emitOpError() << "must have at least one case";
3067 if (getModuleNamesAttr().size() != getCaseNamesAttr().size() + 1)
3068 return emitOpError() << "number of referenced modules does not match the "
3069 "number of options";
3070
3071 // The modules may only be instantiated under their required layers (which
3072 // are the same for all modules).
3073 auto ambientLayers = getAmbientLayersAt(getOperation());
3074 SmallVector<SymbolRefAttr> missingLayers;
3075 for (auto layer : getLayersAttr().getAsRange<SymbolRefAttr>())
3076 if (!isLayerCompatibleWith(layer, ambientLayers))
3077 missingLayers.push_back(layer);
3078
3079 if (missingLayers.empty())
3080 return success();
3081
3082 auto diag =
3083 emitOpError("ambient layers are insufficient to instantiate module");
3084 auto &note = diag.attachNote();
3085 note << "missing layer requirements: ";
3086 interleaveComma(missingLayers, note);
3087 return failure();
3088}
3089
3090LogicalResult
3091InstanceChoiceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3092 auto caseNames = getCaseNamesAttr();
3093 for (auto moduleName : getModuleNamesAttr()) {
3095 *this, symbolTable, cast<FlatSymbolRefAttr>(moduleName))))
3096 return failure();
3097 }
3098
3099 auto root = cast<SymbolRefAttr>(caseNames[0]).getRootReference();
3100 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
3101 auto ref = cast<SymbolRefAttr>(caseNames[i]);
3102 auto refRoot = ref.getRootReference();
3103 if (ref.getRootReference() != root)
3104 return emitOpError() << "case " << ref
3105 << " is not in the same option group as "
3106 << caseNames[0];
3107
3108 if (!symbolTable.lookupNearestSymbolFrom<OptionOp>(*this, refRoot))
3109 return emitOpError() << "option " << refRoot << " does not exist";
3110
3111 if (!symbolTable.lookupNearestSymbolFrom<OptionCaseOp>(*this, ref))
3112 return emitOpError() << "option " << refRoot
3113 << " does not contain option case " << ref;
3114 }
3115
3116 return success();
3117}
3118
3119FlatSymbolRefAttr
3120InstanceChoiceOp::getTargetOrDefaultAttr(OptionCaseOp option) {
3121 auto caseNames = getCaseNamesAttr();
3122 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
3123 StringAttr caseSym = cast<SymbolRefAttr>(caseNames[i]).getLeafReference();
3124 if (caseSym == option.getSymName())
3125 return cast<FlatSymbolRefAttr>(getModuleNamesAttr()[i + 1]);
3126 }
3127 return getDefaultTargetAttr();
3128}
3129
3130SmallVector<std::pair<SymbolRefAttr, FlatSymbolRefAttr>, 1>
3131InstanceChoiceOp::getTargetChoices() {
3132 auto caseNames = getCaseNamesAttr();
3133 auto moduleNames = getModuleNamesAttr();
3134 SmallVector<std::pair<SymbolRefAttr, FlatSymbolRefAttr>, 1> choices;
3135 for (size_t i = 0; i < caseNames.size(); ++i) {
3136 choices.emplace_back(cast<SymbolRefAttr>(caseNames[i]),
3137 cast<FlatSymbolRefAttr>(moduleNames[i + 1]));
3138 }
3139
3140 return choices;
3141}
3142
3143InstanceChoiceOp
3144InstanceChoiceOp::erasePorts(OpBuilder &builder,
3145 const llvm::BitVector &portIndices) {
3146 assert(portIndices.size() >= getNumResults() &&
3147 "portIndices is not at least as large as getNumResults()");
3148
3149 if (portIndices.none())
3150 return *this;
3151
3152 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
3153 SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
3154 SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
3155 direction::unpackAttribute(getPortDirectionsAttr()), portIndices);
3156 SmallVector<Attribute> newPortNames =
3157 removeElementsAtIndices(getPortNames().getValue(), portIndices);
3158 SmallVector<Attribute> newPortAnnotations =
3159 removeElementsAtIndices(getPortAnnotations().getValue(), portIndices);
3160 ArrayAttr newPortDomains =
3161 fixDomainInfoDeletions(getContext(), getDomainInfoAttr(), portIndices,
3162 /*supportsEmptyAttr=*/false);
3163
3164 auto newOp = InstanceChoiceOp::create(
3165 builder, getLoc(), newResultTypes, getModuleNames(), getCaseNames(),
3166 getName(), getNameKind(),
3167 direction::packAttribute(getContext(), newPortDirections),
3168 ArrayAttr::get(getContext(), newPortNames),
3169 ArrayAttr::get(getContext(), newPortDomains), getAnnotationsAttr(),
3170 ArrayAttr::get(getContext(), newPortAnnotations), getLayers(),
3171 getInnerSymAttr());
3172
3173 for (unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
3174 oldIdx != numOldPorts; ++oldIdx) {
3175 if (portIndices.test(oldIdx)) {
3176 assert(getResult(oldIdx).use_empty() && "removed instance port has uses");
3177 continue;
3178 }
3179 getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
3180 ++newIdx;
3181 }
3182
3183 // Copy over "output_file" information so that this is not lost when ports
3184 // are erased.
3185 //
3186 // TODO: Other attributes may need to be copied over.
3187 if (auto outputFile = (*this)->getAttr("output_file"))
3188 newOp->setAttr("output_file", outputFile);
3189
3190 return newOp;
3191}
3192
3193//===----------------------------------------------------------------------===//
3194// MemOp
3195//===----------------------------------------------------------------------===//
3196
3197ArrayAttr MemOp::getPortAnnotation(unsigned portIdx) {
3198 assert(portIdx < getNumResults() &&
3199 "index should be smaller than result number");
3200 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
3201}
3202
3203void MemOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
3204 assert(annotations.size() == getNumResults() &&
3205 "number of annotations is not equal to result number");
3206 (*this)->setAttr("portAnnotations",
3207 ArrayAttr::get(getContext(), annotations));
3208}
3209
3210// Get the number of read, write and read-write ports.
3211void MemOp::getNumPorts(size_t &numReadPorts, size_t &numWritePorts,
3212 size_t &numReadWritePorts, size_t &numDbgsPorts) {
3213 numReadPorts = 0;
3214 numWritePorts = 0;
3215 numReadWritePorts = 0;
3216 numDbgsPorts = 0;
3217 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
3218 auto portKind = getPortKind(i);
3219 if (portKind == MemOp::PortKind::Debug)
3220 ++numDbgsPorts;
3221 else if (portKind == MemOp::PortKind::Read)
3222 ++numReadPorts;
3223 else if (portKind == MemOp::PortKind::Write) {
3224 ++numWritePorts;
3225 } else
3226 ++numReadWritePorts;
3227 }
3228}
3229
3230/// Verify the correctness of a MemOp.
3231LogicalResult MemOp::verify() {
3232
3233 // Store the port names as we find them. This lets us check quickly
3234 // for uniqueneess.
3235 llvm::SmallDenseSet<Attribute, 8> portNamesSet;
3236
3237 // Store the previous data type. This lets us check that the data
3238 // type is consistent across all ports.
3239 FIRRTLType oldDataType;
3240
3241 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
3242 auto portName = getPortName(i);
3243
3244 // Get a bundle type representing this port, stripping an outer
3245 // flip if it exists. If this is not a bundle<> or
3246 // flip<bundle<>>, then this is an error.
3247 BundleType portBundleType =
3248 type_dyn_cast<BundleType>(getResult(i).getType());
3249
3250 // Require that all port names are unique.
3251 if (!portNamesSet.insert(portName).second) {
3252 emitOpError() << "has non-unique port name " << portName;
3253 return failure();
3254 }
3255
3256 // Determine the kind of the memory. If the kind cannot be
3257 // determined, then it's indicative of the wrong number of fields
3258 // in the type (but we don't know any more just yet).
3259
3260 auto elt = getPortNamed(portName);
3261 if (!elt) {
3262 emitOpError() << "could not get port with name " << portName;
3263 return failure();
3264 }
3265 auto firrtlType = type_cast<FIRRTLType>(elt.getType());
3266 MemOp::PortKind portKind = getMemPortKindFromType(firrtlType);
3267
3268 if (portKind == MemOp::PortKind::Debug &&
3269 !type_isa<RefType>(getResult(i).getType()))
3270 return emitOpError() << "has an invalid type on port " << portName
3271 << " (expected Read/Write/ReadWrite/Debug)";
3272 if (type_isa<RefType>(firrtlType) && e == 1)
3273 return emitOpError()
3274 << "cannot have only one port of debug type. Debug port can only "
3275 "exist alongside other read/write/read-write port";
3276
3277 // Safely search for the "data" field, erroring if it can't be
3278 // found.
3279 FIRRTLBaseType dataType;
3280 if (portKind == MemOp::PortKind::Debug) {
3281 auto resType = type_cast<RefType>(getResult(i).getType());
3282 if (!(resType && type_isa<FVectorType>(resType.getType())))
3283 return emitOpError() << "debug ports must be a RefType of FVectorType";
3284 dataType = type_cast<FVectorType>(resType.getType()).getElementType();
3285 } else {
3286 auto dataTypeOption = portBundleType.getElement("data");
3287 if (!dataTypeOption && portKind == MemOp::PortKind::ReadWrite)
3288 dataTypeOption = portBundleType.getElement("wdata");
3289 if (!dataTypeOption) {
3290 emitOpError() << "has no data field on port " << portName
3291 << " (expected to see \"data\" for a read or write "
3292 "port or \"rdata\" for a read/write port)";
3293 return failure();
3294 }
3295 dataType = dataTypeOption->type;
3296 // Read data is expected to ba a flip.
3297 if (portKind == MemOp::PortKind::Read) {
3298 // FIXME error on missing bundle flip
3299 }
3300 }
3301
3302 // Error if the data type isn't passive.
3303 if (!dataType.isPassive()) {
3304 emitOpError() << "has non-passive data type on port " << portName
3305 << " (memory types must be passive)";
3306 return failure();
3307 }
3308
3309 // Error if the data type contains analog types.
3310 if (dataType.containsAnalog()) {
3311 emitOpError() << "has a data type that contains an analog type on port "
3312 << portName
3313 << " (memory types cannot contain analog types)";
3314 return failure();
3315 }
3316
3317 // Check that the port type matches the kind that we determined
3318 // for this port. This catches situations of extraneous port
3319 // fields beind included or the fields being named incorrectly.
3320 FIRRTLType expectedType =
3321 getTypeForPort(getDepth(), dataType, portKind,
3322 dataType.isGround() ? getMaskBits() : 0);
3323 // Compute the original port type as portBundleType may have
3324 // stripped outer flip information.
3325 auto originalType = getResult(i).getType();
3326 if (originalType != expectedType) {
3327 StringRef portKindName;
3328 switch (portKind) {
3329 case MemOp::PortKind::Read:
3330 portKindName = "read";
3331 break;
3332 case MemOp::PortKind::Write:
3333 portKindName = "write";
3334 break;
3335 case MemOp::PortKind::ReadWrite:
3336 portKindName = "readwrite";
3337 break;
3338 case MemOp::PortKind::Debug:
3339 portKindName = "dbg";
3340 break;
3341 }
3342 emitOpError() << "has an invalid type for port " << portName
3343 << " of determined kind \"" << portKindName
3344 << "\" (expected " << expectedType << ", but got "
3345 << originalType << ")";
3346 return failure();
3347 }
3348
3349 // Error if the type of the current port was not the same as the
3350 // last port, but skip checking the first port.
3351 if (oldDataType && oldDataType != dataType) {
3352 emitOpError() << "port " << getPortName(i)
3353 << " has a different type than port " << getPortName(i - 1)
3354 << " (expected " << oldDataType << ", but got " << dataType
3355 << ")";
3356 return failure();
3357 }
3358
3359 oldDataType = dataType;
3360 }
3361
3362 auto maskWidth = getMaskBits();
3363
3364 auto dataWidth = getDataType().getBitWidthOrSentinel();
3365 if (dataWidth > 0 && maskWidth > (size_t)dataWidth)
3366 return emitOpError("the mask width cannot be greater than "
3367 "data width");
3368
3369 if (getPortAnnotations().size() != getNumResults())
3370 return emitOpError("the number of result annotations should be "
3371 "equal to the number of results");
3372
3373 return success();
3374}
3375
3376static size_t getAddressWidth(size_t depth) {
3377 return std::max(1U, llvm::Log2_64_Ceil(depth));
3378}
3379
3380size_t MemOp::getAddrBits() { return getAddressWidth(getDepth()); }
3381
3382FIRRTLType MemOp::getTypeForPort(uint64_t depth, FIRRTLBaseType dataType,
3383 PortKind portKind, size_t maskBits) {
3384
3385 auto *context = dataType.getContext();
3386 if (portKind == PortKind::Debug)
3387 return RefType::get(FVectorType::get(dataType, depth));
3388 FIRRTLBaseType maskType;
3389 // maskBits not specified (==0), then get the mask type from the dataType.
3390 if (maskBits == 0)
3391 maskType = dataType.getMaskType();
3392 else
3393 maskType = UIntType::get(context, maskBits);
3394
3395 auto getId = [&](StringRef name) -> StringAttr {
3396 return StringAttr::get(context, name);
3397 };
3398
3399 SmallVector<BundleType::BundleElement, 7> portFields;
3400
3401 auto addressType = UIntType::get(context, getAddressWidth(depth));
3402
3403 portFields.push_back({getId("addr"), false, addressType});
3404 portFields.push_back({getId("en"), false, UIntType::get(context, 1)});
3405 portFields.push_back({getId("clk"), false, ClockType::get(context)});
3406
3407 switch (portKind) {
3408 case PortKind::Read:
3409 portFields.push_back({getId("data"), true, dataType});
3410 break;
3411
3412 case PortKind::Write:
3413 portFields.push_back({getId("data"), false, dataType});
3414 portFields.push_back({getId("mask"), false, maskType});
3415 break;
3416
3417 case PortKind::ReadWrite:
3418 portFields.push_back({getId("rdata"), true, dataType});
3419 portFields.push_back({getId("wmode"), false, UIntType::get(context, 1)});
3420 portFields.push_back({getId("wdata"), false, dataType});
3421 portFields.push_back({getId("wmask"), false, maskType});
3422 break;
3423 default:
3424 llvm::report_fatal_error("memory port kind not handled");
3425 break;
3426 }
3427
3428 return BundleType::get(context, portFields);
3429}
3430
3431/// Return the name and kind of ports supported by this memory.
3432SmallVector<MemOp::NamedPort> MemOp::getPorts() {
3433 SmallVector<MemOp::NamedPort> result;
3434 // Each entry in the bundle is a port.
3435 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
3436 // Each port is a bundle.
3437 auto portType = type_cast<FIRRTLType>(getResult(i).getType());
3438 result.push_back({getPortName(i), getMemPortKindFromType(portType)});
3439 }
3440 return result;
3441}
3442
3443/// Return the kind of the specified port.
3444MemOp::PortKind MemOp::getPortKind(StringRef portName) {
3446 type_cast<FIRRTLType>(getPortNamed(portName).getType()));
3447}
3448
3449/// Return the kind of the specified port number.
3450MemOp::PortKind MemOp::getPortKind(size_t resultNo) {
3452 type_cast<FIRRTLType>(getResult(resultNo).getType()));
3453}
3454
3455/// Return the number of bits in the mask for the memory.
3456size_t MemOp::getMaskBits() {
3457
3458 for (auto res : getResults()) {
3459 if (type_isa<RefType>(res.getType()))
3460 continue;
3461 auto firstPortType = type_cast<FIRRTLBaseType>(res.getType());
3462 if (getMemPortKindFromType(firstPortType) == PortKind::Read ||
3463 getMemPortKindFromType(firstPortType) == PortKind::Debug)
3464 continue;
3465
3466 FIRRTLBaseType mType;
3467 for (auto t : type_cast<BundleType>(firstPortType.getPassiveType())) {
3468 if (t.name.getValue().contains("mask"))
3469 mType = t.type;
3470 }
3471 if (type_isa<UIntType>(mType))
3472 return mType.getBitWidthOrSentinel();
3473 }
3474 // Mask of zero bits means, either there are no write/readwrite ports or the
3475 // mask is of aggregate type.
3476 return 0;
3477}
3478
3479/// Return the data-type field of the memory, the type of each element.
3480FIRRTLBaseType MemOp::getDataType() {
3481 assert(getNumResults() != 0 && "Mems with no read/write ports are illegal");
3482
3483 if (auto refType = type_dyn_cast<RefType>(getResult(0).getType()))
3484 return type_cast<FVectorType>(refType.getType()).getElementType();
3485 auto firstPortType = type_cast<FIRRTLBaseType>(getResult(0).getType());
3486
3487 StringRef dataFieldName = "data";
3488 if (getMemPortKindFromType(firstPortType) == PortKind::ReadWrite)
3489 dataFieldName = "rdata";
3490
3491 return type_cast<BundleType>(firstPortType.getPassiveType())
3492 .getElementType(dataFieldName);
3493}
3494
3495StringAttr MemOp::getPortName(size_t resultNo) {
3496 return cast<StringAttr>(getPortNames()[resultNo]);
3497}
3498
3499FIRRTLBaseType MemOp::getPortType(size_t resultNo) {
3500 return type_cast<FIRRTLBaseType>(getResults()[resultNo].getType());
3501}
3502
3503Value MemOp::getPortNamed(StringAttr name) {
3504 auto namesArray = getPortNames();
3505 for (size_t i = 0, e = namesArray.size(); i != e; ++i) {
3506 if (namesArray[i] == name) {
3507 assert(i < getNumResults() && " names array out of sync with results");
3508 return getResult(i);
3509 }
3510 }
3511 return Value();
3512}
3513
3514// Extract all the relevant attributes from the MemOp and return the FirMemory.
3515FirMemory MemOp::getSummary() {
3516 auto op = *this;
3517 size_t numReadPorts = 0;
3518 size_t numWritePorts = 0;
3519 size_t numReadWritePorts = 0;
3521 SmallVector<int32_t> writeClockIDs;
3522
3523 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3524 auto portKind = op.getPortKind(i);
3525 if (portKind == MemOp::PortKind::Read)
3526 ++numReadPorts;
3527 else if (portKind == MemOp::PortKind::Write) {
3528 for (auto *a : op.getResult(i).getUsers()) {
3529 auto subfield = dyn_cast<SubfieldOp>(a);
3530 if (!subfield || subfield.getFieldIndex() != 2)
3531 continue;
3532 auto clockPort = a->getResult(0);
3533 for (auto *b : clockPort.getUsers()) {
3534 if (auto connect = dyn_cast<FConnectLike>(b)) {
3535 if (connect.getDest() == clockPort) {
3536 auto result =
3537 clockToLeader.insert({circt::firrtl::getModuleScopedDriver(
3538 connect.getSrc(), true, true, true),
3539 numWritePorts});
3540 if (result.second) {
3541 writeClockIDs.push_back(numWritePorts);
3542 } else {
3543 writeClockIDs.push_back(result.first->second);
3544 }
3545 }
3546 }
3547 }
3548 break;
3549 }
3550 ++numWritePorts;
3551 } else
3552 ++numReadWritePorts;
3553 }
3554
3555 size_t width = 0;
3556 if (auto widthV = getBitWidth(op.getDataType()))
3557 width = *widthV;
3558 else
3559 op.emitError("'firrtl.mem' should have simple type and known width");
3560 MemoryInitAttr init = op->getAttrOfType<MemoryInitAttr>("init");
3561 StringAttr modName;
3562 if (op->hasAttr("modName"))
3563 modName = op->getAttrOfType<StringAttr>("modName");
3564 else {
3565 SmallString<8> clocks;
3566 for (auto a : writeClockIDs)
3567 clocks.append(Twine((char)(a + 'a')).str());
3568 SmallString<32> initStr;
3569 // If there is a file initialization, then come up with a decent
3570 // representation for this. Use the filename, but only characters
3571 // [a-zA-Z0-9] and the bool/hex and inline booleans.
3572 if (init) {
3573 for (auto c : init.getFilename().getValue())
3574 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3575 (c >= '0' && c <= '9'))
3576 initStr.push_back(c);
3577 initStr.push_back('_');
3578 initStr.push_back(init.getIsBinary() ? 't' : 'f');
3579 initStr.push_back('_');
3580 initStr.push_back(init.getIsInline() ? 't' : 'f');
3581 }
3582 modName = StringAttr::get(
3583 op->getContext(),
3584 llvm::formatv(
3585 "{0}FIRRTLMem_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}{11}{12}",
3586 op.getPrefix().value_or(""), numReadPorts, numWritePorts,
3587 numReadWritePorts, (size_t)width, op.getDepth(),
3588 op.getReadLatency(), op.getWriteLatency(), op.getMaskBits(),
3589 (unsigned)op.getRuw(), (unsigned)seq::WUW::PortOrder,
3590 clocks.empty() ? "" : "_" + clocks, init ? initStr.str() : ""));
3591 }
3592 return {numReadPorts,
3593 numWritePorts,
3594 numReadWritePorts,
3595 (size_t)width,
3596 op.getDepth(),
3597 op.getReadLatency(),
3598 op.getWriteLatency(),
3599 op.getMaskBits(),
3600 *seq::symbolizeRUW(unsigned(op.getRuw())),
3601 seq::WUW::PortOrder,
3602 writeClockIDs,
3603 modName,
3604 op.getMaskBits() > 1,
3605 init,
3606 op.getPrefixAttr(),
3607 op.getLoc()};
3608}
3609
3610void MemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3611 StringRef base = getName();
3612 if (base.empty())
3613 base = "mem";
3614
3615 for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
3616 setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
3617 }
3618}
3619
3620std::optional<size_t> MemOp::getTargetResultIndex() {
3621 // Inner symbols on memory operations target the op not any result.
3622 return std::nullopt;
3623}
3624
3625// Construct name of the module which will be used for the memory definition.
3626StringAttr FirMemory::getFirMemoryName() const { return modName; }
3627
3628/// Helper for naming forceable declarations (and their optional ref result).
3629static void forceableAsmResultNames(Forceable op, StringRef name,
3630 OpAsmSetValueNameFn setNameFn) {
3631 if (name.empty())
3632 return;
3633 setNameFn(op.getDataRaw(), name);
3634 if (op.isForceable())
3635 setNameFn(op.getDataRef(), (name + "_ref").str());
3636}
3637
3638void NodeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3639 return forceableAsmResultNames(*this, getName(), setNameFn);
3640}
3641
3642LogicalResult NodeOp::inferReturnTypes(
3643 mlir::MLIRContext *context, std::optional<mlir::Location> location,
3644 ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
3645 ::mlir::OpaqueProperties properties, ::mlir::RegionRange regions,
3646 ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
3647 if (operands.empty())
3648 return failure();
3649 Adaptor adaptor(operands, attributes, properties, regions);
3650 inferredReturnTypes.push_back(adaptor.getInput().getType());
3651 if (adaptor.getForceable()) {
3652 auto forceableType = firrtl::detail::getForceableResultType(
3653 true, adaptor.getInput().getType());
3654 if (!forceableType) {
3655 if (location)
3656 ::mlir::emitError(*location, "cannot force a node of type ")
3657 << operands[0].getType();
3658 return failure();
3659 }
3660 inferredReturnTypes.push_back(forceableType);
3661 }
3662 return success();
3663}
3664
3665std::optional<size_t> NodeOp::getTargetResultIndex() { return 0; }
3666
3667void RegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3668 return forceableAsmResultNames(*this, getName(), setNameFn);
3669}
3670
3671std::optional<size_t> RegOp::getTargetResultIndex() { return 0; }
3672
3673SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
3674RegOp::computeDataFlow() {
3675 // A register does't have any combinational dataflow.
3676 return {};
3677}
3678
3679LogicalResult RegResetOp::verify() {
3680 auto reset = getResetValue();
3681
3682 FIRRTLBaseType resetType = reset.getType();
3683 FIRRTLBaseType regType = getResult().getType();
3684
3685 // The type of the initialiser must be equivalent to the register type.
3686 if (!areTypesEquivalent(regType, resetType))
3687 return emitError("type mismatch between register ")
3688 << regType << " and reset value " << resetType;
3689
3690 return success();
3691}
3692
3693std::optional<size_t> RegResetOp::getTargetResultIndex() { return 0; }
3694
3695void RegResetOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3696 return forceableAsmResultNames(*this, getName(), setNameFn);
3697}
3698
3699//===----------------------------------------------------------------------===//
3700// FormalOp
3701//===----------------------------------------------------------------------===//
3702
3703LogicalResult
3704FormalOp::verifySymbolUses(mlir::SymbolTableCollection &symbolTable) {
3705 auto *op = symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
3706 if (!op)
3707 return emitOpError() << "targets unknown module " << getModuleNameAttr();
3708
3709 if (!isa<FModuleLike>(op)) {
3710 auto d = emitOpError() << "target " << getModuleNameAttr()
3711 << " is not a module";
3712 d.attachNote(op->getLoc()) << "target defined here";
3713 return d;
3714 }
3715
3716 return success();
3717}
3718
3719//===----------------------------------------------------------------------===//
3720// SimulationOp
3721//===----------------------------------------------------------------------===//
3722
3723LogicalResult
3724SimulationOp::verifySymbolUses(mlir::SymbolTableCollection &symbolTable) {
3725 auto *op = symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
3726 if (!op)
3727 return emitOpError() << "targets unknown module " << getModuleNameAttr();
3728
3729 auto complain = [&] {
3730 auto d = emitOpError() << "target " << getModuleNameAttr() << " ";
3731 d.attachNote(op->getLoc()) << "target defined here";
3732 return d;
3733 };
3734
3735 auto module = dyn_cast<FModuleLike>(op);
3736 if (!module)
3737 return complain() << "is not a module";
3738
3739 auto numPorts = module.getPortDirections().size();
3740 if (numPorts != 4)
3741 return complain() << "must have 4 ports, got " << numPorts << " instead";
3742
3743 auto checkPort = [&](unsigned idx, StringRef expName, Direction expDir,
3744 llvm::function_ref<bool(Type)> checkType,
3745 StringRef expType) {
3746 auto name = module.getPortNameAttr(idx);
3747 if (name != expName) {
3748 complain() << "port " << idx << " must be called \"" << expName
3749 << "\", got " << name << " instead";
3750 return false;
3751 }
3752 if (auto dir = module.getPortDirection(idx); dir != expDir) {
3753 auto stringify = [](Direction dir) {
3754 return dir == Direction::In ? "an input" : "an output";
3755 };
3756 complain() << "port " << name << " must be " << stringify(expDir)
3757 << ", got " << stringify(dir) << " instead";
3758 return false;
3759 }
3760 if (auto type = module.getPortType(idx); !checkType(type)) {
3761 complain() << "port " << name << " must be a '!firrtl." << expType
3762 << "', got " << type << " instead";
3763 return false;
3764 }
3765 return true;
3766 };
3767
3768 auto isClock = [](Type type) { return isa<ClockType>(type); };
3769 auto isBool = [](Type type) {
3770 if (auto uintType = dyn_cast<UIntType>(type))
3771 return uintType.getWidth() == 1;
3772 return false;
3773 };
3774 return success(checkPort(0, "clock", Direction::In, isClock, "clock") &&
3775 checkPort(1, "init", Direction::In, isBool, "uint<1>") &&
3776 checkPort(2, "done", Direction::Out, isBool, "uint<1>") &&
3777 checkPort(3, "success", Direction::Out, isBool, "uint<1>"));
3778}
3779
3780//===----------------------------------------------------------------------===//
3781// WireOp
3782//===----------------------------------------------------------------------===//
3783
3784void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3785 return forceableAsmResultNames(*this, getName(), setNameFn);
3786}
3787
3788SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
3789RegResetOp::computeDataFlow() {
3790 // A register does't have any combinational dataflow.
3791 return {};
3792}
3793
3794std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
3795
3796LogicalResult WireOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3797 auto refType = type_dyn_cast<RefType>(getType(0));
3798 if (!refType)
3799 return success();
3800
3801 return verifyProbeType(
3802 refType, getLoc(), getOperation()->getParentOfType<CircuitOp>(),
3803 symbolTable, Twine("'") + getOperationName() + "' op is");
3804}
3805
3806//===----------------------------------------------------------------------===//
3807// ContractOp
3808//===----------------------------------------------------------------------===//
3809
3810LogicalResult ContractOp::verify() {
3811 if (getBody().getArgumentTypes() != getInputs().getType())
3812 return emitOpError("result types and region argument types must match");
3813 return success();
3814}
3815
3816//===----------------------------------------------------------------------===//
3817// ObjectOp
3818//===----------------------------------------------------------------------===//
3819
3820void ObjectOp::build(OpBuilder &builder, OperationState &state, ClassLike klass,
3821 StringRef name) {
3822 build(builder, state, klass.getInstanceType(),
3823 StringAttr::get(builder.getContext(), name));
3824}
3825
3826LogicalResult ObjectOp::verify() { return success(); }
3827
3828LogicalResult ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3829 auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
3830 auto classType = getType();
3831 auto className = classType.getNameAttr();
3832
3833 // verify that the class exists.
3834 auto classOp = dyn_cast_or_null<ClassLike>(
3835 symbolTable.lookupSymbolIn(circuitOp, className));
3836 if (!classOp)
3837 return emitOpError() << "references unknown class " << className;
3838
3839 // verify that the result type agrees with the class definition.
3840 if (failed(classOp.verifyType(classType, [&]() { return emitOpError(); })))
3841 return failure();
3842
3843 return success();
3844}
3845
3846StringAttr ObjectOp::getClassNameAttr() {
3847 return getType().getNameAttr().getAttr();
3848}
3849
3850StringRef ObjectOp::getClassName() { return getType().getName(); }
3851
3852ClassLike ObjectOp::getReferencedClass(const SymbolTable &symbolTable) {
3853 auto symRef = getType().getNameAttr();
3854 return symbolTable.lookup<ClassLike>(symRef.getLeafReference());
3855}
3856
3857Operation *ObjectOp::getReferencedOperation(const SymbolTable &symtbl) {
3858 return getReferencedClass(symtbl);
3859}
3860
3861StringRef ObjectOp::getInstanceName() { return getName(); }
3862
3863StringAttr ObjectOp::getInstanceNameAttr() { return getNameAttr(); }
3864
3865StringRef ObjectOp::getReferencedModuleName() { return getClassName(); }
3866
3867StringAttr ObjectOp::getReferencedModuleNameAttr() {
3868 return getClassNameAttr();
3869}
3870
3871void ObjectOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3872 setNameFn(getResult(), getName());
3873}
3874
3875//===----------------------------------------------------------------------===//
3876// Statements
3877//===----------------------------------------------------------------------===//
3878
3879LogicalResult AttachOp::verify() {
3880 // All known widths must match.
3881 std::optional<int32_t> commonWidth;
3882 for (auto operand : getOperands()) {
3883 auto thisWidth = type_cast<AnalogType>(operand.getType()).getWidth();
3884 if (!thisWidth)
3885 continue;
3886 if (!commonWidth) {
3887 commonWidth = thisWidth;
3888 continue;
3889 }
3890 if (commonWidth != thisWidth)
3891 return emitOpError("is inavlid as not all known operand widths match");
3892 }
3893 return success();
3894}
3895
3896/// Check if the source and sink are of appropriate flow.
3897static LogicalResult checkConnectFlow(Operation *connect) {
3898 Value dst = connect->getOperand(0);
3899 Value src = connect->getOperand(1);
3900
3901 // TODO: Relax this to allow reads from output ports,
3902 // instance/memory input ports.
3903 auto srcFlow = foldFlow(src);
3904 if (!isValidSrc(srcFlow)) {
3905 // A sink that is a port output or instance input used as a source is okay,
3906 // as long as it is not a property.
3907 auto kind = getDeclarationKind(src);
3908 if (isa<PropertyType>(src.getType()) ||
3909 (kind != DeclKind::Port && kind != DeclKind::Instance)) {
3910 auto srcRef = getFieldRefFromValue(src, /*lookThroughCasts=*/true);
3911 auto [srcName, rootKnown] = getFieldName(srcRef);
3912 auto diag = emitError(connect->getLoc());
3913 diag << "connect has invalid flow: the source expression ";
3914 if (rootKnown)
3915 diag << "\"" << srcName << "\" ";
3916 diag << "has " << toString(srcFlow) << ", expected source or duplex flow";
3917 return diag.attachNote(srcRef.getLoc()) << "the source was defined here";
3918 }
3919 }
3920
3921 auto dstFlow = foldFlow(dst);
3922 if (!isValidDst(dstFlow)) {
3923 auto dstRef = getFieldRefFromValue(dst, /*lookThroughCasts=*/true);
3924 auto [dstName, rootKnown] = getFieldName(dstRef);
3925 auto diag = emitError(connect->getLoc());
3926 diag << "connect has invalid flow: the destination expression ";
3927 if (rootKnown)
3928 diag << "\"" << dstName << "\" ";
3929 diag << "has " << toString(dstFlow) << ", expected sink or duplex flow";
3930 return diag.attachNote(dstRef.getLoc())
3931 << "the destination was defined here";
3932 }
3933 return success();
3934}
3935
3936// NOLINTBEGIN(misc-no-recursion)
3937/// Checks if the type has any 'const' leaf elements . If `isFlip` is `true`,
3938/// the `const` leaf is not considered to be driven.
3939static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip = false,
3940 bool outerTypeIsConst = false) {
3941 auto typeIsConst = outerTypeIsConst || type.isConst();
3942
3943 if (typeIsConst && type.isPassive())
3944 return !isFlip;
3945
3946 if (auto bundleType = type_dyn_cast<BundleType>(type))
3947 return llvm::any_of(bundleType.getElements(), [&](auto &element) {
3948 return isConstFieldDriven(element.type, isFlip ^ element.isFlip,
3949 typeIsConst);
3950 });
3951
3952 if (auto vectorType = type_dyn_cast<FVectorType>(type))
3953 return isConstFieldDriven(vectorType.getElementType(), isFlip, typeIsConst);
3954
3955 if (typeIsConst)
3956 return !isFlip;
3957 return false;
3958}
3959// NOLINTEND(misc-no-recursion)
3960
3961/// Checks that connections to 'const' destinations are not dependent on
3962/// non-'const' conditions in when blocks.
3963static LogicalResult checkConnectConditionality(FConnectLike connect) {
3964 auto dest = connect.getDest();
3965 auto destType = type_dyn_cast<FIRRTLBaseType>(dest.getType());
3966 auto src = connect.getSrc();
3967 auto srcType = type_dyn_cast<FIRRTLBaseType>(src.getType());
3968 if (!destType || !srcType)
3969 return success();
3970
3971 auto destRefinedType = destType;
3972 auto srcRefinedType = srcType;
3973
3974 /// Looks up the value's defining op until the defining op is null or a
3975 /// declaration of the value. If a SubAccessOp is encountered with a 'const'
3976 /// input, `originalFieldType` is made 'const'.
3977 auto findFieldDeclarationRefiningFieldType =
3978 [](Value value, FIRRTLBaseType &originalFieldType) -> Value {
3979 while (auto *definingOp = value.getDefiningOp()) {
3980 bool shouldContinue = true;
3981 TypeSwitch<Operation *>(definingOp)
3982 .Case<SubfieldOp, SubindexOp>([&](auto op) { value = op.getInput(); })
3983 .Case<SubaccessOp>([&](SubaccessOp op) {
3984 if (op.getInput()
3985 .getType()
3986 .base()
3987 .getElementTypePreservingConst()
3988 .isConst())
3989 originalFieldType = originalFieldType.getConstType(true);
3990 value = op.getInput();
3991 })
3992 .Default([&](Operation *) { shouldContinue = false; });
3993 if (!shouldContinue)
3994 break;
3995 }
3996 return value;
3997 };
3998
3999 auto destDeclaration =
4000 findFieldDeclarationRefiningFieldType(dest, destRefinedType);
4001 auto srcDeclaration =
4002 findFieldDeclarationRefiningFieldType(src, srcRefinedType);
4003
4004 auto checkConstConditionality = [&](Value value, FIRRTLBaseType type,
4005 Value declaration) -> LogicalResult {
4006 auto *declarationBlock = declaration.getParentBlock();
4007 auto *block = connect->getBlock();
4008 while (block && block != declarationBlock) {
4009 auto *parentOp = block->getParentOp();
4010
4011 if (auto whenOp = dyn_cast<WhenOp>(parentOp);
4012 whenOp && !whenOp.getCondition().getType().isConst()) {
4013 if (type.isConst())
4014 return connect.emitOpError()
4015 << "assignment to 'const' type " << type
4016 << " is dependent on a non-'const' condition";
4017 return connect->emitOpError()
4018 << "assignment to nested 'const' member of type " << type
4019 << " is dependent on a non-'const' condition";
4020 }
4021
4022 block = parentOp->getBlock();
4023 }
4024 return success();
4025 };
4026
4027 auto emitSubaccessError = [&] {
4028 return connect.emitError(
4029 "assignment to non-'const' subaccess of 'const' type is disallowed");
4030 };
4031
4032 // Check destination if it contains 'const' leaves
4033 if (destRefinedType.containsConst() && isConstFieldDriven(destRefinedType)) {
4034 // Disallow assignment to non-'const' subaccesses of 'const' types
4035 if (destType != destRefinedType)
4036 return emitSubaccessError();
4037
4038 if (failed(checkConstConditionality(dest, destType, destDeclaration)))
4039 return failure();
4040 }
4041
4042 // Check source if it contains 'const' 'flip' leaves
4043 if (srcRefinedType.containsConst() &&
4044 isConstFieldDriven(srcRefinedType, /*isFlip=*/true)) {
4045 // Disallow assignment to non-'const' subaccesses of 'const' types
4046 if (srcType != srcRefinedType)
4047 return emitSubaccessError();
4048 if (failed(checkConstConditionality(src, srcType, srcDeclaration)))
4049 return failure();
4050 }
4051
4052 return success();
4053}
4054
4055LogicalResult ConnectOp::verify() {
4056 auto dstType = getDest().getType();
4057 auto srcType = getSrc().getType();
4058 auto dstBaseType = type_dyn_cast<FIRRTLBaseType>(dstType);
4059 auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(srcType);
4060 if (!dstBaseType || !srcBaseType) {
4061 if (dstType != srcType)
4062 return emitError("may not connect different non-base types");
4063 } else {
4064 // Analog types cannot be connected and must be attached.
4065 if (dstBaseType.containsAnalog() || srcBaseType.containsAnalog())
4066 return emitError("analog types may not be connected");
4067
4068 // Destination and source types must be equivalent.
4069 if (!areTypesEquivalent(dstBaseType, srcBaseType))
4070 return emitError("type mismatch between destination ")
4071 << dstBaseType << " and source " << srcBaseType;
4072
4073 // Truncation is banned in a connection: destination bit width must be
4074 // greater than or equal to source bit width.
4075 if (!isTypeLarger(dstBaseType, srcBaseType))
4076 return emitError("destination ")
4077 << dstBaseType << " is not as wide as the source " << srcBaseType;
4078 }
4079
4080 // Check that the flows make sense.
4081 if (failed(checkConnectFlow(*this)))
4082 return failure();
4083
4084 if (failed(checkConnectConditionality(*this)))
4085 return failure();
4086
4087 return success();
4088}
4089
4090LogicalResult MatchingConnectOp::verify() {
4091 if (auto type = type_dyn_cast<FIRRTLType>(getDest().getType())) {
4092 auto baseType = type_cast<FIRRTLBaseType>(type);
4093
4094 // Analog types cannot be connected and must be attached.
4095 if (baseType && baseType.containsAnalog())
4096 return emitError("analog types may not be connected");
4097
4098 // The anonymous types of operands must be equivalent.
4099 assert(areAnonymousTypesEquivalent(cast<FIRRTLBaseType>(getSrc().getType()),
4100 baseType) &&
4101 "`SameAnonTypeOperands` trait should have already rejected "
4102 "structurally non-equivalent types");
4103 }
4104
4105 // Check that the flows make sense.
4106 if (failed(checkConnectFlow(*this)))
4107 return failure();
4108
4109 if (failed(checkConnectConditionality(*this)))
4110 return failure();
4111
4112 return success();
4113}
4114
4115LogicalResult RefDefineOp::verify() {
4116 // Check that the flows make sense.
4117 if (failed(checkConnectFlow(*this)))
4118 return failure();
4119
4120 // For now, refs can't be in bundles so this is sufficient.
4121 // In the future need to ensure no other define's to same "fieldSource".
4122 // (When aggregates can have references, we can define a reference within,
4123 // but this must be unique. Checking this here may be expensive,
4124 // consider adding something to FModuleLike's to check it there instead)
4125 for (auto *user : getDest().getUsers()) {
4126 if (auto conn = dyn_cast<FConnectLike>(user);
4127 conn && conn.getDest() == getDest() && conn != *this)
4128 return emitError("destination reference cannot be reused by multiple "
4129 "operations, it can only capture a unique dataflow");
4130 }
4131
4132 // Check "static" source/dest
4133 if (auto *op = getDest().getDefiningOp()) {
4134 // TODO: Make ref.sub only source flow?
4135 if (isa<RefSubOp>(op))
4136 return emitError(
4137 "destination reference cannot be a sub-element of a reference");
4138 if (isa<RefCastOp>(op)) // Source flow, check anyway for now.
4139 return emitError(
4140 "destination reference cannot be a cast of another reference");
4141 }
4142
4143 // This define is only enabled when its ambient layers are active. Check
4144 // that whenever the destination's layer requirements are met, that this
4145 // op is enabled.
4146 auto ambientLayers = getAmbientLayersAt(getOperation());
4147 auto dstLayers = getLayersFor(getDest());
4148 SmallVector<SymbolRefAttr> missingLayers;
4149
4150 return checkLayerCompatibility(getOperation(), ambientLayers, dstLayers,
4151 "has more layer requirements than destination",
4152 "additional layers required");
4153}
4154
4155LogicalResult PropAssignOp::verify() {
4156 // Check that the flows make sense.
4157 if (failed(checkConnectFlow(*this)))
4158 return failure();
4159
4160 // Verify that there is a single value driving the destination.
4161 for (auto *user : getDest().getUsers()) {
4162 if (auto conn = dyn_cast<FConnectLike>(user);
4163 conn && conn.getDest() == getDest() && conn != *this)
4164 return emitError("destination property cannot be reused by multiple "
4165 "operations, it can only capture a unique dataflow");
4166 }
4167
4168 return success();
4169}
4170
4171void WhenOp::createElseRegion() {
4172 assert(!hasElseRegion() && "already has an else region");
4173 getElseRegion().push_back(new Block());
4174}
4175
4176void WhenOp::build(OpBuilder &builder, OperationState &result, Value condition,
4177 bool withElseRegion, std::function<void()> thenCtor,
4178 std::function<void()> elseCtor) {
4179 OpBuilder::InsertionGuard guard(builder);
4180 result.addOperands(condition);
4181
4182 // Create "then" region.
4183 builder.createBlock(result.addRegion());
4184 if (thenCtor)
4185 thenCtor();
4186
4187 // Create "else" region.
4188 Region *elseRegion = result.addRegion();
4189 if (withElseRegion) {
4190 builder.createBlock(elseRegion);
4191 if (elseCtor)
4192 elseCtor();
4193 }
4194}
4195
4196//===----------------------------------------------------------------------===//
4197// MatchOp
4198//===----------------------------------------------------------------------===//
4199
4200LogicalResult MatchOp::verify() {
4201 FEnumType type = getInput().getType();
4202
4203 // Make sure that the number of tags matches the number of regions.
4204 auto numCases = getTags().size();
4205 auto numRegions = getNumRegions();
4206 if (numRegions != numCases)
4207 return emitOpError("expected ")
4208 << numRegions << " tags but got " << numCases;
4209
4210 auto numTags = type.getNumElements();
4211
4212 SmallDenseSet<int64_t> seen;
4213 for (const auto &[tag, region] : llvm::zip(getTags(), getRegions())) {
4214 auto tagIndex = size_t(cast<IntegerAttr>(tag).getInt());
4215
4216 // Ensure that the block has a single argument.
4217 if (region.getNumArguments() != 1)
4218 return emitOpError("region should have exactly one argument");
4219
4220 // Make sure that it is a valid tag.
4221 if (tagIndex >= numTags)
4222 return emitOpError("the tag index ")
4223 << tagIndex << " is out of the range of valid tags in " << type;
4224
4225 // Make sure we have not already matched this tag.
4226 auto [it, inserted] = seen.insert(tagIndex);
4227 if (!inserted)
4228 return emitOpError("the tag ") << type.getElementNameAttr(tagIndex)
4229 << " is matched more than once";
4230
4231 // Check that the block argument type matches the tag's type.
4232 auto expectedType = type.getElementTypePreservingConst(tagIndex);
4233 auto regionType = region.getArgument(0).getType();
4234 if (regionType != expectedType)
4235 return emitOpError("region type ")
4236 << regionType << " does not match the expected type "
4237 << expectedType;
4238 }
4239
4240 // Check that the match statement is exhaustive.
4241 for (size_t i = 0, e = type.getNumElements(); i < e; ++i)
4242 if (!seen.contains(i))
4243 return emitOpError("missing case for tag ") << type.getElementNameAttr(i);
4244
4245 return success();
4246}
4247
4248void MatchOp::print(OpAsmPrinter &p) {
4249 auto input = getInput();
4250 FEnumType type = input.getType();
4251 auto regions = getRegions();
4252 p << " " << input << " : " << type;
4253 SmallVector<StringRef> elided = {"tags"};
4254 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elided);
4255 p << " {";
4256 p.increaseIndent();
4257 for (const auto &[tag, region] : llvm::zip(getTags(), regions)) {
4258 p.printNewline();
4259 p << "case ";
4260 p.printKeywordOrString(
4261 type.getElementName(cast<IntegerAttr>(tag).getInt()));
4262 p << "(";
4263 p.printRegionArgument(region.front().getArgument(0), /*attrs=*/{},
4264 /*omitType=*/true);
4265 p << ") ";
4266 p.printRegion(region, /*printEntryBlockArgs=*/false);
4267 }
4268 p.decreaseIndent();
4269 p.printNewline();
4270 p << "}";
4271}
4272
4273ParseResult MatchOp::parse(OpAsmParser &parser, OperationState &result) {
4274 auto *context = parser.getContext();
4275 auto &properties = result.getOrAddProperties<Properties>();
4276 OpAsmParser::UnresolvedOperand input;
4277 if (parser.parseOperand(input) || parser.parseColon())
4278 return failure();
4279
4280 auto loc = parser.getCurrentLocation();
4281 Type type;
4282 if (parser.parseType(type))
4283 return failure();
4284 auto enumType = type_dyn_cast<FEnumType>(type);
4285 if (!enumType)
4286 return parser.emitError(loc, "expected enumeration type but got") << type;
4287
4288 if (parser.resolveOperand(input, type, result.operands) ||
4289 parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
4290 parser.parseLBrace())
4291 return failure();
4292
4293 auto i32Type = IntegerType::get(context, 32);
4294 SmallVector<Attribute> tags;
4295 while (true) {
4296 // Stop parsing when we don't find another "case" keyword.
4297 if (failed(parser.parseOptionalKeyword("case")))
4298 break;
4299
4300 // Parse the tag and region argument.
4301 auto nameLoc = parser.getCurrentLocation();
4302 std::string name;
4303 OpAsmParser::Argument arg;
4304 auto *region = result.addRegion();
4305 if (parser.parseKeywordOrString(&name) || parser.parseLParen() ||
4306 parser.parseArgument(arg) || parser.parseRParen())
4307 return failure();
4308
4309 // Figure out the enum index of the tag.
4310 auto index = enumType.getElementIndex(name);
4311 if (!index)
4312 return parser.emitError(nameLoc, "the tag \"")
4313 << name << "\" is not a member of the enumeration " << enumType;
4314 tags.push_back(IntegerAttr::get(i32Type, *index));
4315
4316 // Parse the region.
4317 arg.type = enumType.getElementTypePreservingConst(*index);
4318 if (parser.parseRegion(*region, arg))
4319 return failure();
4320 }
4321 properties.setTags(ArrayAttr::get(context, tags));
4322
4323 return parser.parseRBrace();
4324}
4325
4326void MatchOp::build(OpBuilder &builder, OperationState &result, Value input,
4327 ArrayAttr tags,
4328 MutableArrayRef<std::unique_ptr<Region>> regions) {
4329 auto &properties = result.getOrAddProperties<Properties>();
4330 result.addOperands(input);
4331 properties.setTags(tags);
4332 result.addRegions(regions);
4333}
4334
4335//===----------------------------------------------------------------------===//
4336// Expressions
4337//===----------------------------------------------------------------------===//
4338
4339/// Return true if the specified operation is a firrtl expression.
4340bool firrtl::isExpression(Operation *op) {
4341 struct IsExprClassifier : public ExprVisitor<IsExprClassifier, bool> {
4342 bool visitInvalidExpr(Operation *op) { return false; }
4343 bool visitUnhandledExpr(Operation *op) { return true; }
4344 };
4345
4346 return IsExprClassifier().dispatchExprVisitor(op);
4347}
4348
4349void InvalidValueOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4350 // Set invalid values to have a distinct name.
4351 std::string name;
4352 if (auto ty = type_dyn_cast<IntType>(getType())) {
4353 const char *base = ty.isSigned() ? "invalid_si" : "invalid_ui";
4354 auto width = ty.getWidthOrSentinel();
4355 if (width == -1)
4356 name = base;
4357 else
4358 name = (Twine(base) + Twine(width)).str();
4359 } else if (auto ty = type_dyn_cast<AnalogType>(getType())) {
4360 auto width = ty.getWidthOrSentinel();
4361 if (width == -1)
4362 name = "invalid_analog";
4363 else
4364 name = ("invalid_analog" + Twine(width)).str();
4365 } else if (type_isa<AsyncResetType>(getType()))
4366 name = "invalid_asyncreset";
4367 else if (type_isa<ResetType>(getType()))
4368 name = "invalid_reset";
4369 else if (type_isa<ClockType>(getType()))
4370 name = "invalid_clock";
4371 else
4372 name = "invalid";
4373
4374 setNameFn(getResult(), name);
4375}
4376
4377void ConstantOp::print(OpAsmPrinter &p) {
4378 p << " ";
4379 p.printAttributeWithoutType(getValueAttr());
4380 p << " : ";
4381 p.printType(getType());
4382 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4383}
4384
4385ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
4386 auto &properties = result.getOrAddProperties<Properties>();
4387 // Parse the constant value, without knowing its width.
4388 APInt value;
4389 auto loc = parser.getCurrentLocation();
4390 auto valueResult = parser.parseOptionalInteger(value);
4391 if (!valueResult.has_value())
4392 return parser.emitError(loc, "expected integer value");
4393
4394 // Parse the result firrtl integer type.
4395 IntType resultType;
4396 if (failed(*valueResult) || parser.parseColonType(resultType) ||
4397 parser.parseOptionalAttrDict(result.attributes))
4398 return failure();
4399 result.addTypes(resultType);
4400
4401 // Now that we know the width and sign of the result type, we can munge the
4402 // APInt as appropriate.
4403 if (resultType.hasWidth()) {
4404 auto width = (unsigned)resultType.getWidthOrSentinel();
4405 if (width > value.getBitWidth()) {
4406 // sext is always safe here, even for unsigned values, because the
4407 // parseOptionalInteger method will return something with a zero in the
4408 // top bits if it is a positive number.
4409 value = value.sext(width);
4410 } else if (width < value.getBitWidth()) {
4411 // The parser can return an unnecessarily wide result with leading
4412 // zeros. This isn't a problem, but truncating off bits is bad.
4413 unsigned neededBits = value.isNegative() ? value.getSignificantBits()
4414 : value.getActiveBits();
4415 if (width < neededBits)
4416 return parser.emitError(loc, "constant out of range for result type ")
4417 << resultType;
4418 value = value.trunc(width);
4419 }
4420 }
4421
4422 auto intType = parser.getBuilder().getIntegerType(value.getBitWidth(),
4423 resultType.isSigned());
4424 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
4425 properties.setValue(valueAttr);
4426 return success();
4427}
4428
4429LogicalResult ConstantOp::verify() {
4430 // If the result type has a bitwidth, then the attribute must match its width.
4431 IntType intType = getType();
4432 auto width = intType.getWidthOrSentinel();
4433 if (width != -1 && (int)getValue().getBitWidth() != width)
4434 return emitError(
4435 "firrtl.constant attribute bitwidth doesn't match return type");
4436
4437 // The sign of the attribute's integer type must match our integer type sign.
4438 auto attrType = type_cast<IntegerType>(getValueAttr().getType());
4439 if (attrType.isSignless() || attrType.isSigned() != intType.isSigned())
4440 return emitError("firrtl.constant attribute has wrong sign");
4441
4442 return success();
4443}
4444
4445/// Build a ConstantOp from an APInt and a FIRRTL type, handling the attribute
4446/// formation for the 'value' attribute.
4447void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
4448 const APInt &value) {
4449 int32_t width = type.getWidthOrSentinel();
4450 (void)width;
4451 assert((width == -1 || (int32_t)value.getBitWidth() == width) &&
4452 "incorrect attribute bitwidth for firrtl.constant");
4453
4454 auto attr =
4455 IntegerAttr::get(type.getContext(), APSInt(value, !type.isSigned()));
4456 return build(builder, result, type, attr);
4457}
4458
4459/// Build a ConstantOp from an APSInt, handling the attribute formation for the
4460/// 'value' attribute and inferring the FIRRTL type.
4461void ConstantOp::build(OpBuilder &builder, OperationState &result,
4462 const APSInt &value) {
4463 auto attr = IntegerAttr::get(builder.getContext(), value);
4464 auto type =
4465 IntType::get(builder.getContext(), value.isSigned(), value.getBitWidth());
4466 return build(builder, result, type, attr);
4467}
4468
4469void ConstantOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4470 // For constants in particular, propagate the value into the result name to
4471 // make it easier to read the IR.
4472 IntType intTy = getType();
4473 assert(intTy);
4474
4475 // Otherwise, build a complex name with the value and type.
4476 SmallString<32> specialNameBuffer;
4477 llvm::raw_svector_ostream specialName(specialNameBuffer);
4478 specialName << 'c';
4479 getValue().print(specialName, /*isSigned:*/ intTy.isSigned());
4480
4481 specialName << (intTy.isSigned() ? "_si" : "_ui");
4482 auto width = intTy.getWidthOrSentinel();
4483 if (width != -1)
4484 specialName << width;
4485 setNameFn(getResult(), specialName.str());
4486}
4487
4488void SpecialConstantOp::print(OpAsmPrinter &p) {
4489 p << " ";
4490 // SpecialConstant uses a BoolAttr, and we want to print `true` as `1`.
4491 p << static_cast<unsigned>(getValue());
4492 p << " : ";
4493 p.printType(getType());
4494 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4495}
4496
4497ParseResult SpecialConstantOp::parse(OpAsmParser &parser,
4498 OperationState &result) {
4499 auto &properties = result.getOrAddProperties<Properties>();
4500 // Parse the constant value. SpecialConstant uses bool attributes, but it
4501 // prints as an integer.
4502 APInt value;
4503 auto loc = parser.getCurrentLocation();
4504 auto valueResult = parser.parseOptionalInteger(value);
4505 if (!valueResult.has_value())
4506 return parser.emitError(loc, "expected integer value");
4507
4508 // Clocks and resets can only be 0 or 1.
4509 if (value != 0 && value != 1)
4510 return parser.emitError(loc, "special constants can only be 0 or 1.");
4511
4512 // Parse the result firrtl type.
4513 Type resultType;
4514 if (failed(*valueResult) || parser.parseColonType(resultType) ||
4515 parser.parseOptionalAttrDict(result.attributes))
4516 return failure();
4517 result.addTypes(resultType);
4518
4519 // Create the attribute.
4520 auto valueAttr = parser.getBuilder().getBoolAttr(value == 1);
4521 properties.setValue(valueAttr);
4522 return success();
4523}
4524
4525void SpecialConstantOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4526 SmallString<32> specialNameBuffer;
4527 llvm::raw_svector_ostream specialName(specialNameBuffer);
4528 specialName << 'c';
4529 specialName << static_cast<unsigned>(getValue());
4530 auto type = getType();
4531 if (type_isa<ClockType>(type)) {
4532 specialName << "_clock";
4533 } else if (type_isa<ResetType>(type)) {
4534 specialName << "_reset";
4535 } else if (type_isa<AsyncResetType>(type)) {
4536 specialName << "_asyncreset";
4537 }
4538 setNameFn(getResult(), specialName.str());
4539}
4540
4541// Checks that an array attr representing an aggregate constant has the correct
4542// shape. This recurses on the type.
4543static bool checkAggConstant(Operation *op, Attribute attr,
4544 FIRRTLBaseType type) {
4545 if (type.isGround()) {
4546 if (!isa<IntegerAttr>(attr)) {
4547 op->emitOpError("Ground type is not an integer attribute");
4548 return false;
4549 }
4550 return true;
4551 }
4552 auto attrlist = dyn_cast<ArrayAttr>(attr);
4553 if (!attrlist) {
4554 op->emitOpError("expected array attribute for aggregate constant");
4555 return false;
4556 }
4557 if (auto array = type_dyn_cast<FVectorType>(type)) {
4558 if (array.getNumElements() != attrlist.size()) {
4559 op->emitOpError("array attribute (")
4560 << attrlist.size() << ") has wrong size for vector constant ("
4561 << array.getNumElements() << ")";
4562 return false;
4563 }
4564 return llvm::all_of(attrlist, [&array, op](Attribute attr) {
4565 return checkAggConstant(op, attr, array.getElementType());
4566 });
4567 }
4568 if (auto bundle = type_dyn_cast<BundleType>(type)) {
4569 if (bundle.getNumElements() != attrlist.size()) {
4570 op->emitOpError("array attribute (")
4571 << attrlist.size() << ") has wrong size for bundle constant ("
4572 << bundle.getNumElements() << ")";
4573 return false;
4574 }
4575 for (size_t i = 0; i < bundle.getNumElements(); ++i) {
4576 if (bundle.getElement(i).isFlip) {
4577 op->emitOpError("Cannot have constant bundle type with flip");
4578 return false;
4579 }
4580 if (!checkAggConstant(op, attrlist[i], bundle.getElement(i).type))
4581 return false;
4582 }
4583 return true;
4584 }
4585 op->emitOpError("Unknown aggregate type");
4586 return false;
4587}
4588
4589LogicalResult AggregateConstantOp::verify() {
4590 if (checkAggConstant(getOperation(), getFields(), getType()))
4591 return success();
4592 return failure();
4593}
4594
4595Attribute AggregateConstantOp::getAttributeFromFieldID(uint64_t fieldID) {
4596 FIRRTLBaseType type = getType();
4597 Attribute value = getFields();
4598 while (fieldID != 0) {
4599 if (auto bundle = type_dyn_cast<BundleType>(type)) {
4600 auto index = bundle.getIndexForFieldID(fieldID);
4601 fieldID -= bundle.getFieldID(index);
4602 type = bundle.getElementType(index);
4603 value = cast<ArrayAttr>(value)[index];
4604 } else {
4605 auto vector = type_cast<FVectorType>(type);
4606 auto index = vector.getIndexForFieldID(fieldID);
4607 fieldID -= vector.getFieldID(index);
4608 type = vector.getElementType();
4609 value = cast<ArrayAttr>(value)[index];
4610 }
4611 }
4612 return value;
4613}
4614
4615void FIntegerConstantOp::print(OpAsmPrinter &p) {
4616 p << " ";
4617 p.printAttributeWithoutType(getValueAttr());
4618 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4619}
4620
4621ParseResult FIntegerConstantOp::parse(OpAsmParser &parser,
4622 OperationState &result) {
4623 auto *context = parser.getContext();
4624 auto &properties = result.getOrAddProperties<Properties>();
4625 APInt value;
4626 if (parser.parseInteger(value) ||
4627 parser.parseOptionalAttrDict(result.attributes))
4628 return failure();
4629 result.addTypes(FIntegerType::get(context));
4630 auto intType =
4631 IntegerType::get(context, value.getBitWidth(), IntegerType::Signed);
4632 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
4633 properties.setValue(valueAttr);
4634 return success();
4635}
4636
4637ParseResult ListCreateOp::parse(OpAsmParser &parser, OperationState &result) {
4638 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
4639 ListType type;
4640
4641 if (parser.parseOperandList(operands) ||
4642 parser.parseOptionalAttrDict(result.attributes) ||
4643 parser.parseColonType(type))
4644 return failure();
4645 result.addTypes(type);
4646
4647 return parser.resolveOperands(operands, type.getElementType(),
4648 result.operands);
4649}
4650
4651void ListCreateOp::print(OpAsmPrinter &p) {
4652 p << " ";
4653 p.printOperands(getElements());
4654 p.printOptionalAttrDict((*this)->getAttrs());
4655 p << " : " << getType();
4656}
4657
4658LogicalResult ListCreateOp::verify() {
4659 if (getElements().empty())
4660 return success();
4661
4662 auto elementType = getElements().front().getType();
4663 auto listElementType = getType().getElementType();
4664 if (elementType != listElementType)
4665 return emitOpError("has elements of type ")
4666 << elementType << " instead of " << listElementType;
4667
4668 return success();
4669}
4670
4671LogicalResult BundleCreateOp::verify() {
4672 BundleType resultType = getType();
4673 if (resultType.getNumElements() != getFields().size())
4674 return emitOpError("number of fields doesn't match type");
4675 for (size_t i = 0; i < resultType.getNumElements(); ++i)
4677 resultType.getElementTypePreservingConst(i),
4678 type_cast<FIRRTLBaseType>(getOperand(i).getType())))
4679 return emitOpError("type of element doesn't match bundle for field ")
4680 << resultType.getElement(i).name;
4681 // TODO: check flow
4682 return success();
4683}
4684
4685LogicalResult VectorCreateOp::verify() {
4686 FVectorType resultType = getType();
4687 if (resultType.getNumElements() != getFields().size())
4688 return emitOpError("number of fields doesn't match type");
4689 auto elemTy = resultType.getElementTypePreservingConst();
4690 for (size_t i = 0; i < resultType.getNumElements(); ++i)
4692 elemTy, type_cast<FIRRTLBaseType>(getOperand(i).getType())))
4693 return emitOpError("type of element doesn't match vector element");
4694 // TODO: check flow
4695 return success();
4696}
4697
4698//===----------------------------------------------------------------------===//
4699// FEnumCreateOp
4700//===----------------------------------------------------------------------===//
4701
4702LogicalResult FEnumCreateOp::verify() {
4703 FEnumType resultType = getResult().getType();
4704 auto elementIndex = resultType.getElementIndex(getFieldName());
4705 if (!elementIndex)
4706 return emitOpError("label ")
4707 << getFieldName() << " is not a member of the enumeration type "
4708 << resultType;
4710 resultType.getElementTypePreservingConst(*elementIndex),
4711 getInput().getType()))
4712 return emitOpError("type of element doesn't match enum element");
4713 return success();
4714}
4715
4716void FEnumCreateOp::print(OpAsmPrinter &printer) {
4717 printer << ' ';
4718 printer.printKeywordOrString(getFieldName());
4719 printer << '(' << getInput() << ')';
4720 SmallVector<StringRef> elidedAttrs = {"fieldIndex"};
4721 printer.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elidedAttrs);
4722 printer << " : ";
4723 printer.printFunctionalType(ArrayRef<Type>{getInput().getType()},
4724 ArrayRef<Type>{getResult().getType()});
4725}
4726
4727ParseResult FEnumCreateOp::parse(OpAsmParser &parser, OperationState &result) {
4728 auto *context = parser.getContext();
4729 auto &properties = result.getOrAddProperties<Properties>();
4730
4731 OpAsmParser::UnresolvedOperand input;
4732 std::string fieldName;
4733 mlir::FunctionType functionType;
4734 if (parser.parseKeywordOrString(&fieldName) || parser.parseLParen() ||
4735 parser.parseOperand(input) || parser.parseRParen() ||
4736 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4737 parser.parseType(functionType))
4738 return failure();
4739
4740 if (functionType.getNumInputs() != 1)
4741 return parser.emitError(parser.getNameLoc(), "single input type required");
4742 if (functionType.getNumResults() != 1)
4743 return parser.emitError(parser.getNameLoc(), "single result type required");
4744
4745 auto inputType = functionType.getInput(0);
4746 if (parser.resolveOperand(input, inputType, result.operands))
4747 return failure();
4748
4749 auto outputType = functionType.getResult(0);
4750 auto enumType = type_dyn_cast<FEnumType>(outputType);
4751 if (!enumType)
4752 return parser.emitError(parser.getNameLoc(),
4753 "output must be enum type, got ")
4754 << outputType;
4755 auto fieldIndex = enumType.getElementIndex(fieldName);
4756 if (!fieldIndex)
4757 return parser.emitError(parser.getNameLoc(),
4758 "unknown field " + fieldName + " in enum type ")
4759 << enumType;
4760
4761 properties.setFieldIndex(
4762 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4763
4764 result.addTypes(enumType);
4765
4766 return success();
4767}
4768
4769//===----------------------------------------------------------------------===//
4770// IsTagOp
4771//===----------------------------------------------------------------------===//
4772
4773LogicalResult IsTagOp::verify() {
4774 if (getFieldIndex() >= getInput().getType().base().getNumElements())
4775 return emitOpError("element index is greater than the number of fields in "
4776 "the bundle type");
4777 return success();
4778}
4779
4780void IsTagOp::print(::mlir::OpAsmPrinter &printer) {
4781 printer << ' ' << getInput() << ' ';
4782 printer.printKeywordOrString(getFieldName());
4783 SmallVector<::llvm::StringRef, 1> elidedAttrs = {"fieldIndex"};
4784 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4785 printer << " : " << getInput().getType();
4786}
4787
4788ParseResult IsTagOp::parse(OpAsmParser &parser, OperationState &result) {
4789 auto *context = parser.getContext();
4790 auto &properties = result.getOrAddProperties<Properties>();
4791
4792 OpAsmParser::UnresolvedOperand input;
4793 std::string fieldName;
4794 Type inputType;
4795 if (parser.parseOperand(input) || parser.parseKeywordOrString(&fieldName) ||
4796 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4797 parser.parseType(inputType))
4798 return failure();
4799
4800 if (parser.resolveOperand(input, inputType, result.operands))
4801 return failure();
4802
4803 auto enumType = type_dyn_cast<FEnumType>(inputType);
4804 if (!enumType)
4805 return parser.emitError(parser.getNameLoc(),
4806 "input must be enum type, got ")
4807 << inputType;
4808 auto fieldIndex = enumType.getElementIndex(fieldName);
4809 if (!fieldIndex)
4810 return parser.emitError(parser.getNameLoc(),
4811 "unknown field " + fieldName + " in enum type ")
4812 << enumType;
4813
4814 properties.setFieldIndex(
4815 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4816
4817 result.addTypes(UIntType::get(context, 1, /*isConst=*/false));
4818
4819 return success();
4820}
4821
4822FIRRTLType IsTagOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
4823 OpaqueProperties properties,
4824 mlir::RegionRange regions,
4825 std::optional<Location> loc) {
4826 Adaptor adaptor(operands, attrs, properties, regions);
4827 return UIntType::get(attrs.getContext(), 1,
4828 isConst(adaptor.getInput().getType()));
4829}
4830
4831template <typename OpTy>
4832ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result) {
4833 auto *context = parser.getContext();
4834
4835 OpAsmParser::UnresolvedOperand input;
4836 std::string fieldName;
4837 Type inputType;
4838 if (parser.parseOperand(input) || parser.parseLSquare() ||
4839 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4840 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4841 parser.parseType(inputType))
4842 return failure();
4843
4844 if (parser.resolveOperand(input, inputType, result.operands))
4845 return failure();
4846
4847 auto bundleType = type_dyn_cast<typename OpTy::InputType>(inputType);
4848 if (!bundleType)
4849 return parser.emitError(parser.getNameLoc(),
4850 "input must be bundle type, got ")
4851 << inputType;
4852 auto fieldIndex = bundleType.getElementIndex(fieldName);
4853 if (!fieldIndex)
4854 return parser.emitError(parser.getNameLoc(),
4855 "unknown field " + fieldName + " in bundle type ")
4856 << bundleType;
4857
4858 result.getOrAddProperties<typename OpTy::Properties>().setFieldIndex(
4859 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4860
4861 auto type = OpTy::inferReturnType(inputType, *fieldIndex, {});
4862 if (!type)
4863 return failure();
4864 result.addTypes(type);
4865
4866 return success();
4867}
4868
4869ParseResult SubtagOp::parse(OpAsmParser &parser, OperationState &result) {
4870 auto *context = parser.getContext();
4871
4872 OpAsmParser::UnresolvedOperand input;
4873 std::string fieldName;
4874 Type inputType;
4875 if (parser.parseOperand(input) || parser.parseLSquare() ||
4876 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4877 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4878 parser.parseType(inputType))
4879 return failure();
4880
4881 if (parser.resolveOperand(input, inputType, result.operands))
4882 return failure();
4883
4884 auto enumType = type_dyn_cast<FEnumType>(inputType);
4885 if (!enumType)
4886 return parser.emitError(parser.getNameLoc(),
4887 "input must be enum type, got ")
4888 << inputType;
4889 auto fieldIndex = enumType.getElementIndex(fieldName);
4890 if (!fieldIndex)
4891 return parser.emitError(parser.getNameLoc(),
4892 "unknown field " + fieldName + " in enum type ")
4893 << enumType;
4894
4895 result.getOrAddProperties<Properties>().setFieldIndex(
4896 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4897
4898 SmallVector<Type> inferredReturnTypes;
4899 if (failed(SubtagOp::inferReturnTypes(
4900 context, result.location, result.operands,
4901 result.attributes.getDictionary(context), result.getRawProperties(),
4902 result.regions, inferredReturnTypes)))
4903 return failure();
4904 result.addTypes(inferredReturnTypes);
4905
4906 return success();
4907}
4908
4909ParseResult SubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4910 return parseSubfieldLikeOp<SubfieldOp>(parser, result);
4911}
4912ParseResult OpenSubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4913 return parseSubfieldLikeOp<OpenSubfieldOp>(parser, result);
4914}
4915
4916template <typename OpTy>
4917static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer) {
4918 printer << ' ' << op.getInput() << '[';
4919 printer.printKeywordOrString(op.getFieldName());
4920 printer << ']';
4921 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4922 elidedAttrs.push_back("fieldIndex");
4923 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
4924 printer << " : " << op.getInput().getType();
4925}
4926void SubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4927 return printSubfieldLikeOp<SubfieldOp>(*this, printer);
4928}
4929void OpenSubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4930 return printSubfieldLikeOp<OpenSubfieldOp>(*this, printer);
4931}
4932
4933void SubtagOp::print(::mlir::OpAsmPrinter &printer) {
4934 printer << ' ' << getInput() << '[';
4935 printer.printKeywordOrString(getFieldName());
4936 printer << ']';
4937 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4938 elidedAttrs.push_back("fieldIndex");
4939 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4940 printer << " : " << getInput().getType();
4941}
4942
4943template <typename OpTy>
4944static LogicalResult verifySubfieldLike(OpTy op) {
4945 if (op.getFieldIndex() >=
4946 firrtl::type_cast<typename OpTy::InputType>(op.getInput().getType())
4947 .getNumElements())
4948 return op.emitOpError("subfield element index is greater than the number "
4949 "of fields in the bundle type");
4950 return success();
4951}
4952LogicalResult SubfieldOp::verify() {
4953 return verifySubfieldLike<SubfieldOp>(*this);
4954}
4955LogicalResult OpenSubfieldOp::verify() {
4956 return verifySubfieldLike<OpenSubfieldOp>(*this);
4957}
4958
4959LogicalResult SubtagOp::verify() {
4960 if (getFieldIndex() >= getInput().getType().base().getNumElements())
4961 return emitOpError("subfield element index is greater than the number "
4962 "of fields in the bundle type");
4963 return success();
4964}
4965
4966/// Return true if the specified operation has a constant value. This trivially
4967/// checks for `firrtl.constant` and friends, but also looks through subaccesses
4968/// and correctly handles wires driven with only constant values.
4969bool firrtl::isConstant(Operation *op) {
4970 // Worklist of ops that need to be examined that should all be constant in
4971 // order for the input operation to be constant.
4972 SmallVector<Operation *, 8> worklist({op});
4973
4974 // Mutable state indicating if this op is a constant. Assume it is a constant
4975 // and look for counterexamples.
4976 bool constant = true;
4977
4978 // While we haven't found a counterexample and there are still ops in the
4979 // worklist, pull ops off the worklist. If it provides a counterexample, set
4980 // the `constant` to false (and exit on the next loop iteration). Otherwise,
4981 // look through the op or spawn off more ops to look at.
4982 while (constant && !(worklist.empty()))
4983 TypeSwitch<Operation *>(worklist.pop_back_val())
4984 .Case<NodeOp, AsSIntPrimOp, AsUIntPrimOp>([&](auto op) {
4985 if (auto definingOp = op.getInput().getDefiningOp())
4986 worklist.push_back(definingOp);
4987 constant = false;
4988 })
4989 .Case<WireOp, SubindexOp, SubfieldOp>([&](auto op) {
4990 for (auto &use : op.getResult().getUses())
4991 worklist.push_back(use.getOwner());
4992 })
4993 .Case<ConstantOp, SpecialConstantOp, AggregateConstantOp>([](auto) {})
4994 .Default([&](auto) { constant = false; });
4995
4996 return constant;
4997}
4998
4999/// Return true if the specified value is a constant. This trivially checks for
5000/// `firrtl.constant` and friends, but also looks through subaccesses and
5001/// correctly handles wires driven with only constant values.
5002bool firrtl::isConstant(Value value) {
5003 if (auto *op = value.getDefiningOp())
5004 return isConstant(op);
5005 return false;
5006}
5007
5008LogicalResult ConstCastOp::verify() {
5009 if (!areTypesConstCastable(getResult().getType(), getInput().getType()))
5010 return emitOpError() << getInput().getType()
5011 << " is not 'const'-castable to "
5012 << getResult().getType();
5013 return success();
5014}
5015
5016FIRRTLType SubfieldOp::inferReturnType(Type type, uint32_t fieldIndex,
5017 std::optional<Location> loc) {
5018 auto inType = type_cast<BundleType>(type);
5019
5020 if (fieldIndex >= inType.getNumElements())
5021 return emitInferRetTypeError(loc,
5022 "subfield element index is greater than the "
5023 "number of fields in the bundle type");
5024
5025 // SubfieldOp verifier checks that the field index is valid with number of
5026 // subelements.
5027 return inType.getElementTypePreservingConst(fieldIndex);
5028}
5029
5030FIRRTLType OpenSubfieldOp::inferReturnType(Type type, uint32_t fieldIndex,
5031 std::optional<Location> loc) {
5032 auto inType = type_cast<OpenBundleType>(type);
5033
5034 if (fieldIndex >= inType.getNumElements())
5035 return emitInferRetTypeError(loc,
5036 "subfield element index is greater than the "
5037 "number of fields in the bundle type");
5038
5039 // OpenSubfieldOp verifier checks that the field index is valid with number of
5040 // subelements.
5041 return inType.getElementTypePreservingConst(fieldIndex);
5042}
5043
5044bool SubfieldOp::isFieldFlipped() {
5045 BundleType bundle = getInput().getType();
5046 return bundle.getElement(getFieldIndex()).isFlip;
5047}
5048bool OpenSubfieldOp::isFieldFlipped() {
5049 auto bundle = getInput().getType();
5050 return bundle.getElement(getFieldIndex()).isFlip;
5051}
5052
5053FIRRTLType SubindexOp::inferReturnType(Type type, uint32_t fieldIndex,
5054 std::optional<Location> loc) {
5055 if (auto vectorType = type_dyn_cast<FVectorType>(type)) {
5056 if (fieldIndex < vectorType.getNumElements())
5057 return vectorType.getElementTypePreservingConst();
5058 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
5059 "' in vector type ", type);
5060 }
5061 return emitInferRetTypeError(loc, "subindex requires vector operand");
5062}
5063
5064FIRRTLType OpenSubindexOp::inferReturnType(Type type, uint32_t fieldIndex,
5065 std::optional<Location> loc) {
5066 if (auto vectorType = type_dyn_cast<OpenVectorType>(type)) {
5067 if (fieldIndex < vectorType.getNumElements())
5068 return vectorType.getElementTypePreservingConst();
5069 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
5070 "' in vector type ", type);
5071 }
5072
5073 return emitInferRetTypeError(loc, "subindex requires vector operand");
5074}
5075
5076FIRRTLType SubtagOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
5077 OpaqueProperties properties,
5078 mlir::RegionRange regions,
5079 std::optional<Location> loc) {
5080 Adaptor adaptor(operands, attrs, properties, regions);
5081 auto inType = type_cast<FEnumType>(adaptor.getInput().getType());
5082 auto fieldIndex = adaptor.getFieldIndex();
5083
5084 if (fieldIndex >= inType.getNumElements())
5085 return emitInferRetTypeError(loc,
5086 "subtag element index is greater than the "
5087 "number of fields in the enum type");
5088
5089 // SubtagOp verifier checks that the field index is valid with number of
5090 // subelements.
5091 auto elementType = inType.getElement(fieldIndex).type;
5092 return elementType.getConstType(elementType.isConst() || inType.isConst());
5093}
5094
5095FIRRTLType SubaccessOp::inferReturnType(Type inType, Type indexType,
5096 std::optional<Location> loc) {
5097 if (!type_isa<UIntType>(indexType))
5098 return emitInferRetTypeError(loc, "subaccess index must be UInt type, not ",
5099 indexType);
5100
5101 if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
5102 if (isConst(indexType))
5103 return vectorType.getElementTypePreservingConst();
5104 return vectorType.getElementType().getAllConstDroppedType();
5105 }
5106
5107 return emitInferRetTypeError(loc, "subaccess requires vector operand, not ",
5108 inType);
5109}
5110
5111FIRRTLType TagExtractOp::inferReturnType(FIRRTLType input,
5112 std::optional<Location> loc) {
5113 auto inType = type_cast<FEnumType>(input);
5114 return UIntType::get(inType.getContext(), inType.getTagWidth());
5115}
5116
5117ParseResult MultibitMuxOp::parse(OpAsmParser &parser, OperationState &result) {
5118 OpAsmParser::UnresolvedOperand index;
5119 SmallVector<OpAsmParser::UnresolvedOperand, 16> inputs;
5120 Type indexType, elemType;
5121
5122 if (parser.parseOperand(index) || parser.parseComma() ||
5123 parser.parseOperandList(inputs) ||
5124 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
5125 parser.parseType(indexType) || parser.parseComma() ||
5126 parser.parseType(elemType))
5127 return failure();
5128
5129 if (parser.resolveOperand(index, indexType, result.operands))
5130 return failure();
5131
5132 result.addTypes(elemType);
5133
5134 return parser.resolveOperands(inputs, elemType, result.operands);
5135}
5136
5137void MultibitMuxOp::print(OpAsmPrinter &p) {
5138 p << " " << getIndex() << ", ";
5139 p.printOperands(getInputs());
5140 p.printOptionalAttrDict((*this)->getAttrs());
5141 p << " : " << getIndex().getType() << ", " << getType();
5142}
5143
5144FIRRTLType MultibitMuxOp::inferReturnType(ValueRange operands,
5145 DictionaryAttr attrs,
5146 OpaqueProperties properties,
5147 mlir::RegionRange regions,
5148 std::optional<Location> loc) {
5149 if (operands.size() < 2)
5150 return emitInferRetTypeError(loc, "at least one input is required");
5151
5152 // Check all mux inputs have the same type.
5153 if (!llvm::all_of(operands.drop_front(2), [&](auto op) {
5154 return operands[1].getType() == op.getType();
5155 }))
5156 return emitInferRetTypeError(loc, "all inputs must have the same type");
5157
5158 return type_cast<FIRRTLType>(operands[1].getType());
5159}
5160
5161//===----------------------------------------------------------------------===//
5162// ObjectSubfieldOp
5163//===----------------------------------------------------------------------===//
5164
5165LogicalResult ObjectSubfieldOp::inferReturnTypes(
5166 MLIRContext *context, std::optional<mlir::Location> location,
5167 ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
5168 RegionRange regions, llvm::SmallVectorImpl<Type> &inferredReturnTypes) {
5169 auto type =
5170 inferReturnType(operands, attributes, properties, regions, location);
5171 if (!type)
5172 return failure();
5173 inferredReturnTypes.push_back(type);
5174 return success();
5175}
5176
5177Type ObjectSubfieldOp::inferReturnType(Type inType, uint32_t fieldIndex,
5178 std::optional<Location> loc) {
5179 auto classType = dyn_cast<ClassType>(inType);
5180 if (!classType)
5181 return emitInferRetTypeError(loc, "base object is not a class");
5182
5183 if (classType.getNumElements() <= fieldIndex)
5184 return emitInferRetTypeError(loc, "element index is greater than the "
5185 "number of fields in the object");
5186 return classType.getElement(fieldIndex).type;
5187}
5188
5189void ObjectSubfieldOp::print(OpAsmPrinter &p) {
5190 auto input = getInput();
5191 auto classType = input.getType();
5192 p << ' ' << input << "[";
5193 p.printKeywordOrString(classType.getElement(getIndex()).name);
5194 p << "]";
5195 p.printOptionalAttrDict((*this)->getAttrs(), std::array{StringRef("index")});
5196 p << " : " << classType;
5197}
5198
5199ParseResult ObjectSubfieldOp::parse(OpAsmParser &parser,
5200 OperationState &result) {
5201 auto *context = parser.getContext();
5202
5203 OpAsmParser::UnresolvedOperand input;
5204 std::string fieldName;
5205 ClassType inputType;
5206 if (parser.parseOperand(input) || parser.parseLSquare() ||
5207 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
5208 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
5209 parser.parseType(inputType) ||
5210 parser.resolveOperand(input, inputType, result.operands))
5211 return failure();
5212
5213 auto index = inputType.getElementIndex(fieldName);
5214 if (!index)
5215 return parser.emitError(parser.getNameLoc(),
5216 "unknown field " + fieldName + " in class type ")
5217 << inputType;
5218 result.getOrAddProperties<Properties>().setIndex(
5219 IntegerAttr::get(IntegerType::get(context, 32), *index));
5220
5221 SmallVector<Type> inferredReturnTypes;
5222 if (failed(inferReturnTypes(context, result.location, result.operands,
5223 result.attributes.getDictionary(context),
5224 result.getRawProperties(), result.regions,
5225 inferredReturnTypes)))
5226 return failure();
5227 result.addTypes(inferredReturnTypes);
5228
5229 return success();
5230}
5231
5232//===----------------------------------------------------------------------===//
5233// Binary Primitives
5234//===----------------------------------------------------------------------===//
5235
5236/// If LHS and RHS are both UInt or SInt types, the return true and fill in the
5237/// width of them if known. If unknown, return -1 for the widths.
5238/// The constness of the result is also returned, where if both lhs and rhs are
5239/// const, then the result is const.
5240///
5241/// On failure, this reports and error and returns false. This function should
5242/// not be used if you don't want an error reported.
5243static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth,
5244 int32_t &rhsWidth, bool &isConstResult,
5245 std::optional<Location> loc) {
5246 // Must have two integer types with the same signedness.
5247 auto lhsi = type_dyn_cast<IntType>(lhs);
5248 auto rhsi = type_dyn_cast<IntType>(rhs);
5249 if (!lhsi || !rhsi || lhsi.isSigned() != rhsi.isSigned()) {
5250 if (loc) {
5251 if (lhsi && !rhsi)
5252 mlir::emitError(*loc, "second operand must be an integer type, not ")
5253 << rhs;
5254 else if (!lhsi && rhsi)
5255 mlir::emitError(*loc, "first operand must be an integer type, not ")
5256 << lhs;
5257 else if (!lhsi && !rhsi)
5258 mlir::emitError(*loc, "operands must be integer types, not ")
5259 << lhs << " and " << rhs;
5260 else
5261 mlir::emitError(*loc, "operand signedness must match");
5262 }
5263 return false;
5264 }
5265
5266 lhsWidth = lhsi.getWidthOrSentinel();
5267 rhsWidth = rhsi.getWidthOrSentinel();
5268 isConstResult = lhsi.isConst() && rhsi.isConst();
5269 return true;
5270}
5271
5272LogicalResult impl::verifySameOperandsIntTypeKind(Operation *op) {
5273 assert(op->getNumOperands() == 2 &&
5274 "SameOperandsIntTypeKind on non-binary op");
5275 int32_t lhsWidth, rhsWidth;
5276 bool isConstResult;
5277 return success(isSameIntTypeKind(op->getOperand(0).getType(),
5278 op->getOperand(1).getType(), lhsWidth,
5279 rhsWidth, isConstResult, op->getLoc()));
5280}
5281
5283 std::optional<Location> loc) {
5284 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5285 bool isConstResult = false;
5286 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5287 return {};
5288
5289 if (lhsWidth != -1 && rhsWidth != -1)
5290 resultWidth = std::max(lhsWidth, rhsWidth) + 1;
5291 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5292 isConstResult);
5293}
5294
5295FIRRTLType MulPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5296 std::optional<Location> loc) {
5297 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5298 bool isConstResult = false;
5299 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5300 return {};
5301
5302 if (lhsWidth != -1 && rhsWidth != -1)
5303 resultWidth = lhsWidth + rhsWidth;
5304
5305 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5306 isConstResult);
5307}
5308
5309FIRRTLType DivPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5310 std::optional<Location> loc) {
5311 int32_t lhsWidth, rhsWidth;
5312 bool isConstResult = false;
5313 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5314 return {};
5315
5316 // For unsigned, the width is the width of the numerator on the LHS.
5317 if (type_isa<UIntType>(lhs))
5318 return UIntType::get(lhs.getContext(), lhsWidth, isConstResult);
5319
5320 // For signed, the width is the width of the numerator on the LHS, plus 1.
5321 int32_t resultWidth = lhsWidth != -1 ? lhsWidth + 1 : -1;
5322 return SIntType::get(lhs.getContext(), resultWidth, isConstResult);
5323}
5324
5325FIRRTLType RemPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5326 std::optional<Location> loc) {
5327 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5328 bool isConstResult = false;
5329 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5330 return {};
5331
5332 if (lhsWidth != -1 && rhsWidth != -1)
5333 resultWidth = std::min(lhsWidth, rhsWidth);
5334 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5335 isConstResult);
5336}
5337
5339 std::optional<Location> loc) {
5340 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5341 bool isConstResult = false;
5342 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5343 return {};
5344
5345 if (lhsWidth != -1 && rhsWidth != -1) {
5346 resultWidth = std::max(lhsWidth, rhsWidth);
5347 if (lhsWidth == resultWidth && lhs.isConst() == isConstResult &&
5348 isa<UIntType>(lhs))
5349 return lhs;
5350 if (rhsWidth == resultWidth && rhs.isConst() == isConstResult &&
5351 isa<UIntType>(rhs))
5352 return rhs;
5353 }
5354 return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
5355}
5356
5358 std::optional<Location> loc) {
5359 if (!type_isa<FVectorType>(lhs) || !type_isa<FVectorType>(rhs))
5360 return {};
5361
5362 auto lhsVec = type_cast<FVectorType>(lhs);
5363 auto rhsVec = type_cast<FVectorType>(rhs);
5364
5365 if (lhsVec.getNumElements() != rhsVec.getNumElements())
5366 return {};
5367
5368 auto elemType =
5369 impl::inferBitwiseResult(lhsVec.getElementTypePreservingConst(),
5370 rhsVec.getElementTypePreservingConst(), loc);
5371 if (!elemType)
5372 return {};
5373 auto elemBaseType = type_cast<FIRRTLBaseType>(elemType);
5374 return FVectorType::get(elemBaseType, lhsVec.getNumElements(),
5375 lhsVec.isConst() && rhsVec.isConst() &&
5376 elemBaseType.isConst());
5377}
5378
5380 std::optional<Location> loc) {
5381 return UIntType::get(lhs.getContext(), 1, isConst(lhs) && isConst(rhs));
5382}
5383
5384FIRRTLType CatPrimOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
5385 OpaqueProperties properties,
5386 mlir::RegionRange regions,
5387 std::optional<Location> loc) {
5388 // If no operands, return a 0-bit UInt
5389 if (operands.empty())
5390 return UIntType::get(attrs.getContext(), 0);
5391
5392 // Check that all operands are Int types with same signedness
5393 bool isSigned = type_isa<SIntType>(operands[0].getType());
5394 for (auto operand : operands) {
5395 auto type = type_dyn_cast<IntType>(operand.getType());
5396 if (!type)
5397 return emitInferRetTypeError(loc, "all operands must be Int type");
5398 if (type.isSigned() != isSigned)
5399 return emitInferRetTypeError(loc,
5400 "all operands must have same signedness");
5401 }
5402
5403 // Calculate the total width and determine if result is const
5404 int32_t resultWidth = 0;
5405 bool isConstResult = true;
5406
5407 for (auto operand : operands) {
5408 auto type = type_cast<IntType>(operand.getType());
5409 int32_t width = type.getWidthOrSentinel();
5410
5411 // If any width is unknown, the result width is unknown
5412 if (width == -1) {
5413 resultWidth = -1;
5414 }
5415
5416 if (resultWidth != -1)
5417 resultWidth += width;
5418
5419 // Result is const only if all operands are const
5420 isConstResult &= type.isConst();
5421 }
5422
5423 // Create and return the result type
5424 return UIntType::get(attrs.getContext(), resultWidth, isConstResult);
5425}
5426
5427FIRRTLType DShlPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5428 std::optional<Location> loc) {
5429 auto lhsi = type_dyn_cast<IntType>(lhs);
5430 auto rhsui = type_dyn_cast<UIntType>(rhs);
5431 if (!rhsui || !lhsi)
5432 return emitInferRetTypeError(
5433 loc, "first operand should be integer, second unsigned int");
5434
5435 // If the left or right has unknown result type, then the operation does
5436 // too.
5437 auto width = lhsi.getWidthOrSentinel();
5438 if (width == -1 || !rhsui.getWidth().has_value()) {
5439 width = -1;
5440 } else {
5441 auto amount = *rhsui.getWidth();
5442 if (amount >= 32)
5443 return emitInferRetTypeError(loc,
5444 "shift amount too large: second operand of "
5445 "dshl is wider than 31 bits");
5446 int64_t newWidth = (int64_t)width + ((int64_t)1 << amount) - 1;
5447 if (newWidth > INT32_MAX)
5448 return emitInferRetTypeError(
5449 loc, "shift amount too large: first operand shifted by maximum "
5450 "amount exceeds maximum width");
5451 width = newWidth;
5452 }
5453 return IntType::get(lhs.getContext(), lhsi.isSigned(), width,
5454 lhsi.isConst() && rhsui.isConst());
5455}
5456
5457FIRRTLType DShlwPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5458 std::optional<Location> loc) {
5459 auto lhsi = type_dyn_cast<IntType>(lhs);
5460 auto rhsu = type_dyn_cast<UIntType>(rhs);
5461 if (!lhsi || !rhsu)
5462 return emitInferRetTypeError(
5463 loc, "first operand should be integer, second unsigned int");
5464 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
5465}
5466
5467FIRRTLType DShrPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5468 std::optional<Location> loc) {
5469 auto lhsi = type_dyn_cast<IntType>(lhs);
5470 auto rhsu = type_dyn_cast<UIntType>(rhs);
5471 if (!lhsi || !rhsu)
5472 return emitInferRetTypeError(
5473 loc, "first operand should be integer, second unsigned int");
5474 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
5475}
5476
5477//===----------------------------------------------------------------------===//
5478// Unary Primitives
5479//===----------------------------------------------------------------------===//
5480
5481FIRRTLType SizeOfIntrinsicOp::inferReturnType(FIRRTLType input,
5482 std::optional<Location> loc) {
5483 return UIntType::get(input.getContext(), 32);
5484}
5485
5486FIRRTLType AsSIntPrimOp::inferReturnType(FIRRTLType input,
5487 std::optional<Location> loc) {
5488 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5489 if (!base)
5490 return emitInferRetTypeError(loc, "operand must be a scalar base type");
5491 int32_t width = base.getBitWidthOrSentinel();
5492 if (width == -2)
5493 return emitInferRetTypeError(loc, "operand must be a scalar type");
5494 return SIntType::get(input.getContext(), width, base.isConst());
5495}
5496
5497FIRRTLType AsUIntPrimOp::inferReturnType(FIRRTLType input,
5498 std::optional<Location> loc) {
5499 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5500 if (!base)
5501 return emitInferRetTypeError(loc, "operand must be a scalar base type");
5502 int32_t width = base.getBitWidthOrSentinel();
5503 if (width == -2)
5504 return emitInferRetTypeError(loc, "operand must be a scalar type");
5505 return UIntType::get(input.getContext(), width, base.isConst());
5506}
5507
5508FIRRTLType AsAsyncResetPrimOp::inferReturnType(FIRRTLType input,
5509 std::optional<Location> loc) {
5510 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5511 if (!base)
5512 return emitInferRetTypeError(loc,
5513 "operand must be single bit scalar base type");
5514 int32_t width = base.getBitWidthOrSentinel();
5515 if (width == -2 || width == 0 || width > 1)
5516 return emitInferRetTypeError(loc, "operand must be single bit scalar type");
5517 return AsyncResetType::get(input.getContext(), base.isConst());
5518}
5519
5520FIRRTLType AsClockPrimOp::inferReturnType(FIRRTLType input,
5521 std::optional<Location> loc) {
5522 return ClockType::get(input.getContext(), isConst(input));
5523}
5524
5525FIRRTLType CvtPrimOp::inferReturnType(FIRRTLType input,
5526 std::optional<Location> loc) {
5527 if (auto uiType = type_dyn_cast<UIntType>(input)) {
5528 auto width = uiType.getWidthOrSentinel();
5529 if (width != -1)
5530 ++width;
5531 return SIntType::get(input.getContext(), width, uiType.isConst());
5532 }
5533
5534 if (type_isa<SIntType>(input))
5535 return input;
5536
5537 return emitInferRetTypeError(loc, "operand must have integer type");
5538}
5539
5540FIRRTLType NegPrimOp::inferReturnType(FIRRTLType input,
5541 std::optional<Location> loc) {
5542 auto inputi = type_dyn_cast<IntType>(input);
5543 if (!inputi)
5544 return emitInferRetTypeError(loc, "operand must have integer type");
5545 int32_t width = inputi.getWidthOrSentinel();
5546 if (width != -1)
5547 ++width;
5548 return SIntType::get(input.getContext(), width, inputi.isConst());
5549}
5550
5551FIRRTLType NotPrimOp::inferReturnType(FIRRTLType input,
5552 std::optional<Location> loc) {
5553 auto inputi = type_dyn_cast<IntType>(input);
5554 if (!inputi)
5555 return emitInferRetTypeError(loc, "operand must have integer type");
5556 if (isa<UIntType>(inputi))
5557 return inputi;
5558 return UIntType::get(input.getContext(), inputi.getWidthOrSentinel(),
5559 inputi.isConst());
5560}
5561
5563 std::optional<Location> loc) {
5564 return UIntType::get(input.getContext(), 1, isConst(input));
5565}
5566
5567//===----------------------------------------------------------------------===//
5568// Other Operations
5569//===----------------------------------------------------------------------===//
5570
5571FIRRTLType BitsPrimOp::inferReturnType(FIRRTLType input, int64_t high,
5572 int64_t low,
5573 std::optional<Location> loc) {
5574 auto inputi = type_dyn_cast<IntType>(input);
5575 if (!inputi)
5576 return emitInferRetTypeError(
5577 loc, "input type should be the int type but got ", input);
5578
5579 // High must be >= low and both most be non-negative.
5580 if (high < low)
5581 return emitInferRetTypeError(
5582 loc, "high must be equal or greater than low, but got high = ", high,
5583 ", low = ", low);
5584
5585 if (low < 0)
5586 return emitInferRetTypeError(loc, "low must be non-negative but got ", low);
5587
5588 // If the input has staticly known width, check it. Both and low must be
5589 // strictly less than width.
5590 int32_t width = inputi.getWidthOrSentinel();
5591 if (width != -1 && high >= width)
5592 return emitInferRetTypeError(
5593 loc,
5594 "high must be smaller than the width of input, but got high = ", high,
5595 ", width = ", width);
5596
5597 return UIntType::get(input.getContext(), high - low + 1, inputi.isConst());
5598}
5599
5600FIRRTLType HeadPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5601 std::optional<Location> loc) {
5602
5603 auto inputi = type_dyn_cast<IntType>(input);
5604 if (amount < 0 || !inputi)
5605 return emitInferRetTypeError(
5606 loc, "operand must have integer type and amount must be >= 0");
5607
5608 int32_t width = inputi.getWidthOrSentinel();
5609 if (width != -1 && amount > width)
5610 return emitInferRetTypeError(loc, "amount larger than input width");
5611
5612 return UIntType::get(input.getContext(), amount, inputi.isConst());
5613}
5614
5615/// Infer the result type for a multiplexer given its two operand types, which
5616/// may be aggregates.
5617///
5618/// This essentially performs a pairwise comparison of fields and elements, as
5619/// follows:
5620/// - Identical operands inferred to their common type
5621/// - Integer operands inferred to the larger one if both have a known width, a
5622/// widthless integer otherwise.
5623/// - Vectors inferred based on the element type.
5624/// - Bundles inferred in a pairwise fashion based on the field types.
5626 FIRRTLBaseType low,
5627 bool isConstCondition,
5628 std::optional<Location> loc) {
5629 // If the types are identical we're done.
5630 if (high == low)
5631 return isConstCondition ? low : low.getAllConstDroppedType();
5632
5633 // The base types need to be equivalent.
5634 if (high.getTypeID() != low.getTypeID())
5635 return emitInferRetTypeError<FIRRTLBaseType>(
5636 loc, "incompatible mux operand types, true value type: ", high,
5637 ", false value type: ", low);
5638
5639 bool outerTypeIsConst = isConstCondition && low.isConst() && high.isConst();
5640
5641 // Two different Int types can be compatible. If either has unknown width,
5642 // then return it. If both are known but different width, then return the
5643 // larger one.
5644 if (type_isa<IntType>(low)) {
5645 int32_t highWidth = high.getBitWidthOrSentinel();
5646 int32_t lowWidth = low.getBitWidthOrSentinel();
5647 if (lowWidth == -1)
5648 return low.getConstType(outerTypeIsConst);
5649 if (highWidth == -1)
5650 return high.getConstType(outerTypeIsConst);
5651 return (lowWidth > highWidth ? low : high).getConstType(outerTypeIsConst);
5652 }
5653
5654 // Two different Enum types can be compatible if one is the constant version
5655 // of the other.
5656 auto highEnum = type_dyn_cast<FEnumType>(high);
5657 auto lowEnum = type_dyn_cast<FEnumType>(low);
5658 if (lowEnum && highEnum) {
5659 if (lowEnum.getNumElements() != highEnum.getNumElements())
5660 return emitInferRetTypeError<FIRRTLBaseType>(
5661 loc, "incompatible mux operand types, true value type: ", high,
5662 ", false value type: ", low);
5663 SmallVector<FEnumType::EnumElement> elements;
5664 for (auto [high, low] : llvm::zip_equal(highEnum, lowEnum)) {
5665 // Variants should have the same name and value.
5666 if (high.name != low.name || high.value != low.value)
5667 return emitInferRetTypeError<FIRRTLBaseType>(
5668 loc, "incompatible mux operand types, true value type: ", highEnum,
5669 ", false value type: ", lowEnum);
5670 // Enumerations can only have constant variants only if the whole
5671 // enumeration is constant, so this logic can differ a bit from bundles.
5672 auto inner =
5673 inferMuxReturnType(high.type, low.type, isConstCondition, loc);
5674 if (!inner)
5675 return {};
5676 elements.emplace_back(high.name, high.value, inner);
5677 }
5678 return FEnumType::get(high.getContext(), elements, outerTypeIsConst);
5679 }
5680
5681 // Infer vector types by comparing the element types.
5682 auto highVector = type_dyn_cast<FVectorType>(high);
5683 auto lowVector = type_dyn_cast<FVectorType>(low);
5684 if (highVector && lowVector &&
5685 highVector.getNumElements() == lowVector.getNumElements()) {
5686 auto inner = inferMuxReturnType(highVector.getElementTypePreservingConst(),
5687 lowVector.getElementTypePreservingConst(),
5688 isConstCondition, loc);
5689 if (!inner)
5690 return {};
5691 return FVectorType::get(inner, lowVector.getNumElements(),
5692 outerTypeIsConst);
5693 }
5694
5695 // Infer bundle types by inferring names in a pairwise fashion.
5696 auto highBundle = type_dyn_cast<BundleType>(high);
5697 auto lowBundle = type_dyn_cast<BundleType>(low);
5698 if (highBundle && lowBundle) {
5699 auto highElements = highBundle.getElements();
5700 auto lowElements = lowBundle.getElements();
5701 size_t numElements = highElements.size();
5702
5703 SmallVector<BundleType::BundleElement> newElements;
5704 if (numElements == lowElements.size()) {
5705 bool failed = false;
5706 for (size_t i = 0; i < numElements; ++i) {
5707 if (highElements[i].name != lowElements[i].name ||
5708 highElements[i].isFlip != lowElements[i].isFlip) {
5709 failed = true;
5710 break;
5711 }
5712 auto element = highElements[i];
5713 element.type = inferMuxReturnType(
5714 highBundle.getElementTypePreservingConst(i),
5715 lowBundle.getElementTypePreservingConst(i), isConstCondition, loc);
5716 if (!element.type)
5717 return {};
5718 newElements.push_back(element);
5719 }
5720 if (!failed)
5721 return BundleType::get(low.getContext(), newElements, outerTypeIsConst);
5722 }
5723 return emitInferRetTypeError<FIRRTLBaseType>(
5724 loc, "incompatible mux operand bundle fields, true value type: ", high,
5725 ", false value type: ", low);
5726 }
5727
5728 // If we arrive here the types of the two mux arms are fundamentally
5729 // incompatible.
5730 return emitInferRetTypeError<FIRRTLBaseType>(
5731 loc, "invalid mux operand types, true value type: ", high,
5732 ", false value type: ", low);
5733}
5734
5735FIRRTLType MuxPrimOp::inferReturnType(FIRRTLType sel, FIRRTLType high,
5736 FIRRTLType low,
5737 std::optional<Location> loc) {
5738 auto highType = type_dyn_cast<FIRRTLBaseType>(high);
5739 auto lowType = type_dyn_cast<FIRRTLBaseType>(low);
5740 if (!highType || !lowType)
5741 return emitInferRetTypeError(loc, "operands must be base type");
5742 return inferMuxReturnType(highType, lowType, isConst(sel), loc);
5743}
5744
5745FIRRTLType Mux2CellIntrinsicOp::inferReturnType(ValueRange operands,
5746 DictionaryAttr attrs,
5747 OpaqueProperties properties,
5748 mlir::RegionRange regions,
5749 std::optional<Location> loc) {
5750 auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
5751 auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
5752 if (!highType || !lowType)
5753 return emitInferRetTypeError(loc, "operands must be base type");
5754 return inferMuxReturnType(highType, lowType, isConst(operands[0].getType()),
5755 loc);
5756}
5757
5758FIRRTLType Mux4CellIntrinsicOp::inferReturnType(ValueRange operands,
5759 DictionaryAttr attrs,
5760 OpaqueProperties properties,
5761 mlir::RegionRange regions,
5762 std::optional<Location> loc) {
5763 SmallVector<FIRRTLBaseType> types;
5764 FIRRTLBaseType result;
5765 for (unsigned i = 1; i < 5; i++) {
5766 types.push_back(type_dyn_cast<FIRRTLBaseType>(operands[i].getType()));
5767 if (!types.back())
5768 return emitInferRetTypeError(loc, "operands must be base type");
5769 if (result) {
5770 result = inferMuxReturnType(result, types.back(),
5771 isConst(operands[0].getType()), loc);
5772 if (!result)
5773 return result;
5774 } else {
5775 result = types.back();
5776 }
5777 }
5778 return result;
5779}
5780
5781FIRRTLType PadPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5782 std::optional<Location> loc) {
5783 auto inputi = type_dyn_cast<IntType>(input);
5784 if (amount < 0 || !inputi)
5785 return emitInferRetTypeError(
5786 loc, "pad input must be integer and amount must be >= 0");
5787
5788 int32_t width = inputi.getWidthOrSentinel();
5789 if (width == -1)
5790 return inputi;
5791
5792 width = std::max<int32_t>(width, amount);
5793 return IntType::get(input.getContext(), inputi.isSigned(), width,
5794 inputi.isConst());
5795}
5796
5797FIRRTLType ShlPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5798 std::optional<Location> loc) {
5799 auto inputi = type_dyn_cast<IntType>(input);
5800 if (amount < 0 || !inputi)
5801 return emitInferRetTypeError(
5802 loc, "shl input must be integer and amount must be >= 0");
5803
5804 int32_t width = inputi.getWidthOrSentinel();
5805 if (width != -1)
5806 width += amount;
5807
5808 return IntType::get(input.getContext(), inputi.isSigned(), width,
5809 inputi.isConst());
5810}
5811
5812FIRRTLType ShrPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5813 std::optional<Location> loc) {
5814 auto inputi = type_dyn_cast<IntType>(input);
5815 if (amount < 0 || !inputi)
5816 return emitInferRetTypeError(
5817 loc, "shr input must be integer and amount must be >= 0");
5818
5819 int32_t width = inputi.getWidthOrSentinel();
5820 if (width != -1) {
5821 // UInt saturates at 0 bits, SInt at 1 bit
5822 int32_t minWidth = inputi.isUnsigned() ? 0 : 1;
5823 width = std::max<int32_t>(minWidth, width - amount);
5824 }
5825
5826 return IntType::get(input.getContext(), inputi.isSigned(), width,
5827 inputi.isConst());
5828}
5829
5830FIRRTLType TailPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5831 std::optional<Location> loc) {
5832
5833 auto inputi = type_dyn_cast<IntType>(input);
5834 if (amount < 0 || !inputi)
5835 return emitInferRetTypeError(
5836 loc, "tail input must be integer and amount must be >= 0");
5837
5838 int32_t width = inputi.getWidthOrSentinel();
5839 if (width != -1) {
5840 if (width < amount)
5841 return emitInferRetTypeError(
5842 loc, "amount must be less than or equal operand width");
5843 width -= amount;
5844 }
5845
5846 return IntType::get(input.getContext(), false, width, inputi.isConst());
5847}
5848
5849//===----------------------------------------------------------------------===//
5850// VerbatimExprOp
5851//===----------------------------------------------------------------------===//
5852
5853void VerbatimExprOp::getAsmResultNames(
5854 function_ref<void(Value, StringRef)> setNameFn) {
5855 // If the text is macro like, then use a pretty name. We only take the
5856 // text up to a weird character (like a paren) and currently ignore
5857 // parenthesized expressions.
5858 auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5859 auto name = getText();
5860 // Ignore a leading ` in macro name.
5861 if (name.starts_with("`"))
5862 name = name.drop_front();
5863 name = name.take_while(isOkCharacter);
5864 if (!name.empty())
5865 setNameFn(getResult(), name);
5866}
5867
5868//===----------------------------------------------------------------------===//
5869// VerbatimWireOp
5870//===----------------------------------------------------------------------===//
5871
5872void VerbatimWireOp::getAsmResultNames(
5873 function_ref<void(Value, StringRef)> setNameFn) {
5874 // If the text is macro like, then use a pretty name. We only take the
5875 // text up to a weird character (like a paren) and currently ignore
5876 // parenthesized expressions.
5877 auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5878 auto name = getText();
5879 // Ignore a leading ` in macro name.
5880 if (name.starts_with("`"))
5881 name = name.drop_front();
5882 name = name.take_while(isOkCharacter);
5883 if (!name.empty())
5884 setNameFn(getResult(), name);
5885}
5886
5887//===----------------------------------------------------------------------===//
5888// DPICallIntrinsicOp
5889//===----------------------------------------------------------------------===//
5890
5891static bool isTypeAllowedForDPI(Operation *op, Type type) {
5892 return !type.walk([&](firrtl::IntType intType) -> mlir::WalkResult {
5893 auto width = intType.getWidth();
5894 if (width < 0) {
5895 op->emitError() << "unknown width is not allowed for DPI";
5896 return WalkResult::interrupt();
5897 }
5898 if (width == 1 || width == 8 || width == 16 || width == 32 ||
5899 width >= 64)
5900 return WalkResult::advance();
5901 op->emitError()
5902 << "integer types used by DPI functions must have a "
5903 "specific bit width; "
5904 "it must be equal to 1(bit), 8(byte), 16(shortint), "
5905 "32(int), 64(longint) "
5906 "or greater than 64, but got "
5907 << intType;
5908 return WalkResult::interrupt();
5909 })
5910 .wasInterrupted();
5911}
5912
5913LogicalResult DPICallIntrinsicOp::verify() {
5914 if (auto inputNames = getInputNames()) {
5915 if (getInputs().size() != inputNames->size())
5916 return emitError() << "inputNames has " << inputNames->size()
5917 << " elements but there are " << getInputs().size()
5918 << " input arguments";
5919 }
5920 if (auto outputName = getOutputName())
5921 if (getNumResults() == 0)
5922 return emitError() << "output name is given but there is no result";
5923
5924 auto checkType = [this](Type type) {
5925 return isTypeAllowedForDPI(*this, type);
5926 };
5927 return success(llvm::all_of(this->getResultTypes(), checkType) &&
5928 llvm::all_of(this->getOperandTypes(), checkType));
5929}
5930
5931SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
5932DPICallIntrinsicOp::computeDataFlow() {
5933 if (getClock())
5934 return {};
5935
5936 SmallVector<std::pair<circt::FieldRef, circt::FieldRef>> deps;
5937
5938 for (auto operand : getOperands()) {
5939 auto type = type_cast<FIRRTLBaseType>(operand.getType());
5940 auto baseFieldRef = getFieldRefFromValue(operand);
5941 SmallVector<circt::FieldRef> operandFields;
5943 type, [&](uint64_t dstIndex, FIRRTLBaseType t, bool dstIsFlip) {
5944 operandFields.push_back(baseFieldRef.getSubField(dstIndex));
5945 });
5946
5947 // Record operand -> result dependency.
5948 for (auto result : getResults())
5950 type, [&](uint64_t dstIndex, FIRRTLBaseType t, bool dstIsFlip) {
5951 for (auto field : operandFields)
5952 deps.emplace_back(circt::FieldRef(result, dstIndex), field);
5953 });
5954 }
5955 return deps;
5956}
5957
5958//===----------------------------------------------------------------------===//
5959// Conversions to/from structs in the standard dialect.
5960//===----------------------------------------------------------------------===//
5961
5962LogicalResult HWStructCastOp::verify() {
5963 // We must have a bundle and a struct, with matching pairwise fields
5964 BundleType bundleType;
5965 hw::StructType structType;
5966 if ((bundleType = type_dyn_cast<BundleType>(getOperand().getType()))) {
5967 structType = dyn_cast<hw::StructType>(getType());
5968 if (!structType)
5969 return emitError("result type must be a struct");
5970 } else if ((bundleType = type_dyn_cast<BundleType>(getType()))) {
5971 structType = dyn_cast<hw::StructType>(getOperand().getType());
5972 if (!structType)
5973 return emitError("operand type must be a struct");
5974 } else {
5975 return emitError("either source or result type must be a bundle type");
5976 }
5977
5978 auto firFields = bundleType.getElements();
5979 auto hwFields = structType.getElements();
5980 if (firFields.size() != hwFields.size())
5981 return emitError("bundle and struct have different number of fields");
5982
5983 for (size_t findex = 0, fend = firFields.size(); findex < fend; ++findex) {
5984 if (firFields[findex].name.getValue() != hwFields[findex].name)
5985 return emitError("field names don't match '")
5986 << firFields[findex].name.getValue() << "', '"
5987 << hwFields[findex].name.getValue() << "'";
5988 int64_t firWidth =
5989 FIRRTLBaseType(firFields[findex].type).getBitWidthOrSentinel();
5990 int64_t hwWidth = hw::getBitWidth(hwFields[findex].type);
5991 if (firWidth > 0 && hwWidth > 0 && firWidth != hwWidth)
5992 return emitError("size of field '")
5993 << hwFields[findex].name.getValue() << "' don't match " << firWidth
5994 << ", " << hwWidth;
5995 }
5996
5997 return success();
5998}
5999
6000LogicalResult BitCastOp::verify() {
6001 auto inTypeBits = getBitWidth(getInput().getType(), /*ignoreFlip=*/true);
6002 auto resTypeBits = getBitWidth(getType());
6003 if (inTypeBits.has_value() && resTypeBits.has_value()) {
6004 // Bitwidths must match for valid bit
6005 if (*inTypeBits == *resTypeBits) {
6006 // non-'const' cannot be casted to 'const'
6007 if (containsConst(getType()) && !isConst(getOperand().getType()))
6008 return emitError("cannot cast non-'const' input type ")
6009 << getOperand().getType() << " to 'const' result type "
6010 << getType();
6011 return success();
6012 }
6013 return emitError("the bitwidth of input (")
6014 << *inTypeBits << ") and result (" << *resTypeBits
6015 << ") don't match";
6016 }
6017 if (!inTypeBits.has_value())
6018 return emitError("bitwidth cannot be determined for input operand type ")
6019 << getInput().getType();
6020 return emitError("bitwidth cannot be determined for result type ")
6021 << getType();
6022}
6023
6024//===----------------------------------------------------------------------===//
6025// Custom attr-dict Directive that Elides Annotations
6026//===----------------------------------------------------------------------===//
6027
6028/// Parse an optional attribute dictionary, adding an empty 'annotations'
6029/// attribute if not specified.
6030static ParseResult parseElideAnnotations(OpAsmParser &parser,
6031 NamedAttrList &resultAttrs) {
6032 auto result = parser.parseOptionalAttrDict(resultAttrs);
6033 if (!resultAttrs.get("annotations"))
6034 resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
6035
6036 return result;
6037}
6038
6039static void printElideAnnotations(OpAsmPrinter &p, Operation *op,
6040 DictionaryAttr attr,
6041 ArrayRef<StringRef> extraElides = {}) {
6042 SmallVector<StringRef> elidedAttrs(extraElides.begin(), extraElides.end());
6043 // Elide "annotations" if it is empty.
6044 if (op->getAttrOfType<ArrayAttr>("annotations").empty())
6045 elidedAttrs.push_back("annotations");
6046 // Elide "nameKind".
6047 elidedAttrs.push_back("nameKind");
6048
6049 p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
6050}
6051
6052/// Parse an optional attribute dictionary, adding empty 'annotations' and
6053/// 'portAnnotations' attributes if not specified.
6054static ParseResult parseElidePortAnnotations(OpAsmParser &parser,
6055 NamedAttrList &resultAttrs) {
6056 auto result = parseElideAnnotations(parser, resultAttrs);
6057
6058 if (!resultAttrs.get("portAnnotations")) {
6059 SmallVector<Attribute, 16> portAnnotations(
6060 parser.getNumResults(), parser.getBuilder().getArrayAttr({}));
6061 resultAttrs.append("portAnnotations",
6062 parser.getBuilder().getArrayAttr(portAnnotations));
6063 }
6064 return result;
6065}
6066
6067// Elide 'annotations' and 'portAnnotations' attributes if they are empty.
6068static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op,
6069 DictionaryAttr attr,
6070 ArrayRef<StringRef> extraElides = {}) {
6071 SmallVector<StringRef, 2> elidedAttrs(extraElides.begin(), extraElides.end());
6072
6073 if (llvm::all_of(op->getAttrOfType<ArrayAttr>("portAnnotations"),
6074 [&](Attribute a) { return cast<ArrayAttr>(a).empty(); }))
6075 elidedAttrs.push_back("portAnnotations");
6076 printElideAnnotations(p, op, attr, elidedAttrs);
6077}
6078
6079//===----------------------------------------------------------------------===//
6080// NameKind Custom Directive
6081//===----------------------------------------------------------------------===//
6082
6083static ParseResult parseNameKind(OpAsmParser &parser,
6084 firrtl::NameKindEnumAttr &result) {
6085 StringRef keyword;
6086
6087 if (!parser.parseOptionalKeyword(&keyword,
6088 {"interesting_name", "droppable_name"})) {
6089 auto kind = symbolizeNameKindEnum(keyword);
6090 result = NameKindEnumAttr::get(parser.getContext(), kind.value());
6091 return success();
6092 }
6093
6094 // Default is droppable name.
6095 result =
6096 NameKindEnumAttr::get(parser.getContext(), NameKindEnum::DroppableName);
6097 return success();
6098}
6099
6100static void printNameKind(OpAsmPrinter &p, Operation *op,
6101 firrtl::NameKindEnumAttr attr,
6102 ArrayRef<StringRef> extraElides = {}) {
6103 if (attr.getValue() != NameKindEnum::DroppableName)
6104 p << " " << stringifyNameKindEnum(attr.getValue());
6105}
6106
6107//===----------------------------------------------------------------------===//
6108// ImplicitSSAName Custom Directive
6109//===----------------------------------------------------------------------===//
6110
6111static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser,
6112 NamedAttrList &resultAttrs) {
6113 if (parseElideAnnotations(parser, resultAttrs))
6114 return failure();
6115 inferImplicitSSAName(parser, resultAttrs);
6116 return success();
6117}
6118
6119static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op,
6120 DictionaryAttr attrs) {
6121 SmallVector<StringRef, 4> elides;
6123 elides.push_back(Forceable::getForceableAttrName());
6124 elideImplicitSSAName(p, op, attrs, elides);
6125 printElideAnnotations(p, op, attrs, elides);
6126}
6127
6128//===----------------------------------------------------------------------===//
6129// MemOp Custom attr-dict Directive
6130//===----------------------------------------------------------------------===//
6131
6132static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs) {
6133 return parseElidePortAnnotations(parser, resultAttrs);
6134}
6135
6136/// Always elide "ruw" and elide "annotations" if it exists or if it is empty.
6137static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
6138 // "ruw" and "inner_sym" is always elided.
6139 printElidePortAnnotations(p, op, attr, {"ruw", "inner_sym"});
6140}
6141
6142//===----------------------------------------------------------------------===//
6143// ClassInterface custom directive
6144//===----------------------------------------------------------------------===//
6145
6146static ParseResult parseClassInterface(OpAsmParser &parser, Type &result) {
6147 ClassType type;
6148 if (ClassType::parseInterface(parser, type))
6149 return failure();
6150 result = type;
6151 return success();
6152}
6153
6154static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type) {
6155 type.printInterface(p);
6156}
6157
6158//===----------------------------------------------------------------------===//
6159// Miscellaneous custom elision logic.
6160//===----------------------------------------------------------------------===//
6161
6162static ParseResult parseElideEmptyName(OpAsmParser &p,
6163 NamedAttrList &resultAttrs) {
6164 auto result = p.parseOptionalAttrDict(resultAttrs);
6165 if (!resultAttrs.get("name"))
6166 resultAttrs.append("name", p.getBuilder().getStringAttr(""));
6167
6168 return result;
6169}
6170
6171static void printElideEmptyName(OpAsmPrinter &p, Operation *op,
6172 DictionaryAttr attr,
6173 ArrayRef<StringRef> extraElides = {}) {
6174 SmallVector<StringRef> elides(extraElides.begin(), extraElides.end());
6175 if (op->getAttrOfType<StringAttr>("name").getValue().empty())
6176 elides.push_back("name");
6177
6178 p.printOptionalAttrDict(op->getAttrs(), elides);
6179}
6180
6181static ParseResult parsePrintfAttrs(OpAsmParser &p,
6182 NamedAttrList &resultAttrs) {
6183 return parseElideEmptyName(p, resultAttrs);
6184}
6185
6186static void printPrintfAttrs(OpAsmPrinter &p, Operation *op,
6187 DictionaryAttr attr) {
6188 printElideEmptyName(p, op, attr, {"formatString"});
6189}
6190
6191static ParseResult parseFPrintfAttrs(OpAsmParser &p,
6192 NamedAttrList &resultAttrs) {
6193 return parseElideEmptyName(p, resultAttrs);
6194}
6195
6196static void printFPrintfAttrs(OpAsmPrinter &p, Operation *op,
6197 DictionaryAttr attr) {
6198 printElideEmptyName(p, op, attr,
6199 {"formatString", "outputFile", "operandSegmentSizes"});
6200}
6201
6202static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
6203 return parseElideEmptyName(p, resultAttrs);
6204}
6205
6206static void printStopAttrs(OpAsmPrinter &p, Operation *op,
6207 DictionaryAttr attr) {
6208 printElideEmptyName(p, op, attr, {"exitCode"});
6209}
6210
6211static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
6212 return parseElideEmptyName(p, resultAttrs);
6213}
6214
6215static void printVerifAttrs(OpAsmPrinter &p, Operation *op,
6216 DictionaryAttr attr) {
6217 printElideEmptyName(p, op, attr, {"message"});
6218}
6219
6220//===----------------------------------------------------------------------===//
6221// Various namers.
6222//===----------------------------------------------------------------------===//
6223
6224static void genericAsmResultNames(Operation *op,
6225 OpAsmSetValueNameFn setNameFn) {
6226 // Many firrtl dialect operations have an optional 'name' attribute. If
6227 // present, use it.
6228 if (op->getNumResults() == 1)
6229 if (auto nameAttr = op->getAttrOfType<StringAttr>("name"))
6230 setNameFn(op->getResult(0), nameAttr.getValue());
6231}
6232
6233void AddPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6234 genericAsmResultNames(*this, setNameFn);
6235}
6236
6237void AndPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6238 genericAsmResultNames(*this, setNameFn);
6239}
6240
6241void AndRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6242 genericAsmResultNames(*this, setNameFn);
6243}
6244
6245void SizeOfIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6246 genericAsmResultNames(*this, setNameFn);
6247}
6248void AsAsyncResetPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6249 genericAsmResultNames(*this, setNameFn);
6250}
6251void AsClockPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6252 genericAsmResultNames(*this, setNameFn);
6253}
6254void AsSIntPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6255 genericAsmResultNames(*this, setNameFn);
6256}
6257void AsUIntPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6258 genericAsmResultNames(*this, setNameFn);
6259}
6260void BitsPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6261 genericAsmResultNames(*this, setNameFn);
6262}
6263void CatPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6264 genericAsmResultNames(*this, setNameFn);
6265}
6266void CvtPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6267 genericAsmResultNames(*this, setNameFn);
6268}
6269void DShlPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6270 genericAsmResultNames(*this, setNameFn);
6271}
6272void DShlwPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6273 genericAsmResultNames(*this, setNameFn);
6274}
6275void DShrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6276 genericAsmResultNames(*this, setNameFn);
6277}
6278void DivPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6279 genericAsmResultNames(*this, setNameFn);
6280}
6281void EQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6282 genericAsmResultNames(*this, setNameFn);
6283}
6284void GEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6285 genericAsmResultNames(*this, setNameFn);
6286}
6287void GTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6288 genericAsmResultNames(*this, setNameFn);
6289}
6290void GenericIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6291 genericAsmResultNames(*this, setNameFn);
6292}
6293void HeadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6294 genericAsmResultNames(*this, setNameFn);
6295}
6296void IntegerAddOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6297 genericAsmResultNames(*this, setNameFn);
6298}
6299void IntegerMulOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6300 genericAsmResultNames(*this, setNameFn);
6301}
6302void IntegerShrOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6303 genericAsmResultNames(*this, setNameFn);
6304}
6305void IntegerShlOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6306 genericAsmResultNames(*this, setNameFn);
6307}
6308void IsTagOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6309 genericAsmResultNames(*this, setNameFn);
6310}
6311void IsXIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6312 genericAsmResultNames(*this, setNameFn);
6313}
6314void PlusArgsValueIntrinsicOp::getAsmResultNames(
6315 OpAsmSetValueNameFn setNameFn) {
6316 genericAsmResultNames(*this, setNameFn);
6317}
6318void PlusArgsTestIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6319 genericAsmResultNames(*this, setNameFn);
6320}
6321void LEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6322 genericAsmResultNames(*this, setNameFn);
6323}
6324void LTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6325 genericAsmResultNames(*this, setNameFn);
6326}
6327void MulPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6328 genericAsmResultNames(*this, setNameFn);
6329}
6330void MultibitMuxOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6331 genericAsmResultNames(*this, setNameFn);
6332}
6333void MuxPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6334 genericAsmResultNames(*this, setNameFn);
6335}
6336void Mux4CellIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6337 genericAsmResultNames(*this, setNameFn);
6338}
6339void Mux2CellIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6340 genericAsmResultNames(*this, setNameFn);
6341}
6342void NEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6343 genericAsmResultNames(*this, setNameFn);
6344}
6345void NegPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6346 genericAsmResultNames(*this, setNameFn);
6347}
6348void NotPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6349 genericAsmResultNames(*this, setNameFn);
6350}
6351void OrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6352 genericAsmResultNames(*this, setNameFn);
6353}
6354void OrRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6355 genericAsmResultNames(*this, setNameFn);
6356}
6357void PadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6358 genericAsmResultNames(*this, setNameFn);
6359}
6360void RemPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6361 genericAsmResultNames(*this, setNameFn);
6362}
6363void ShlPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6364 genericAsmResultNames(*this, setNameFn);
6365}
6366void ShrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6367 genericAsmResultNames(*this, setNameFn);
6368}
6369
6370void SubPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6371 genericAsmResultNames(*this, setNameFn);
6372}
6373
6374void SubaccessOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6375 genericAsmResultNames(*this, setNameFn);
6376}
6377
6378void SubfieldOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6379 genericAsmResultNames(*this, setNameFn);
6380}
6381
6382void OpenSubfieldOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6383 genericAsmResultNames(*this, setNameFn);
6384}
6385
6386void SubtagOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6387 genericAsmResultNames(*this, setNameFn);
6388}
6389
6390void SubindexOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6391 genericAsmResultNames(*this, setNameFn);
6392}
6393
6394void OpenSubindexOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6395 genericAsmResultNames(*this, setNameFn);
6396}
6397
6398void TagExtractOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6399 genericAsmResultNames(*this, setNameFn);
6400}
6401
6402void TailPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6403 genericAsmResultNames(*this, setNameFn);
6404}
6405
6406void XorPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6407 genericAsmResultNames(*this, setNameFn);
6408}
6409
6410void XorRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6411 genericAsmResultNames(*this, setNameFn);
6412}
6413
6414void UninferredResetCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6415 genericAsmResultNames(*this, setNameFn);
6416}
6417
6418void ConstCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6419 genericAsmResultNames(*this, setNameFn);
6420}
6421
6422void ElementwiseXorPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6423 genericAsmResultNames(*this, setNameFn);
6424}
6425
6426void ElementwiseOrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6427 genericAsmResultNames(*this, setNameFn);
6428}
6429
6430void ElementwiseAndPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6431 genericAsmResultNames(*this, setNameFn);
6432}
6433
6434//===----------------------------------------------------------------------===//
6435// RefOps
6436//===----------------------------------------------------------------------===//
6437
6438void RefCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6439 genericAsmResultNames(*this, setNameFn);
6440}
6441
6442void RefResolveOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6443 genericAsmResultNames(*this, setNameFn);
6444}
6445
6446void RefSendOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6447 genericAsmResultNames(*this, setNameFn);
6448}
6449
6450void RefSubOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6451 genericAsmResultNames(*this, setNameFn);
6452}
6453
6454void RWProbeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6455 genericAsmResultNames(*this, setNameFn);
6456}
6457
6458FIRRTLType RefResolveOp::inferReturnType(ValueRange operands,
6459 DictionaryAttr attrs,
6460 OpaqueProperties properties,
6461 mlir::RegionRange regions,
6462 std::optional<Location> loc) {
6463 Type inType = operands[0].getType();
6464 auto inRefType = type_dyn_cast<RefType>(inType);
6465 if (!inRefType)
6466 return emitInferRetTypeError(
6467 loc, "ref.resolve operand must be ref type, not ", inType);
6468 return inRefType.getType();
6469}
6470
6471FIRRTLType RefSendOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
6472 OpaqueProperties properties,
6473 mlir::RegionRange regions,
6474 std::optional<Location> loc) {
6475 Type inType = operands[0].getType();
6476 auto inBaseType = type_dyn_cast<FIRRTLBaseType>(inType);
6477 if (!inBaseType)
6478 return emitInferRetTypeError(
6479 loc, "ref.send operand must be base type, not ", inType);
6480 return RefType::get(inBaseType.getPassiveType());
6481}
6482
6483FIRRTLType RefSubOp::inferReturnType(Type type, uint32_t fieldIndex,
6484 std::optional<Location> loc) {
6485 auto refType = type_dyn_cast<RefType>(type);
6486 if (!refType)
6487 return emitInferRetTypeError(loc, "input must be of reference type");
6488 auto inType = refType.getType();
6489
6490 // TODO: Determine ref.sub + rwprobe behavior, test.
6491 // Probably best to demote to non-rw, but that has implications
6492 // for any LowerTypes behavior being relied on.
6493 // Allow for now, as need to LowerTypes things generally.
6494 if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
6495 if (fieldIndex < vectorType.getNumElements())
6496 return RefType::get(
6497 vectorType.getElementType().getConstType(
6498 vectorType.isConst() || vectorType.getElementType().isConst()),
6499 refType.getForceable(), refType.getLayer());
6500 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
6501 "' in RefType of vector type ", refType);
6502 }
6503 if (auto bundleType = type_dyn_cast<BundleType>(inType)) {
6504 if (fieldIndex >= bundleType.getNumElements()) {
6505 return emitInferRetTypeError(loc,
6506 "subfield element index is greater than "
6507 "the number of fields in the bundle type");
6508 }
6509 return RefType::get(
6510 bundleType.getElement(fieldIndex)
6511 .type.getConstType(
6512 bundleType.isConst() ||
6513 bundleType.getElement(fieldIndex).type.isConst()),
6514 refType.getForceable(), refType.getLayer());
6515 }
6516
6517 return emitInferRetTypeError(
6518 loc, "ref.sub op requires a RefType of vector or bundle base type");
6519}
6520
6521LogicalResult RefCastOp::verify() {
6522 auto srcLayers = getLayersFor(getInput());
6523 auto dstLayers = getLayersFor(getResult());
6525 getOperation(), srcLayers, dstLayers,
6526 "cannot discard layer requirements of input reference",
6527 "discarding layer requirements");
6528}
6529
6530LogicalResult RefResolveOp::verify() {
6531 auto srcLayers = getLayersFor(getRef());
6532 auto dstLayers = getAmbientLayersAt(getOperation());
6534 getOperation(), srcLayers, dstLayers,
6535 "ambient layers are insufficient to resolve reference");
6536}
6537
6538LogicalResult RWProbeOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
6539 auto targetRef = getTarget();
6540 if (targetRef.getModule() !=
6541 (*this)->getParentOfType<FModuleLike>().getModuleNameAttr())
6542 return emitOpError() << "has non-local target";
6543
6544 auto target = ns.lookup(targetRef);
6545 if (!target)
6546 return emitOpError() << "has target that cannot be resolved: " << targetRef;
6547
6548 auto checkFinalType = [&](auto type, Location loc) -> LogicalResult {
6549 // Determine final type.
6550 mlir::Type fType =
6551 hw::FieldIdImpl::getFinalTypeByFieldID(type, target.getField());
6552 // Check.
6553 auto baseType = type_dyn_cast<FIRRTLBaseType>(fType);
6554 if (!baseType || baseType.getPassiveType() != getType().getType()) {
6555 auto diag = emitOpError("has type mismatch: target resolves to ")
6556 << fType << " instead of expected " << getType().getType();
6557 diag.attachNote(loc) << "target resolves here";
6558 return diag;
6559 }
6560 return success();
6561 };
6562 if (target.isPort()) {
6563 auto mod = cast<FModuleLike>(target.getOp());
6564 return checkFinalType(mod.getPortType(target.getPort()),
6565 mod.getPortLocation(target.getPort()));
6566 }
6567 hw::InnerSymbolOpInterface symOp =
6568 cast<hw::InnerSymbolOpInterface>(target.getOp());
6569 if (!symOp.getTargetResult())
6570 return emitOpError("has target that cannot be probed")
6571 .attachNote(symOp.getLoc())
6572 .append("target resolves here");
6573 auto *ancestor =
6574 symOp.getTargetResult().getParentBlock()->findAncestorOpInBlock(**this);
6575 if (!ancestor || !symOp->isBeforeInBlock(ancestor))
6576 return emitOpError("is not dominated by target")
6577 .attachNote(symOp.getLoc())
6578 .append("target here");
6579 return checkFinalType(symOp.getTargetResult().getType(), symOp.getLoc());
6580}
6581
6582LogicalResult RefForceOp::verify() {
6583 auto ambientLayers = getAmbientLayersAt(getOperation());
6584 auto destLayers = getLayersFor(getDest());
6586 getOperation(), destLayers, ambientLayers,
6587 "has insufficient ambient layers to force its reference");
6588}
6589
6590LogicalResult RefForceInitialOp::verify() {
6591 auto ambientLayers = getAmbientLayersAt(getOperation());
6592 auto destLayers = getLayersFor(getDest());
6594 getOperation(), destLayers, ambientLayers,
6595 "has insufficient ambient layers to force its reference");
6596}
6597
6598LogicalResult RefReleaseOp::verify() {
6599 auto ambientLayers = getAmbientLayersAt(getOperation());
6600 auto destLayers = getLayersFor(getDest());
6602 getOperation(), destLayers, ambientLayers,
6603 "has insufficient ambient layers to release its reference");
6604}
6605
6606LogicalResult RefReleaseInitialOp::verify() {
6607 auto ambientLayers = getAmbientLayersAt(getOperation());
6608 auto destLayers = getLayersFor(getDest());
6610 getOperation(), destLayers, ambientLayers,
6611 "has insufficient ambient layers to release its reference");
6612}
6613
6614LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
6615 auto *target = symbolTable.lookupNearestSymbolFrom(*this, getRefAttr());
6616 if (!target)
6617 return emitOpError("has an invalid symbol reference");
6618
6619 if (!isa<hw::HierPathOp>(target))
6620 return emitOpError("does not target a hierpath op");
6621
6622 // TODO: Verify that the target's type matches the type of this op.
6623 return success();
6624}
6625
6626LogicalResult XMRDerefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
6627 auto *target = symbolTable.lookupNearestSymbolFrom(*this, getRefAttr());
6628 if (!target)
6629 return emitOpError("has an invalid symbol reference");
6630
6631 if (!isa<hw::HierPathOp>(target))
6632 return emitOpError("does not target a hierpath op");
6633
6634 // TODO: Verify that the target's type matches the type of this op.
6635 return success();
6636}
6637
6638//===----------------------------------------------------------------------===//
6639// Layer Block Operations
6640//===----------------------------------------------------------------------===//
6641
6642LogicalResult LayerBlockOp::verify() {
6643 auto layerName = getLayerName();
6644 auto *parentOp = (*this)->getParentOp();
6645
6646 // Get parent operation that isn't a when or match.
6647 while (isa<WhenOp, MatchOp>(parentOp))
6648 parentOp = parentOp->getParentOp();
6649
6650 // Verify the correctness of the symbol reference. Only verify that this
6651 // layer block makes sense in its parent module or layer block.
6652 auto nestedReferences = layerName.getNestedReferences();
6653 if (nestedReferences.empty()) {
6654 if (!isa<FModuleOp>(parentOp)) {
6655 auto diag = emitOpError() << "has an un-nested layer symbol, but does "
6656 "not have a 'firrtl.module' op as a parent";
6657 return diag.attachNote(parentOp->getLoc())
6658 << "illegal parent op defined here";
6659 }
6660 } else {
6661 auto parentLayerBlock = dyn_cast<LayerBlockOp>(parentOp);
6662 if (!parentLayerBlock) {
6663 auto diag = emitOpError()
6664 << "has a nested layer symbol, but does not have a '"
6665 << getOperationName() << "' op as a parent'";
6666 return diag.attachNote(parentOp->getLoc())
6667 << "illegal parent op defined here";
6668 }
6669 auto parentLayerBlockName = parentLayerBlock.getLayerName();
6670 if (parentLayerBlockName.getRootReference() !=
6671 layerName.getRootReference() ||
6672 parentLayerBlockName.getNestedReferences() !=
6673 layerName.getNestedReferences().drop_back()) {
6674 auto diag = emitOpError() << "is nested under an illegal layer block";
6675 return diag.attachNote(parentLayerBlock->getLoc())
6676 << "illegal parent layer block defined here";
6677 }
6678 }
6679
6680 // Verify the body of the region.
6681 FieldRefCache fieldRefCache;
6682 auto result = getBody(0)->walk<mlir::WalkOrder::PreOrder>(
6683 [&](Operation *op) -> WalkResult {
6684 // Skip nested layer blocks. Those will be verified separately.
6685 if (isa<LayerBlockOp>(op))
6686 return WalkResult::skip();
6687
6688 // Check all the operands of each op to make sure that only legal things
6689 // are captured.
6690 for (auto operand : op->getOperands()) {
6691 // Any value captured from the current layer block is fine.
6692 if (auto *definingOp = operand.getDefiningOp())
6693 if (getOperation()->isAncestor(definingOp))
6694 continue;
6695
6696 auto type = operand.getType();
6697
6698 // Capture of a non-base type, e.g., reference, is allowed.
6699 if (isa<PropertyType>(type)) {
6700 auto diag = emitOpError() << "captures a property operand";
6701 diag.attachNote(operand.getLoc()) << "operand is defined here";
6702 diag.attachNote(op->getLoc()) << "operand is used here";
6703 return WalkResult::interrupt();
6704 }
6705 }
6706
6707 // Ensure that the layer block does not drive any sinks outside.
6708 if (auto connect = dyn_cast<FConnectLike>(op)) {
6709 // ref.define is allowed to drive probes outside the layerblock.
6710 if (isa<RefDefineOp>(connect))
6711 return WalkResult::advance();
6712
6713 // Verify that connects only drive values declared in the layer block.
6714 // If we see a non-passive connect destination, then verify that the
6715 // source is in the same layer block so that the source is not driven.
6716 auto dest =
6717 fieldRefCache.getFieldRefFromValue(connect.getDest()).getValue();
6718 bool passive = true;
6719 if (auto type =
6720 type_dyn_cast<FIRRTLBaseType>(connect.getDest().getType()))
6721 passive = type.isPassive();
6722 // TODO: Improve this verifier. This is intentionally _not_ verifying
6723 // a non-passive ConnectLike because it is hugely annoying to do
6724 // so---it requires a full understanding of if the connect is driving
6725 // destination-to-source, source-to-destination, or bi-directionally
6726 // which requires deep inspection of the type. Eventually, the FIRRTL
6727 // pass pipeline will remove all flips (e.g., canonicalize connect to
6728 // matchingconnect) and this hole won't exist.
6729 if (!passive)
6730 return WalkResult::advance();
6731
6732 if (isAncestorOfValueOwner(getOperation(), dest))
6733 return WalkResult::advance();
6734
6735 auto diag =
6736 connect.emitOpError()
6737 << "connects to a destination which is defined outside its "
6738 "enclosing layer block";
6739 diag.attachNote(getLoc()) << "enclosing layer block is defined here";
6740 diag.attachNote(dest.getLoc()) << "destination is defined here";
6741 return WalkResult::interrupt();
6742 }
6743
6744 return WalkResult::advance();
6745 });
6746
6747 return failure(result.wasInterrupted());
6748}
6749
6750LogicalResult
6751LayerBlockOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
6752 auto layerOp =
6753 symbolTable.lookupNearestSymbolFrom<LayerOp>(*this, getLayerNameAttr());
6754 if (!layerOp) {
6755 return emitOpError("invalid symbol reference");
6756 }
6757
6758 return success();
6759}
6760
6761//===----------------------------------------------------------------------===//
6762// Format String operations
6763//===----------------------------------------------------------------------===//
6764
6765void TimeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6766 setNameFn(getResult(), "time");
6767}
6768
6769void HierarchicalModuleNameOp::getAsmResultNames(
6770 OpAsmSetValueNameFn setNameFn) {
6771 setNameFn(getResult(), "hierarchicalmodulename");
6772}
6773
6774ParseResult FPrintFOp::parse(::mlir::OpAsmParser &parser,
6775 ::mlir::OperationState &result) {
6776 // Parse required clock and condition operands
6777 OpAsmParser::UnresolvedOperand clock, cond;
6778 if (parser.parseOperand(clock) || parser.parseComma() ||
6779 parser.parseOperand(cond) || parser.parseComma())
6780 return failure();
6781
6782 auto parseFormatString =
6783 [&parser](llvm::SMLoc &loc, StringAttr &result,
6784 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &operands)
6785 -> ParseResult {
6786 loc = parser.getCurrentLocation();
6787 // NOTE: Don't use parseAttribute. "format_string" : !firrtl.clock is
6788 // considered as a typed attr.
6789 std::string resultStr;
6790 if (parser.parseString(&resultStr))
6791 return failure();
6792 result = parser.getBuilder().getStringAttr(resultStr);
6793
6794 // This is an optional
6795 if (parser.parseOperandList(operands, AsmParser::Delimiter::OptionalParen))
6796 return failure();
6797 return success();
6798 };
6799
6800 // Parse output file and format string substitutions
6801 SmallVector<OpAsmParser::UnresolvedOperand> outputFileSubstitutions,
6802 substitutions;
6803 llvm::SMLoc outputFileLoc, formatStringLoc;
6804
6805 if (parseFormatString(
6806 outputFileLoc,
6807 result.getOrAddProperties<FPrintFOp::Properties>().outputFile,
6808 outputFileSubstitutions) ||
6809 parser.parseComma() ||
6810 parseFormatString(
6811 formatStringLoc,
6812 result.getOrAddProperties<FPrintFOp::Properties>().formatString,
6813 substitutions))
6814 return failure();
6815
6816 if (parseFPrintfAttrs(parser, result.attributes))
6817 return failure();
6818
6819 // Parse types
6820 Type clockType, condType;
6821 SmallVector<Type> restTypes;
6822
6823 if (parser.parseColon() || parser.parseType(clockType) ||
6824 parser.parseComma() || parser.parseType(condType))
6825 return failure();
6826
6827 if (succeeded(parser.parseOptionalComma())) {
6828 if (parser.parseTypeList(restTypes))
6829 return failure();
6830 }
6831
6832 // Set operand segment sizes
6833 result.getOrAddProperties<FPrintFOp::Properties>().operandSegmentSizes = {
6834 1, 1, static_cast<int32_t>(outputFileSubstitutions.size()),
6835 static_cast<int32_t>(substitutions.size())};
6836
6837 // Resolve all operands
6838 if (parser.resolveOperand(clock, clockType, result.operands) ||
6839 parser.resolveOperand(cond, condType, result.operands) ||
6840 parser.resolveOperands(
6841 outputFileSubstitutions,
6842 ArrayRef(restTypes).take_front(outputFileSubstitutions.size()),
6843 outputFileLoc, result.operands) ||
6844 parser.resolveOperands(
6845 substitutions,
6846 ArrayRef(restTypes).drop_front(outputFileSubstitutions.size()),
6847 formatStringLoc, result.operands))
6848 return failure();
6849
6850 return success();
6851}
6852
6853void FPrintFOp::print(OpAsmPrinter &p) {
6854 p << " " << getClock() << ", " << getCond() << ", ";
6855 p.printAttributeWithoutType(getOutputFileAttr());
6856 if (!getOutputFileSubstitutions().empty()) {
6857 p << "(";
6858 p.printOperands(getOutputFileSubstitutions());
6859 p << ")";
6860 }
6861 p << ", ";
6862 p.printAttributeWithoutType(getFormatStringAttr());
6863 if (!getSubstitutions().empty()) {
6864 p << "(";
6865 p.printOperands(getSubstitutions());
6866 p << ")";
6867 }
6868 printFPrintfAttrs(p, *this, (*this)->getAttrDictionary());
6869 p << " : " << getClock().getType() << ", " << getCond().getType();
6870 if (!getOutputFileSubstitutions().empty() || !getSubstitutions().empty()) {
6871 for (auto type : getOperands().drop_front(2).getTypes()) {
6872 p << ", ";
6873 p.printType(type);
6874 }
6875 }
6876}
6877
6878//===----------------------------------------------------------------------===//
6879// FFlushOp
6880//===----------------------------------------------------------------------===//
6881
6882LogicalResult FFlushOp::verify() {
6883 if (!getOutputFileAttr() && !getOutputFileSubstitutions().empty())
6884 return emitOpError("substitutions without output file are not allowed");
6885 return success();
6886}
6887
6888//===----------------------------------------------------------------------===//
6889// BindOp
6890//===----------------------------------------------------------------------===//
6891
6892LogicalResult BindOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
6893 auto ref = getInstanceAttr();
6894 auto target = ns.lookup(ref);
6895 if (!target)
6896 return emitError() << "target " << ref << " cannot be resolved";
6897
6898 if (!target.isOpOnly())
6899 return emitError() << "target " << ref << " is not an operation";
6900
6901 auto instance = dyn_cast<InstanceOp>(target.getOp());
6902 if (!instance)
6903 return emitError() << "target " << ref << " is not an instance";
6904
6905 if (!instance.getDoNotPrint())
6906 return emitError() << "target " << ref << " is not marked doNotPrint";
6907
6908 return success();
6909}
6910
6911//===----------------------------------------------------------------------===//
6912// TblGen Generated Logic.
6913//===----------------------------------------------------------------------===//
6914
6915// Provide the autogenerated implementation guts for the Op classes.
6916#define GET_OP_CLASSES
6917#include "circt/Dialect/FIRRTL/FIRRTL.cpp.inc"
static void printNameKind(OpAsmPrinter &p, Operation *op, firrtl::NameKindEnumAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result)
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
Definition CHIRRTL.cpp:30
MlirType elementType
Definition CHIRRTL.cpp:29
#define isdigit(x)
Definition FIRLexer.cpp:26
static LogicalResult verifyProbeType(RefType refType, Location loc, CircuitOp circuitOp, SymbolTableCollection &symbolTable, Twine start)
static ArrayAttr fixDomainInfoDeletions(MLIRContext *context, ArrayAttr domainInfoAttr, const llvm::BitVector &portIndices, bool supportsEmptyAttr)
static SmallVector< PortInfo > getPortImpl(FModuleLike module)
static void buildClass(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports)
static void printStopAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static void buildModule(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, ArrayAttr annotations, ArrayAttr layers)
static LayerSet getLayersFor(Value value)
Get the effective layer requirements for the given value.
static SmallVector< hw::PortInfo > getPortListImpl(FModuleLike module)
ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result)
static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth, int32_t &rhsWidth, bool &isConstResult, std::optional< Location > loc)
If LHS and RHS are both UInt or SInt types, the return true and fill in the width of them if known.
static LogicalResult verifySubfieldLike(OpTy op)
static void eraseInternalPaths(T op, const llvm::BitVector &portIndices)
static void printFPrintfAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip=false, bool outerTypeIsConst=false)
Checks if the type has any 'const' leaf elements .
static ParseResult parsePrintfAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseParameterList(OpAsmParser &parser, ArrayAttr &parameters)
Shim to use with assemblyFormat, custom<ParameterList>.
static RetTy emitInferRetTypeError(std::optional< Location > loc, const Twine &message, Args &&...args)
Emit an error if optional location is non-null, return null of return type.
Definition FIRRTLOps.cpp:97
static SmallVector< T > removeElementsAtIndices(ArrayRef< T > input, const llvm::BitVector &indicesToDrop)
Remove elements from the input array corresponding to set bits in indicesToDrop, returning the elemen...
Definition FIRRTLOps.cpp:58
static LogicalResult checkLayerCompatibility(Operation *op, const LayerSet &src, const LayerSet &dst, const Twine &errorMsg, const Twine &noteMsg=Twine("missing layer requirements"))
static ParseResult parseModulePorts(OpAsmParser &parser, bool hasSSAIdentifiers, bool supportsSymbols, bool supportsDomains, SmallVectorImpl< OpAsmParser::Argument > &entryArgs, SmallVectorImpl< Direction > &portDirections, SmallVectorImpl< Attribute > &portNames, SmallVectorImpl< Attribute > &portTypes, SmallVectorImpl< Attribute > &portAnnotations, SmallVectorImpl< Attribute > &portSyms, SmallVectorImpl< Attribute > &portLocs, SmallVectorImpl< Attribute > &domains)
Parse a list of module ports.
static LogicalResult checkConnectConditionality(FConnectLike connect)
Checks that connections to 'const' destinations are not dependent on non-'const' conditions in when b...
static void erasePorts(FModuleLike op, const llvm::BitVector &portIndices)
Erases the ports that have their corresponding bit set in portIndices.
static ParseResult parseClassInterface(OpAsmParser &parser, Type &result)
static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseNameKind(OpAsmParser &parser, firrtl::NameKindEnumAttr &result)
A forward declaration for NameKind attribute parser.
static void insertPorts(FModuleLike op, ArrayRef< std::pair< unsigned, PortInfo > > ports, bool supportsInternalPaths=false)
Inserts the given ports.
static size_t getAddressWidth(size_t depth)
static void forceableAsmResultNames(Forceable op, StringRef name, OpAsmSetValueNameFn setNameFn)
Helper for naming forceable declarations (and their optional ref result).
static void printFModuleLikeOp(OpAsmPrinter &p, FModuleLike op)
static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer)
static bool checkAggConstant(Operation *op, Attribute attr, FIRRTLBaseType type)
static void printClassLike(OpAsmPrinter &p, ClassLike op)
static hw::ModulePort::Direction dirFtoH(Direction dir)
static ParseResult parseOptionalParameters(OpAsmParser &parser, SmallVectorImpl< Attribute > &parameters)
Parse an parameter list if present.
static MemOp::PortKind getMemPortKindFromType(FIRRTLType type)
Return the kind of port this is given the port type from a 'mem' decl.
static LogicalResult verifyInternalPaths(FModuleLike op, std::optional<::mlir::ArrayAttr > internalPaths)
static void genericAsmResultNames(Operation *op, OpAsmSetValueNameFn setNameFn)
static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type)
static void printPrintfAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
const char * toString(Flow flow)
static bool isLayerSetCompatibleWith(const LayerSet &src, const LayerSet &dst, SmallVectorImpl< SymbolRefAttr > &missing)
Check that the source layers are all present in the destination layers.
static bool isLayerCompatibleWith(mlir::SymbolRefAttr srcLayer, mlir::SymbolRefAttr dstLayer)
Check that the source layer is compatible with the destination layer.
static LayerSet getAmbientLayersFor(Value value)
Get the ambient layer requirements at the definition site of the value.
void buildModuleLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports)
static LayerSet getAmbientLayersAt(Operation *op)
Get the ambient layers active at the given op.
static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op, DictionaryAttr attrs)
static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser, NamedAttrList &resultAttrs)
static FIRRTLBaseType inferMuxReturnType(FIRRTLBaseType high, FIRRTLBaseType low, bool isConstCondition, std::optional< Location > loc)
Infer the result type for a multiplexer given its two operand types, which may be aggregates.
static ParseResult parseCircuitOpAttrs(OpAsmParser &parser, NamedAttrList &resultAttrs)
void getAsmBlockArgumentNamesImpl(Operation *op, mlir::Region &region, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
static void printElideAnnotations(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseElidePortAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding empty 'annotations' and 'portAnnotations' attributes i...
static ParseResult parseFPrintfAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs)
static Attribute fixDomainInfoInsertions(MLIRContext *context, Attribute domainInfoAttr, const SmallVectorImpl< unsigned > &numInserted)
Return an updated domain info Attribute with domain indices updated based on port insertions.
static LogicalResult checkConnectFlow(Operation *connect)
Check if the source and sink are of appropriate flow.
static void printParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a paramter list for a module or instance.
static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
static ParseResult parseElideAnnotations(OpAsmParser &parser, NamedAttrList &resultAttrs)
Parse an optional attribute dictionary, adding an empty 'annotations' attribute if not specified.
ParseResult parseClassLike(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
static void printCircuitOpAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static LogicalResult verifyPortSymbolUses(FModuleLike module, SymbolTableCollection &symbolTable)
static void printVerifAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
Always elide "ruw" and elide "annotations" if it exists or if it is empty.
static bool isTypeAllowedForDPI(Operation *op, Type type)
static ParseResult parseElideEmptyName(OpAsmParser &p, NamedAttrList &resultAttrs)
static bool printModulePorts(OpAsmPrinter &p, Block *block, ArrayRef< bool > portDirections, ArrayRef< Attribute > portNames, ArrayRef< Attribute > portTypes, ArrayRef< Attribute > portAnnotations, ArrayRef< Attribute > portSyms, ArrayRef< Attribute > portLocs, ArrayRef< Attribute > domainInfo)
Print a list of module ports in the following form: in x: !firrtl.uint<1> [{class = "DontTouch}],...
static void printElideEmptyName(OpAsmPrinter &p, Operation *op, DictionaryAttr attr, ArrayRef< StringRef > extraElides={})
static ParseResult parseFModuleLikeOp(OpAsmParser &parser, OperationState &result, bool hasSSAIdentifiers)
static bool isAncestor(Block *block, Block *other)
Definition LayerSink.cpp:60
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
This class represents a reference to a specific field or element of an aggregate value.
Definition FieldRef.h:28
Value getValue() const
Get the Value which created this location.
Definition FieldRef.h:37
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool hasDontTouch() const
firrtl.transforms.DontTouchAnnotation
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
ExprVisitor is a visitor for FIRRTL expression nodes.
ResultType dispatchExprVisitor(Operation *op, ExtraArgs... args)
FIRRTLBaseType getConstType(bool isConst) const
Return a 'const' or non-'const' version of this type.
FIRRTLBaseType getMaskType()
Return this type with all ground types replaced with UInt<1>.
int32_t getBitWidthOrSentinel()
If this is an IntType, AnalogType, or sugar type for a single bit (Clock, Reset, etc) then return the...
FIRRTLBaseType getAllConstDroppedType()
Return this type with a 'const' modifiers dropped.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
bool isConst() const
Returns true if this is a 'const' type that can only hold compile-time constant values.
Caching version of getFieldRefFromValue.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Caching version of getFieldRefFromValue.
This is the common base class between SIntType and UIntType.
int32_t getWidthOrSentinel() const
Return the width of this type, or -1 if it has none specified.
static IntType get(MLIRContext *context, bool isSigned, int32_t widthOrSentinel=-1, bool isConst=false)
Return an SIntType or UIntType with the specified signedness, width, and constness.
bool hasWidth() const
Return true if this integer type has a known width.
std::optional< int32_t > getWidth() const
Return an optional containing the width, if the width is known (or empty if width is unknown).
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
int main(int argc, char **argv)
Definition driver.cpp:48
connect(destination, source)
Definition support.py:39
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
ClassType getInstanceTypeForClassLike(ClassLike classOp)
LogicalResult verifyTypeAgainstClassLike(ClassLike classOp, ClassType type, function_ref< InFlightDiagnostic()> emitError)
Assuming that the classOp is the source of truth, verify that the type accurately matches the signatu...
RefType getForceableResultType(bool forceable, Type type)
Return null or forceable reference result type.
mlir::DenseBoolArrayAttr packAttribute(MLIRContext *context, ArrayRef< Direction > directions)
Return a DenseBoolArrayAttr containing the packed representation of an array of directions.
static bool unGet(Direction dir)
Convert from Direction to bool. The opposite of get;.
SmallVector< Direction > unpackAttribute(mlir::DenseBoolArrayAttr directions)
Turn a packed representation of port attributes into a vector that can be worked with.
static Direction get(bool isOutput)
Return an output direction if isOutput is true, otherwise return an input direction.
static StringRef toString(Direction direction)
FIRRTLType inferElementwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferBitwiseResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferAddSubResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferComparisonResult(FIRRTLType lhs, FIRRTLType rhs, std::optional< Location > loc)
FIRRTLType inferReductionResult(FIRRTLType arg, std::optional< Location > loc)
LogicalResult verifySameOperandsIntTypeKind(Operation *op)
LogicalResult verifyReferencedModule(Operation *instanceOp, SymbolTableCollection &symbolTable, mlir::FlatSymbolRefAttr moduleName)
Verify that the instance refers to a valid FIRRTL module.
BaseTy type_cast(Type type)
Flow swapFlow(Flow flow)
Get a flow's reverse.
Direction
This represents the direction of a single port.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
void walkGroundTypes(FIRRTLType firrtlType, llvm::function_ref< void(uint64_t, FIRRTLBaseType, bool)> fn)
Walk leaf ground types in the firrtlType and apply the function fn.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
bool areAnonymousTypesEquivalent(FIRRTLBaseType lhs, FIRRTLBaseType rhs)
Return true if anonymous types of given arguments are equivalent by pointer comparison.
constexpr bool isValidDst(Flow flow)
Definition FIRRTLOps.h:69
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
constexpr const char * dutAnnoClass
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
bool hasDontTouch(Value value)
Check whether a block argument ("port") or the operation defining a value has a DontTouch annotation,...
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
bool isTypeLarger(FIRRTLBaseType dstType, FIRRTLBaseType srcType)
Returns true if the destination is at least as wide as a source.
bool containsConst(Type type)
Returns true if the type is or contains a 'const' type whose value is guaranteed to be unchanging at ...
bool isDuplexValue(Value val)
Returns true if the value results from an expression with duplex flow.
SmallSet< SymbolRefAttr, 4, LayerSetCompare > LayerSet
Definition LayerSet.h:42
constexpr bool isValidSrc(Flow flow)
Definition FIRRTLOps.h:65
Value getModuleScopedDriver(Value val, bool lookThroughWires, bool lookThroughNodes, bool lookThroughCasts)
Return the value that drives another FIRRTL value within module scope.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
BaseTy type_dyn_cast(Type type)
bool isConst(Type type)
Returns true if this is a 'const' type whose value is guaranteed to be unchanging at circuit executio...
bool areTypesConstCastable(FIRRTLType destType, FIRRTLType srcType, bool srcOuterTypeIsConst=false)
Returns whether the srcType can be const-casted to the destType.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
DeclKind getDeclarationKind(Value val)
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void elideImplicitSSAName(OpAsmPrinter &printer, Operation *op, DictionaryAttr attrs, SmallVectorImpl< StringRef > &elides)
Check if the name attribute in attrs matches the SSA name of the operation's first result.
bool isAncestorOfValueOwner(Operation *op, Value value)
Return true if a Value is created "underneath" an operation.
Definition Utils.h:27
bool inferImplicitSSAName(OpAsmParser &parser, NamedAttrList &attrs)
Ensure that attrs contains a name attribute by inferring its value from the SSA name of the operation...
Definition hw.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:183
StringAttr getFirMemoryName() const
Compares two SymbolRefAttr lexicographically, returning true if LHS should be ordered before RHS.
Definition LayerSet.h:19
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
This holds the name, type, direction of a module's ports.