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