23 #include "mlir/IR/BuiltinOps.h"
24 #include "mlir/IR/IRMapping.h"
25 #include "mlir/IR/PatternMatch.h"
26 #include "mlir/IR/Threading.h"
27 #include "mlir/Pass/Pass.h"
28 #include "mlir/Support/LogicalResult.h"
29 #include "mlir/Transforms/DialectConversion.h"
30 #include "llvm/ADT/DepthFirstIterator.h"
31 #include "llvm/ADT/STLExtras.h"
32 #include "llvm/Support/raw_ostream.h"
36 #define GEN_PASS_DEF_LOWERCLASSES
37 #include "circt/Dialect/FIRRTL/Passes.h.inc"
42 using namespace circt;
49 auto moduleLike = node->
getModule<FModuleLike>();
53 if (isa<firrtl::ClassLike>(moduleLike.getOperation()))
57 if (moduleLike.isPublic())
61 bool hasClassPorts = llvm::any_of(moduleLike.getPorts(), [](
PortInfo port) {
62 return isa<PropertyType>(port.type);
70 for (
auto *instance : *node) {
71 if (
auto op = instance->getInstance<FInstanceLike>())
72 for (
auto result : op->getResults())
73 if (type_isa<PropertyType>(result.getType()))
84 PathInfo(Operation *op, FlatSymbolRefAttr symRef,
85 StringAttr altBasePathModule)
86 : op(op), symRef(symRef), altBasePathModule(altBasePathModule) {
87 assert(op &&
"op must not be null");
88 assert(symRef &&
"symRef must not be null");
91 operator bool()
const {
return op !=
nullptr; }
94 Operation *op =
nullptr;
97 FlatSymbolRefAttr symRef =
nullptr;
101 StringAttr altBasePathModule =
nullptr;
105 struct PathInfoTable {
108 void addAltBasePathRoot(StringAttr rootModuleName) {
109 altBasePathRoots.insert(rootModuleName);
114 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
115 StringAttr rootModuleName) {
116 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
117 rootSequence.push_back(rootModuleName);
121 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
122 getAltBasePathRoots()
const {
123 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
128 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
129 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
134 llvm::iterator_range<const StringAttr *>
135 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
136 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
137 assert(it != altBasePathsPassthroughs.end() &&
138 "expected passthrough module to already exist");
139 return llvm::make_range(it->second.begin(), it->second.end());
144 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
145 SmallVectorImpl<Value> &result)
const {
146 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
147 auto parent = instance->getParentOfType<om::ClassOp>();
150 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
151 if (parent.getName().starts_with(altBasePath)) {
153 result.push_back(instance->getBlock()->getArgument(0));
157 auto basePath = instance->getBlock()->getArgument(1 + i);
158 assert(isa<om::BasePathType>(basePath.getType()) &&
159 "expected a passthrough base path");
160 result.push_back(basePath);
166 DenseMap<DistinctAttr, PathInfo> table;
171 SmallPtrSet<StringAttr, 16> altBasePathRoots;
175 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
179 static constexpr StringRef kClassNameSuffix =
"_Class";
190 struct ClassLoweringState {
191 FModuleLike moduleLike;
192 std::vector<hw::HierPathOp> paths;
195 struct LoweringState {
196 PathInfoTable pathInfoTable;
197 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
200 struct LowerClassesPass
201 :
public circt::firrtl::impl::LowerClassesBase<LowerClassesPass> {
202 void runOnOperation()
override;
206 hw::InnerSymbolNamespaceCollection &namespaces,
208 SymbolTable &symbolTable);
211 bool shouldCreateClass(StringAttr modName);
214 om::ClassLike createClass(FModuleLike moduleLike,
215 const PathInfoTable &pathInfoTable);
218 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
219 const PathInfoTable &pathInfoTable);
220 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
221 const PathInfoTable &pathInfoTable);
222 void lowerClassExtern(ClassExternOp classExternOp, FModuleLike moduleLike);
225 LogicalResult updateInstances(Operation *op,
InstanceGraph &instanceGraph,
226 const LoweringState &state,
227 const PathInfoTable &pathInfoTable);
230 LogicalResult dialectConversion(
231 Operation *op,
const PathInfoTable &pathInfoTable,
232 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
235 DenseMap<StringAttr, bool> shouldCreateClassMemo;
243 hw::InnerSymbolNamespaceCollection &namespaces,
HierPathCache &cache,
244 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
245 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
247 PathTracker(FModuleLike module,
248 hw::InnerSymbolNamespaceCollection &namespaces,
249 InstanceGraph &instanceGraph,
const SymbolTable &symbolTable,
250 const DenseMap<DistinctAttr, FModuleOp> &owningModules)
251 : module(module), moduleNamespace(namespaces[module]),
252 namespaces(namespaces), instanceGraph(instanceGraph),
253 symbolTable(symbolTable), owningModules(owningModules) {}
256 struct PathInfoTableEntry {
259 StringAttr altBasePathModule;
265 LogicalResult runOnModule();
268 FailureOr<AnnotationSet> processPathTrackers(
const AnnoTarget &target);
270 LogicalResult updatePathInfoTable(PathInfoTable &pathInfoTable,
275 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
276 StringAttr moduleName,
277 FModuleOp owningModule);
281 hw::InnerSymbolNamespace &moduleNamespace;
282 hw::InnerSymbolNamespaceCollection &namespaces;
283 DenseMap<std::pair<StringAttr, FModuleOp>,
bool> needsAltBasePathCache;
287 const SymbolTable &symbolTable;
288 const DenseMap<DistinctAttr, FModuleOp> &owningModules;
291 SmallVector<PathInfoTableEntry> entries;
292 SetVector<StringAttr> altBasePathRoots;
298 PathTracker::run(CircuitOp circuit,
InstanceGraph &instanceGraph,
299 hw::InnerSymbolNamespaceCollection &namespaces,
301 const SymbolTable &symbolTable,
302 const DenseMap<DistinctAttr, FModuleOp> &owningModules) {
303 SmallVector<PathTracker> trackers;
307 for (
auto *node : instanceGraph)
308 if (
auto module = node->
getModule<FModuleLike>())
309 (void)namespaces.get(module);
311 for (
auto *node : instanceGraph)
312 if (
auto module = node->
getModule<FModuleLike>()) {
313 PathTracker tracker(module, namespaces, instanceGraph, symbolTable,
315 if (failed(tracker.runOnModule()))
317 if (failed(tracker.updatePathInfoTable(pathInfoTable, cache)))
324 LogicalResult PathTracker::runOnModule() {
325 auto processAndUpdateAnnoTarget = [&](
AnnoTarget target) -> LogicalResult {
326 auto anno = processPathTrackers(target);
329 target.setAnnotations(*anno);
334 if (failed(processAndUpdateAnnoTarget(
OpAnnoTarget(module))))
338 SmallVector<Attribute> portAnnotations;
339 portAnnotations.reserve(module.getNumPorts());
340 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
344 portAnnotations.push_back(annos->getArrayAttr());
347 module.setPortAnnotationsAttr(
351 auto result = module.walk([&](hw::InnerSymbolOpInterface op) {
352 if (failed(processAndUpdateAnnoTarget(
OpAnnoTarget(op))))
353 return WalkResult::interrupt();
354 return WalkResult::advance();
357 if (result.wasInterrupted())
365 PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
366 FModuleOp owningModule) {
368 auto it = needsAltBasePathCache.find({moduleName, owningModule});
369 if (it != needsAltBasePathCache.end())
371 bool needsAltBasePath =
false;
372 auto *node = instanceGraph.lookup(moduleName);
382 needsAltBasePath =
true;
391 auto diag = mlir::emitWarning(loc)
392 <<
"unable to uniquely resolve target due "
393 "to multiple instantiation";
394 for (
auto *use : node->
uses())
395 diag.attachNote(use->getInstance().getLoc()) <<
"instance here";
397 node = (*node->
usesBegin())->getParent();
399 needsAltBasePathCache[{moduleName, owningModule}] = needsAltBasePath;
400 return needsAltBasePath;
403 FailureOr<AnnotationSet>
404 PathTracker::processPathTrackers(
const AnnoTarget &target) {
407 auto *op = target.
getOp();
408 annotations.removeAnnotations([&](
Annotation anno) {
414 if (!anno.
isClass(
"circt.tracker"))
418 auto id = anno.
getMember<DistinctAttr>(
"id");
420 op->emitError(
"circt.tracker annotation missing id field");
430 if (
auto portTarget = dyn_cast<PortAnnoTarget>(target)) {
432 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), fieldID},
433 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
434 return moduleNamespace;
436 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
437 assert(!fieldID &&
"field not valid for modules");
442 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
443 return moduleNamespace;
448 SmallVector<Attribute> path;
451 path.push_back(targetSym);
453 auto moduleName = target.
getModule().getModuleNameAttr();
456 hw::HierPathOp hierPathOp;
457 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
459 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
461 op->emitError(
"annotation does not point at a HierPathOp");
469 auto owningModule = owningModules.lookup(
id);
471 PathInfoTableEntry entry;
475 entries.push_back(entry);
482 auto oldPath = hierPathOp.getNamepath().getValue();
487 bool pathContainsOwningModule =
false;
488 size_t owningModuleIndex = 0;
489 for (
auto [idx, pathFramgent] : llvm::enumerate(oldPath)) {
490 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(pathFramgent)) {
491 if (innerRef.getModule() == owningModule.getModuleNameAttr()) {
492 pathContainsOwningModule =
true;
493 owningModuleIndex = idx;
495 }
else if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(pathFramgent)) {
496 if (symRef.getAttr() == owningModule.getModuleNameAttr()) {
497 pathContainsOwningModule =
true;
498 owningModuleIndex = idx;
503 if (pathContainsOwningModule) {
505 moduleName = owningModule.getModuleNameAttr();
509 llvm::append_range(path, llvm::reverse(oldPath.drop_back().drop_front(
510 owningModuleIndex)));
513 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
516 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
521 auto needsAltBasePath =
522 getOrComputeNeedsAltBasePath(op->getLoc(), moduleName, owningModule);
523 if (failed(needsAltBasePath)) {
549 std::reverse(path.begin(), path.end());
554 StringAttr altBasePathModule;
555 if (*needsAltBasePath) {
557 TypeSwitch<Attribute, StringAttr>(path.front())
558 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
559 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
561 altBasePathRoots.insert(altBasePathModule);
565 entries.push_back({op, id, altBasePathModule, pathAttr});
577 LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
579 for (
auto root : altBasePathRoots)
580 pathInfoTable.addAltBasePathRoot(root);
582 for (
const auto &entry : entries) {
584 auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
585 auto &pathInfo = it->second;
588 emitError(pathInfo.op->getLoc(),
"duplicate identifier found");
589 diag.attachNote(entry.op->getLoc()) <<
"other identifier here";
594 pathInfo = {entry.op, cache.
getRefFor(entry.pathAttr),
595 entry.altBasePathModule};
597 pathInfo.op = entry.op;
608 LogicalResult LowerClassesPass::processPaths(
610 hw::InnerSymbolNamespaceCollection &namespaces,
HierPathCache &cache,
611 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
612 auto circuit = getOperation();
616 DenseMap<DistinctAttr, FModuleOp> owningModules;
617 std::vector<Operation *> declarations;
618 auto result = circuit.walk([&](Operation *op) {
619 if (
auto pathOp = dyn_cast<PathOp>(op)) {
621 auto owningModule = owningModuleCache.lookup(pathOp);
624 pathOp->emitError(
"path does not have a single owning module");
625 return WalkResult::interrupt();
627 auto target = pathOp.getTargetAttr();
628 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
631 if (!inserted && it->second != owningModule) {
633 <<
"path reference " << target <<
" has conflicting owning modules "
634 << it->second.getModuleNameAttr() <<
" and "
635 << owningModule.getModuleNameAttr();
636 return WalkResult::interrupt();
639 return WalkResult::advance();
642 if (result.wasInterrupted())
645 if (failed(PathTracker::run(circuit, instanceGraph, namespaces, cache,
646 pathInfoTable, symbolTable, owningModules)))
651 for (
auto rootModule : pathInfoTable.getAltBasePathRoots()) {
656 auto start = llvm::df_begin(node);
657 auto end = llvm::df_end(node);
667 if (!shouldCreateClass(
668 it->getModule<FModuleLike>().getModuleNameAttr())) {
669 it = it.skipChildren();
674 if (it->begin() == it->end()) {
680 StringAttr passthroughModule = it->getModule().getModuleNameAttr();
681 pathInfoTable.addAltBasePathPassthrough(passthroughModule, rootModule);
691 void LowerClassesPass::runOnOperation() {
692 MLIRContext *ctx = &getContext();
695 CircuitOp circuit = getOperation();
699 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
701 hw::InnerSymbolNamespaceCollection namespaces;
705 for (
auto *node : instanceGraph)
706 if (
auto moduleLike = node->
getModule<firrtl::FModuleLike>())
707 shouldCreateClassMemo.insert({moduleLike.getModuleNameAttr(),
false});
709 parallelForEach(circuit.getContext(), instanceGraph,
711 if (auto moduleLike = node->getModule<FModuleLike>())
712 shouldCreateClassMemo[moduleLike.getModuleNameAttr()] =
713 shouldCreateClassImpl(node);
717 PathInfoTable pathInfoTable;
718 if (failed(processPaths(instanceGraph, namespaces, cache, pathInfoTable,
727 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
728 for (
auto *node : instanceGraph) {
729 auto moduleLike = node->
getModule<firrtl::FModuleLike>();
733 if (shouldCreateClass(moduleLike.getModuleNameAttr())) {
734 auto omClass = createClass(moduleLike, pathInfoTable);
735 auto &classLoweringState =
loweringState.classLoweringStateTable[omClass];
736 classLoweringState.moduleLike = moduleLike;
743 for (
auto *instance : *node) {
744 auto inst = instance->
getInstance<firrtl::InstanceOp>();
748 auto module = instance->getTarget()->getModule<FModuleLike>();
749 if (module && shouldCreateClass(module.getModuleNameAttr())) {
751 {inst, 0}, [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
752 return namespaces[module];
754 SmallVector<Attribute> path = {targetSym};
756 auto hierPath = cache.
getOpFor(pathAttr);
757 classLoweringState.paths.push_back(hierPath);
762 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
763 classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
768 mlir::parallelForEach(ctx,
loweringState.classLoweringStateTable,
769 [
this, &pathInfoTable](
auto &entry) {
770 const auto &[classLike, state] = entry;
771 lowerClassLike(state.moduleLike, classLike,
776 for (
auto &[omClass, state] :
loweringState.classLoweringStateTable) {
777 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
779 for (
auto *use : llvm::make_early_inc_range(node->
uses()))
781 instanceGraph.erase(node);
782 state.moduleLike.erase();
787 SmallVector<Operation *> objectContainers;
788 for (
auto &op : circuit.getOps())
789 if (isa<FModuleOp, om::ClassLike>(op))
790 objectContainers.push_back(&op);
794 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
798 return signalPassFailure();
802 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
803 return dialectConversion(op, pathInfoTable, classTypeTable);
805 return signalPassFailure();
808 markAnalysesPreserved<InstanceGraph>();
812 return std::make_unique<LowerClassesPass>();
816 bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
818 return shouldCreateClassMemo.at(modName);
823 LowerClassesPass::createClass(FModuleLike moduleLike,
824 const PathInfoTable &pathInfoTable) {
826 SmallVector<StringRef> formalParamNames;
828 formalParamNames.emplace_back(
"basepath");
831 size_t nAltBasePaths =
832 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
833 for (
size_t i = 0; i < nAltBasePaths; ++i)
835 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
837 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts()))
838 if (port.isInput() && isa<PropertyType>(port.type))
839 formalParamNames.push_back(port.name);
841 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
844 StringRef className = moduleLike.getName();
847 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation()))
848 if (
auto defname = externMod.getDefname())
849 className = defname.value();
854 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
857 om::ClassLike loweredClassOp;
858 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(moduleLike.getOperation()))
859 loweredClassOp = builder.create<om::ClassExternOp>(
860 moduleLike.getLoc(), className + suffix, formalParamNames);
862 loweredClassOp = builder.create<om::ClassOp>(
863 moduleLike.getLoc(), className + suffix, formalParamNames);
865 return loweredClassOp;
868 void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
869 om::ClassLike classLike,
870 const PathInfoTable &pathInfoTable) {
872 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
873 return lowerClass(classOp, moduleLike, pathInfoTable);
875 if (
auto classExternOp =
876 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
877 return lowerClassExtern(classExternOp, moduleLike);
879 llvm_unreachable(
"unhandled class-like op");
882 void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
883 const PathInfoTable &pathInfoTable) {
888 SmallVector<Property> inputProperties;
889 BitVector portsToErase(moduleLike.getNumPorts());
890 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts())) {
892 if (!isa<PropertyType>(port.type))
897 inputProperties.push_back({index, port.name, port.type, port.loc});
900 portsToErase.set(index);
905 Block *classBody = &classOp->getRegion(0).emplaceBlock();
909 classBody->addArgument(basePathType, unknownLoc);
912 size_t nAltBasePaths =
913 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
914 for (
size_t i = 0; i < nAltBasePaths; ++i)
915 classBody->addArgument(basePathType, unknownLoc);
917 for (
auto inputProperty : inputProperties) {
918 BlockArgument parameterValue =
919 classBody->addArgument(inputProperty.type, inputProperty.loc);
920 BlockArgument inputValue =
921 moduleLike->getRegion(0).getArgument(inputProperty.index);
922 mapping.map(inputValue, parameterValue);
926 SmallVector<Operation *> opsToErase;
927 OpBuilder builder = OpBuilder::atBlockBegin(classOp.getBodyBlock());
928 for (
auto &op : moduleLike->getRegion(0).getOps()) {
930 auto propertyOperands = llvm::any_of(op.getOperandTypes(), [](Type type) {
931 return isa<PropertyType>(type);
933 bool needsClone =
false;
934 if (
auto instance = dyn_cast<InstanceOp>(op))
935 needsClone = shouldCreateClass(instance.getReferencedModuleNameAttr());
938 auto propertyResults = llvm::any_of(
939 op.getResultTypes(), [](Type type) { return isa<PropertyType>(type); });
942 if (!needsClone && !propertyOperands && !propertyResults)
946 builder.clone(op, mapping);
950 if (!isa<InstanceOp>(op))
951 opsToErase.push_back(&op);
955 for (
auto op : llvm::make_early_inc_range(classOp.getOps<PropAssignOp>())) {
958 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
963 auto name = moduleLike.getPortName(outputPort.getArgNumber());
964 builder.create<ClassFieldOp>(op.getLoc(), name, op.getSrc());
970 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
972 for (
auto *op : llvm::reverse(opsToErase))
976 moduleLike.erasePorts(portsToErase);
980 void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
981 FModuleLike moduleLike) {
985 BitVector portsToErase(moduleLike.getNumPorts());
986 Block *classBody = &classExternOp.getRegion().emplaceBlock();
987 OpBuilder builder = OpBuilder::atBlockBegin(classBody);
993 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
994 auto type = moduleLike.getPortType(i);
995 if (!isa<PropertyType>(type))
998 auto loc = moduleLike.getPortLocation(i);
999 auto direction = moduleLike.getPortDirection(i);
1000 if (direction == Direction::In)
1001 classBody->addArgument(type, loc);
1003 auto name = moduleLike.getPortNameAttr(i);
1004 builder.create<om::ClassExternFieldOp>(loc, name, type);
1008 portsToErase.set(i);
1013 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
1015 moduleLike.erasePorts(portsToErase);
1021 static LogicalResult
1023 const PathInfoTable &pathInfoTable,
1024 SmallVectorImpl<Operation *> &opsToErase) {
1026 auto basePath = firrtlObject->getBlock()->getArgument(0);
1029 auto firrtlClassType = firrtlObject.getType();
1030 auto numElements = firrtlClassType.getNumElements();
1031 llvm::SmallVector<unsigned> argIndexTable;
1035 SmallVector<Value> altBasePaths;
1036 pathInfoTable.collectAltBasePaths(
1037 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1040 unsigned nextArgIndex = 1 + altBasePaths.size();
1043 auto direction = firrtlClassType.getElement(i).direction;
1044 if (direction == Direction::In)
1045 argIndexTable[i] = nextArgIndex++;
1051 llvm::SmallVector<Value> args;
1052 args.resize(nextArgIndex);
1056 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1057 args[1 + i] = altBasePath;
1059 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1060 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1061 auto index = subfield.getIndex();
1062 auto direction = firrtlClassType.getElement(index).direction;
1066 if (direction == Direction::Out)
1069 for (
auto *subfieldUser :
1070 llvm::make_early_inc_range(subfield->getUsers())) {
1071 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1075 auto dst = propassign.getOperand(0);
1076 auto src = propassign.getOperand(1);
1077 if (dst == subfield.getResult()) {
1078 args[argIndexTable[index]] = src;
1079 opsToErase.push_back(propassign);
1084 opsToErase.push_back(subfield);
1090 auto element = firrtlClassType.getElement(i);
1091 if (element.direction == Direction::Out)
1094 auto argIndex = argIndexTable[i];
1095 if (!args[argIndex])
1096 return emitError(firrtlObject.getLoc())
1097 <<
"uninitialized input port " << element.name;
1101 auto className = firrtlObject.getType().getNameAttr();
1105 OpBuilder builder(firrtlObject);
1106 auto object = builder.create<om::ObjectOp>(
1107 firrtlObject.getLoc(), classType, firrtlObject.getClassNameAttr(), args);
1111 firrtlObject.replaceAllUsesWith(
object.getResult());
1114 opsToErase.push_back(firrtlObject);
1121 static LogicalResult
1124 const PathInfoTable &pathInfoTable,
1125 SmallVectorImpl<Operation *> &opsToErase) {
1128 OpBuilder builder(firrtlInstance);
1133 SmallVector<Value> actualParameters;
1135 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1137 auto rebasedPath = builder.create<om::BasePathCreateOp>(
1138 firrtlInstance->getLoc(), basePath, symRef);
1140 actualParameters.push_back(rebasedPath);
1143 pathInfoTable.collectAltBasePaths(
1144 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1147 for (
auto result : firrtlInstance.getResults()) {
1149 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1154 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1155 if (!propertyResult)
1161 assert(propertyAssignment &&
"properties require single assignment");
1162 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1165 opsToErase.push_back(propertyAssignment);
1169 auto referencedModule =
1170 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1172 StringRef moduleName = referencedModule.getName();
1175 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1176 if (
auto defname = externMod.getDefname())
1177 moduleName = defname.value();
1181 builder.getStringAttr(moduleName + kClassNameSuffix));
1187 builder.create<om::ObjectOp>(firrtlInstance.getLoc(), classType,
1188 className.getAttr(), actualParameters);
1193 for (
auto result : firrtlInstance.getResults()) {
1195 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1200 if (!isa<PropertyType>(result.getType()))
1205 firrtlInstance.getPortName(result.getResultNumber()))});
1208 auto objectField = builder.create<ObjectFieldOp>(
1209 object.getLoc(), result.getType(), object, objectFieldPath);
1211 result.replaceAllUsesWith(objectField);
1215 opsToErase.push_back(firrtlInstance);
1221 static LogicalResult
1223 SmallVectorImpl<Operation *> &opsToErase) {
1225 BitVector portsToErase(firrtlInstance.getNumResults());
1226 for (
auto result : firrtlInstance.getResults())
1227 if (isa<PropertyType>(result.getType()))
1228 portsToErase.set(result.getResultNumber());
1231 if (portsToErase.none())
1235 OpBuilder builder(firrtlInstance);
1236 InstanceOp newInstance = firrtlInstance.erasePorts(builder, portsToErase);
1245 opsToErase.push_back(firrtlInstance);
1249 static LogicalResult
1251 SmallVectorImpl<Operation *> &opsToErase) {
1252 OpBuilder builder(moduleOp);
1253 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1254 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1255 assert(0 &&
"should be no objects in modules");
1256 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1266 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1267 SmallVectorImpl<Operation *> &opsToErase) {
1268 OpBuilder builder(classOp);
1269 auto &classState = state.classLoweringStateTable.at(classOp);
1270 auto it = classState.paths.begin();
1271 for (
auto &op : classOp->getRegion(0).getOps()) {
1272 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1275 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1277 pathInfoTable, opsToErase)))
1286 LowerClassesPass::updateInstances(Operation *op,
InstanceGraph &instanceGraph,
1287 const LoweringState &state,
1288 const PathInfoTable &pathInfoTable) {
1293 SmallVector<Operation *> opsToErase;
1295 TypeSwitch<Operation *, LogicalResult>(op)
1297 .Case([&](FModuleOp moduleOp) {
1302 .Case([&](om::ClassOp classOp) {
1306 classOp, instanceGraph, state, pathInfoTable, opsToErase);
1308 .Default([](
auto *op) {
return success(); });
1312 for (
auto *op : opsToErase)
1322 using OpConversionPattern::OpConversionPattern;
1326 ConversionPatternRewriter &rewriter)
const override {
1327 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1335 using OpConversionPattern::OpConversionPattern;
1339 ConversionPatternRewriter &rewriter)
const override {
1340 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1341 op, rewriter.getBoolAttr(adaptor.getValue()));
1348 using OpConversionPattern::OpConversionPattern;
1352 ConversionPatternRewriter &rewriter)
const override {
1353 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1360 using OpConversionPattern::OpConversionPattern;
1364 ConversionPatternRewriter &rewriter)
const override {
1366 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1374 using OpConversionPattern::OpConversionPattern;
1378 ConversionPatternRewriter &rewriter)
const override {
1379 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1382 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1383 adaptor.getElements());
1390 using OpConversionPattern::OpConversionPattern;
1394 ConversionPatternRewriter &rewriter)
const override {
1395 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1403 using OpConversionPattern::OpConversionPattern;
1407 ConversionPatternRewriter &rewriter)
const override {
1408 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1416 using OpConversionPattern::OpConversionPattern;
1420 ConversionPatternRewriter &rewriter)
const override {
1421 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1430 const PathInfoTable &pathInfoTable,
1431 PatternBenefit benefit = 1)
1433 pathInfoTable(pathInfoTable) {}
1437 ConversionPatternRewriter &rewriter)
const override {
1438 auto *context = op->getContext();
1440 auto pathInfo = pathInfoTable.table.lookup(op.getTarget());
1443 auto basePath = op->getBlock()->getArgument(0);
1448 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1449 return emitError(op.getLoc(),
"DontTouch target was deleted");
1450 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1451 return emitError(op.getLoc(),
"Instance target was deleted");
1452 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1456 auto symbol = pathInfo.symRef;
1460 om::TargetKind targetKind;
1461 switch (op.getTargetKind()) {
1462 case firrtl::TargetKind::DontTouch:
1463 targetKind = om::TargetKind::DontTouch;
1465 case firrtl::TargetKind::Reference:
1466 targetKind = om::TargetKind::Reference;
1468 case firrtl::TargetKind::Instance:
1469 if (!isa<InstanceOp, FModuleLike>(pathInfo.op))
1470 return emitError(op.getLoc(),
"invalid target for instance path")
1471 .attachNote(pathInfo.op->getLoc())
1472 <<
"target not instance or module";
1473 targetKind = om::TargetKind::Instance;
1475 case firrtl::TargetKind::MemberInstance:
1476 case firrtl::TargetKind::MemberReference:
1477 if (isa<InstanceOp, FModuleLike>(pathInfo.op))
1478 targetKind = om::TargetKind::MemberInstance;
1480 targetKind = om::TargetKind::MemberReference;
1486 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1490 auto parent = op->getParentOfType<om::ClassOp>();
1491 auto parentName = parent.getName();
1492 if (parentName.ends_with(kClassNameSuffix))
1493 parentName = parentName.drop_back(kClassNameSuffix.size());
1494 auto originalParentName =
StringAttr::get(op->getContext(), parentName);
1498 pathInfoTable.getRootsForPassthrough(originalParentName);
1499 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1502 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
1503 if (altBasePathModule == altBasePath) {
1505 auto basePathArg = op->getBlock()->getArgument(1 + i);
1506 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1507 "expected a passthrough base path");
1508 basePath = basePathArg;
1513 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1523 using OpConversionPattern::OpConversionPattern;
1527 ConversionPatternRewriter &rewriter)
const override {
1528 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1537 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1538 if (!regionKindInterface)
1540 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1547 rewriter.replaceOp(wireOp, propAssign.getSrc());
1550 rewriter.eraseOp(propAssign);
1557 using OpConversionPattern::OpConversionPattern;
1561 ConversionPatternRewriter &rewriter)
const override {
1562 rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
1569 using OpConversionPattern::OpConversionPattern;
1572 const TypeConverter &typeConverter, MLIRContext *context,
1573 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1575 classTypeTable(classTypeTable) {}
1579 ConversionPatternRewriter &rewriter)
const override {
1580 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1586 auto firrtlClassType =
1587 classTypeTable.lookup(omClassType.getClassName().getAttr());
1588 if (!firrtlClassType)
1591 const auto &element = firrtlClassType.getElement(op.getIndex());
1593 if (element.direction == Direction::In)
1597 auto path = rewriter.getArrayAttr({field});
1598 auto type = typeConverter->convertType(element.type);
1599 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1608 using OpConversionPattern::OpConversionPattern;
1612 ConversionPatternRewriter &rewriter)
const override {
1613 rewriter.replaceOpWithNewOp<ClassFieldOp>(op, adaptor.getNameAttr(),
1614 adaptor.getValue());
1621 using OpConversionPattern::OpConversionPattern;
1625 ConversionPatternRewriter &rewriter)
const override {
1626 auto type = typeConverter->convertType(adaptor.getType());
1629 rewriter.replaceOpWithNewOp<ClassExternFieldOp>(op, adaptor.getNameAttr(),
1636 using OpConversionPattern::OpConversionPattern;
1640 ConversionPatternRewriter &rewriter)
const override {
1643 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1644 adaptor.getClassNameAttr(),
1645 adaptor.getActualParams());
1651 using OpConversionPattern::OpConversionPattern;
1655 ConversionPatternRewriter &rewriter)
const override {
1656 Block *body = classOp.getBodyBlock();
1657 TypeConverter::SignatureConversion result(body->getNumArguments());
1660 if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1665 if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1669 rewriter.modifyOpInPlace(classOp, []() {});
1677 using OpConversionPattern::OpConversionPattern;
1681 ConversionPatternRewriter &rewriter)
const override {
1682 Block *body = classOp.getBodyBlock();
1683 TypeConverter::SignatureConversion result(body->getNumArguments());
1686 if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1691 if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1695 rewriter.modifyOpInPlace(classOp, []() {});
1702 using OpConversionPattern::OpConversionPattern;
1706 ConversionPatternRewriter &rewriter)
const override {
1709 auto type = typeConverter->convertType(op.getType());
1713 rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1714 adaptor.getFieldPathAttr());
1725 target.addDynamicallyLegalDialect<FIRRTLDialect>(
1726 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
1729 target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
1730 auto containsFIRRTLType = [](Type type) {
1732 .walk([](Type type) {
1733 return failure(isa<FIRRTLDialect>(type.getDialect()));
1737 auto noFIRRTLOperands =
1738 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
1739 return containsFIRRTLType(type);
1741 auto noFIRRTLResults =
1742 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
1743 return containsFIRRTLType(type);
1745 return noFIRRTLOperands && noFIRRTLResults;
1750 target.addDynamicallyLegalOp<ClassExternFieldOp>(
1751 [](ClassExternFieldOp op) {
return !isa<FIRRTLType>(op.getType()); });
1754 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
1755 [](Operation *op) -> std::optional<bool> {
1756 auto classLike = dyn_cast<om::ClassLike>(op);
1758 return std::nullopt;
1760 return llvm::none_of(
1761 classLike.getBodyBlock()->getArgumentTypes(),
1762 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
1768 converter.addConversion(
1770 converter.addConversion([](FIntegerType type) {
1778 converter.addConversion([](om::StringType type) {
return type; });
1779 converter.addConversion([](firrtl::StringType type) {
1784 converter.addConversion([](om::PathType type) {
return type; });
1785 converter.addConversion([](om::BasePathType type) {
return type; });
1786 converter.addConversion([](om::FrozenPathType type) {
return type; });
1787 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
1788 converter.addConversion([](firrtl::PathType type) {
1793 converter.addConversion([](om::ClassType type) {
return type; });
1794 converter.addConversion([](firrtl::ClassType type) {
1799 converter.addConversion([](om::AnyType type) {
return type; });
1800 converter.addConversion([](firrtl::AnyRefType type) {
1805 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
1806 auto elementType = converter.convertType(type.getElementType());
1812 converter.addConversion(
1813 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
1815 return convertListType(type);
1818 converter.addConversion(
1819 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
1821 return convertListType(type);
1825 converter.addConversion(
1829 converter.addConversion(
1830 [](DoubleType type) {
return FloatType::getF64(type.getContext()); });
1833 converter.addTargetMaterialization(
1834 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
1835 assert(values.size() == 1);
1840 converter.addSourceMaterialization(
1841 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
1842 assert(values.size() == 1);
1848 RewritePatternSet &
patterns, TypeConverter &converter,
1849 const PathInfoTable &pathInfoTable,
1850 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1875 LogicalResult LowerClassesPass::dialectConversion(
1876 Operation *op,
const PathInfoTable &pathInfoTable,
1877 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1878 ConversionTarget target(getContext());
1881 TypeConverter typeConverter;
1884 RewritePatternSet
patterns(&getContext());
1888 return applyPartialConversion(op, target, std::move(
patterns));
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
static LogicalResult updateInstancesInModule(FModuleOp moduleOp, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static void populateConversionTarget(ConversionTarget &target)
static LogicalResult updateInstanceInClass(InstanceOp firrtlInstance, hw::HierPathOp hierPath, InstanceGraph &instanceGraph, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static void populateTypeConverter(TypeConverter &converter)
static LogicalResult updateInstanceInModule(InstanceOp firrtlInstance, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateObjectsAndInstancesInClass(om::ClassOp classOp, InstanceGraph &instanceGraph, const LoweringState &state, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateObjectInClass(firrtl::ObjectOp firrtlObject, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static void populateRewritePatterns(RewritePatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
UseIterator usesBegin()
Iterate the instance records which instantiate this module.
bool hasOneUse()
Return true if this module has exactly one use.
llvm::iterator_range< UseIterator > uses()
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
This is an edge in the InstanceGraph.
auto getInstance()
Get the instance-like op that this is tracking.
InstanceGraphNode * getParent() const
Get the module where the instantiation lives.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
PropAssignOp getPropertyAssignment(FIRRTLPropertyValue value)
Return the single assignment to a Property value.
std::unique_ptr< mlir::Pass > createLowerClassesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
LogicalResult matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassExternFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
ObjectSubfieldOpConversion(const TypeConverter &typeConverter, MLIRContext *context, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
const DenseMap< StringAttr, firrtl::ClassType > & classTypeTable
LogicalResult matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
PathOpConversion(TypeConverter &typeConverter, MLIRContext *context, const PathInfoTable &pathInfoTable, PatternBenefit benefit=1)
const PathInfoTable & pathInfoTable
LogicalResult matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(StringConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(WireOp wireOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
An annotation target is used to keep track of something that is targeted by an Annotation.
Operation * getOp() const
FModuleLike getModule() const
Get the parent module of the target.
AnnotationSet getAnnotations() const
Get the annotations associated with the target.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
hw::HierPathOp getOpFor(ArrayAttr attr)
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
This implements an analysis to determine which module owns a given path operation.
This represents an annotation targeting a specific port of a module, memory, or instance.
This holds the name and type that describes the module's ports.