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