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