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