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