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