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