Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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(OpBuilder &builder, OperationState &result,
2221 TypeRange resultTypes, StringRef moduleName,
2222 StringRef name, NameKindEnum nameKind,
2223 ArrayRef<Direction> portDirections,
2224 ArrayRef<Attribute> portNames,
2225 ArrayRef<Attribute> annotations,
2226 ArrayRef<Attribute> portAnnotations,
2227 ArrayRef<Attribute> layers, bool lowerToBind,
2228 bool doNotPrint, StringAttr innerSym) {
2229 build(builder, result, resultTypes, moduleName, name, nameKind,
2230 portDirections, portNames, annotations, portAnnotations, layers,
2231 lowerToBind, doNotPrint,
2232 innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
2233}
2234
2235void InstanceOp::build(OpBuilder &builder, OperationState &result,
2236 TypeRange resultTypes, StringRef moduleName,
2237 StringRef name, NameKindEnum nameKind,
2238 ArrayRef<Direction> portDirections,
2239 ArrayRef<Attribute> portNames,
2240 ArrayRef<Attribute> annotations,
2241 ArrayRef<Attribute> portAnnotations,
2242 ArrayRef<Attribute> layers, bool lowerToBind,
2243 bool doNotPrint, hw::InnerSymAttr innerSym) {
2244 result.addTypes(resultTypes);
2245 result.getOrAddProperties<Properties>().setModuleName(
2246 SymbolRefAttr::get(builder.getContext(), moduleName));
2247 result.getOrAddProperties<Properties>().setName(builder.getStringAttr(name));
2248 result.getOrAddProperties<Properties>().setPortDirections(
2249 direction::packAttribute(builder.getContext(), portDirections));
2250 result.getOrAddProperties<Properties>().setPortNames(
2251 builder.getArrayAttr(portNames));
2252 result.getOrAddProperties<Properties>().setAnnotations(
2253 builder.getArrayAttr(annotations));
2254 result.getOrAddProperties<Properties>().setLayers(
2255 builder.getArrayAttr(layers));
2256 if (lowerToBind)
2257 result.getOrAddProperties<Properties>().setLowerToBind(
2258 builder.getUnitAttr());
2259 if (doNotPrint)
2260 result.getOrAddProperties<Properties>().setDoNotPrint(
2261 builder.getUnitAttr());
2262 if (innerSym)
2263 result.getOrAddProperties<Properties>().setInnerSym(innerSym);
2264
2265 result.getOrAddProperties<Properties>().setNameKind(
2266 NameKindEnumAttr::get(builder.getContext(), nameKind));
2267
2268 if (portAnnotations.empty()) {
2269 SmallVector<Attribute, 16> portAnnotationsVec(resultTypes.size(),
2270 builder.getArrayAttr({}));
2271 result.getOrAddProperties<Properties>().setPortAnnotations(
2272 builder.getArrayAttr(portAnnotationsVec));
2273 } else {
2274 assert(portAnnotations.size() == resultTypes.size());
2275 result.getOrAddProperties<Properties>().setPortAnnotations(
2276 builder.getArrayAttr(portAnnotations));
2277 }
2278}
2279
2280void InstanceOp::build(OpBuilder &builder, OperationState &result,
2281 FModuleLike module, StringRef name,
2282 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2283 ArrayRef<Attribute> portAnnotations, bool lowerToBind,
2284 bool doNotPrint, hw::InnerSymAttr innerSym) {
2285
2286 // Gather the result types.
2287 SmallVector<Type> resultTypes;
2288 resultTypes.reserve(module.getNumPorts());
2289 llvm::transform(
2290 module.getPortTypes(), std::back_inserter(resultTypes),
2291 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2292
2293 // Create the port annotations.
2294 ArrayAttr portAnnotationsAttr;
2295 if (portAnnotations.empty()) {
2296 portAnnotationsAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
2297 resultTypes.size(), builder.getArrayAttr({})));
2298 } else {
2299 portAnnotationsAttr = builder.getArrayAttr(portAnnotations);
2300 }
2301
2302 return build(
2303 builder, result, resultTypes,
2304 SymbolRefAttr::get(builder.getContext(), module.getModuleNameAttr()),
2305 builder.getStringAttr(name),
2306 NameKindEnumAttr::get(builder.getContext(), nameKind),
2307 module.getPortDirectionsAttr(), module.getPortNamesAttr(),
2308 builder.getArrayAttr(annotations), portAnnotationsAttr,
2309 module.getLayersAttr(), lowerToBind ? builder.getUnitAttr() : UnitAttr(),
2310 doNotPrint ? builder.getUnitAttr() : UnitAttr(), innerSym);
2311}
2312
2313void InstanceOp::build(OpBuilder &builder, OperationState &odsState,
2314 ArrayRef<PortInfo> ports, StringRef moduleName,
2315 StringRef name, NameKindEnum nameKind,
2316 ArrayRef<Attribute> annotations,
2317 ArrayRef<Attribute> layers, bool lowerToBind,
2318 bool doNotPrint, hw::InnerSymAttr innerSym) {
2319 // Gather the result types.
2320 SmallVector<Type> newResultTypes;
2321 SmallVector<Direction> newPortDirections;
2322 SmallVector<Attribute> newPortNames;
2323 SmallVector<Attribute> newPortAnnotations;
2324 for (auto &p : ports) {
2325 newResultTypes.push_back(p.type);
2326 newPortDirections.push_back(p.direction);
2327 newPortNames.push_back(p.name);
2328 newPortAnnotations.push_back(p.annotations.getArrayAttr());
2329 }
2330
2331 return build(builder, odsState, newResultTypes, moduleName, name, nameKind,
2332 newPortDirections, newPortNames, annotations, newPortAnnotations,
2333 layers, lowerToBind, doNotPrint, innerSym);
2334}
2335
2336LogicalResult InstanceOp::verify() {
2337 // The instance may only be instantiated under its required layers.
2338 auto ambientLayers = getAmbientLayersAt(getOperation());
2339 SmallVector<SymbolRefAttr> missingLayers;
2340 for (auto layer : getLayersAttr().getAsRange<SymbolRefAttr>())
2341 if (!isLayerCompatibleWith(layer, ambientLayers))
2342 missingLayers.push_back(layer);
2343
2344 if (missingLayers.empty())
2345 return success();
2346
2347 auto diag =
2348 emitOpError("ambient layers are insufficient to instantiate module");
2349 auto &note = diag.attachNote();
2350 note << "missing layer requirements: ";
2351 interleaveComma(missingLayers, note);
2352 return failure();
2353}
2354
2355/// Builds a new `InstanceOp` with the ports listed in `portIndices` erased, and
2356/// updates any users of the remaining ports to point at the new instance.
2357InstanceOp InstanceOp::erasePorts(OpBuilder &builder,
2358 const llvm::BitVector &portIndices) {
2359 assert(portIndices.size() >= getNumResults() &&
2360 "portIndices is not at least as large as getNumResults()");
2361
2362 if (portIndices.none())
2363 return *this;
2364
2365 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
2366 SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
2367 SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
2368 direction::unpackAttribute(getPortDirectionsAttr()), portIndices);
2369 SmallVector<Attribute> newPortNames =
2370 removeElementsAtIndices(getPortNames().getValue(), portIndices);
2371 SmallVector<Attribute> newPortAnnotations =
2372 removeElementsAtIndices(getPortAnnotations().getValue(), portIndices);
2373
2374 auto newOp = builder.create<InstanceOp>(
2375 getLoc(), newResultTypes, getModuleName(), getName(), getNameKind(),
2376 newPortDirections, newPortNames, getAnnotations().getValue(),
2377 newPortAnnotations, getLayers(), getLowerToBind(), getDoNotPrint(),
2378 getInnerSymAttr());
2379
2380 for (unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
2381 oldIdx != numOldPorts; ++oldIdx) {
2382 if (portIndices.test(oldIdx)) {
2383 assert(getResult(oldIdx).use_empty() && "removed instance port has uses");
2384 continue;
2385 }
2386 getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
2387 ++newIdx;
2388 }
2389
2390 // Compy over "output_file" information so that this is not lost when ports
2391 // are erased.
2392 //
2393 // TODO: Other attributes may need to be copied over.
2394 if (auto outputFile = (*this)->getAttr("output_file"))
2395 newOp->setAttr("output_file", outputFile);
2396
2397 return newOp;
2398}
2399
2400ArrayAttr InstanceOp::getPortAnnotation(unsigned portIdx) {
2401 assert(portIdx < getNumResults() &&
2402 "index should be smaller than result number");
2403 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2404}
2405
2406void InstanceOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2407 assert(annotations.size() == getNumResults() &&
2408 "number of annotations is not equal to result number");
2409 (*this)->setAttr("portAnnotations",
2410 ArrayAttr::get(getContext(), annotations));
2411}
2412
2413InstanceOp
2414InstanceOp::cloneAndInsertPorts(ArrayRef<std::pair<unsigned, PortInfo>> ports) {
2415 auto portSize = ports.size();
2416 auto newPortCount = getNumResults() + portSize;
2417 SmallVector<Direction> newPortDirections;
2418 newPortDirections.reserve(newPortCount);
2419 SmallVector<Attribute> newPortNames;
2420 newPortNames.reserve(newPortCount);
2421 SmallVector<Type> newPortTypes;
2422 newPortTypes.reserve(newPortCount);
2423 SmallVector<Attribute> newPortAnnos;
2424 newPortAnnos.reserve(newPortCount);
2425
2426 unsigned oldIndex = 0;
2427 unsigned newIndex = 0;
2428 while (oldIndex + newIndex < newPortCount) {
2429 // Check if we should insert a port here.
2430 if (newIndex < portSize && ports[newIndex].first == oldIndex) {
2431 auto &newPort = ports[newIndex].second;
2432 newPortDirections.push_back(newPort.direction);
2433 newPortNames.push_back(newPort.name);
2434 newPortTypes.push_back(newPort.type);
2435 newPortAnnos.push_back(newPort.annotations.getArrayAttr());
2436 ++newIndex;
2437 } else {
2438 // Copy the next old port.
2439 newPortDirections.push_back(getPortDirection(oldIndex));
2440 newPortNames.push_back(getPortName(oldIndex));
2441 newPortTypes.push_back(getType(oldIndex));
2442 newPortAnnos.push_back(getPortAnnotation(oldIndex));
2443 ++oldIndex;
2444 }
2445 }
2446
2447 // Create a new instance op with the reset inserted.
2448 return OpBuilder(*this).create<InstanceOp>(
2449 getLoc(), newPortTypes, getModuleName(), getName(), getNameKind(),
2450 newPortDirections, newPortNames, getAnnotations().getValue(),
2451 newPortAnnos, getLayers(), getLowerToBind(), getDoNotPrint(),
2452 getInnerSymAttr());
2453}
2454
2455LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2456 return instance_like_impl::verifyReferencedModule(*this, symbolTable,
2457 getModuleNameAttr());
2458}
2459
2460StringRef InstanceOp::getInstanceName() { return getName(); }
2461
2462StringAttr InstanceOp::getInstanceNameAttr() { return getNameAttr(); }
2463
2464void InstanceOp::print(OpAsmPrinter &p) {
2465 // Print the instance name.
2466 p << " ";
2467 p.printKeywordOrString(getName());
2468 if (auto attr = getInnerSymAttr()) {
2469 p << " sym ";
2470 p.printSymbolName(attr.getSymName());
2471 }
2472 if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2473 p << ' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2474
2475 // Print the attr-dict.
2476 SmallVector<StringRef, 10> omittedAttrs = {
2477 "moduleName", "name", "portDirections",
2478 "portNames", "portTypes", "portAnnotations",
2479 "inner_sym", "nameKind"};
2480 if (getAnnotations().empty())
2481 omittedAttrs.push_back("annotations");
2482 if (getLayers().empty())
2483 omittedAttrs.push_back("layers");
2484 p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2485
2486 // Print the module name.
2487 p << " ";
2488 p.printSymbolName(getModuleName());
2489
2490 // Collect all the result types as TypeAttrs for printing.
2491 SmallVector<Attribute> portTypes;
2492 portTypes.reserve(getNumResults());
2493 llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2494 &TypeAttr::get);
2495 printModulePorts(p, /*block=*/nullptr, getPortDirectionsAttr(),
2496 getPortNames().getValue(), portTypes,
2497 getPortAnnotations().getValue(), {}, {});
2498}
2499
2500ParseResult InstanceOp::parse(OpAsmParser &parser, OperationState &result) {
2501 auto *context = parser.getContext();
2502 auto &properties = result.getOrAddProperties<Properties>();
2503
2504 std::string name;
2505 hw::InnerSymAttr innerSymAttr;
2506 FlatSymbolRefAttr moduleName;
2507 SmallVector<OpAsmParser::Argument> entryArgs;
2508 SmallVector<Direction, 4> portDirections;
2509 SmallVector<Attribute, 4> portNames;
2510 SmallVector<Attribute, 4> portTypes;
2511 SmallVector<Attribute, 4> portAnnotations;
2512 SmallVector<Attribute, 4> portSyms;
2513 SmallVector<Attribute, 4> portLocs;
2514 NameKindEnumAttr nameKind;
2515
2516 if (parser.parseKeywordOrString(&name))
2517 return failure();
2518 if (succeeded(parser.parseOptionalKeyword("sym"))) {
2519 if (parser.parseCustomAttributeWithFallback(
2520 innerSymAttr, ::mlir::Type{},
2522 result.attributes)) {
2523 return ::mlir::failure();
2524 }
2525 }
2526 if (parseNameKind(parser, nameKind) ||
2527 parser.parseOptionalAttrDict(result.attributes) ||
2528 parser.parseAttribute(moduleName) ||
2529 parseModulePorts(parser, /*hasSSAIdentifiers=*/false,
2530 /*supportsSymbols=*/false, entryArgs, portDirections,
2531 portNames, portTypes, portAnnotations, portSyms,
2532 portLocs))
2533 return failure();
2534
2535 // Add the attributes. We let attributes defined in the attr-dict override
2536 // attributes parsed out of the module signature.
2537
2538 properties.setModuleName(moduleName);
2539 properties.setName(StringAttr::get(context, name));
2540 properties.setNameKind(nameKind);
2541 properties.setPortDirections(
2542 direction::packAttribute(context, portDirections));
2543 properties.setPortNames(ArrayAttr::get(context, portNames));
2544 properties.setPortAnnotations(ArrayAttr::get(context, portAnnotations));
2545
2546 // Annotations, layers, and LowerToBind are omitted in the printed format
2547 // if they are empty, empty, and false (respectively).
2548 properties.setAnnotations(parser.getBuilder().getArrayAttr({}));
2549 properties.setLayers(parser.getBuilder().getArrayAttr({}));
2550
2551 // Add result types.
2552 result.types.reserve(portTypes.size());
2553 llvm::transform(
2554 portTypes, std::back_inserter(result.types),
2555 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2556
2557 return success();
2558}
2559
2560void InstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2561 StringRef base = getName();
2562 if (base.empty())
2563 base = "inst";
2564
2565 for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
2566 setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
2567 }
2568}
2569
2570std::optional<size_t> InstanceOp::getTargetResultIndex() {
2571 // Inner symbols on instance operations target the op not any result.
2572 return std::nullopt;
2573}
2574
2575// -----------------------------------------------------------------------------
2576// InstanceChoiceOp
2577// -----------------------------------------------------------------------------
2578
2579void InstanceChoiceOp::build(
2580 OpBuilder &builder, OperationState &result, FModuleLike defaultModule,
2581 ArrayRef<std::pair<OptionCaseOp, FModuleLike>> cases, StringRef name,
2582 NameKindEnum nameKind, ArrayRef<Attribute> annotations,
2583 ArrayRef<Attribute> portAnnotations, StringAttr innerSym) {
2584 // Gather the result types.
2585 SmallVector<Type> resultTypes;
2586 for (Attribute portType : defaultModule.getPortTypes())
2587 resultTypes.push_back(cast<TypeAttr>(portType).getValue());
2588
2589 // Create the port annotations.
2590 ArrayAttr portAnnotationsAttr;
2591 if (portAnnotations.empty()) {
2592 portAnnotationsAttr = builder.getArrayAttr(SmallVector<Attribute, 16>(
2593 resultTypes.size(), builder.getArrayAttr({})));
2594 } else {
2595 portAnnotationsAttr = builder.getArrayAttr(portAnnotations);
2596 }
2597
2598 // Gather the module & case names.
2599 SmallVector<Attribute> moduleNames, caseNames;
2600 moduleNames.push_back(SymbolRefAttr::get(defaultModule.getModuleNameAttr()));
2601 for (auto [caseOption, caseModule] : cases) {
2602 auto caseGroup = caseOption->getParentOfType<OptionOp>();
2603 caseNames.push_back(SymbolRefAttr::get(caseGroup.getSymNameAttr(),
2604 {SymbolRefAttr::get(caseOption)}));
2605 moduleNames.push_back(SymbolRefAttr::get(caseModule.getModuleNameAttr()));
2606 }
2607
2608 return build(builder, result, resultTypes, builder.getArrayAttr(moduleNames),
2609 builder.getArrayAttr(caseNames), builder.getStringAttr(name),
2610 NameKindEnumAttr::get(builder.getContext(), nameKind),
2611 defaultModule.getPortDirectionsAttr(),
2612 defaultModule.getPortNamesAttr(),
2613 builder.getArrayAttr(annotations), portAnnotationsAttr,
2614 defaultModule.getLayersAttr(),
2615 innerSym ? hw::InnerSymAttr::get(innerSym) : hw::InnerSymAttr());
2616}
2617
2618std::optional<size_t> InstanceChoiceOp::getTargetResultIndex() {
2619 return std::nullopt;
2620}
2621
2622void InstanceChoiceOp::print(OpAsmPrinter &p) {
2623 // Print the instance name.
2624 p << " ";
2625 p.printKeywordOrString(getName());
2626 if (auto attr = getInnerSymAttr()) {
2627 p << " sym ";
2628 p.printSymbolName(attr.getSymName());
2629 }
2630 if (getNameKindAttr().getValue() != NameKindEnum::DroppableName)
2631 p << ' ' << stringifyNameKindEnum(getNameKindAttr().getValue());
2632
2633 // Print the attr-dict.
2634 SmallVector<StringRef, 10> omittedAttrs = {
2635 "moduleNames", "caseNames", "name",
2636 "portDirections", "portNames", "portTypes",
2637 "portAnnotations", "inner_sym", "nameKind"};
2638 if (getAnnotations().empty())
2639 omittedAttrs.push_back("annotations");
2640 if (getLayers().empty())
2641 omittedAttrs.push_back("layers");
2642 p.printOptionalAttrDict((*this)->getAttrs(), omittedAttrs);
2643
2644 // Print the module name.
2645 p << ' ';
2646
2647 auto moduleNames = getModuleNamesAttr();
2648 auto caseNames = getCaseNamesAttr();
2649
2650 p.printSymbolName(cast<FlatSymbolRefAttr>(moduleNames[0]).getValue());
2651
2652 p << " alternatives ";
2653 p.printSymbolName(
2654 cast<SymbolRefAttr>(caseNames[0]).getRootReference().getValue());
2655 p << " { ";
2656 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
2657 if (i != 0)
2658 p << ", ";
2659
2660 auto symbol = cast<SymbolRefAttr>(caseNames[i]);
2661 p.printSymbolName(symbol.getNestedReferences()[0].getValue());
2662 p << " -> ";
2663 p.printSymbolName(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getValue());
2664 }
2665
2666 p << " } ";
2667
2668 // Collect all the result types as TypeAttrs for printing.
2669 SmallVector<Attribute> portTypes;
2670 portTypes.reserve(getNumResults());
2671 llvm::transform(getResultTypes(), std::back_inserter(portTypes),
2672 &TypeAttr::get);
2673 printModulePorts(p, /*block=*/nullptr, getPortDirectionsAttr(),
2674 getPortNames().getValue(), portTypes,
2675 getPortAnnotations().getValue(), {}, {});
2676}
2677
2678ParseResult InstanceChoiceOp::parse(OpAsmParser &parser,
2679 OperationState &result) {
2680 auto *context = parser.getContext();
2681 auto &properties = result.getOrAddProperties<Properties>();
2682
2683 std::string name;
2684 hw::InnerSymAttr innerSymAttr;
2685 SmallVector<Attribute> moduleNames;
2686 SmallVector<Attribute> caseNames;
2687 SmallVector<OpAsmParser::Argument> entryArgs;
2688 SmallVector<Direction, 4> portDirections;
2689 SmallVector<Attribute, 4> portNames;
2690 SmallVector<Attribute, 4> portTypes;
2691 SmallVector<Attribute, 4> portAnnotations;
2692 SmallVector<Attribute, 4> portSyms;
2693 SmallVector<Attribute, 4> portLocs;
2694 NameKindEnumAttr nameKind;
2695
2696 if (parser.parseKeywordOrString(&name))
2697 return failure();
2698 if (succeeded(parser.parseOptionalKeyword("sym"))) {
2699 if (parser.parseCustomAttributeWithFallback(
2700 innerSymAttr, Type{},
2702 result.attributes)) {
2703 return failure();
2704 }
2705 }
2706 if (parseNameKind(parser, nameKind) ||
2707 parser.parseOptionalAttrDict(result.attributes))
2708 return failure();
2709
2710 FlatSymbolRefAttr defaultModuleName;
2711 if (parser.parseAttribute(defaultModuleName))
2712 return failure();
2713 moduleNames.push_back(defaultModuleName);
2714
2715 // alternatives { @opt::@case -> @target, ... }
2716 {
2717 FlatSymbolRefAttr optionName;
2718 if (parser.parseKeyword("alternatives") ||
2719 parser.parseAttribute(optionName) || parser.parseLBrace())
2720 return failure();
2721
2722 FlatSymbolRefAttr moduleName;
2723 StringAttr caseName;
2724 while (succeeded(parser.parseOptionalSymbolName(caseName))) {
2725 if (parser.parseArrow() || parser.parseAttribute(moduleName))
2726 return failure();
2727 moduleNames.push_back(moduleName);
2728 caseNames.push_back(SymbolRefAttr::get(
2729 optionName.getAttr(), {FlatSymbolRefAttr::get(caseName)}));
2730 if (failed(parser.parseOptionalComma()))
2731 break;
2732 }
2733 if (parser.parseRBrace())
2734 return failure();
2735 }
2736
2737 if (parseModulePorts(parser, /*hasSSAIdentifiers=*/false,
2738 /*supportsSymbols=*/false, entryArgs, portDirections,
2739 portNames, portTypes, portAnnotations, portSyms,
2740 portLocs))
2741 return failure();
2742
2743 // Add the attributes. We let attributes defined in the attr-dict override
2744 // attributes parsed out of the module signature.
2745 properties.setModuleNames(ArrayAttr::get(context, moduleNames));
2746 properties.setCaseNames(ArrayAttr::get(context, caseNames));
2747 properties.setName(StringAttr::get(context, name));
2748 properties.setNameKind(nameKind);
2749 properties.setPortDirections(
2750 direction::packAttribute(context, portDirections));
2751 properties.setPortNames(ArrayAttr::get(context, portNames));
2752 properties.setPortAnnotations(ArrayAttr::get(context, portAnnotations));
2753
2754 // Annotations, layers, and LowerToBind are omitted in the printed format if
2755 // they are empty, empty, and false (respectively).
2756 properties.setAnnotations(parser.getBuilder().getArrayAttr({}));
2757 properties.setLayers(parser.getBuilder().getArrayAttr({}));
2758
2759 // Add result types.
2760 result.types.reserve(portTypes.size());
2761 llvm::transform(
2762 portTypes, std::back_inserter(result.types),
2763 [](Attribute typeAttr) { return cast<TypeAttr>(typeAttr).getValue(); });
2764
2765 return success();
2766}
2767
2768void InstanceChoiceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
2769 StringRef base = getName().empty() ? "inst" : getName();
2770 for (auto [result, name] : llvm::zip(getResults(), getPortNames()))
2771 setNameFn(result, (base + "_" + cast<StringAttr>(name).getValue()).str());
2772}
2773
2774LogicalResult InstanceChoiceOp::verify() {
2775 if (getCaseNamesAttr().empty())
2776 return emitOpError() << "must have at least one case";
2777 if (getModuleNamesAttr().size() != getCaseNamesAttr().size() + 1)
2778 return emitOpError() << "number of referenced modules does not match the "
2779 "number of options";
2780
2781 // The modules may only be instantiated under their required layers (which
2782 // are the same for all modules).
2783 auto ambientLayers = getAmbientLayersAt(getOperation());
2784 SmallVector<SymbolRefAttr> missingLayers;
2785 for (auto layer : getLayersAttr().getAsRange<SymbolRefAttr>())
2786 if (!isLayerCompatibleWith(layer, ambientLayers))
2787 missingLayers.push_back(layer);
2788
2789 if (missingLayers.empty())
2790 return success();
2791
2792 auto diag =
2793 emitOpError("ambient layers are insufficient to instantiate module");
2794 auto &note = diag.attachNote();
2795 note << "missing layer requirements: ";
2796 interleaveComma(missingLayers, note);
2797 return failure();
2798}
2799
2800LogicalResult
2801InstanceChoiceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2802 auto caseNames = getCaseNamesAttr();
2803 for (auto moduleName : getModuleNamesAttr()) {
2805 *this, symbolTable, cast<FlatSymbolRefAttr>(moduleName))))
2806 return failure();
2807 }
2808
2809 auto root = cast<SymbolRefAttr>(caseNames[0]).getRootReference();
2810 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
2811 auto ref = cast<SymbolRefAttr>(caseNames[i]);
2812 auto refRoot = ref.getRootReference();
2813 if (ref.getRootReference() != root)
2814 return emitOpError() << "case " << ref
2815 << " is not in the same option group as "
2816 << caseNames[0];
2817
2818 if (!symbolTable.lookupNearestSymbolFrom<OptionOp>(*this, refRoot))
2819 return emitOpError() << "option " << refRoot << " does not exist";
2820
2821 if (!symbolTable.lookupNearestSymbolFrom<OptionCaseOp>(*this, ref))
2822 return emitOpError() << "option " << refRoot
2823 << " does not contain option case " << ref;
2824 }
2825
2826 return success();
2827}
2828
2829FlatSymbolRefAttr
2830InstanceChoiceOp::getTargetOrDefaultAttr(OptionCaseOp option) {
2831 auto caseNames = getCaseNamesAttr();
2832 for (size_t i = 0, n = caseNames.size(); i < n; ++i) {
2833 StringAttr caseSym = cast<SymbolRefAttr>(caseNames[i]).getLeafReference();
2834 if (caseSym == option.getSymName())
2835 return cast<FlatSymbolRefAttr>(getModuleNamesAttr()[i + 1]);
2836 }
2837 return getDefaultTargetAttr();
2838}
2839
2840SmallVector<std::pair<SymbolRefAttr, FlatSymbolRefAttr>, 1>
2841InstanceChoiceOp::getTargetChoices() {
2842 auto caseNames = getCaseNamesAttr();
2843 auto moduleNames = getModuleNamesAttr();
2844 SmallVector<std::pair<SymbolRefAttr, FlatSymbolRefAttr>, 1> choices;
2845 for (size_t i = 0; i < caseNames.size(); ++i) {
2846 choices.emplace_back(cast<SymbolRefAttr>(caseNames[i]),
2847 cast<FlatSymbolRefAttr>(moduleNames[i + 1]));
2848 }
2849
2850 return choices;
2851}
2852
2853InstanceChoiceOp
2854InstanceChoiceOp::erasePorts(OpBuilder &builder,
2855 const llvm::BitVector &portIndices) {
2856 assert(portIndices.size() >= getNumResults() &&
2857 "portIndices is not at least as large as getNumResults()");
2858
2859 if (portIndices.none())
2860 return *this;
2861
2862 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
2863 SmallVector<Type>(result_type_begin(), result_type_end()), portIndices);
2864 SmallVector<Direction> newPortDirections = removeElementsAtIndices<Direction>(
2865 direction::unpackAttribute(getPortDirectionsAttr()), portIndices);
2866 SmallVector<Attribute> newPortNames =
2867 removeElementsAtIndices(getPortNames().getValue(), portIndices);
2868 SmallVector<Attribute> newPortAnnotations =
2869 removeElementsAtIndices(getPortAnnotations().getValue(), portIndices);
2870
2871 auto newOp = builder.create<InstanceChoiceOp>(
2872 getLoc(), newResultTypes, getModuleNames(), getCaseNames(), getName(),
2873 getNameKind(), direction::packAttribute(getContext(), newPortDirections),
2874 ArrayAttr::get(getContext(), newPortNames), getAnnotationsAttr(),
2875 ArrayAttr::get(getContext(), newPortAnnotations), getLayers(),
2876 getInnerSymAttr());
2877
2878 for (unsigned oldIdx = 0, newIdx = 0, numOldPorts = getNumResults();
2879 oldIdx != numOldPorts; ++oldIdx) {
2880 if (portIndices.test(oldIdx)) {
2881 assert(getResult(oldIdx).use_empty() && "removed instance port has uses");
2882 continue;
2883 }
2884 getResult(oldIdx).replaceAllUsesWith(newOp.getResult(newIdx));
2885 ++newIdx;
2886 }
2887
2888 // Copy over "output_file" information so that this is not lost when ports
2889 // are erased.
2890 //
2891 // TODO: Other attributes may need to be copied over.
2892 if (auto outputFile = (*this)->getAttr("output_file"))
2893 newOp->setAttr("output_file", outputFile);
2894
2895 return newOp;
2896}
2897
2898//===----------------------------------------------------------------------===//
2899// MemOp
2900//===----------------------------------------------------------------------===//
2901
2902ArrayAttr MemOp::getPortAnnotation(unsigned portIdx) {
2903 assert(portIdx < getNumResults() &&
2904 "index should be smaller than result number");
2905 return cast<ArrayAttr>(getPortAnnotations()[portIdx]);
2906}
2907
2908void MemOp::setAllPortAnnotations(ArrayRef<Attribute> annotations) {
2909 assert(annotations.size() == getNumResults() &&
2910 "number of annotations is not equal to result number");
2911 (*this)->setAttr("portAnnotations",
2912 ArrayAttr::get(getContext(), annotations));
2913}
2914
2915// Get the number of read, write and read-write ports.
2916void MemOp::getNumPorts(size_t &numReadPorts, size_t &numWritePorts,
2917 size_t &numReadWritePorts, size_t &numDbgsPorts) {
2918 numReadPorts = 0;
2919 numWritePorts = 0;
2920 numReadWritePorts = 0;
2921 numDbgsPorts = 0;
2922 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
2923 auto portKind = getPortKind(i);
2924 if (portKind == MemOp::PortKind::Debug)
2925 ++numDbgsPorts;
2926 else if (portKind == MemOp::PortKind::Read)
2927 ++numReadPorts;
2928 else if (portKind == MemOp::PortKind::Write) {
2929 ++numWritePorts;
2930 } else
2931 ++numReadWritePorts;
2932 }
2933}
2934
2935/// Verify the correctness of a MemOp.
2936LogicalResult MemOp::verify() {
2937
2938 // Store the port names as we find them. This lets us check quickly
2939 // for uniqueneess.
2940 llvm::SmallDenseSet<Attribute, 8> portNamesSet;
2941
2942 // Store the previous data type. This lets us check that the data
2943 // type is consistent across all ports.
2944 FIRRTLType oldDataType;
2945
2946 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
2947 auto portName = getPortName(i);
2948
2949 // Get a bundle type representing this port, stripping an outer
2950 // flip if it exists. If this is not a bundle<> or
2951 // flip<bundle<>>, then this is an error.
2952 BundleType portBundleType =
2953 type_dyn_cast<BundleType>(getResult(i).getType());
2954
2955 // Require that all port names are unique.
2956 if (!portNamesSet.insert(portName).second) {
2957 emitOpError() << "has non-unique port name " << portName;
2958 return failure();
2959 }
2960
2961 // Determine the kind of the memory. If the kind cannot be
2962 // determined, then it's indicative of the wrong number of fields
2963 // in the type (but we don't know any more just yet).
2964
2965 auto elt = getPortNamed(portName);
2966 if (!elt) {
2967 emitOpError() << "could not get port with name " << portName;
2968 return failure();
2969 }
2970 auto firrtlType = type_cast<FIRRTLType>(elt.getType());
2971 MemOp::PortKind portKind = getMemPortKindFromType(firrtlType);
2972
2973 if (portKind == MemOp::PortKind::Debug &&
2974 !type_isa<RefType>(getResult(i).getType()))
2975 return emitOpError() << "has an invalid type on port " << portName
2976 << " (expected Read/Write/ReadWrite/Debug)";
2977 if (type_isa<RefType>(firrtlType) && e == 1)
2978 return emitOpError()
2979 << "cannot have only one port of debug type. Debug port can only "
2980 "exist alongside other read/write/read-write port";
2981
2982 // Safely search for the "data" field, erroring if it can't be
2983 // found.
2984 FIRRTLBaseType dataType;
2985 if (portKind == MemOp::PortKind::Debug) {
2986 auto resType = type_cast<RefType>(getResult(i).getType());
2987 if (!(resType && type_isa<FVectorType>(resType.getType())))
2988 return emitOpError() << "debug ports must be a RefType of FVectorType";
2989 dataType = type_cast<FVectorType>(resType.getType()).getElementType();
2990 } else {
2991 auto dataTypeOption = portBundleType.getElement("data");
2992 if (!dataTypeOption && portKind == MemOp::PortKind::ReadWrite)
2993 dataTypeOption = portBundleType.getElement("wdata");
2994 if (!dataTypeOption) {
2995 emitOpError() << "has no data field on port " << portName
2996 << " (expected to see \"data\" for a read or write "
2997 "port or \"rdata\" for a read/write port)";
2998 return failure();
2999 }
3000 dataType = dataTypeOption->type;
3001 // Read data is expected to ba a flip.
3002 if (portKind == MemOp::PortKind::Read) {
3003 // FIXME error on missing bundle flip
3004 }
3005 }
3006
3007 // Error if the data type isn't passive.
3008 if (!dataType.isPassive()) {
3009 emitOpError() << "has non-passive data type on port " << portName
3010 << " (memory types must be passive)";
3011 return failure();
3012 }
3013
3014 // Error if the data type contains analog types.
3015 if (dataType.containsAnalog()) {
3016 emitOpError() << "has a data type that contains an analog type on port "
3017 << portName
3018 << " (memory types cannot contain analog types)";
3019 return failure();
3020 }
3021
3022 // Check that the port type matches the kind that we determined
3023 // for this port. This catches situations of extraneous port
3024 // fields beind included or the fields being named incorrectly.
3025 FIRRTLType expectedType =
3026 getTypeForPort(getDepth(), dataType, portKind,
3027 dataType.isGround() ? getMaskBits() : 0);
3028 // Compute the original port type as portBundleType may have
3029 // stripped outer flip information.
3030 auto originalType = getResult(i).getType();
3031 if (originalType != expectedType) {
3032 StringRef portKindName;
3033 switch (portKind) {
3034 case MemOp::PortKind::Read:
3035 portKindName = "read";
3036 break;
3037 case MemOp::PortKind::Write:
3038 portKindName = "write";
3039 break;
3040 case MemOp::PortKind::ReadWrite:
3041 portKindName = "readwrite";
3042 break;
3043 case MemOp::PortKind::Debug:
3044 portKindName = "dbg";
3045 break;
3046 }
3047 emitOpError() << "has an invalid type for port " << portName
3048 << " of determined kind \"" << portKindName
3049 << "\" (expected " << expectedType << ", but got "
3050 << originalType << ")";
3051 return failure();
3052 }
3053
3054 // Error if the type of the current port was not the same as the
3055 // last port, but skip checking the first port.
3056 if (oldDataType && oldDataType != dataType) {
3057 emitOpError() << "port " << getPortName(i)
3058 << " has a different type than port " << getPortName(i - 1)
3059 << " (expected " << oldDataType << ", but got " << dataType
3060 << ")";
3061 return failure();
3062 }
3063
3064 oldDataType = dataType;
3065 }
3066
3067 auto maskWidth = getMaskBits();
3068
3069 auto dataWidth = getDataType().getBitWidthOrSentinel();
3070 if (dataWidth > 0 && maskWidth > (size_t)dataWidth)
3071 return emitOpError("the mask width cannot be greater than "
3072 "data width");
3073
3074 if (getPortAnnotations().size() != getNumResults())
3075 return emitOpError("the number of result annotations should be "
3076 "equal to the number of results");
3077
3078 return success();
3079}
3080
3081static size_t getAddressWidth(size_t depth) {
3082 return std::max(1U, llvm::Log2_64_Ceil(depth));
3083}
3084
3085size_t MemOp::getAddrBits() { return getAddressWidth(getDepth()); }
3086
3087FIRRTLType MemOp::getTypeForPort(uint64_t depth, FIRRTLBaseType dataType,
3088 PortKind portKind, size_t maskBits) {
3089
3090 auto *context = dataType.getContext();
3091 if (portKind == PortKind::Debug)
3092 return RefType::get(FVectorType::get(dataType, depth));
3093 FIRRTLBaseType maskType;
3094 // maskBits not specified (==0), then get the mask type from the dataType.
3095 if (maskBits == 0)
3096 maskType = dataType.getMaskType();
3097 else
3098 maskType = UIntType::get(context, maskBits);
3099
3100 auto getId = [&](StringRef name) -> StringAttr {
3101 return StringAttr::get(context, name);
3102 };
3103
3104 SmallVector<BundleType::BundleElement, 7> portFields;
3105
3106 auto addressType = UIntType::get(context, getAddressWidth(depth));
3107
3108 portFields.push_back({getId("addr"), false, addressType});
3109 portFields.push_back({getId("en"), false, UIntType::get(context, 1)});
3110 portFields.push_back({getId("clk"), false, ClockType::get(context)});
3111
3112 switch (portKind) {
3113 case PortKind::Read:
3114 portFields.push_back({getId("data"), true, dataType});
3115 break;
3116
3117 case PortKind::Write:
3118 portFields.push_back({getId("data"), false, dataType});
3119 portFields.push_back({getId("mask"), false, maskType});
3120 break;
3121
3122 case PortKind::ReadWrite:
3123 portFields.push_back({getId("rdata"), true, dataType});
3124 portFields.push_back({getId("wmode"), false, UIntType::get(context, 1)});
3125 portFields.push_back({getId("wdata"), false, dataType});
3126 portFields.push_back({getId("wmask"), false, maskType});
3127 break;
3128 default:
3129 llvm::report_fatal_error("memory port kind not handled");
3130 break;
3131 }
3132
3133 return BundleType::get(context, portFields);
3134}
3135
3136/// Return the name and kind of ports supported by this memory.
3137SmallVector<MemOp::NamedPort> MemOp::getPorts() {
3138 SmallVector<MemOp::NamedPort> result;
3139 // Each entry in the bundle is a port.
3140 for (size_t i = 0, e = getNumResults(); i != e; ++i) {
3141 // Each port is a bundle.
3142 auto portType = type_cast<FIRRTLType>(getResult(i).getType());
3143 result.push_back({getPortName(i), getMemPortKindFromType(portType)});
3144 }
3145 return result;
3146}
3147
3148/// Return the kind of the specified port.
3149MemOp::PortKind MemOp::getPortKind(StringRef portName) {
3151 type_cast<FIRRTLType>(getPortNamed(portName).getType()));
3152}
3153
3154/// Return the kind of the specified port number.
3155MemOp::PortKind MemOp::getPortKind(size_t resultNo) {
3157 type_cast<FIRRTLType>(getResult(resultNo).getType()));
3158}
3159
3160/// Return the number of bits in the mask for the memory.
3161size_t MemOp::getMaskBits() {
3162
3163 for (auto res : getResults()) {
3164 if (type_isa<RefType>(res.getType()))
3165 continue;
3166 auto firstPortType = type_cast<FIRRTLBaseType>(res.getType());
3167 if (getMemPortKindFromType(firstPortType) == PortKind::Read ||
3168 getMemPortKindFromType(firstPortType) == PortKind::Debug)
3169 continue;
3170
3171 FIRRTLBaseType mType;
3172 for (auto t : type_cast<BundleType>(firstPortType.getPassiveType())) {
3173 if (t.name.getValue().contains("mask"))
3174 mType = t.type;
3175 }
3176 if (type_isa<UIntType>(mType))
3177 return mType.getBitWidthOrSentinel();
3178 }
3179 // Mask of zero bits means, either there are no write/readwrite ports or the
3180 // mask is of aggregate type.
3181 return 0;
3182}
3183
3184/// Return the data-type field of the memory, the type of each element.
3185FIRRTLBaseType MemOp::getDataType() {
3186 assert(getNumResults() != 0 && "Mems with no read/write ports are illegal");
3187
3188 if (auto refType = type_dyn_cast<RefType>(getResult(0).getType()))
3189 return type_cast<FVectorType>(refType.getType()).getElementType();
3190 auto firstPortType = type_cast<FIRRTLBaseType>(getResult(0).getType());
3191
3192 StringRef dataFieldName = "data";
3193 if (getMemPortKindFromType(firstPortType) == PortKind::ReadWrite)
3194 dataFieldName = "rdata";
3195
3196 return type_cast<BundleType>(firstPortType.getPassiveType())
3197 .getElementType(dataFieldName);
3198}
3199
3200StringAttr MemOp::getPortName(size_t resultNo) {
3201 return cast<StringAttr>(getPortNames()[resultNo]);
3202}
3203
3204FIRRTLBaseType MemOp::getPortType(size_t resultNo) {
3205 return type_cast<FIRRTLBaseType>(getResults()[resultNo].getType());
3206}
3207
3208Value MemOp::getPortNamed(StringAttr name) {
3209 auto namesArray = getPortNames();
3210 for (size_t i = 0, e = namesArray.size(); i != e; ++i) {
3211 if (namesArray[i] == name) {
3212 assert(i < getNumResults() && " names array out of sync with results");
3213 return getResult(i);
3214 }
3215 }
3216 return Value();
3217}
3218
3219// Extract all the relevant attributes from the MemOp and return the FirMemory.
3220FirMemory MemOp::getSummary() {
3221 auto op = *this;
3222 size_t numReadPorts = 0;
3223 size_t numWritePorts = 0;
3224 size_t numReadWritePorts = 0;
3226 SmallVector<int32_t> writeClockIDs;
3227
3228 for (size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3229 auto portKind = op.getPortKind(i);
3230 if (portKind == MemOp::PortKind::Read)
3231 ++numReadPorts;
3232 else if (portKind == MemOp::PortKind::Write) {
3233 for (auto *a : op.getResult(i).getUsers()) {
3234 auto subfield = dyn_cast<SubfieldOp>(a);
3235 if (!subfield || subfield.getFieldIndex() != 2)
3236 continue;
3237 auto clockPort = a->getResult(0);
3238 for (auto *b : clockPort.getUsers()) {
3239 if (auto connect = dyn_cast<FConnectLike>(b)) {
3240 if (connect.getDest() == clockPort) {
3241 auto result =
3242 clockToLeader.insert({circt::firrtl::getModuleScopedDriver(
3243 connect.getSrc(), true, true, true),
3244 numWritePorts});
3245 if (result.second) {
3246 writeClockIDs.push_back(numWritePorts);
3247 } else {
3248 writeClockIDs.push_back(result.first->second);
3249 }
3250 }
3251 }
3252 }
3253 break;
3254 }
3255 ++numWritePorts;
3256 } else
3257 ++numReadWritePorts;
3258 }
3259
3260 size_t width = 0;
3261 if (auto widthV = getBitWidth(op.getDataType()))
3262 width = *widthV;
3263 else
3264 op.emitError("'firrtl.mem' should have simple type and known width");
3265 MemoryInitAttr init = op->getAttrOfType<MemoryInitAttr>("init");
3266 StringAttr modName;
3267 if (op->hasAttr("modName"))
3268 modName = op->getAttrOfType<StringAttr>("modName");
3269 else {
3270 SmallString<8> clocks;
3271 for (auto a : writeClockIDs)
3272 clocks.append(Twine((char)(a + 'a')).str());
3273 SmallString<32> initStr;
3274 // If there is a file initialization, then come up with a decent
3275 // representation for this. Use the filename, but only characters
3276 // [a-zA-Z0-9] and the bool/hex and inline booleans.
3277 if (init) {
3278 for (auto c : init.getFilename().getValue())
3279 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3280 (c >= '0' && c <= '9'))
3281 initStr.push_back(c);
3282 initStr.push_back('_');
3283 initStr.push_back(init.getIsBinary() ? 't' : 'f');
3284 initStr.push_back('_');
3285 initStr.push_back(init.getIsInline() ? 't' : 'f');
3286 }
3287 modName = StringAttr::get(
3288 op->getContext(),
3289 llvm::formatv(
3290 "{0}FIRRTLMem_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}{11}{12}",
3291 op.getPrefix().value_or(""), numReadPorts, numWritePorts,
3292 numReadWritePorts, (size_t)width, op.getDepth(),
3293 op.getReadLatency(), op.getWriteLatency(), op.getMaskBits(),
3294 (unsigned)op.getRuw(), (unsigned)seq::WUW::PortOrder,
3295 clocks.empty() ? "" : "_" + clocks, init ? initStr.str() : ""));
3296 }
3297 return {numReadPorts,
3298 numWritePorts,
3299 numReadWritePorts,
3300 (size_t)width,
3301 op.getDepth(),
3302 op.getReadLatency(),
3303 op.getWriteLatency(),
3304 op.getMaskBits(),
3305 *seq::symbolizeRUW(unsigned(op.getRuw())),
3306 seq::WUW::PortOrder,
3307 writeClockIDs,
3308 modName,
3309 op.getMaskBits() > 1,
3310 init,
3311 op.getPrefixAttr(),
3312 op.getLoc()};
3313}
3314
3315void MemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3316 StringRef base = getName();
3317 if (base.empty())
3318 base = "mem";
3319
3320 for (size_t i = 0, e = (*this)->getNumResults(); i != e; ++i) {
3321 setNameFn(getResult(i), (base + "_" + getPortNameStr(i)).str());
3322 }
3323}
3324
3325std::optional<size_t> MemOp::getTargetResultIndex() {
3326 // Inner symbols on memory operations target the op not any result.
3327 return std::nullopt;
3328}
3329
3330// Construct name of the module which will be used for the memory definition.
3331StringAttr FirMemory::getFirMemoryName() const { return modName; }
3332
3333/// Helper for naming forceable declarations (and their optional ref result).
3334static void forceableAsmResultNames(Forceable op, StringRef name,
3335 OpAsmSetValueNameFn setNameFn) {
3336 if (name.empty())
3337 return;
3338 setNameFn(op.getDataRaw(), name);
3339 if (op.isForceable())
3340 setNameFn(op.getDataRef(), (name + "_ref").str());
3341}
3342
3343void NodeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3344 return forceableAsmResultNames(*this, getName(), setNameFn);
3345}
3346
3347LogicalResult NodeOp::inferReturnTypes(
3348 mlir::MLIRContext *context, std::optional<mlir::Location> location,
3349 ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
3350 ::mlir::OpaqueProperties properties, ::mlir::RegionRange regions,
3351 ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
3352 if (operands.empty())
3353 return failure();
3354 Adaptor adaptor(operands, attributes, properties, regions);
3355 inferredReturnTypes.push_back(adaptor.getInput().getType());
3356 if (adaptor.getForceable()) {
3357 auto forceableType = firrtl::detail::getForceableResultType(
3358 true, adaptor.getInput().getType());
3359 if (!forceableType) {
3360 if (location)
3361 ::mlir::emitError(*location, "cannot force a node of type ")
3362 << operands[0].getType();
3363 return failure();
3364 }
3365 inferredReturnTypes.push_back(forceableType);
3366 }
3367 return success();
3368}
3369
3370std::optional<size_t> NodeOp::getTargetResultIndex() { return 0; }
3371
3372void RegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3373 return forceableAsmResultNames(*this, getName(), setNameFn);
3374}
3375
3376std::optional<size_t> RegOp::getTargetResultIndex() { return 0; }
3377
3378SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
3379RegOp::computeDataFlow() {
3380 // A register does't have any combinational dataflow.
3381 return {};
3382}
3383
3384LogicalResult RegResetOp::verify() {
3385 auto reset = getResetValue();
3386
3387 FIRRTLBaseType resetType = reset.getType();
3388 FIRRTLBaseType regType = getResult().getType();
3389
3390 // The type of the initialiser must be equivalent to the register type.
3391 if (!areTypesEquivalent(regType, resetType))
3392 return emitError("type mismatch between register ")
3393 << regType << " and reset value " << resetType;
3394
3395 return success();
3396}
3397
3398std::optional<size_t> RegResetOp::getTargetResultIndex() { return 0; }
3399
3400void RegResetOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3401 return forceableAsmResultNames(*this, getName(), setNameFn);
3402}
3403
3404//===----------------------------------------------------------------------===//
3405// FormalOp
3406//===----------------------------------------------------------------------===//
3407
3408LogicalResult
3409FormalOp::verifySymbolUses(mlir::SymbolTableCollection &symbolTable) {
3410 auto *op = symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
3411 if (!op)
3412 return emitOpError() << "targets unknown module " << getModuleNameAttr();
3413
3414 if (!isa<FModuleLike>(op)) {
3415 auto d = emitOpError() << "target " << getModuleNameAttr()
3416 << " is not a module";
3417 d.attachNote(op->getLoc()) << "target defined here";
3418 return d;
3419 }
3420
3421 return success();
3422}
3423
3424//===----------------------------------------------------------------------===//
3425// SimulationOp
3426//===----------------------------------------------------------------------===//
3427
3428LogicalResult
3429SimulationOp::verifySymbolUses(mlir::SymbolTableCollection &symbolTable) {
3430 auto *op = symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
3431 if (!op)
3432 return emitOpError() << "targets unknown module " << getModuleNameAttr();
3433
3434 auto complain = [&] {
3435 auto d = emitOpError() << "target " << getModuleNameAttr() << " ";
3436 d.attachNote(op->getLoc()) << "target defined here";
3437 return d;
3438 };
3439
3440 auto module = dyn_cast<FModuleLike>(op);
3441 if (!module)
3442 return complain() << "is not a module";
3443
3444 auto numPorts = module.getPortDirections().size();
3445 if (numPorts != 4)
3446 return complain() << "must have 4 ports, got " << numPorts << " instead";
3447
3448 auto checkPort = [&](unsigned idx, StringRef expName, Direction expDir,
3449 llvm::function_ref<bool(Type)> checkType,
3450 StringRef expType) {
3451 auto name = module.getPortNameAttr(idx);
3452 if (name != expName) {
3453 complain() << "port " << idx << " must be called \"" << expName
3454 << "\", got " << name << " instead";
3455 return false;
3456 }
3457 if (auto dir = module.getPortDirection(idx); dir != expDir) {
3458 auto stringify = [](Direction dir) {
3459 return dir == Direction::In ? "an input" : "an output";
3460 };
3461 complain() << "port " << name << " must be " << stringify(expDir)
3462 << ", got " << stringify(dir) << " instead";
3463 return false;
3464 }
3465 if (auto type = module.getPortType(idx); !checkType(type)) {
3466 complain() << "port " << name << " must be a '!firrtl." << expType
3467 << "', got " << type << " instead";
3468 return false;
3469 }
3470 return true;
3471 };
3472
3473 auto isClock = [](Type type) { return isa<ClockType>(type); };
3474 auto isBool = [](Type type) {
3475 if (auto uintType = dyn_cast<UIntType>(type))
3476 return uintType.getWidth() == 1;
3477 return false;
3478 };
3479 return success(checkPort(0, "clock", Direction::In, isClock, "clock") &&
3480 checkPort(1, "init", Direction::In, isBool, "uint<1>") &&
3481 checkPort(2, "done", Direction::Out, isBool, "uint<1>") &&
3482 checkPort(3, "success", Direction::Out, isBool, "uint<1>"));
3483}
3484
3485//===----------------------------------------------------------------------===//
3486// WireOp
3487//===----------------------------------------------------------------------===//
3488
3489void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3490 return forceableAsmResultNames(*this, getName(), setNameFn);
3491}
3492
3493SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
3494RegResetOp::computeDataFlow() {
3495 // A register does't have any combinational dataflow.
3496 return {};
3497}
3498
3499std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
3500
3501LogicalResult WireOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3502 auto refType = type_dyn_cast<RefType>(getType(0));
3503 if (!refType)
3504 return success();
3505
3506 return verifyProbeType(
3507 refType, getLoc(), getOperation()->getParentOfType<CircuitOp>(),
3508 symbolTable, Twine("'") + getOperationName() + "' op is");
3509}
3510
3511//===----------------------------------------------------------------------===//
3512// ContractOp
3513//===----------------------------------------------------------------------===//
3514
3515LogicalResult ContractOp::verify() {
3516 if (getBody().getArgumentTypes() != getInputs().getType())
3517 return emitOpError("result types and region argument types must match");
3518 return success();
3519}
3520
3521//===----------------------------------------------------------------------===//
3522// ObjectOp
3523//===----------------------------------------------------------------------===//
3524
3525void ObjectOp::build(OpBuilder &builder, OperationState &state, ClassLike klass,
3526 StringRef name) {
3527 build(builder, state, klass.getInstanceType(),
3528 StringAttr::get(builder.getContext(), name));
3529}
3530
3531LogicalResult ObjectOp::verify() { return success(); }
3532
3533LogicalResult ObjectOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3534 auto circuitOp = getOperation()->getParentOfType<CircuitOp>();
3535 auto classType = getType();
3536 auto className = classType.getNameAttr();
3537
3538 // verify that the class exists.
3539 auto classOp = dyn_cast_or_null<ClassLike>(
3540 symbolTable.lookupSymbolIn(circuitOp, className));
3541 if (!classOp)
3542 return emitOpError() << "references unknown class " << className;
3543
3544 // verify that the result type agrees with the class definition.
3545 if (failed(classOp.verifyType(classType, [&]() { return emitOpError(); })))
3546 return failure();
3547
3548 return success();
3549}
3550
3551StringAttr ObjectOp::getClassNameAttr() {
3552 return getType().getNameAttr().getAttr();
3553}
3554
3555StringRef ObjectOp::getClassName() { return getType().getName(); }
3556
3557ClassLike ObjectOp::getReferencedClass(const SymbolTable &symbolTable) {
3558 auto symRef = getType().getNameAttr();
3559 return symbolTable.lookup<ClassLike>(symRef.getLeafReference());
3560}
3561
3562Operation *ObjectOp::getReferencedOperation(const SymbolTable &symtbl) {
3563 return getReferencedClass(symtbl);
3564}
3565
3566StringRef ObjectOp::getInstanceName() { return getName(); }
3567
3568StringAttr ObjectOp::getInstanceNameAttr() { return getNameAttr(); }
3569
3570StringRef ObjectOp::getReferencedModuleName() { return getClassName(); }
3571
3572StringAttr ObjectOp::getReferencedModuleNameAttr() {
3573 return getClassNameAttr();
3574}
3575
3576void ObjectOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
3577 setNameFn(getResult(), getName());
3578}
3579
3580//===----------------------------------------------------------------------===//
3581// Statements
3582//===----------------------------------------------------------------------===//
3583
3584LogicalResult AttachOp::verify() {
3585 // All known widths must match.
3586 std::optional<int32_t> commonWidth;
3587 for (auto operand : getOperands()) {
3588 auto thisWidth = type_cast<AnalogType>(operand.getType()).getWidth();
3589 if (!thisWidth)
3590 continue;
3591 if (!commonWidth) {
3592 commonWidth = thisWidth;
3593 continue;
3594 }
3595 if (commonWidth != thisWidth)
3596 return emitOpError("is inavlid as not all known operand widths match");
3597 }
3598 return success();
3599}
3600
3601/// Check if the source and sink are of appropriate flow.
3602static LogicalResult checkConnectFlow(Operation *connect) {
3603 Value dst = connect->getOperand(0);
3604 Value src = connect->getOperand(1);
3605
3606 // TODO: Relax this to allow reads from output ports,
3607 // instance/memory input ports.
3608 auto srcFlow = foldFlow(src);
3609 if (!isValidSrc(srcFlow)) {
3610 // A sink that is a port output or instance input used as a source is okay,
3611 // as long as it is not a property.
3612 auto kind = getDeclarationKind(src);
3613 if (isa<PropertyType>(src.getType()) ||
3614 (kind != DeclKind::Port && kind != DeclKind::Instance)) {
3615 auto srcRef = getFieldRefFromValue(src, /*lookThroughCasts=*/true);
3616 auto [srcName, rootKnown] = getFieldName(srcRef);
3617 auto diag = emitError(connect->getLoc());
3618 diag << "connect has invalid flow: the source expression ";
3619 if (rootKnown)
3620 diag << "\"" << srcName << "\" ";
3621 diag << "has " << toString(srcFlow) << ", expected source or duplex flow";
3622 return diag.attachNote(srcRef.getLoc()) << "the source was defined here";
3623 }
3624 }
3625
3626 auto dstFlow = foldFlow(dst);
3627 if (!isValidDst(dstFlow)) {
3628 auto dstRef = getFieldRefFromValue(dst, /*lookThroughCasts=*/true);
3629 auto [dstName, rootKnown] = getFieldName(dstRef);
3630 auto diag = emitError(connect->getLoc());
3631 diag << "connect has invalid flow: the destination expression ";
3632 if (rootKnown)
3633 diag << "\"" << dstName << "\" ";
3634 diag << "has " << toString(dstFlow) << ", expected sink or duplex flow";
3635 return diag.attachNote(dstRef.getLoc())
3636 << "the destination was defined here";
3637 }
3638 return success();
3639}
3640
3641// NOLINTBEGIN(misc-no-recursion)
3642/// Checks if the type has any 'const' leaf elements . If `isFlip` is `true`,
3643/// the `const` leaf is not considered to be driven.
3644static bool isConstFieldDriven(FIRRTLBaseType type, bool isFlip = false,
3645 bool outerTypeIsConst = false) {
3646 auto typeIsConst = outerTypeIsConst || type.isConst();
3647
3648 if (typeIsConst && type.isPassive())
3649 return !isFlip;
3650
3651 if (auto bundleType = type_dyn_cast<BundleType>(type))
3652 return llvm::any_of(bundleType.getElements(), [&](auto &element) {
3653 return isConstFieldDriven(element.type, isFlip ^ element.isFlip,
3654 typeIsConst);
3655 });
3656
3657 if (auto vectorType = type_dyn_cast<FVectorType>(type))
3658 return isConstFieldDriven(vectorType.getElementType(), isFlip, typeIsConst);
3659
3660 if (typeIsConst)
3661 return !isFlip;
3662 return false;
3663}
3664// NOLINTEND(misc-no-recursion)
3665
3666/// Checks that connections to 'const' destinations are not dependent on
3667/// non-'const' conditions in when blocks.
3668static LogicalResult checkConnectConditionality(FConnectLike connect) {
3669 auto dest = connect.getDest();
3670 auto destType = type_dyn_cast<FIRRTLBaseType>(dest.getType());
3671 auto src = connect.getSrc();
3672 auto srcType = type_dyn_cast<FIRRTLBaseType>(src.getType());
3673 if (!destType || !srcType)
3674 return success();
3675
3676 auto destRefinedType = destType;
3677 auto srcRefinedType = srcType;
3678
3679 /// Looks up the value's defining op until the defining op is null or a
3680 /// declaration of the value. If a SubAccessOp is encountered with a 'const'
3681 /// input, `originalFieldType` is made 'const'.
3682 auto findFieldDeclarationRefiningFieldType =
3683 [](Value value, FIRRTLBaseType &originalFieldType) -> Value {
3684 while (auto *definingOp = value.getDefiningOp()) {
3685 bool shouldContinue = true;
3686 TypeSwitch<Operation *>(definingOp)
3687 .Case<SubfieldOp, SubindexOp>([&](auto op) { value = op.getInput(); })
3688 .Case<SubaccessOp>([&](SubaccessOp op) {
3689 if (op.getInput()
3690 .getType()
3691 .base()
3692 .getElementTypePreservingConst()
3693 .isConst())
3694 originalFieldType = originalFieldType.getConstType(true);
3695 value = op.getInput();
3696 })
3697 .Default([&](Operation *) { shouldContinue = false; });
3698 if (!shouldContinue)
3699 break;
3700 }
3701 return value;
3702 };
3703
3704 auto destDeclaration =
3705 findFieldDeclarationRefiningFieldType(dest, destRefinedType);
3706 auto srcDeclaration =
3707 findFieldDeclarationRefiningFieldType(src, srcRefinedType);
3708
3709 auto checkConstConditionality = [&](Value value, FIRRTLBaseType type,
3710 Value declaration) -> LogicalResult {
3711 auto *declarationBlock = declaration.getParentBlock();
3712 auto *block = connect->getBlock();
3713 while (block && block != declarationBlock) {
3714 auto *parentOp = block->getParentOp();
3715
3716 if (auto whenOp = dyn_cast<WhenOp>(parentOp);
3717 whenOp && !whenOp.getCondition().getType().isConst()) {
3718 if (type.isConst())
3719 return connect.emitOpError()
3720 << "assignment to 'const' type " << type
3721 << " is dependent on a non-'const' condition";
3722 return connect->emitOpError()
3723 << "assignment to nested 'const' member of type " << type
3724 << " is dependent on a non-'const' condition";
3725 }
3726
3727 block = parentOp->getBlock();
3728 }
3729 return success();
3730 };
3731
3732 auto emitSubaccessError = [&] {
3733 return connect.emitError(
3734 "assignment to non-'const' subaccess of 'const' type is disallowed");
3735 };
3736
3737 // Check destination if it contains 'const' leaves
3738 if (destRefinedType.containsConst() && isConstFieldDriven(destRefinedType)) {
3739 // Disallow assignment to non-'const' subaccesses of 'const' types
3740 if (destType != destRefinedType)
3741 return emitSubaccessError();
3742
3743 if (failed(checkConstConditionality(dest, destType, destDeclaration)))
3744 return failure();
3745 }
3746
3747 // Check source if it contains 'const' 'flip' leaves
3748 if (srcRefinedType.containsConst() &&
3749 isConstFieldDriven(srcRefinedType, /*isFlip=*/true)) {
3750 // Disallow assignment to non-'const' subaccesses of 'const' types
3751 if (srcType != srcRefinedType)
3752 return emitSubaccessError();
3753 if (failed(checkConstConditionality(src, srcType, srcDeclaration)))
3754 return failure();
3755 }
3756
3757 return success();
3758}
3759
3760LogicalResult ConnectOp::verify() {
3761 auto dstType = getDest().getType();
3762 auto srcType = getSrc().getType();
3763 auto dstBaseType = type_dyn_cast<FIRRTLBaseType>(dstType);
3764 auto srcBaseType = type_dyn_cast<FIRRTLBaseType>(srcType);
3765 if (!dstBaseType || !srcBaseType) {
3766 if (dstType != srcType)
3767 return emitError("may not connect different non-base types");
3768 } else {
3769 // Analog types cannot be connected and must be attached.
3770 if (dstBaseType.containsAnalog() || srcBaseType.containsAnalog())
3771 return emitError("analog types may not be connected");
3772
3773 // Destination and source types must be equivalent.
3774 if (!areTypesEquivalent(dstBaseType, srcBaseType))
3775 return emitError("type mismatch between destination ")
3776 << dstBaseType << " and source " << srcBaseType;
3777
3778 // Truncation is banned in a connection: destination bit width must be
3779 // greater than or equal to source bit width.
3780 if (!isTypeLarger(dstBaseType, srcBaseType))
3781 return emitError("destination ")
3782 << dstBaseType << " is not as wide as the source " << srcBaseType;
3783 }
3784
3785 // Check that the flows make sense.
3786 if (failed(checkConnectFlow(*this)))
3787 return failure();
3788
3789 if (failed(checkConnectConditionality(*this)))
3790 return failure();
3791
3792 return success();
3793}
3794
3795LogicalResult MatchingConnectOp::verify() {
3796 if (auto type = type_dyn_cast<FIRRTLType>(getDest().getType())) {
3797 auto baseType = type_cast<FIRRTLBaseType>(type);
3798
3799 // Analog types cannot be connected and must be attached.
3800 if (baseType && baseType.containsAnalog())
3801 return emitError("analog types may not be connected");
3802
3803 // The anonymous types of operands must be equivalent.
3804 assert(areAnonymousTypesEquivalent(cast<FIRRTLBaseType>(getSrc().getType()),
3805 baseType) &&
3806 "`SameAnonTypeOperands` trait should have already rejected "
3807 "structurally non-equivalent types");
3808 }
3809
3810 // Check that the flows make sense.
3811 if (failed(checkConnectFlow(*this)))
3812 return failure();
3813
3814 if (failed(checkConnectConditionality(*this)))
3815 return failure();
3816
3817 return success();
3818}
3819
3820LogicalResult RefDefineOp::verify() {
3821 // Check that the flows make sense.
3822 if (failed(checkConnectFlow(*this)))
3823 return failure();
3824
3825 // For now, refs can't be in bundles so this is sufficient.
3826 // In the future need to ensure no other define's to same "fieldSource".
3827 // (When aggregates can have references, we can define a reference within,
3828 // but this must be unique. Checking this here may be expensive,
3829 // consider adding something to FModuleLike's to check it there instead)
3830 for (auto *user : getDest().getUsers()) {
3831 if (auto conn = dyn_cast<FConnectLike>(user);
3832 conn && conn.getDest() == getDest() && conn != *this)
3833 return emitError("destination reference cannot be reused by multiple "
3834 "operations, it can only capture a unique dataflow");
3835 }
3836
3837 // Check "static" source/dest
3838 if (auto *op = getDest().getDefiningOp()) {
3839 // TODO: Make ref.sub only source flow?
3840 if (isa<RefSubOp>(op))
3841 return emitError(
3842 "destination reference cannot be a sub-element of a reference");
3843 if (isa<RefCastOp>(op)) // Source flow, check anyway for now.
3844 return emitError(
3845 "destination reference cannot be a cast of another reference");
3846 }
3847
3848 // This define is only enabled when its ambient layers are active. Check
3849 // that whenever the destination's layer requirements are met, that this
3850 // op is enabled.
3851 auto ambientLayers = getAmbientLayersAt(getOperation());
3852 auto dstLayers = getLayersFor(getDest());
3853 SmallVector<SymbolRefAttr> missingLayers;
3854 if (!isLayerSetCompatibleWith(ambientLayers, dstLayers, missingLayers)) {
3855 auto diag = emitOpError("has more layer requirements than destination");
3856 auto &note = diag.attachNote();
3857 note << "additional layers required: ";
3858 interleaveComma(missingLayers, note);
3859 return failure();
3860 }
3861
3862 return success();
3863}
3864
3865LogicalResult PropAssignOp::verify() {
3866 // Check that the flows make sense.
3867 if (failed(checkConnectFlow(*this)))
3868 return failure();
3869
3870 // Verify that there is a single value driving the destination.
3871 for (auto *user : getDest().getUsers()) {
3872 if (auto conn = dyn_cast<FConnectLike>(user);
3873 conn && conn.getDest() == getDest() && conn != *this)
3874 return emitError("destination property cannot be reused by multiple "
3875 "operations, it can only capture a unique dataflow");
3876 }
3877
3878 return success();
3879}
3880
3881void WhenOp::createElseRegion() {
3882 assert(!hasElseRegion() && "already has an else region");
3883 getElseRegion().push_back(new Block());
3884}
3885
3886void WhenOp::build(OpBuilder &builder, OperationState &result, Value condition,
3887 bool withElseRegion, std::function<void()> thenCtor,
3888 std::function<void()> elseCtor) {
3889 OpBuilder::InsertionGuard guard(builder);
3890 result.addOperands(condition);
3891
3892 // Create "then" region.
3893 builder.createBlock(result.addRegion());
3894 if (thenCtor)
3895 thenCtor();
3896
3897 // Create "else" region.
3898 Region *elseRegion = result.addRegion();
3899 if (withElseRegion) {
3900 builder.createBlock(elseRegion);
3901 if (elseCtor)
3902 elseCtor();
3903 }
3904}
3905
3906//===----------------------------------------------------------------------===//
3907// MatchOp
3908//===----------------------------------------------------------------------===//
3909
3910LogicalResult MatchOp::verify() {
3911 FEnumType type = getInput().getType();
3912
3913 // Make sure that the number of tags matches the number of regions.
3914 auto numCases = getTags().size();
3915 auto numRegions = getNumRegions();
3916 if (numRegions != numCases)
3917 return emitOpError("expected ")
3918 << numRegions << " tags but got " << numCases;
3919
3920 auto numTags = type.getNumElements();
3921
3922 SmallDenseSet<int64_t> seen;
3923 for (const auto &[tag, region] : llvm::zip(getTags(), getRegions())) {
3924 auto tagIndex = size_t(cast<IntegerAttr>(tag).getInt());
3925
3926 // Ensure that the block has a single argument.
3927 if (region.getNumArguments() != 1)
3928 return emitOpError("region should have exactly one argument");
3929
3930 // Make sure that it is a valid tag.
3931 if (tagIndex >= numTags)
3932 return emitOpError("the tag index ")
3933 << tagIndex << " is out of the range of valid tags in " << type;
3934
3935 // Make sure we have not already matched this tag.
3936 auto [it, inserted] = seen.insert(tagIndex);
3937 if (!inserted)
3938 return emitOpError("the tag ") << type.getElementNameAttr(tagIndex)
3939 << " is matched more than once";
3940
3941 // Check that the block argument type matches the tag's type.
3942 auto expectedType = type.getElementTypePreservingConst(tagIndex);
3943 auto regionType = region.getArgument(0).getType();
3944 if (regionType != expectedType)
3945 return emitOpError("region type ")
3946 << regionType << " does not match the expected type "
3947 << expectedType;
3948 }
3949
3950 // Check that the match statement is exhaustive.
3951 for (size_t i = 0, e = type.getNumElements(); i < e; ++i)
3952 if (!seen.contains(i))
3953 return emitOpError("missing case for tag ") << type.getElementNameAttr(i);
3954
3955 return success();
3956}
3957
3958void MatchOp::print(OpAsmPrinter &p) {
3959 auto input = getInput();
3960 FEnumType type = input.getType();
3961 auto regions = getRegions();
3962 p << " " << input << " : " << type;
3963 SmallVector<StringRef> elided = {"tags"};
3964 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elided);
3965 p << " {";
3966 p.increaseIndent();
3967 for (const auto &[tag, region] : llvm::zip(getTags(), regions)) {
3968 p.printNewline();
3969 p << "case ";
3970 p.printKeywordOrString(
3971 type.getElementName(cast<IntegerAttr>(tag).getInt()));
3972 p << "(";
3973 p.printRegionArgument(region.front().getArgument(0), /*attrs=*/{},
3974 /*omitType=*/true);
3975 p << ") ";
3976 p.printRegion(region, /*printEntryBlockArgs=*/false);
3977 }
3978 p.decreaseIndent();
3979 p.printNewline();
3980 p << "}";
3981}
3982
3983ParseResult MatchOp::parse(OpAsmParser &parser, OperationState &result) {
3984 auto *context = parser.getContext();
3985 auto &properties = result.getOrAddProperties<Properties>();
3986 OpAsmParser::UnresolvedOperand input;
3987 if (parser.parseOperand(input) || parser.parseColon())
3988 return failure();
3989
3990 auto loc = parser.getCurrentLocation();
3991 Type type;
3992 if (parser.parseType(type))
3993 return failure();
3994 auto enumType = type_dyn_cast<FEnumType>(type);
3995 if (!enumType)
3996 return parser.emitError(loc, "expected enumeration type but got") << type;
3997
3998 if (parser.resolveOperand(input, type, result.operands) ||
3999 parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
4000 parser.parseLBrace())
4001 return failure();
4002
4003 auto i32Type = IntegerType::get(context, 32);
4004 SmallVector<Attribute> tags;
4005 while (true) {
4006 // Stop parsing when we don't find another "case" keyword.
4007 if (failed(parser.parseOptionalKeyword("case")))
4008 break;
4009
4010 // Parse the tag and region argument.
4011 auto nameLoc = parser.getCurrentLocation();
4012 std::string name;
4013 OpAsmParser::Argument arg;
4014 auto *region = result.addRegion();
4015 if (parser.parseKeywordOrString(&name) || parser.parseLParen() ||
4016 parser.parseArgument(arg) || parser.parseRParen())
4017 return failure();
4018
4019 // Figure out the enum index of the tag.
4020 auto index = enumType.getElementIndex(name);
4021 if (!index)
4022 return parser.emitError(nameLoc, "the tag \"")
4023 << name << "\" is not a member of the enumeration " << enumType;
4024 tags.push_back(IntegerAttr::get(i32Type, *index));
4025
4026 // Parse the region.
4027 arg.type = enumType.getElementTypePreservingConst(*index);
4028 if (parser.parseRegion(*region, arg))
4029 return failure();
4030 }
4031 properties.setTags(ArrayAttr::get(context, tags));
4032
4033 return parser.parseRBrace();
4034}
4035
4036void MatchOp::build(OpBuilder &builder, OperationState &result, Value input,
4037 ArrayAttr tags,
4038 MutableArrayRef<std::unique_ptr<Region>> regions) {
4039 auto &properties = result.getOrAddProperties<Properties>();
4040 result.addOperands(input);
4041 properties.setTags(tags);
4042 result.addRegions(regions);
4043}
4044
4045//===----------------------------------------------------------------------===//
4046// Expressions
4047//===----------------------------------------------------------------------===//
4048
4049/// Return true if the specified operation is a firrtl expression.
4050bool firrtl::isExpression(Operation *op) {
4051 struct IsExprClassifier : public ExprVisitor<IsExprClassifier, bool> {
4052 bool visitInvalidExpr(Operation *op) { return false; }
4053 bool visitUnhandledExpr(Operation *op) { return true; }
4054 };
4055
4056 return IsExprClassifier().dispatchExprVisitor(op);
4057}
4058
4059void InvalidValueOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4060 // Set invalid values to have a distinct name.
4061 std::string name;
4062 if (auto ty = type_dyn_cast<IntType>(getType())) {
4063 const char *base = ty.isSigned() ? "invalid_si" : "invalid_ui";
4064 auto width = ty.getWidthOrSentinel();
4065 if (width == -1)
4066 name = base;
4067 else
4068 name = (Twine(base) + Twine(width)).str();
4069 } else if (auto ty = type_dyn_cast<AnalogType>(getType())) {
4070 auto width = ty.getWidthOrSentinel();
4071 if (width == -1)
4072 name = "invalid_analog";
4073 else
4074 name = ("invalid_analog" + Twine(width)).str();
4075 } else if (type_isa<AsyncResetType>(getType()))
4076 name = "invalid_asyncreset";
4077 else if (type_isa<ResetType>(getType()))
4078 name = "invalid_reset";
4079 else if (type_isa<ClockType>(getType()))
4080 name = "invalid_clock";
4081 else
4082 name = "invalid";
4083
4084 setNameFn(getResult(), name);
4085}
4086
4087void ConstantOp::print(OpAsmPrinter &p) {
4088 p << " ";
4089 p.printAttributeWithoutType(getValueAttr());
4090 p << " : ";
4091 p.printType(getType());
4092 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4093}
4094
4095ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
4096 auto &properties = result.getOrAddProperties<Properties>();
4097 // Parse the constant value, without knowing its width.
4098 APInt value;
4099 auto loc = parser.getCurrentLocation();
4100 auto valueResult = parser.parseOptionalInteger(value);
4101 if (!valueResult.has_value())
4102 return parser.emitError(loc, "expected integer value");
4103
4104 // Parse the result firrtl integer type.
4105 IntType resultType;
4106 if (failed(*valueResult) || parser.parseColonType(resultType) ||
4107 parser.parseOptionalAttrDict(result.attributes))
4108 return failure();
4109 result.addTypes(resultType);
4110
4111 // Now that we know the width and sign of the result type, we can munge the
4112 // APInt as appropriate.
4113 if (resultType.hasWidth()) {
4114 auto width = (unsigned)resultType.getWidthOrSentinel();
4115 if (width > value.getBitWidth()) {
4116 // sext is always safe here, even for unsigned values, because the
4117 // parseOptionalInteger method will return something with a zero in the
4118 // top bits if it is a positive number.
4119 value = value.sext(width);
4120 } else if (width < value.getBitWidth()) {
4121 // The parser can return an unnecessarily wide result with leading
4122 // zeros. This isn't a problem, but truncating off bits is bad.
4123 unsigned neededBits = value.isNegative() ? value.getSignificantBits()
4124 : value.getActiveBits();
4125 if (width < neededBits)
4126 return parser.emitError(loc, "constant out of range for result type ")
4127 << resultType;
4128 value = value.trunc(width);
4129 }
4130 }
4131
4132 auto intType = parser.getBuilder().getIntegerType(value.getBitWidth(),
4133 resultType.isSigned());
4134 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
4135 properties.setValue(valueAttr);
4136 return success();
4137}
4138
4139LogicalResult ConstantOp::verify() {
4140 // If the result type has a bitwidth, then the attribute must match its width.
4141 IntType intType = getType();
4142 auto width = intType.getWidthOrSentinel();
4143 if (width != -1 && (int)getValue().getBitWidth() != width)
4144 return emitError(
4145 "firrtl.constant attribute bitwidth doesn't match return type");
4146
4147 // The sign of the attribute's integer type must match our integer type sign.
4148 auto attrType = type_cast<IntegerType>(getValueAttr().getType());
4149 if (attrType.isSignless() || attrType.isSigned() != intType.isSigned())
4150 return emitError("firrtl.constant attribute has wrong sign");
4151
4152 return success();
4153}
4154
4155/// Build a ConstantOp from an APInt and a FIRRTL type, handling the attribute
4156/// formation for the 'value' attribute.
4157void ConstantOp::build(OpBuilder &builder, OperationState &result, IntType type,
4158 const APInt &value) {
4159 int32_t width = type.getWidthOrSentinel();
4160 (void)width;
4161 assert((width == -1 || (int32_t)value.getBitWidth() == width) &&
4162 "incorrect attribute bitwidth for firrtl.constant");
4163
4164 auto attr =
4165 IntegerAttr::get(type.getContext(), APSInt(value, !type.isSigned()));
4166 return build(builder, result, type, attr);
4167}
4168
4169/// Build a ConstantOp from an APSInt, handling the attribute formation for the
4170/// 'value' attribute and inferring the FIRRTL type.
4171void ConstantOp::build(OpBuilder &builder, OperationState &result,
4172 const APSInt &value) {
4173 auto attr = IntegerAttr::get(builder.getContext(), value);
4174 auto type =
4175 IntType::get(builder.getContext(), value.isSigned(), value.getBitWidth());
4176 return build(builder, result, type, attr);
4177}
4178
4179void ConstantOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4180 // For constants in particular, propagate the value into the result name to
4181 // make it easier to read the IR.
4182 IntType intTy = getType();
4183 assert(intTy);
4184
4185 // Otherwise, build a complex name with the value and type.
4186 SmallString<32> specialNameBuffer;
4187 llvm::raw_svector_ostream specialName(specialNameBuffer);
4188 specialName << 'c';
4189 getValue().print(specialName, /*isSigned:*/ intTy.isSigned());
4190
4191 specialName << (intTy.isSigned() ? "_si" : "_ui");
4192 auto width = intTy.getWidthOrSentinel();
4193 if (width != -1)
4194 specialName << width;
4195 setNameFn(getResult(), specialName.str());
4196}
4197
4198void SpecialConstantOp::print(OpAsmPrinter &p) {
4199 p << " ";
4200 // SpecialConstant uses a BoolAttr, and we want to print `true` as `1`.
4201 p << static_cast<unsigned>(getValue());
4202 p << " : ";
4203 p.printType(getType());
4204 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4205}
4206
4207ParseResult SpecialConstantOp::parse(OpAsmParser &parser,
4208 OperationState &result) {
4209 auto &properties = result.getOrAddProperties<Properties>();
4210 // Parse the constant value. SpecialConstant uses bool attributes, but it
4211 // prints as an integer.
4212 APInt value;
4213 auto loc = parser.getCurrentLocation();
4214 auto valueResult = parser.parseOptionalInteger(value);
4215 if (!valueResult.has_value())
4216 return parser.emitError(loc, "expected integer value");
4217
4218 // Clocks and resets can only be 0 or 1.
4219 if (value != 0 && value != 1)
4220 return parser.emitError(loc, "special constants can only be 0 or 1.");
4221
4222 // Parse the result firrtl type.
4223 Type resultType;
4224 if (failed(*valueResult) || parser.parseColonType(resultType) ||
4225 parser.parseOptionalAttrDict(result.attributes))
4226 return failure();
4227 result.addTypes(resultType);
4228
4229 // Create the attribute.
4230 auto valueAttr = parser.getBuilder().getBoolAttr(value == 1);
4231 properties.setValue(valueAttr);
4232 return success();
4233}
4234
4235void SpecialConstantOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4236 SmallString<32> specialNameBuffer;
4237 llvm::raw_svector_ostream specialName(specialNameBuffer);
4238 specialName << 'c';
4239 specialName << static_cast<unsigned>(getValue());
4240 auto type = getType();
4241 if (type_isa<ClockType>(type)) {
4242 specialName << "_clock";
4243 } else if (type_isa<ResetType>(type)) {
4244 specialName << "_reset";
4245 } else if (type_isa<AsyncResetType>(type)) {
4246 specialName << "_asyncreset";
4247 }
4248 setNameFn(getResult(), specialName.str());
4249}
4250
4251// Checks that an array attr representing an aggregate constant has the correct
4252// shape. This recurses on the type.
4253static bool checkAggConstant(Operation *op, Attribute attr,
4254 FIRRTLBaseType type) {
4255 if (type.isGround()) {
4256 if (!isa<IntegerAttr>(attr)) {
4257 op->emitOpError("Ground type is not an integer attribute");
4258 return false;
4259 }
4260 return true;
4261 }
4262 auto attrlist = dyn_cast<ArrayAttr>(attr);
4263 if (!attrlist) {
4264 op->emitOpError("expected array attribute for aggregate constant");
4265 return false;
4266 }
4267 if (auto array = type_dyn_cast<FVectorType>(type)) {
4268 if (array.getNumElements() != attrlist.size()) {
4269 op->emitOpError("array attribute (")
4270 << attrlist.size() << ") has wrong size for vector constant ("
4271 << array.getNumElements() << ")";
4272 return false;
4273 }
4274 return llvm::all_of(attrlist, [&array, op](Attribute attr) {
4275 return checkAggConstant(op, attr, array.getElementType());
4276 });
4277 }
4278 if (auto bundle = type_dyn_cast<BundleType>(type)) {
4279 if (bundle.getNumElements() != attrlist.size()) {
4280 op->emitOpError("array attribute (")
4281 << attrlist.size() << ") has wrong size for bundle constant ("
4282 << bundle.getNumElements() << ")";
4283 return false;
4284 }
4285 for (size_t i = 0; i < bundle.getNumElements(); ++i) {
4286 if (bundle.getElement(i).isFlip) {
4287 op->emitOpError("Cannot have constant bundle type with flip");
4288 return false;
4289 }
4290 if (!checkAggConstant(op, attrlist[i], bundle.getElement(i).type))
4291 return false;
4292 }
4293 return true;
4294 }
4295 op->emitOpError("Unknown aggregate type");
4296 return false;
4297}
4298
4299LogicalResult AggregateConstantOp::verify() {
4300 if (checkAggConstant(getOperation(), getFields(), getType()))
4301 return success();
4302 return failure();
4303}
4304
4305Attribute AggregateConstantOp::getAttributeFromFieldID(uint64_t fieldID) {
4306 FIRRTLBaseType type = getType();
4307 Attribute value = getFields();
4308 while (fieldID != 0) {
4309 if (auto bundle = type_dyn_cast<BundleType>(type)) {
4310 auto index = bundle.getIndexForFieldID(fieldID);
4311 fieldID -= bundle.getFieldID(index);
4312 type = bundle.getElementType(index);
4313 value = cast<ArrayAttr>(value)[index];
4314 } else {
4315 auto vector = type_cast<FVectorType>(type);
4316 auto index = vector.getIndexForFieldID(fieldID);
4317 fieldID -= vector.getFieldID(index);
4318 type = vector.getElementType();
4319 value = cast<ArrayAttr>(value)[index];
4320 }
4321 }
4322 return value;
4323}
4324
4325void FIntegerConstantOp::print(OpAsmPrinter &p) {
4326 p << " ";
4327 p.printAttributeWithoutType(getValueAttr());
4328 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"value"});
4329}
4330
4331ParseResult FIntegerConstantOp::parse(OpAsmParser &parser,
4332 OperationState &result) {
4333 auto *context = parser.getContext();
4334 auto &properties = result.getOrAddProperties<Properties>();
4335 APInt value;
4336 if (parser.parseInteger(value) ||
4337 parser.parseOptionalAttrDict(result.attributes))
4338 return failure();
4339 result.addTypes(FIntegerType::get(context));
4340 auto intType =
4341 IntegerType::get(context, value.getBitWidth(), IntegerType::Signed);
4342 auto valueAttr = parser.getBuilder().getIntegerAttr(intType, value);
4343 properties.setValue(valueAttr);
4344 return success();
4345}
4346
4347ParseResult ListCreateOp::parse(OpAsmParser &parser, OperationState &result) {
4348 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> operands;
4349 ListType type;
4350
4351 if (parser.parseOperandList(operands) ||
4352 parser.parseOptionalAttrDict(result.attributes) ||
4353 parser.parseColonType(type))
4354 return failure();
4355 result.addTypes(type);
4356
4357 return parser.resolveOperands(operands, type.getElementType(),
4358 result.operands);
4359}
4360
4361void ListCreateOp::print(OpAsmPrinter &p) {
4362 p << " ";
4363 p.printOperands(getElements());
4364 p.printOptionalAttrDict((*this)->getAttrs());
4365 p << " : " << getType();
4366}
4367
4368LogicalResult ListCreateOp::verify() {
4369 if (getElements().empty())
4370 return success();
4371
4372 auto elementType = getElements().front().getType();
4373 auto listElementType = getType().getElementType();
4374 if (elementType != listElementType)
4375 return emitOpError("has elements of type ")
4376 << elementType << " instead of " << listElementType;
4377
4378 return success();
4379}
4380
4381LogicalResult BundleCreateOp::verify() {
4382 BundleType resultType = getType();
4383 if (resultType.getNumElements() != getFields().size())
4384 return emitOpError("number of fields doesn't match type");
4385 for (size_t i = 0; i < resultType.getNumElements(); ++i)
4387 resultType.getElementTypePreservingConst(i),
4388 type_cast<FIRRTLBaseType>(getOperand(i).getType())))
4389 return emitOpError("type of element doesn't match bundle for field ")
4390 << resultType.getElement(i).name;
4391 // TODO: check flow
4392 return success();
4393}
4394
4395LogicalResult VectorCreateOp::verify() {
4396 FVectorType resultType = getType();
4397 if (resultType.getNumElements() != getFields().size())
4398 return emitOpError("number of fields doesn't match type");
4399 auto elemTy = resultType.getElementTypePreservingConst();
4400 for (size_t i = 0; i < resultType.getNumElements(); ++i)
4402 elemTy, type_cast<FIRRTLBaseType>(getOperand(i).getType())))
4403 return emitOpError("type of element doesn't match vector element");
4404 // TODO: check flow
4405 return success();
4406}
4407
4408//===----------------------------------------------------------------------===//
4409// FEnumCreateOp
4410//===----------------------------------------------------------------------===//
4411
4412LogicalResult FEnumCreateOp::verify() {
4413 FEnumType resultType = getResult().getType();
4414 auto elementIndex = resultType.getElementIndex(getFieldName());
4415 if (!elementIndex)
4416 return emitOpError("label ")
4417 << getFieldName() << " is not a member of the enumeration type "
4418 << resultType;
4420 resultType.getElementTypePreservingConst(*elementIndex),
4421 getInput().getType()))
4422 return emitOpError("type of element doesn't match enum element");
4423 return success();
4424}
4425
4426void FEnumCreateOp::print(OpAsmPrinter &printer) {
4427 printer << ' ';
4428 printer.printKeywordOrString(getFieldName());
4429 printer << '(' << getInput() << ')';
4430 SmallVector<StringRef> elidedAttrs = {"fieldIndex"};
4431 printer.printOptionalAttrDictWithKeyword((*this)->getAttrs(), elidedAttrs);
4432 printer << " : ";
4433 printer.printFunctionalType(ArrayRef<Type>{getInput().getType()},
4434 ArrayRef<Type>{getResult().getType()});
4435}
4436
4437ParseResult FEnumCreateOp::parse(OpAsmParser &parser, OperationState &result) {
4438 auto *context = parser.getContext();
4439 auto &properties = result.getOrAddProperties<Properties>();
4440
4441 OpAsmParser::UnresolvedOperand input;
4442 std::string fieldName;
4443 mlir::FunctionType functionType;
4444 if (parser.parseKeywordOrString(&fieldName) || parser.parseLParen() ||
4445 parser.parseOperand(input) || parser.parseRParen() ||
4446 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4447 parser.parseType(functionType))
4448 return failure();
4449
4450 if (functionType.getNumInputs() != 1)
4451 return parser.emitError(parser.getNameLoc(), "single input type required");
4452 if (functionType.getNumResults() != 1)
4453 return parser.emitError(parser.getNameLoc(), "single result type required");
4454
4455 auto inputType = functionType.getInput(0);
4456 if (parser.resolveOperand(input, inputType, result.operands))
4457 return failure();
4458
4459 auto outputType = functionType.getResult(0);
4460 auto enumType = type_dyn_cast<FEnumType>(outputType);
4461 if (!enumType)
4462 return parser.emitError(parser.getNameLoc(),
4463 "output must be enum type, got ")
4464 << outputType;
4465 auto fieldIndex = enumType.getElementIndex(fieldName);
4466 if (!fieldIndex)
4467 return parser.emitError(parser.getNameLoc(),
4468 "unknown field " + fieldName + " in enum type ")
4469 << enumType;
4470
4471 properties.setFieldIndex(
4472 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4473
4474 result.addTypes(enumType);
4475
4476 return success();
4477}
4478
4479//===----------------------------------------------------------------------===//
4480// IsTagOp
4481//===----------------------------------------------------------------------===//
4482
4483LogicalResult IsTagOp::verify() {
4484 if (getFieldIndex() >= getInput().getType().base().getNumElements())
4485 return emitOpError("element index is greater than the number of fields in "
4486 "the bundle type");
4487 return success();
4488}
4489
4490void IsTagOp::print(::mlir::OpAsmPrinter &printer) {
4491 printer << ' ' << getInput() << ' ';
4492 printer.printKeywordOrString(getFieldName());
4493 SmallVector<::llvm::StringRef, 1> elidedAttrs = {"fieldIndex"};
4494 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4495 printer << " : " << getInput().getType();
4496}
4497
4498ParseResult IsTagOp::parse(OpAsmParser &parser, OperationState &result) {
4499 auto *context = parser.getContext();
4500 auto &properties = result.getOrAddProperties<Properties>();
4501
4502 OpAsmParser::UnresolvedOperand input;
4503 std::string fieldName;
4504 Type inputType;
4505 if (parser.parseOperand(input) || parser.parseKeywordOrString(&fieldName) ||
4506 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4507 parser.parseType(inputType))
4508 return failure();
4509
4510 if (parser.resolveOperand(input, inputType, result.operands))
4511 return failure();
4512
4513 auto enumType = type_dyn_cast<FEnumType>(inputType);
4514 if (!enumType)
4515 return parser.emitError(parser.getNameLoc(),
4516 "input must be enum type, got ")
4517 << inputType;
4518 auto fieldIndex = enumType.getElementIndex(fieldName);
4519 if (!fieldIndex)
4520 return parser.emitError(parser.getNameLoc(),
4521 "unknown field " + fieldName + " in enum type ")
4522 << enumType;
4523
4524 properties.setFieldIndex(
4525 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4526
4527 result.addTypes(UIntType::get(context, 1, /*isConst=*/false));
4528
4529 return success();
4530}
4531
4532FIRRTLType IsTagOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
4533 OpaqueProperties properties,
4534 mlir::RegionRange regions,
4535 std::optional<Location> loc) {
4536 Adaptor adaptor(operands, attrs, properties, regions);
4537 return UIntType::get(attrs.getContext(), 1,
4538 isConst(adaptor.getInput().getType()));
4539}
4540
4541template <typename OpTy>
4542ParseResult parseSubfieldLikeOp(OpAsmParser &parser, OperationState &result) {
4543 auto *context = parser.getContext();
4544
4545 OpAsmParser::UnresolvedOperand input;
4546 std::string fieldName;
4547 Type inputType;
4548 if (parser.parseOperand(input) || parser.parseLSquare() ||
4549 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4550 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4551 parser.parseType(inputType))
4552 return failure();
4553
4554 if (parser.resolveOperand(input, inputType, result.operands))
4555 return failure();
4556
4557 auto bundleType = type_dyn_cast<typename OpTy::InputType>(inputType);
4558 if (!bundleType)
4559 return parser.emitError(parser.getNameLoc(),
4560 "input must be bundle type, got ")
4561 << inputType;
4562 auto fieldIndex = bundleType.getElementIndex(fieldName);
4563 if (!fieldIndex)
4564 return parser.emitError(parser.getNameLoc(),
4565 "unknown field " + fieldName + " in bundle type ")
4566 << bundleType;
4567
4568 result.getOrAddProperties<typename OpTy::Properties>().setFieldIndex(
4569 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4570
4571 auto type = OpTy::inferReturnType(inputType, *fieldIndex, {});
4572 if (!type)
4573 return failure();
4574 result.addTypes(type);
4575
4576 return success();
4577}
4578
4579ParseResult SubtagOp::parse(OpAsmParser &parser, OperationState &result) {
4580 auto *context = parser.getContext();
4581
4582 OpAsmParser::UnresolvedOperand input;
4583 std::string fieldName;
4584 Type inputType;
4585 if (parser.parseOperand(input) || parser.parseLSquare() ||
4586 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4587 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4588 parser.parseType(inputType))
4589 return failure();
4590
4591 if (parser.resolveOperand(input, inputType, result.operands))
4592 return failure();
4593
4594 auto enumType = type_dyn_cast<FEnumType>(inputType);
4595 if (!enumType)
4596 return parser.emitError(parser.getNameLoc(),
4597 "input must be enum type, got ")
4598 << inputType;
4599 auto fieldIndex = enumType.getElementIndex(fieldName);
4600 if (!fieldIndex)
4601 return parser.emitError(parser.getNameLoc(),
4602 "unknown field " + fieldName + " in enum type ")
4603 << enumType;
4604
4605 result.getOrAddProperties<Properties>().setFieldIndex(
4606 IntegerAttr::get(IntegerType::get(context, 32), *fieldIndex));
4607
4608 SmallVector<Type> inferredReturnTypes;
4609 if (failed(SubtagOp::inferReturnTypes(
4610 context, result.location, result.operands,
4611 result.attributes.getDictionary(context), result.getRawProperties(),
4612 result.regions, inferredReturnTypes)))
4613 return failure();
4614 result.addTypes(inferredReturnTypes);
4615
4616 return success();
4617}
4618
4619ParseResult SubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4620 return parseSubfieldLikeOp<SubfieldOp>(parser, result);
4621}
4622ParseResult OpenSubfieldOp::parse(OpAsmParser &parser, OperationState &result) {
4623 return parseSubfieldLikeOp<OpenSubfieldOp>(parser, result);
4624}
4625
4626template <typename OpTy>
4627static void printSubfieldLikeOp(OpTy op, ::mlir::OpAsmPrinter &printer) {
4628 printer << ' ' << op.getInput() << '[';
4629 printer.printKeywordOrString(op.getFieldName());
4630 printer << ']';
4631 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4632 elidedAttrs.push_back("fieldIndex");
4633 printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
4634 printer << " : " << op.getInput().getType();
4635}
4636void SubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4637 return printSubfieldLikeOp<SubfieldOp>(*this, printer);
4638}
4639void OpenSubfieldOp::print(::mlir::OpAsmPrinter &printer) {
4640 return printSubfieldLikeOp<OpenSubfieldOp>(*this, printer);
4641}
4642
4643void SubtagOp::print(::mlir::OpAsmPrinter &printer) {
4644 printer << ' ' << getInput() << '[';
4645 printer.printKeywordOrString(getFieldName());
4646 printer << ']';
4647 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
4648 elidedAttrs.push_back("fieldIndex");
4649 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
4650 printer << " : " << getInput().getType();
4651}
4652
4653template <typename OpTy>
4654static LogicalResult verifySubfieldLike(OpTy op) {
4655 if (op.getFieldIndex() >=
4656 firrtl::type_cast<typename OpTy::InputType>(op.getInput().getType())
4657 .getNumElements())
4658 return op.emitOpError("subfield element index is greater than the number "
4659 "of fields in the bundle type");
4660 return success();
4661}
4662LogicalResult SubfieldOp::verify() {
4663 return verifySubfieldLike<SubfieldOp>(*this);
4664}
4665LogicalResult OpenSubfieldOp::verify() {
4666 return verifySubfieldLike<OpenSubfieldOp>(*this);
4667}
4668
4669LogicalResult SubtagOp::verify() {
4670 if (getFieldIndex() >= getInput().getType().base().getNumElements())
4671 return emitOpError("subfield element index is greater than the number "
4672 "of fields in the bundle type");
4673 return success();
4674}
4675
4676/// Return true if the specified operation has a constant value. This trivially
4677/// checks for `firrtl.constant` and friends, but also looks through subaccesses
4678/// and correctly handles wires driven with only constant values.
4679bool firrtl::isConstant(Operation *op) {
4680 // Worklist of ops that need to be examined that should all be constant in
4681 // order for the input operation to be constant.
4682 SmallVector<Operation *, 8> worklist({op});
4683
4684 // Mutable state indicating if this op is a constant. Assume it is a constant
4685 // and look for counterexamples.
4686 bool constant = true;
4687
4688 // While we haven't found a counterexample and there are still ops in the
4689 // worklist, pull ops off the worklist. If it provides a counterexample, set
4690 // the `constant` to false (and exit on the next loop iteration). Otherwise,
4691 // look through the op or spawn off more ops to look at.
4692 while (constant && !(worklist.empty()))
4693 TypeSwitch<Operation *>(worklist.pop_back_val())
4694 .Case<NodeOp, AsSIntPrimOp, AsUIntPrimOp>([&](auto op) {
4695 if (auto definingOp = op.getInput().getDefiningOp())
4696 worklist.push_back(definingOp);
4697 constant = false;
4698 })
4699 .Case<WireOp, SubindexOp, SubfieldOp>([&](auto op) {
4700 for (auto &use : op.getResult().getUses())
4701 worklist.push_back(use.getOwner());
4702 })
4703 .Case<ConstantOp, SpecialConstantOp, AggregateConstantOp>([](auto) {})
4704 .Default([&](auto) { constant = false; });
4705
4706 return constant;
4707}
4708
4709/// Return true if the specified value is a constant. This trivially checks for
4710/// `firrtl.constant` and friends, but also looks through subaccesses and
4711/// correctly handles wires driven with only constant values.
4712bool firrtl::isConstant(Value value) {
4713 if (auto *op = value.getDefiningOp())
4714 return isConstant(op);
4715 return false;
4716}
4717
4718LogicalResult ConstCastOp::verify() {
4719 if (!areTypesConstCastable(getResult().getType(), getInput().getType()))
4720 return emitOpError() << getInput().getType()
4721 << " is not 'const'-castable to "
4722 << getResult().getType();
4723 return success();
4724}
4725
4726FIRRTLType SubfieldOp::inferReturnType(Type type, uint32_t fieldIndex,
4727 std::optional<Location> loc) {
4728 auto inType = type_cast<BundleType>(type);
4729
4730 if (fieldIndex >= inType.getNumElements())
4731 return emitInferRetTypeError(loc,
4732 "subfield element index is greater than the "
4733 "number of fields in the bundle type");
4734
4735 // SubfieldOp verifier checks that the field index is valid with number of
4736 // subelements.
4737 return inType.getElementTypePreservingConst(fieldIndex);
4738}
4739
4740FIRRTLType OpenSubfieldOp::inferReturnType(Type type, uint32_t fieldIndex,
4741 std::optional<Location> loc) {
4742 auto inType = type_cast<OpenBundleType>(type);
4743
4744 if (fieldIndex >= inType.getNumElements())
4745 return emitInferRetTypeError(loc,
4746 "subfield element index is greater than the "
4747 "number of fields in the bundle type");
4748
4749 // OpenSubfieldOp verifier checks that the field index is valid with number of
4750 // subelements.
4751 return inType.getElementTypePreservingConst(fieldIndex);
4752}
4753
4754bool SubfieldOp::isFieldFlipped() {
4755 BundleType bundle = getInput().getType();
4756 return bundle.getElement(getFieldIndex()).isFlip;
4757}
4758bool OpenSubfieldOp::isFieldFlipped() {
4759 auto bundle = getInput().getType();
4760 return bundle.getElement(getFieldIndex()).isFlip;
4761}
4762
4763FIRRTLType SubindexOp::inferReturnType(Type type, uint32_t fieldIndex,
4764 std::optional<Location> loc) {
4765 if (auto vectorType = type_dyn_cast<FVectorType>(type)) {
4766 if (fieldIndex < vectorType.getNumElements())
4767 return vectorType.getElementTypePreservingConst();
4768 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
4769 "' in vector type ", type);
4770 }
4771 return emitInferRetTypeError(loc, "subindex requires vector operand");
4772}
4773
4774FIRRTLType OpenSubindexOp::inferReturnType(Type type, uint32_t fieldIndex,
4775 std::optional<Location> loc) {
4776 if (auto vectorType = type_dyn_cast<OpenVectorType>(type)) {
4777 if (fieldIndex < vectorType.getNumElements())
4778 return vectorType.getElementTypePreservingConst();
4779 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
4780 "' in vector type ", type);
4781 }
4782
4783 return emitInferRetTypeError(loc, "subindex requires vector operand");
4784}
4785
4786FIRRTLType SubtagOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
4787 OpaqueProperties properties,
4788 mlir::RegionRange regions,
4789 std::optional<Location> loc) {
4790 Adaptor adaptor(operands, attrs, properties, regions);
4791 auto inType = type_cast<FEnumType>(adaptor.getInput().getType());
4792 auto fieldIndex = adaptor.getFieldIndex();
4793
4794 if (fieldIndex >= inType.getNumElements())
4795 return emitInferRetTypeError(loc,
4796 "subtag element index is greater than the "
4797 "number of fields in the enum type");
4798
4799 // SubtagOp verifier checks that the field index is valid with number of
4800 // subelements.
4801 auto elementType = inType.getElement(fieldIndex).type;
4802 return elementType.getConstType(elementType.isConst() || inType.isConst());
4803}
4804
4805FIRRTLType SubaccessOp::inferReturnType(Type inType, Type indexType,
4806 std::optional<Location> loc) {
4807 if (!type_isa<UIntType>(indexType))
4808 return emitInferRetTypeError(loc, "subaccess index must be UInt type, not ",
4809 indexType);
4810
4811 if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
4812 if (isConst(indexType))
4813 return vectorType.getElementTypePreservingConst();
4814 return vectorType.getElementType().getAllConstDroppedType();
4815 }
4816
4817 return emitInferRetTypeError(loc, "subaccess requires vector operand, not ",
4818 inType);
4819}
4820
4821FIRRTLType TagExtractOp::inferReturnType(ValueRange operands,
4822 DictionaryAttr attrs,
4823 OpaqueProperties properties,
4824 mlir::RegionRange regions,
4825 std::optional<Location> loc) {
4826 Adaptor adaptor(operands, attrs, properties, regions);
4827 auto inType = type_cast<FEnumType>(adaptor.getInput().getType());
4828 auto i = llvm::Log2_32_Ceil(inType.getNumElements());
4829 return UIntType::get(inType.getContext(), i);
4830}
4831
4832ParseResult MultibitMuxOp::parse(OpAsmParser &parser, OperationState &result) {
4833 OpAsmParser::UnresolvedOperand index;
4834 SmallVector<OpAsmParser::UnresolvedOperand, 16> inputs;
4835 Type indexType, elemType;
4836
4837 if (parser.parseOperand(index) || parser.parseComma() ||
4838 parser.parseOperandList(inputs) ||
4839 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4840 parser.parseType(indexType) || parser.parseComma() ||
4841 parser.parseType(elemType))
4842 return failure();
4843
4844 if (parser.resolveOperand(index, indexType, result.operands))
4845 return failure();
4846
4847 result.addTypes(elemType);
4848
4849 return parser.resolveOperands(inputs, elemType, result.operands);
4850}
4851
4852void MultibitMuxOp::print(OpAsmPrinter &p) {
4853 p << " " << getIndex() << ", ";
4854 p.printOperands(getInputs());
4855 p.printOptionalAttrDict((*this)->getAttrs());
4856 p << " : " << getIndex().getType() << ", " << getType();
4857}
4858
4859FIRRTLType MultibitMuxOp::inferReturnType(ValueRange operands,
4860 DictionaryAttr attrs,
4861 OpaqueProperties properties,
4862 mlir::RegionRange regions,
4863 std::optional<Location> loc) {
4864 if (operands.size() < 2)
4865 return emitInferRetTypeError(loc, "at least one input is required");
4866
4867 // Check all mux inputs have the same type.
4868 if (!llvm::all_of(operands.drop_front(2), [&](auto op) {
4869 return operands[1].getType() == op.getType();
4870 }))
4871 return emitInferRetTypeError(loc, "all inputs must have the same type");
4872
4873 return type_cast<FIRRTLType>(operands[1].getType());
4874}
4875
4876//===----------------------------------------------------------------------===//
4877// ObjectSubfieldOp
4878//===----------------------------------------------------------------------===//
4879
4880LogicalResult ObjectSubfieldOp::inferReturnTypes(
4881 MLIRContext *context, std::optional<mlir::Location> location,
4882 ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
4883 RegionRange regions, llvm::SmallVectorImpl<Type> &inferredReturnTypes) {
4884 auto type =
4885 inferReturnType(operands, attributes, properties, regions, location);
4886 if (!type)
4887 return failure();
4888 inferredReturnTypes.push_back(type);
4889 return success();
4890}
4891
4892Type ObjectSubfieldOp::inferReturnType(Type inType, uint32_t fieldIndex,
4893 std::optional<Location> loc) {
4894 auto classType = dyn_cast<ClassType>(inType);
4895 if (!classType)
4896 return emitInferRetTypeError(loc, "base object is not a class");
4897
4898 if (classType.getNumElements() <= fieldIndex)
4899 return emitInferRetTypeError(loc, "element index is greater than the "
4900 "number of fields in the object");
4901 return classType.getElement(fieldIndex).type;
4902}
4903
4904void ObjectSubfieldOp::print(OpAsmPrinter &p) {
4905 auto input = getInput();
4906 auto classType = input.getType();
4907 p << ' ' << input << "[";
4908 p.printKeywordOrString(classType.getElement(getIndex()).name);
4909 p << "]";
4910 p.printOptionalAttrDict((*this)->getAttrs(), std::array{StringRef("index")});
4911 p << " : " << classType;
4912}
4913
4914ParseResult ObjectSubfieldOp::parse(OpAsmParser &parser,
4915 OperationState &result) {
4916 auto *context = parser.getContext();
4917
4918 OpAsmParser::UnresolvedOperand input;
4919 std::string fieldName;
4920 ClassType inputType;
4921 if (parser.parseOperand(input) || parser.parseLSquare() ||
4922 parser.parseKeywordOrString(&fieldName) || parser.parseRSquare() ||
4923 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
4924 parser.parseType(inputType) ||
4925 parser.resolveOperand(input, inputType, result.operands))
4926 return failure();
4927
4928 auto index = inputType.getElementIndex(fieldName);
4929 if (!index)
4930 return parser.emitError(parser.getNameLoc(),
4931 "unknown field " + fieldName + " in class type ")
4932 << inputType;
4933 result.getOrAddProperties<Properties>().setIndex(
4934 IntegerAttr::get(IntegerType::get(context, 32), *index));
4935
4936 SmallVector<Type> inferredReturnTypes;
4937 if (failed(inferReturnTypes(context, result.location, result.operands,
4938 result.attributes.getDictionary(context),
4939 result.getRawProperties(), result.regions,
4940 inferredReturnTypes)))
4941 return failure();
4942 result.addTypes(inferredReturnTypes);
4943
4944 return success();
4945}
4946
4947//===----------------------------------------------------------------------===//
4948// Binary Primitives
4949//===----------------------------------------------------------------------===//
4950
4951/// If LHS and RHS are both UInt or SInt types, the return true and fill in the
4952/// width of them if known. If unknown, return -1 for the widths.
4953/// The constness of the result is also returned, where if both lhs and rhs are
4954/// const, then the result is const.
4955///
4956/// On failure, this reports and error and returns false. This function should
4957/// not be used if you don't want an error reported.
4958static bool isSameIntTypeKind(Type lhs, Type rhs, int32_t &lhsWidth,
4959 int32_t &rhsWidth, bool &isConstResult,
4960 std::optional<Location> loc) {
4961 // Must have two integer types with the same signedness.
4962 auto lhsi = type_dyn_cast<IntType>(lhs);
4963 auto rhsi = type_dyn_cast<IntType>(rhs);
4964 if (!lhsi || !rhsi || lhsi.isSigned() != rhsi.isSigned()) {
4965 if (loc) {
4966 if (lhsi && !rhsi)
4967 mlir::emitError(*loc, "second operand must be an integer type, not ")
4968 << rhs;
4969 else if (!lhsi && rhsi)
4970 mlir::emitError(*loc, "first operand must be an integer type, not ")
4971 << lhs;
4972 else if (!lhsi && !rhsi)
4973 mlir::emitError(*loc, "operands must be integer types, not ")
4974 << lhs << " and " << rhs;
4975 else
4976 mlir::emitError(*loc, "operand signedness must match");
4977 }
4978 return false;
4979 }
4980
4981 lhsWidth = lhsi.getWidthOrSentinel();
4982 rhsWidth = rhsi.getWidthOrSentinel();
4983 isConstResult = lhsi.isConst() && rhsi.isConst();
4984 return true;
4985}
4986
4987LogicalResult impl::verifySameOperandsIntTypeKind(Operation *op) {
4988 assert(op->getNumOperands() == 2 &&
4989 "SameOperandsIntTypeKind on non-binary op");
4990 int32_t lhsWidth, rhsWidth;
4991 bool isConstResult;
4992 return success(isSameIntTypeKind(op->getOperand(0).getType(),
4993 op->getOperand(1).getType(), lhsWidth,
4994 rhsWidth, isConstResult, op->getLoc()));
4995}
4996
4998 std::optional<Location> loc) {
4999 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5000 bool isConstResult = false;
5001 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5002 return {};
5003
5004 if (lhsWidth != -1 && rhsWidth != -1)
5005 resultWidth = std::max(lhsWidth, rhsWidth) + 1;
5006 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5007 isConstResult);
5008}
5009
5010FIRRTLType MulPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5011 std::optional<Location> loc) {
5012 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5013 bool isConstResult = false;
5014 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5015 return {};
5016
5017 if (lhsWidth != -1 && rhsWidth != -1)
5018 resultWidth = lhsWidth + rhsWidth;
5019
5020 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5021 isConstResult);
5022}
5023
5024FIRRTLType DivPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5025 std::optional<Location> loc) {
5026 int32_t lhsWidth, rhsWidth;
5027 bool isConstResult = false;
5028 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5029 return {};
5030
5031 // For unsigned, the width is the width of the numerator on the LHS.
5032 if (type_isa<UIntType>(lhs))
5033 return UIntType::get(lhs.getContext(), lhsWidth, isConstResult);
5034
5035 // For signed, the width is the width of the numerator on the LHS, plus 1.
5036 int32_t resultWidth = lhsWidth != -1 ? lhsWidth + 1 : -1;
5037 return SIntType::get(lhs.getContext(), resultWidth, isConstResult);
5038}
5039
5040FIRRTLType RemPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5041 std::optional<Location> loc) {
5042 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5043 bool isConstResult = false;
5044 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5045 return {};
5046
5047 if (lhsWidth != -1 && rhsWidth != -1)
5048 resultWidth = std::min(lhsWidth, rhsWidth);
5049 return IntType::get(lhs.getContext(), type_isa<SIntType>(lhs), resultWidth,
5050 isConstResult);
5051}
5052
5054 std::optional<Location> loc) {
5055 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5056 bool isConstResult = false;
5057 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5058 return {};
5059
5060 if (lhsWidth != -1 && rhsWidth != -1) {
5061 resultWidth = std::max(lhsWidth, rhsWidth);
5062 if (lhsWidth == resultWidth && lhs.isConst() == isConstResult &&
5063 isa<UIntType>(lhs))
5064 return lhs;
5065 if (rhsWidth == resultWidth && rhs.isConst() == isConstResult &&
5066 isa<UIntType>(rhs))
5067 return rhs;
5068 }
5069 return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
5070}
5071
5073 std::optional<Location> loc) {
5074 if (!type_isa<FVectorType>(lhs) || !type_isa<FVectorType>(rhs))
5075 return {};
5076
5077 auto lhsVec = type_cast<FVectorType>(lhs);
5078 auto rhsVec = type_cast<FVectorType>(rhs);
5079
5080 if (lhsVec.getNumElements() != rhsVec.getNumElements())
5081 return {};
5082
5083 auto elemType =
5084 impl::inferBitwiseResult(lhsVec.getElementTypePreservingConst(),
5085 rhsVec.getElementTypePreservingConst(), loc);
5086 if (!elemType)
5087 return {};
5088 auto elemBaseType = type_cast<FIRRTLBaseType>(elemType);
5089 return FVectorType::get(elemBaseType, lhsVec.getNumElements(),
5090 lhsVec.isConst() && rhsVec.isConst() &&
5091 elemBaseType.isConst());
5092}
5093
5095 std::optional<Location> loc) {
5096 return UIntType::get(lhs.getContext(), 1, isConst(lhs) && isConst(rhs));
5097}
5098
5099FIRRTLType CatPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5100 std::optional<Location> loc) {
5101 int32_t lhsWidth, rhsWidth, resultWidth = -1;
5102 bool isConstResult = false;
5103 if (!isSameIntTypeKind(lhs, rhs, lhsWidth, rhsWidth, isConstResult, loc))
5104 return {};
5105
5106 if (lhsWidth != -1 && rhsWidth != -1)
5107 resultWidth = lhsWidth + rhsWidth;
5108 return UIntType::get(lhs.getContext(), resultWidth, isConstResult);
5109}
5110
5111FIRRTLType DShlPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5112 std::optional<Location> loc) {
5113 auto lhsi = type_dyn_cast<IntType>(lhs);
5114 auto rhsui = type_dyn_cast<UIntType>(rhs);
5115 if (!rhsui || !lhsi)
5116 return emitInferRetTypeError(
5117 loc, "first operand should be integer, second unsigned int");
5118
5119 // If the left or right has unknown result type, then the operation does
5120 // too.
5121 auto width = lhsi.getWidthOrSentinel();
5122 if (width == -1 || !rhsui.getWidth().has_value()) {
5123 width = -1;
5124 } else {
5125 auto amount = *rhsui.getWidth();
5126 if (amount >= 32)
5127 return emitInferRetTypeError(loc,
5128 "shift amount too large: second operand of "
5129 "dshl is wider than 31 bits");
5130 int64_t newWidth = (int64_t)width + ((int64_t)1 << amount) - 1;
5131 if (newWidth > INT32_MAX)
5132 return emitInferRetTypeError(
5133 loc, "shift amount too large: first operand shifted by maximum "
5134 "amount exceeds maximum width");
5135 width = newWidth;
5136 }
5137 return IntType::get(lhs.getContext(), lhsi.isSigned(), width,
5138 lhsi.isConst() && rhsui.isConst());
5139}
5140
5141FIRRTLType DShlwPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5142 std::optional<Location> loc) {
5143 auto lhsi = type_dyn_cast<IntType>(lhs);
5144 auto rhsu = type_dyn_cast<UIntType>(rhs);
5145 if (!lhsi || !rhsu)
5146 return emitInferRetTypeError(
5147 loc, "first operand should be integer, second unsigned int");
5148 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
5149}
5150
5151FIRRTLType DShrPrimOp::inferReturnType(FIRRTLType lhs, FIRRTLType rhs,
5152 std::optional<Location> loc) {
5153 auto lhsi = type_dyn_cast<IntType>(lhs);
5154 auto rhsu = type_dyn_cast<UIntType>(rhs);
5155 if (!lhsi || !rhsu)
5156 return emitInferRetTypeError(
5157 loc, "first operand should be integer, second unsigned int");
5158 return lhsi.getConstType(lhsi.isConst() && rhsu.isConst());
5159}
5160
5161//===----------------------------------------------------------------------===//
5162// Unary Primitives
5163//===----------------------------------------------------------------------===//
5164
5165FIRRTLType SizeOfIntrinsicOp::inferReturnType(FIRRTLType input,
5166 std::optional<Location> loc) {
5167 return UIntType::get(input.getContext(), 32);
5168}
5169
5170FIRRTLType AsSIntPrimOp::inferReturnType(FIRRTLType input,
5171 std::optional<Location> loc) {
5172 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5173 if (!base)
5174 return emitInferRetTypeError(loc, "operand must be a scalar base type");
5175 int32_t width = base.getBitWidthOrSentinel();
5176 if (width == -2)
5177 return emitInferRetTypeError(loc, "operand must be a scalar type");
5178 return SIntType::get(input.getContext(), width, base.isConst());
5179}
5180
5181FIRRTLType AsUIntPrimOp::inferReturnType(FIRRTLType input,
5182 std::optional<Location> loc) {
5183 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5184 if (!base)
5185 return emitInferRetTypeError(loc, "operand must be a scalar base type");
5186 int32_t width = base.getBitWidthOrSentinel();
5187 if (width == -2)
5188 return emitInferRetTypeError(loc, "operand must be a scalar type");
5189 return UIntType::get(input.getContext(), width, base.isConst());
5190}
5191
5192FIRRTLType AsAsyncResetPrimOp::inferReturnType(FIRRTLType input,
5193 std::optional<Location> loc) {
5194 auto base = type_dyn_cast<FIRRTLBaseType>(input);
5195 if (!base)
5196 return emitInferRetTypeError(loc,
5197 "operand must be single bit scalar base type");
5198 int32_t width = base.getBitWidthOrSentinel();
5199 if (width == -2 || width == 0 || width > 1)
5200 return emitInferRetTypeError(loc, "operand must be single bit scalar type");
5201 return AsyncResetType::get(input.getContext(), base.isConst());
5202}
5203
5204FIRRTLType AsClockPrimOp::inferReturnType(FIRRTLType input,
5205 std::optional<Location> loc) {
5206 return ClockType::get(input.getContext(), isConst(input));
5207}
5208
5209FIRRTLType CvtPrimOp::inferReturnType(FIRRTLType input,
5210 std::optional<Location> loc) {
5211 if (auto uiType = type_dyn_cast<UIntType>(input)) {
5212 auto width = uiType.getWidthOrSentinel();
5213 if (width != -1)
5214 ++width;
5215 return SIntType::get(input.getContext(), width, uiType.isConst());
5216 }
5217
5218 if (type_isa<SIntType>(input))
5219 return input;
5220
5221 return emitInferRetTypeError(loc, "operand must have integer type");
5222}
5223
5224FIRRTLType NegPrimOp::inferReturnType(FIRRTLType input,
5225 std::optional<Location> loc) {
5226 auto inputi = type_dyn_cast<IntType>(input);
5227 if (!inputi)
5228 return emitInferRetTypeError(loc, "operand must have integer type");
5229 int32_t width = inputi.getWidthOrSentinel();
5230 if (width != -1)
5231 ++width;
5232 return SIntType::get(input.getContext(), width, inputi.isConst());
5233}
5234
5235FIRRTLType NotPrimOp::inferReturnType(FIRRTLType input,
5236 std::optional<Location> loc) {
5237 auto inputi = type_dyn_cast<IntType>(input);
5238 if (!inputi)
5239 return emitInferRetTypeError(loc, "operand must have integer type");
5240 if (isa<UIntType>(inputi))
5241 return inputi;
5242 return UIntType::get(input.getContext(), inputi.getWidthOrSentinel(),
5243 inputi.isConst());
5244}
5245
5247 std::optional<Location> loc) {
5248 return UIntType::get(input.getContext(), 1, isConst(input));
5249}
5250
5251//===----------------------------------------------------------------------===//
5252// Other Operations
5253//===----------------------------------------------------------------------===//
5254
5255FIRRTLType BitsPrimOp::inferReturnType(FIRRTLType input, int64_t high,
5256 int64_t low,
5257 std::optional<Location> loc) {
5258 auto inputi = type_dyn_cast<IntType>(input);
5259 if (!inputi)
5260 return emitInferRetTypeError(
5261 loc, "input type should be the int type but got ", input);
5262
5263 // High must be >= low and both most be non-negative.
5264 if (high < low)
5265 return emitInferRetTypeError(
5266 loc, "high must be equal or greater than low, but got high = ", high,
5267 ", low = ", low);
5268
5269 if (low < 0)
5270 return emitInferRetTypeError(loc, "low must be non-negative but got ", low);
5271
5272 // If the input has staticly known width, check it. Both and low must be
5273 // strictly less than width.
5274 int32_t width = inputi.getWidthOrSentinel();
5275 if (width != -1 && high >= width)
5276 return emitInferRetTypeError(
5277 loc,
5278 "high must be smaller than the width of input, but got high = ", high,
5279 ", width = ", width);
5280
5281 return UIntType::get(input.getContext(), high - low + 1, inputi.isConst());
5282}
5283
5284FIRRTLType HeadPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5285 std::optional<Location> loc) {
5286
5287 auto inputi = type_dyn_cast<IntType>(input);
5288 if (amount < 0 || !inputi)
5289 return emitInferRetTypeError(
5290 loc, "operand must have integer type and amount must be >= 0");
5291
5292 int32_t width = inputi.getWidthOrSentinel();
5293 if (width != -1 && amount > width)
5294 return emitInferRetTypeError(loc, "amount larger than input width");
5295
5296 return UIntType::get(input.getContext(), amount, inputi.isConst());
5297}
5298
5299/// Infer the result type for a multiplexer given its two operand types, which
5300/// may be aggregates.
5301///
5302/// This essentially performs a pairwise comparison of fields and elements, as
5303/// follows:
5304/// - Identical operands inferred to their common type
5305/// - Integer operands inferred to the larger one if both have a known width, a
5306/// widthless integer otherwise.
5307/// - Vectors inferred based on the element type.
5308/// - Bundles inferred in a pairwise fashion based on the field types.
5310 FIRRTLBaseType low,
5311 bool isConstCondition,
5312 std::optional<Location> loc) {
5313 // If the types are identical we're done.
5314 if (high == low)
5315 return isConstCondition ? low : low.getAllConstDroppedType();
5316
5317 // The base types need to be equivalent.
5318 if (high.getTypeID() != low.getTypeID())
5319 return emitInferRetTypeError<FIRRTLBaseType>(
5320 loc, "incompatible mux operand types, true value type: ", high,
5321 ", false value type: ", low);
5322
5323 bool outerTypeIsConst = isConstCondition && low.isConst() && high.isConst();
5324
5325 // Two different Int types can be compatible. If either has unknown width,
5326 // then return it. If both are known but different width, then return the
5327 // larger one.
5328 if (type_isa<IntType>(low)) {
5329 int32_t highWidth = high.getBitWidthOrSentinel();
5330 int32_t lowWidth = low.getBitWidthOrSentinel();
5331 if (lowWidth == -1)
5332 return low.getConstType(outerTypeIsConst);
5333 if (highWidth == -1)
5334 return high.getConstType(outerTypeIsConst);
5335 return (lowWidth > highWidth ? low : high).getConstType(outerTypeIsConst);
5336 }
5337
5338 // Infer vector types by comparing the element types.
5339 auto highVector = type_dyn_cast<FVectorType>(high);
5340 auto lowVector = type_dyn_cast<FVectorType>(low);
5341 if (highVector && lowVector &&
5342 highVector.getNumElements() == lowVector.getNumElements()) {
5343 auto inner = inferMuxReturnType(highVector.getElementTypePreservingConst(),
5344 lowVector.getElementTypePreservingConst(),
5345 isConstCondition, loc);
5346 if (!inner)
5347 return {};
5348 return FVectorType::get(inner, lowVector.getNumElements(),
5349 outerTypeIsConst);
5350 }
5351
5352 // Infer bundle types by inferring names in a pairwise fashion.
5353 auto highBundle = type_dyn_cast<BundleType>(high);
5354 auto lowBundle = type_dyn_cast<BundleType>(low);
5355 if (highBundle && lowBundle) {
5356 auto highElements = highBundle.getElements();
5357 auto lowElements = lowBundle.getElements();
5358 size_t numElements = highElements.size();
5359
5360 SmallVector<BundleType::BundleElement> newElements;
5361 if (numElements == lowElements.size()) {
5362 bool failed = false;
5363 for (size_t i = 0; i < numElements; ++i) {
5364 if (highElements[i].name != lowElements[i].name ||
5365 highElements[i].isFlip != lowElements[i].isFlip) {
5366 failed = true;
5367 break;
5368 }
5369 auto element = highElements[i];
5370 element.type = inferMuxReturnType(
5371 highBundle.getElementTypePreservingConst(i),
5372 lowBundle.getElementTypePreservingConst(i), isConstCondition, loc);
5373 if (!element.type)
5374 return {};
5375 newElements.push_back(element);
5376 }
5377 if (!failed)
5378 return BundleType::get(low.getContext(), newElements, outerTypeIsConst);
5379 }
5380 return emitInferRetTypeError<FIRRTLBaseType>(
5381 loc, "incompatible mux operand bundle fields, true value type: ", high,
5382 ", false value type: ", low);
5383 }
5384
5385 // If we arrive here the types of the two mux arms are fundamentally
5386 // incompatible.
5387 return emitInferRetTypeError<FIRRTLBaseType>(
5388 loc, "invalid mux operand types, true value type: ", high,
5389 ", false value type: ", low);
5390}
5391
5392FIRRTLType MuxPrimOp::inferReturnType(FIRRTLType sel, FIRRTLType high,
5393 FIRRTLType low,
5394 std::optional<Location> loc) {
5395 auto highType = type_dyn_cast<FIRRTLBaseType>(high);
5396 auto lowType = type_dyn_cast<FIRRTLBaseType>(low);
5397 if (!highType || !lowType)
5398 return emitInferRetTypeError(loc, "operands must be base type");
5399 return inferMuxReturnType(highType, lowType, isConst(sel), loc);
5400}
5401
5402FIRRTLType Mux2CellIntrinsicOp::inferReturnType(ValueRange operands,
5403 DictionaryAttr attrs,
5404 OpaqueProperties properties,
5405 mlir::RegionRange regions,
5406 std::optional<Location> loc) {
5407 auto highType = type_dyn_cast<FIRRTLBaseType>(operands[1].getType());
5408 auto lowType = type_dyn_cast<FIRRTLBaseType>(operands[2].getType());
5409 if (!highType || !lowType)
5410 return emitInferRetTypeError(loc, "operands must be base type");
5411 return inferMuxReturnType(highType, lowType, isConst(operands[0].getType()),
5412 loc);
5413}
5414
5415FIRRTLType Mux4CellIntrinsicOp::inferReturnType(ValueRange operands,
5416 DictionaryAttr attrs,
5417 OpaqueProperties properties,
5418 mlir::RegionRange regions,
5419 std::optional<Location> loc) {
5420 SmallVector<FIRRTLBaseType> types;
5421 FIRRTLBaseType result;
5422 for (unsigned i = 1; i < 5; i++) {
5423 types.push_back(type_dyn_cast<FIRRTLBaseType>(operands[i].getType()));
5424 if (!types.back())
5425 return emitInferRetTypeError(loc, "operands must be base type");
5426 if (result) {
5427 result = inferMuxReturnType(result, types.back(),
5428 isConst(operands[0].getType()), loc);
5429 if (!result)
5430 return result;
5431 } else {
5432 result = types.back();
5433 }
5434 }
5435 return result;
5436}
5437
5438FIRRTLType PadPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5439 std::optional<Location> loc) {
5440 auto inputi = type_dyn_cast<IntType>(input);
5441 if (amount < 0 || !inputi)
5442 return emitInferRetTypeError(
5443 loc, "pad input must be integer and amount must be >= 0");
5444
5445 int32_t width = inputi.getWidthOrSentinel();
5446 if (width == -1)
5447 return inputi;
5448
5449 width = std::max<int32_t>(width, amount);
5450 return IntType::get(input.getContext(), inputi.isSigned(), width,
5451 inputi.isConst());
5452}
5453
5454FIRRTLType ShlPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5455 std::optional<Location> loc) {
5456 auto inputi = type_dyn_cast<IntType>(input);
5457 if (amount < 0 || !inputi)
5458 return emitInferRetTypeError(
5459 loc, "shl input must be integer and amount must be >= 0");
5460
5461 int32_t width = inputi.getWidthOrSentinel();
5462 if (width != -1)
5463 width += amount;
5464
5465 return IntType::get(input.getContext(), inputi.isSigned(), width,
5466 inputi.isConst());
5467}
5468
5469FIRRTLType ShrPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5470 std::optional<Location> loc) {
5471 auto inputi = type_dyn_cast<IntType>(input);
5472 if (amount < 0 || !inputi)
5473 return emitInferRetTypeError(
5474 loc, "shr input must be integer and amount must be >= 0");
5475
5476 int32_t width = inputi.getWidthOrSentinel();
5477 if (width != -1) {
5478 // UInt saturates at 0 bits, SInt at 1 bit
5479 int32_t minWidth = inputi.isUnsigned() ? 0 : 1;
5480 width = std::max<int32_t>(minWidth, width - amount);
5481 }
5482
5483 return IntType::get(input.getContext(), inputi.isSigned(), width,
5484 inputi.isConst());
5485}
5486
5487FIRRTLType TailPrimOp::inferReturnType(FIRRTLType input, int64_t amount,
5488 std::optional<Location> loc) {
5489
5490 auto inputi = type_dyn_cast<IntType>(input);
5491 if (amount < 0 || !inputi)
5492 return emitInferRetTypeError(
5493 loc, "tail input must be integer and amount must be >= 0");
5494
5495 int32_t width = inputi.getWidthOrSentinel();
5496 if (width != -1) {
5497 if (width < amount)
5498 return emitInferRetTypeError(
5499 loc, "amount must be less than or equal operand width");
5500 width -= amount;
5501 }
5502
5503 return IntType::get(input.getContext(), false, width, inputi.isConst());
5504}
5505
5506//===----------------------------------------------------------------------===//
5507// VerbatimExprOp
5508//===----------------------------------------------------------------------===//
5509
5510void VerbatimExprOp::getAsmResultNames(
5511 function_ref<void(Value, StringRef)> setNameFn) {
5512 // If the text is macro like, then use a pretty name. We only take the
5513 // text up to a weird character (like a paren) and currently ignore
5514 // parenthesized expressions.
5515 auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5516 auto name = getText();
5517 // Ignore a leading ` in macro name.
5518 if (name.starts_with("`"))
5519 name = name.drop_front();
5520 name = name.take_while(isOkCharacter);
5521 if (!name.empty())
5522 setNameFn(getResult(), name);
5523}
5524
5525//===----------------------------------------------------------------------===//
5526// VerbatimWireOp
5527//===----------------------------------------------------------------------===//
5528
5529void VerbatimWireOp::getAsmResultNames(
5530 function_ref<void(Value, StringRef)> setNameFn) {
5531 // If the text is macro like, then use a pretty name. We only take the
5532 // text up to a weird character (like a paren) and currently ignore
5533 // parenthesized expressions.
5534 auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
5535 auto name = getText();
5536 // Ignore a leading ` in macro name.
5537 if (name.starts_with("`"))
5538 name = name.drop_front();
5539 name = name.take_while(isOkCharacter);
5540 if (!name.empty())
5541 setNameFn(getResult(), name);
5542}
5543
5544//===----------------------------------------------------------------------===//
5545// DPICallIntrinsicOp
5546//===----------------------------------------------------------------------===//
5547
5548static bool isTypeAllowedForDPI(Operation *op, Type type) {
5549 return !type.walk([&](firrtl::IntType intType) -> mlir::WalkResult {
5550 auto width = intType.getWidth();
5551 if (width < 0) {
5552 op->emitError() << "unknown width is not allowed for DPI";
5553 return WalkResult::interrupt();
5554 }
5555 if (width == 1 || width == 8 || width == 16 || width == 32 ||
5556 width >= 64)
5557 return WalkResult::advance();
5558 op->emitError()
5559 << "integer types used by DPI functions must have a "
5560 "specific bit width; "
5561 "it must be equal to 1(bit), 8(byte), 16(shortint), "
5562 "32(int), 64(longint) "
5563 "or greater than 64, but got "
5564 << intType;
5565 return WalkResult::interrupt();
5566 })
5567 .wasInterrupted();
5568}
5569
5570LogicalResult DPICallIntrinsicOp::verify() {
5571 if (auto inputNames = getInputNames()) {
5572 if (getInputs().size() != inputNames->size())
5573 return emitError() << "inputNames has " << inputNames->size()
5574 << " elements but there are " << getInputs().size()
5575 << " input arguments";
5576 }
5577 if (auto outputName = getOutputName())
5578 if (getNumResults() == 0)
5579 return emitError() << "output name is given but there is no result";
5580
5581 auto checkType = [this](Type type) {
5582 return isTypeAllowedForDPI(*this, type);
5583 };
5584 return success(llvm::all_of(this->getResultTypes(), checkType) &&
5585 llvm::all_of(this->getOperandTypes(), checkType));
5586}
5587
5588SmallVector<std::pair<circt::FieldRef, circt::FieldRef>>
5589DPICallIntrinsicOp::computeDataFlow() {
5590 if (getClock())
5591 return {};
5592
5593 SmallVector<std::pair<circt::FieldRef, circt::FieldRef>> deps;
5594
5595 for (auto operand : getOperands()) {
5596 auto type = type_cast<FIRRTLBaseType>(operand.getType());
5597 auto baseFieldRef = getFieldRefFromValue(operand);
5598 SmallVector<circt::FieldRef> operandFields;
5600 type, [&](uint64_t dstIndex, FIRRTLBaseType t, bool dstIsFlip) {
5601 operandFields.push_back(baseFieldRef.getSubField(dstIndex));
5602 });
5603
5604 // Record operand -> result dependency.
5605 for (auto result : getResults())
5607 type, [&](uint64_t dstIndex, FIRRTLBaseType t, bool dstIsFlip) {
5608 for (auto field : operandFields)
5609 deps.emplace_back(circt::FieldRef(result, dstIndex), field);
5610 });
5611 }
5612 return deps;
5613}
5614
5615//===----------------------------------------------------------------------===//
5616// Conversions to/from structs in the standard dialect.
5617//===----------------------------------------------------------------------===//
5618
5619LogicalResult HWStructCastOp::verify() {
5620 // We must have a bundle and a struct, with matching pairwise fields
5621 BundleType bundleType;
5622 hw::StructType structType;
5623 if ((bundleType = type_dyn_cast<BundleType>(getOperand().getType()))) {
5624 structType = dyn_cast<hw::StructType>(getType());
5625 if (!structType)
5626 return emitError("result type must be a struct");
5627 } else if ((bundleType = type_dyn_cast<BundleType>(getType()))) {
5628 structType = dyn_cast<hw::StructType>(getOperand().getType());
5629 if (!structType)
5630 return emitError("operand type must be a struct");
5631 } else {
5632 return emitError("either source or result type must be a bundle type");
5633 }
5634
5635 auto firFields = bundleType.getElements();
5636 auto hwFields = structType.getElements();
5637 if (firFields.size() != hwFields.size())
5638 return emitError("bundle and struct have different number of fields");
5639
5640 for (size_t findex = 0, fend = firFields.size(); findex < fend; ++findex) {
5641 if (firFields[findex].name.getValue() != hwFields[findex].name)
5642 return emitError("field names don't match '")
5643 << firFields[findex].name.getValue() << "', '"
5644 << hwFields[findex].name.getValue() << "'";
5645 int64_t firWidth =
5646 FIRRTLBaseType(firFields[findex].type).getBitWidthOrSentinel();
5647 int64_t hwWidth = hw::getBitWidth(hwFields[findex].type);
5648 if (firWidth > 0 && hwWidth > 0 && firWidth != hwWidth)
5649 return emitError("size of field '")
5650 << hwFields[findex].name.getValue() << "' don't match " << firWidth
5651 << ", " << hwWidth;
5652 }
5653
5654 return success();
5655}
5656
5657LogicalResult BitCastOp::verify() {
5658 auto inTypeBits = getBitWidth(getInput().getType(), /*ignoreFlip=*/true);
5659 auto resTypeBits = getBitWidth(getType());
5660 if (inTypeBits.has_value() && resTypeBits.has_value()) {
5661 // Bitwidths must match for valid bit
5662 if (*inTypeBits == *resTypeBits) {
5663 // non-'const' cannot be casted to 'const'
5664 if (containsConst(getType()) && !isConst(getOperand().getType()))
5665 return emitError("cannot cast non-'const' input type ")
5666 << getOperand().getType() << " to 'const' result type "
5667 << getType();
5668 return success();
5669 }
5670 return emitError("the bitwidth of input (")
5671 << *inTypeBits << ") and result (" << *resTypeBits
5672 << ") don't match";
5673 }
5674 if (!inTypeBits.has_value())
5675 return emitError("bitwidth cannot be determined for input operand type ")
5676 << getInput().getType();
5677 return emitError("bitwidth cannot be determined for result type ")
5678 << getType();
5679}
5680
5681//===----------------------------------------------------------------------===//
5682// Custom attr-dict Directive that Elides Annotations
5683//===----------------------------------------------------------------------===//
5684
5685/// Parse an optional attribute dictionary, adding an empty 'annotations'
5686/// attribute if not specified.
5687static ParseResult parseElideAnnotations(OpAsmParser &parser,
5688 NamedAttrList &resultAttrs) {
5689 auto result = parser.parseOptionalAttrDict(resultAttrs);
5690 if (!resultAttrs.get("annotations"))
5691 resultAttrs.append("annotations", parser.getBuilder().getArrayAttr({}));
5692
5693 return result;
5694}
5695
5696static void printElideAnnotations(OpAsmPrinter &p, Operation *op,
5697 DictionaryAttr attr,
5698 ArrayRef<StringRef> extraElides = {}) {
5699 SmallVector<StringRef> elidedAttrs(extraElides.begin(), extraElides.end());
5700 // Elide "annotations" if it is empty.
5701 if (op->getAttrOfType<ArrayAttr>("annotations").empty())
5702 elidedAttrs.push_back("annotations");
5703 // Elide "nameKind".
5704 elidedAttrs.push_back("nameKind");
5705
5706 p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
5707}
5708
5709/// Parse an optional attribute dictionary, adding empty 'annotations' and
5710/// 'portAnnotations' attributes if not specified.
5711static ParseResult parseElidePortAnnotations(OpAsmParser &parser,
5712 NamedAttrList &resultAttrs) {
5713 auto result = parseElideAnnotations(parser, resultAttrs);
5714
5715 if (!resultAttrs.get("portAnnotations")) {
5716 SmallVector<Attribute, 16> portAnnotations(
5717 parser.getNumResults(), parser.getBuilder().getArrayAttr({}));
5718 resultAttrs.append("portAnnotations",
5719 parser.getBuilder().getArrayAttr(portAnnotations));
5720 }
5721 return result;
5722}
5723
5724// Elide 'annotations' and 'portAnnotations' attributes if they are empty.
5725static void printElidePortAnnotations(OpAsmPrinter &p, Operation *op,
5726 DictionaryAttr attr,
5727 ArrayRef<StringRef> extraElides = {}) {
5728 SmallVector<StringRef, 2> elidedAttrs(extraElides.begin(), extraElides.end());
5729
5730 if (llvm::all_of(op->getAttrOfType<ArrayAttr>("portAnnotations"),
5731 [&](Attribute a) { return cast<ArrayAttr>(a).empty(); }))
5732 elidedAttrs.push_back("portAnnotations");
5733 printElideAnnotations(p, op, attr, elidedAttrs);
5734}
5735
5736//===----------------------------------------------------------------------===//
5737// NameKind Custom Directive
5738//===----------------------------------------------------------------------===//
5739
5740static ParseResult parseNameKind(OpAsmParser &parser,
5741 firrtl::NameKindEnumAttr &result) {
5742 StringRef keyword;
5743
5744 if (!parser.parseOptionalKeyword(&keyword,
5745 {"interesting_name", "droppable_name"})) {
5746 auto kind = symbolizeNameKindEnum(keyword);
5747 result = NameKindEnumAttr::get(parser.getContext(), kind.value());
5748 return success();
5749 }
5750
5751 // Default is droppable name.
5752 result =
5753 NameKindEnumAttr::get(parser.getContext(), NameKindEnum::DroppableName);
5754 return success();
5755}
5756
5757static void printNameKind(OpAsmPrinter &p, Operation *op,
5758 firrtl::NameKindEnumAttr attr,
5759 ArrayRef<StringRef> extraElides = {}) {
5760 if (attr.getValue() != NameKindEnum::DroppableName)
5761 p << " " << stringifyNameKindEnum(attr.getValue());
5762}
5763
5764//===----------------------------------------------------------------------===//
5765// ImplicitSSAName Custom Directive
5766//===----------------------------------------------------------------------===//
5767
5768static ParseResult parseFIRRTLImplicitSSAName(OpAsmParser &parser,
5769 NamedAttrList &resultAttrs) {
5770 if (parseElideAnnotations(parser, resultAttrs))
5771 return failure();
5772 inferImplicitSSAName(parser, resultAttrs);
5773 return success();
5774}
5775
5776static void printFIRRTLImplicitSSAName(OpAsmPrinter &p, Operation *op,
5777 DictionaryAttr attrs) {
5778 SmallVector<StringRef, 4> elides;
5780 elides.push_back(Forceable::getForceableAttrName());
5781 elideImplicitSSAName(p, op, attrs, elides);
5782 printElideAnnotations(p, op, attrs, elides);
5783}
5784
5785//===----------------------------------------------------------------------===//
5786// MemOp Custom attr-dict Directive
5787//===----------------------------------------------------------------------===//
5788
5789static ParseResult parseMemOp(OpAsmParser &parser, NamedAttrList &resultAttrs) {
5790 return parseElidePortAnnotations(parser, resultAttrs);
5791}
5792
5793/// Always elide "ruw" and elide "annotations" if it exists or if it is empty.
5794static void printMemOp(OpAsmPrinter &p, Operation *op, DictionaryAttr attr) {
5795 // "ruw" and "inner_sym" is always elided.
5796 printElidePortAnnotations(p, op, attr, {"ruw", "inner_sym"});
5797}
5798
5799//===----------------------------------------------------------------------===//
5800// ClassInterface custom directive
5801//===----------------------------------------------------------------------===//
5802
5803static ParseResult parseClassInterface(OpAsmParser &parser, Type &result) {
5804 ClassType type;
5805 if (ClassType::parseInterface(parser, type))
5806 return failure();
5807 result = type;
5808 return success();
5809}
5810
5811static void printClassInterface(OpAsmPrinter &p, Operation *, ClassType type) {
5812 type.printInterface(p);
5813}
5814
5815//===----------------------------------------------------------------------===//
5816// Miscellaneous custom elision logic.
5817//===----------------------------------------------------------------------===//
5818
5819static ParseResult parseElideEmptyName(OpAsmParser &p,
5820 NamedAttrList &resultAttrs) {
5821 auto result = p.parseOptionalAttrDict(resultAttrs);
5822 if (!resultAttrs.get("name"))
5823 resultAttrs.append("name", p.getBuilder().getStringAttr(""));
5824
5825 return result;
5826}
5827
5828static void printElideEmptyName(OpAsmPrinter &p, Operation *op,
5829 DictionaryAttr attr,
5830 ArrayRef<StringRef> extraElides = {}) {
5831 SmallVector<StringRef> elides(extraElides.begin(), extraElides.end());
5832 if (op->getAttrOfType<StringAttr>("name").getValue().empty())
5833 elides.push_back("name");
5834
5835 p.printOptionalAttrDict(op->getAttrs(), elides);
5836}
5837
5838static ParseResult parsePrintfAttrs(OpAsmParser &p,
5839 NamedAttrList &resultAttrs) {
5840 return parseElideEmptyName(p, resultAttrs);
5841}
5842
5843static void printPrintfAttrs(OpAsmPrinter &p, Operation *op,
5844 DictionaryAttr attr) {
5845 printElideEmptyName(p, op, attr, {"formatString"});
5846}
5847
5848static ParseResult parseFPrintfAttrs(OpAsmParser &p,
5849 NamedAttrList &resultAttrs) {
5850 return parseElideEmptyName(p, resultAttrs);
5851}
5852
5853static void printFPrintfAttrs(OpAsmPrinter &p, Operation *op,
5854 DictionaryAttr attr) {
5855 printElideEmptyName(p, op, attr, {"formatString", "outputFile"});
5856}
5857
5858static ParseResult parseStopAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
5859 return parseElideEmptyName(p, resultAttrs);
5860}
5861
5862static void printStopAttrs(OpAsmPrinter &p, Operation *op,
5863 DictionaryAttr attr) {
5864 printElideEmptyName(p, op, attr, {"exitCode"});
5865}
5866
5867static ParseResult parseVerifAttrs(OpAsmParser &p, NamedAttrList &resultAttrs) {
5868 return parseElideEmptyName(p, resultAttrs);
5869}
5870
5871static void printVerifAttrs(OpAsmPrinter &p, Operation *op,
5872 DictionaryAttr attr) {
5873 printElideEmptyName(p, op, attr, {"message"});
5874}
5875
5876//===----------------------------------------------------------------------===//
5877// Various namers.
5878//===----------------------------------------------------------------------===//
5879
5880static void genericAsmResultNames(Operation *op,
5881 OpAsmSetValueNameFn setNameFn) {
5882 // Many firrtl dialect operations have an optional 'name' attribute. If
5883 // present, use it.
5884 if (op->getNumResults() == 1)
5885 if (auto nameAttr = op->getAttrOfType<StringAttr>("name"))
5886 setNameFn(op->getResult(0), nameAttr.getValue());
5887}
5888
5889void AddPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5890 genericAsmResultNames(*this, setNameFn);
5891}
5892
5893void AndPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5894 genericAsmResultNames(*this, setNameFn);
5895}
5896
5897void AndRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5898 genericAsmResultNames(*this, setNameFn);
5899}
5900
5901void SizeOfIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5902 genericAsmResultNames(*this, setNameFn);
5903}
5904void AsAsyncResetPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5905 genericAsmResultNames(*this, setNameFn);
5906}
5907void AsClockPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5908 genericAsmResultNames(*this, setNameFn);
5909}
5910void AsSIntPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5911 genericAsmResultNames(*this, setNameFn);
5912}
5913void AsUIntPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5914 genericAsmResultNames(*this, setNameFn);
5915}
5916void BitsPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5917 genericAsmResultNames(*this, setNameFn);
5918}
5919void CatPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5920 genericAsmResultNames(*this, setNameFn);
5921}
5922void CvtPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5923 genericAsmResultNames(*this, setNameFn);
5924}
5925void DShlPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5926 genericAsmResultNames(*this, setNameFn);
5927}
5928void DShlwPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5929 genericAsmResultNames(*this, setNameFn);
5930}
5931void DShrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5932 genericAsmResultNames(*this, setNameFn);
5933}
5934void DivPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5935 genericAsmResultNames(*this, setNameFn);
5936}
5937void EQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5938 genericAsmResultNames(*this, setNameFn);
5939}
5940void GEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5941 genericAsmResultNames(*this, setNameFn);
5942}
5943void GTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5944 genericAsmResultNames(*this, setNameFn);
5945}
5946void GenericIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5947 genericAsmResultNames(*this, setNameFn);
5948}
5949void HeadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5950 genericAsmResultNames(*this, setNameFn);
5951}
5952void IntegerAddOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5953 genericAsmResultNames(*this, setNameFn);
5954}
5955void IntegerMulOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5956 genericAsmResultNames(*this, setNameFn);
5957}
5958void IntegerShrOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5959 genericAsmResultNames(*this, setNameFn);
5960}
5961void IntegerShlOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5962 genericAsmResultNames(*this, setNameFn);
5963}
5964void IsTagOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5965 genericAsmResultNames(*this, setNameFn);
5966}
5967void IsXIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5968 genericAsmResultNames(*this, setNameFn);
5969}
5970void PlusArgsValueIntrinsicOp::getAsmResultNames(
5971 OpAsmSetValueNameFn setNameFn) {
5972 genericAsmResultNames(*this, setNameFn);
5973}
5974void PlusArgsTestIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5975 genericAsmResultNames(*this, setNameFn);
5976}
5977void LEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5978 genericAsmResultNames(*this, setNameFn);
5979}
5980void LTPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5981 genericAsmResultNames(*this, setNameFn);
5982}
5983void MulPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5984 genericAsmResultNames(*this, setNameFn);
5985}
5986void MultibitMuxOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5987 genericAsmResultNames(*this, setNameFn);
5988}
5989void MuxPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5990 genericAsmResultNames(*this, setNameFn);
5991}
5992void Mux4CellIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5993 genericAsmResultNames(*this, setNameFn);
5994}
5995void Mux2CellIntrinsicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5996 genericAsmResultNames(*this, setNameFn);
5997}
5998void NEQPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
5999 genericAsmResultNames(*this, setNameFn);
6000}
6001void NegPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6002 genericAsmResultNames(*this, setNameFn);
6003}
6004void NotPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6005 genericAsmResultNames(*this, setNameFn);
6006}
6007void OrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6008 genericAsmResultNames(*this, setNameFn);
6009}
6010void OrRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6011 genericAsmResultNames(*this, setNameFn);
6012}
6013void PadPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6014 genericAsmResultNames(*this, setNameFn);
6015}
6016void RemPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6017 genericAsmResultNames(*this, setNameFn);
6018}
6019void ShlPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6020 genericAsmResultNames(*this, setNameFn);
6021}
6022void ShrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6023 genericAsmResultNames(*this, setNameFn);
6024}
6025
6026void SubPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6027 genericAsmResultNames(*this, setNameFn);
6028}
6029
6030void SubaccessOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6031 genericAsmResultNames(*this, setNameFn);
6032}
6033
6034void SubfieldOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6035 genericAsmResultNames(*this, setNameFn);
6036}
6037
6038void OpenSubfieldOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6039 genericAsmResultNames(*this, setNameFn);
6040}
6041
6042void SubtagOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6043 genericAsmResultNames(*this, setNameFn);
6044}
6045
6046void SubindexOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6047 genericAsmResultNames(*this, setNameFn);
6048}
6049
6050void OpenSubindexOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6051 genericAsmResultNames(*this, setNameFn);
6052}
6053
6054void TagExtractOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6055 genericAsmResultNames(*this, setNameFn);
6056}
6057
6058void TailPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6059 genericAsmResultNames(*this, setNameFn);
6060}
6061
6062void XorPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6063 genericAsmResultNames(*this, setNameFn);
6064}
6065
6066void XorRPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6067 genericAsmResultNames(*this, setNameFn);
6068}
6069
6070void UninferredResetCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6071 genericAsmResultNames(*this, setNameFn);
6072}
6073
6074void ConstCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6075 genericAsmResultNames(*this, setNameFn);
6076}
6077
6078void ElementwiseXorPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6079 genericAsmResultNames(*this, setNameFn);
6080}
6081
6082void ElementwiseOrPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6083 genericAsmResultNames(*this, setNameFn);
6084}
6085
6086void ElementwiseAndPrimOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6087 genericAsmResultNames(*this, setNameFn);
6088}
6089
6090//===----------------------------------------------------------------------===//
6091// RefOps
6092//===----------------------------------------------------------------------===//
6093
6094void RefCastOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6095 genericAsmResultNames(*this, setNameFn);
6096}
6097
6098void RefResolveOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6099 genericAsmResultNames(*this, setNameFn);
6100}
6101
6102void RefSendOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6103 genericAsmResultNames(*this, setNameFn);
6104}
6105
6106void RefSubOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6107 genericAsmResultNames(*this, setNameFn);
6108}
6109
6110void RWProbeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6111 genericAsmResultNames(*this, setNameFn);
6112}
6113
6114FIRRTLType RefResolveOp::inferReturnType(ValueRange operands,
6115 DictionaryAttr attrs,
6116 OpaqueProperties properties,
6117 mlir::RegionRange regions,
6118 std::optional<Location> loc) {
6119 Type inType = operands[0].getType();
6120 auto inRefType = type_dyn_cast<RefType>(inType);
6121 if (!inRefType)
6122 return emitInferRetTypeError(
6123 loc, "ref.resolve operand must be ref type, not ", inType);
6124 return inRefType.getType();
6125}
6126
6127FIRRTLType RefSendOp::inferReturnType(ValueRange operands, DictionaryAttr attrs,
6128 OpaqueProperties properties,
6129 mlir::RegionRange regions,
6130 std::optional<Location> loc) {
6131 Type inType = operands[0].getType();
6132 auto inBaseType = type_dyn_cast<FIRRTLBaseType>(inType);
6133 if (!inBaseType)
6134 return emitInferRetTypeError(
6135 loc, "ref.send operand must be base type, not ", inType);
6136 return RefType::get(inBaseType.getPassiveType());
6137}
6138
6139FIRRTLType RefSubOp::inferReturnType(Type type, uint32_t fieldIndex,
6140 std::optional<Location> loc) {
6141 auto refType = type_dyn_cast<RefType>(type);
6142 if (!refType)
6143 return emitInferRetTypeError(loc, "input must be of reference type");
6144 auto inType = refType.getType();
6145
6146 // TODO: Determine ref.sub + rwprobe behavior, test.
6147 // Probably best to demote to non-rw, but that has implications
6148 // for any LowerTypes behavior being relied on.
6149 // Allow for now, as need to LowerTypes things generally.
6150 if (auto vectorType = type_dyn_cast<FVectorType>(inType)) {
6151 if (fieldIndex < vectorType.getNumElements())
6152 return RefType::get(
6153 vectorType.getElementType().getConstType(
6154 vectorType.isConst() || vectorType.getElementType().isConst()),
6155 refType.getForceable(), refType.getLayer());
6156 return emitInferRetTypeError(loc, "out of range index '", fieldIndex,
6157 "' in RefType of vector type ", refType);
6158 }
6159 if (auto bundleType = type_dyn_cast<BundleType>(inType)) {
6160 if (fieldIndex >= bundleType.getNumElements()) {
6161 return emitInferRetTypeError(loc,
6162 "subfield element index is greater than "
6163 "the number of fields in the bundle type");
6164 }
6165 return RefType::get(
6166 bundleType.getElement(fieldIndex)
6167 .type.getConstType(
6168 bundleType.isConst() ||
6169 bundleType.getElement(fieldIndex).type.isConst()),
6170 refType.getForceable(), refType.getLayer());
6171 }
6172
6173 return emitInferRetTypeError(
6174 loc, "ref.sub op requires a RefType of vector or bundle base type");
6175}
6176
6177LogicalResult RefCastOp::verify() {
6178 auto srcLayers = getLayersFor(getInput());
6179 auto dstLayers = getLayersFor(getResult());
6180 SmallVector<SymbolRefAttr> missingLayers;
6181 if (!isLayerSetCompatibleWith(srcLayers, dstLayers, missingLayers)) {
6182 auto diag =
6183 emitOpError("cannot discard layer requirements of input reference");
6184 auto &note = diag.attachNote();
6185 note << "discarding layer requirements: ";
6186 llvm::interleaveComma(missingLayers, note);
6187 return failure();
6188 }
6189 return success();
6190}
6191
6192LogicalResult RefResolveOp::verify() {
6193 auto srcLayers = getLayersFor(getRef());
6194 auto dstLayers = getAmbientLayersAt(getOperation());
6195 SmallVector<SymbolRefAttr> missingLayers;
6196 if (!isLayerSetCompatibleWith(srcLayers, dstLayers, missingLayers)) {
6197 auto diag =
6198 emitOpError("ambient layers are insufficient to resolve reference");
6199 auto &note = diag.attachNote();
6200 note << "missing layer requirements: ";
6201 interleaveComma(missingLayers, note);
6202 return failure();
6203 }
6204 return success();
6205}
6206
6207LogicalResult RWProbeOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
6208 auto targetRef = getTarget();
6209 if (targetRef.getModule() !=
6210 (*this)->getParentOfType<FModuleLike>().getModuleNameAttr())
6211 return emitOpError() << "has non-local target";
6212
6213 auto target = ns.lookup(targetRef);
6214 if (!target)
6215 return emitOpError() << "has target that cannot be resolved: " << targetRef;
6216
6217 auto checkFinalType = [&](auto type, Location loc) -> LogicalResult {
6218 // Determine final type.
6219 mlir::Type fType =
6220 hw::FieldIdImpl::getFinalTypeByFieldID(type, target.getField());
6221 // Check.
6222 auto baseType = type_dyn_cast<FIRRTLBaseType>(fType);
6223 if (!baseType || baseType.getPassiveType() != getType().getType()) {
6224 auto diag = emitOpError("has type mismatch: target resolves to ")
6225 << fType << " instead of expected " << getType().getType();
6226 diag.attachNote(loc) << "target resolves here";
6227 return diag;
6228 }
6229 return success();
6230 };
6231
6232 auto checkLayers = [&](Location loc) -> LogicalResult {
6233 auto dstLayers = getAmbientLayersAt(target.getOp());
6234 auto srcLayers = getLayersFor(getResult());
6235 SmallVector<SymbolRefAttr> missingLayers;
6236 if (!isLayerSetCompatibleWith(srcLayers, dstLayers, missingLayers)) {
6237 auto diag = emitOpError("target has insufficient layer requirements");
6238 auto &note = diag.attachNote(loc);
6239 note << "target is missing layer requirements: ";
6240 llvm::interleaveComma(missingLayers, note);
6241 return failure();
6242 }
6243 return success();
6244 };
6245 auto checks = [&](auto type, Location loc) {
6246 if (failed(checkLayers(loc)))
6247 return failure();
6248 return checkFinalType(type, loc);
6249 };
6250
6251 if (target.isPort()) {
6252 auto mod = cast<FModuleLike>(target.getOp());
6253 return checks(mod.getPortType(target.getPort()),
6254 mod.getPortLocation(target.getPort()));
6255 }
6256 hw::InnerSymbolOpInterface symOp =
6257 cast<hw::InnerSymbolOpInterface>(target.getOp());
6258 if (!symOp.getTargetResult())
6259 return emitOpError("has target that cannot be probed")
6260 .attachNote(symOp.getLoc())
6261 .append("target resolves here");
6262 auto *ancestor =
6263 symOp.getTargetResult().getParentBlock()->findAncestorOpInBlock(**this);
6264 if (!ancestor || !symOp->isBeforeInBlock(ancestor))
6265 return emitOpError("is not dominated by target")
6266 .attachNote(symOp.getLoc())
6267 .append("target here");
6268 return checks(symOp.getTargetResult().getType(), symOp.getLoc());
6269}
6270
6271//===----------------------------------------------------------------------===//
6272// Layer Block Operations
6273//===----------------------------------------------------------------------===//
6274
6275LogicalResult LayerBlockOp::verify() {
6276 auto layerName = getLayerName();
6277 auto *parentOp = (*this)->getParentOp();
6278
6279 // Get parent operation that isn't a when or match.
6280 while (isa<WhenOp, MatchOp>(parentOp))
6281 parentOp = parentOp->getParentOp();
6282
6283 // Verify the correctness of the symbol reference. Only verify that this
6284 // layer block makes sense in its parent module or layer block.
6285 auto nestedReferences = layerName.getNestedReferences();
6286 if (nestedReferences.empty()) {
6287 if (!isa<FModuleOp>(parentOp)) {
6288 auto diag = emitOpError() << "has an un-nested layer symbol, but does "
6289 "not have a 'firrtl.module' op as a parent";
6290 return diag.attachNote(parentOp->getLoc())
6291 << "illegal parent op defined here";
6292 }
6293 } else {
6294 auto parentLayerBlock = dyn_cast<LayerBlockOp>(parentOp);
6295 if (!parentLayerBlock) {
6296 auto diag = emitOpError()
6297 << "has a nested layer symbol, but does not have a '"
6298 << getOperationName() << "' op as a parent'";
6299 return diag.attachNote(parentOp->getLoc())
6300 << "illegal parent op defined here";
6301 }
6302 auto parentLayerBlockName = parentLayerBlock.getLayerName();
6303 if (parentLayerBlockName.getRootReference() !=
6304 layerName.getRootReference() ||
6305 parentLayerBlockName.getNestedReferences() !=
6306 layerName.getNestedReferences().drop_back()) {
6307 auto diag = emitOpError() << "is nested under an illegal layer block";
6308 return diag.attachNote(parentLayerBlock->getLoc())
6309 << "illegal parent layer block defined here";
6310 }
6311 }
6312
6313 // Verify the body of the region.
6314 FieldRefCache fieldRefCache;
6315 auto result = getBody(0)->walk<mlir::WalkOrder::PreOrder>(
6316 [&](Operation *op) -> WalkResult {
6317 // Skip nested layer blocks. Those will be verified separately.
6318 if (isa<LayerBlockOp>(op))
6319 return WalkResult::skip();
6320
6321 // Check all the operands of each op to make sure that only legal things
6322 // are captured.
6323 for (auto operand : op->getOperands()) {
6324 // Any value captured from the current layer block is fine.
6325 if (auto *definingOp = operand.getDefiningOp())
6326 if (getOperation()->isAncestor(definingOp))
6327 continue;
6328
6329 auto type = operand.getType();
6330
6331 // Capture of a non-base type, e.g., reference, is allowed.
6332 if (isa<PropertyType>(type)) {
6333 auto diag = emitOpError() << "captures a property operand";
6334 diag.attachNote(operand.getLoc()) << "operand is defined here";
6335 diag.attachNote(op->getLoc()) << "operand is used here";
6336 return WalkResult::interrupt();
6337 }
6338 }
6339
6340 // Ensure that the layer block does not drive any sinks outside.
6341 if (auto connect = dyn_cast<FConnectLike>(op)) {
6342 // ref.define is allowed to drive probes outside the layerblock.
6343 if (isa<RefDefineOp>(connect))
6344 return WalkResult::advance();
6345
6346 // Verify that connects only drive values declared in the layer block.
6347 // If we see a non-passive connect destination, then verify that the
6348 // source is in the same layer block so that the source is not driven.
6349 auto dest =
6350 fieldRefCache.getFieldRefFromValue(connect.getDest()).getValue();
6351 bool passive = true;
6352 if (auto type =
6353 type_dyn_cast<FIRRTLBaseType>(connect.getDest().getType()))
6354 passive = type.isPassive();
6355 // TODO: Improve this verifier. This is intentionally _not_ verifying
6356 // a non-passive ConnectLike because it is hugely annoying to do
6357 // so---it requires a full understanding of if the connect is driving
6358 // destination-to-source, source-to-destination, or bi-directionally
6359 // which requires deep inspection of the type. Eventually, the FIRRTL
6360 // pass pipeline will remove all flips (e.g., canonicalize connect to
6361 // matchingconnect) and this hole won't exist.
6362 if (!passive)
6363 return WalkResult::advance();
6364
6365 if (isAncestorOfValueOwner(getOperation(), dest))
6366 return WalkResult::advance();
6367
6368 auto diag =
6369 connect.emitOpError()
6370 << "connects to a destination which is defined outside its "
6371 "enclosing layer block";
6372 diag.attachNote(getLoc()) << "enclosing layer block is defined here";
6373 diag.attachNote(dest.getLoc()) << "destination is defined here";
6374 return WalkResult::interrupt();
6375 }
6376
6377 return WalkResult::advance();
6378 });
6379
6380 return failure(result.wasInterrupted());
6381}
6382
6383LogicalResult
6384LayerBlockOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
6385 auto layerOp =
6386 symbolTable.lookupNearestSymbolFrom<LayerOp>(*this, getLayerNameAttr());
6387 if (!layerOp) {
6388 return emitOpError("invalid symbol reference");
6389 }
6390
6391 return success();
6392}
6393
6394//===----------------------------------------------------------------------===//
6395// Format Sring operations
6396//===----------------------------------------------------------------------===//
6397
6398void TimeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
6399 setNameFn(getResult(), "time");
6400}
6401
6402void HierarchicalModuleNameOp::getAsmResultNames(
6403 OpAsmSetValueNameFn setNameFn) {
6404 setNameFn(getResult(), "hierarchicalmodulename");
6405}
6406
6407//===----------------------------------------------------------------------===//
6408// BindOp
6409//===----------------------------------------------------------------------===//
6410
6411LogicalResult BindOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
6412 auto ref = getInstanceAttr();
6413 auto target = ns.lookup(ref);
6414 if (!target)
6415 return emitError() << "target " << ref << " cannot be resolved";
6416
6417 if (!target.isOpOnly())
6418 return emitError() << "target " << ref << " is not an operation";
6419
6420 auto instance = dyn_cast<InstanceOp>(target.getOp());
6421 if (!instance)
6422 return emitError() << "target " << ref << " is not an instance";
6423
6424 if (!instance.getDoNotPrint())
6425 return emitError() << "target " << ref << " is not marked doNotPrint";
6426
6427 return success();
6428}
6429
6430//===----------------------------------------------------------------------===//
6431// TblGen Generated Logic.
6432//===----------------------------------------------------------------------===//
6433
6434// Provide the autogenerated implementation guts for the Op classes.
6435#define GET_OP_CLASSES
6436#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 void printFPrintfAttrs(OpAsmPrinter &p, Operation *op, DictionaryAttr attr)
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 parseFPrintfAttrs(OpAsmParser &p, NamedAttrList &resultAttrs)
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 Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:212
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.