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