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