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