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