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