25#include "mlir/IR/BuiltinOps.h"
26#include "mlir/IR/PatternMatch.h"
27#include "mlir/IR/SymbolTable.h"
28#include "mlir/IR/Threading.h"
29#include "mlir/Pass/Pass.h"
30#include "mlir/Support/LogicalResult.h"
31#include "mlir/Transforms/DialectConversion.h"
32#include "llvm/ADT/MapVector.h"
33#include "llvm/ADT/STLExtras.h"
37#define GEN_PASS_DEF_LOWERCLASSES
38#include "circt/Dialect/FIRRTL/Passes.h.inc"
52 PathInfo(Location loc,
bool canBeInstanceTarget, FlatSymbolRefAttr symRef,
53 StringAttr altBasePathModule)
54 : loc(loc), canBeInstanceTarget(canBeInstanceTarget), symRef(symRef),
55 altBasePathModule(altBasePathModule) {
56 assert(symRef &&
"symRef must not be null");
60 std::optional<Location> loc = std::nullopt;
63 bool canBeInstanceTarget =
false;
66 FlatSymbolRefAttr symRef =
nullptr;
70 StringAttr altBasePathModule =
nullptr;
77 void addAltBasePathRoot(StringAttr rootModuleName) {
78 altBasePathRoots.insert(rootModuleName);
83 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
84 StringAttr rootModuleName) {
85 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
86 rootSequence.push_back(rootModuleName);
90 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
91 getAltBasePathRoots()
const {
92 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
97 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
98 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
103 llvm::iterator_range<const StringAttr *>
104 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
105 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
106 assert(it != altBasePathsPassthroughs.end() &&
107 "expected passthrough module to already exist");
108 return llvm::make_range(it->second.begin(), it->second.end());
113 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
114 SmallVectorImpl<Value> &result)
const {
115 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
116 auto parent = instance->getParentOfType<om::ClassOp>();
119 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
120 if (parent.getName().starts_with(altBasePath)) {
122 result.push_back(instance->getBlock()->getArgument(0));
126 auto basePath = instance->getBlock()->getArgument(1 + i);
127 assert(isa<om::BasePathType>(basePath.getType()) &&
128 "expected a passthrough base path");
129 result.push_back(basePath);
136 llvm::MapVector<DistinctAttr, PathInfo> table;
141 SmallPtrSet<StringAttr, 16> altBasePathRoots;
145 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
149static constexpr StringRef kClassNameSuffix =
"_Class";
160struct ClassLoweringState {
161 FModuleLike moduleLike;
162 std::vector<hw::HierPathOp> paths;
165struct LoweringState {
166 PathInfoTable pathInfoTable;
167 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
172 firrtl::PathOp containingModuleRef;
177struct LowerClassesPass
178 :
public circt::firrtl::impl::LowerClassesBase<LowerClassesPass> {
179 void runOnOperation()
override;
182 LogicalResult processPaths(InstanceGraph &instanceGraph,
183 hw::InnerSymbolNamespaceCollection &namespaces,
184 HierPathCache &cache, PathInfoTable &pathInfoTable,
185 SymbolTable &symbolTable);
188 bool shouldCreateClass(igraph::ModuleOpInterface modOp);
189 bool shouldCreateClass(StringAttr modName);
192 om::ClassLike createClass(FModuleLike moduleLike,
193 const PathInfoTable &pathInfoTable,
194 std::mutex &intraPassMutex);
197 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
198 const PathInfoTable &pathInfoTable);
199 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
200 const PathInfoTable &pathInfoTable);
201 void lowerClassExtern(om::ClassExternOp classExternOp,
202 FModuleLike moduleLike);
205 LogicalResult updateInstances(Operation *op, InstanceGraph &instanceGraph,
206 const LoweringState &state,
207 const PathInfoTable &pathInfoTable,
208 std::mutex &intraPassMutex);
211 void createAllRtlPorts(
const PathInfoTable &pathInfoTable,
212 hw::InnerSymbolNamespaceCollection &namespaces,
213 HierPathCache &hierPathCache);
216 LogicalResult dialectConversion(
217 Operation *op,
const PathInfoTable &pathInfoTable,
218 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
221 InstanceInfo *instanceInfo =
nullptr;
227 SmallVector<RtlPortsInfo> rtlPortsToCreate;
234 DenseMap<StringAttr, om::ClassLike> externalClassMap;
241 run(CircuitOp circuit, InstanceGraph &instanceGraph,
242 hw::InnerSymbolNamespaceCollection &namespaces, HierPathCache &cache,
243 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
244 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
246 PathTracker(FModuleLike module,
247 hw::InnerSymbolNamespaceCollection &namespaces,
248 InstanceGraph &instanceGraph,
const SymbolTable &symbolTable,
249 const DenseMap<DistinctAttr, FModuleOp> &owningModules)
250 : module(module), moduleNamespace(namespaces[module]),
251 namespaces(namespaces), instanceGraph(instanceGraph),
252 symbolTable(symbolTable), owningModules(owningModules) {}
255 struct PathInfoTableEntry {
258 StringAttr altBasePathModule;
264 LogicalResult runOnModule();
267 FailureOr<AnnotationSet> processPathTrackers(
const AnnoTarget &target);
269 LogicalResult updatePathInfoTable(PathInfoTable &pathInfoTable,
270 HierPathCache &cache)
const;
274 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
275 StringAttr moduleName,
276 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;
297static constexpr StringRef kContainingModuleName =
"containingModule";
298static constexpr StringRef kPortsName =
"ports";
299static constexpr StringRef kRtlPortClassName =
"RtlPort";
301static Type getRtlPortsType(MLIRContext *
context) {
302 return om::ListType::get(om::ClassType::get(
307static void createRtlPorts(
const RtlPortsInfo &rtlPortToCreate,
308 const PathInfoTable &pathInfoTable,
311 firrtl::PathOp containingModuleRef = rtlPortToCreate.containingModuleRef;
312 Value basePath = rtlPortToCreate.basePath;
313 om::ObjectOp
object = rtlPortToCreate.object;
316 OpBuilder::InsertionGuard guard(builder);
317 builder.setInsertionPoint(
object);
321 FlatSymbolRefAttr containingModulePathRef =
322 pathInfoTable.table.at(containingModuleRef.getTarget()).symRef;
326 hw::HierPathOp containingModulePath =
327 symbolTable.lookup<hw::HierPathOp>(containingModulePathRef.getAttr());
329 assert(containingModulePath.isModule() &&
330 "expected containing module path to target a module");
332 StringAttr moduleName = containingModulePath.leafMod();
334 FModuleLike mod = symbolTable.lookup<FModuleLike>(moduleName);
335 MLIRContext *ctx = mod.getContext();
336 Location loc = mod.getLoc();
340 auto portClassName = StringAttr::get(ctx, kRtlPortClassName);
342 om::ClassType::get(ctx, FlatSymbolRefAttr::get(portClassName));
344 SmallVector<Value> ports;
345 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
348 if (!portType || portType.getBitWidthOrSentinel() == 0)
357 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), 0},
359 return namespaces[m];
362 FlatSymbolRefAttr portPathRef =
363 hierPathCache.
getRefFor(ArrayAttr::get(ctx, {portSym}));
365 auto portPath = om::PathCreateOp::create(
366 builder, loc, om::PathType::get(ctx),
367 om::TargetKindAttr::get(ctx, om::TargetKind::DontTouch), basePath,
372 StringRef portDirectionName =
375 auto portDirection = om::ConstantOp::create(
376 builder, loc, om::StringType::get(ctx),
377 StringAttr::get(portDirectionName, om::StringType::get(ctx)));
381 auto portWidth = om::ConstantOp::create(
382 builder, loc, om::OMIntegerType::get(ctx),
383 om::IntegerAttr::get(
384 ctx, mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
385 portType.getBitWidthOrSentinel())));
389 auto portObj = om::ObjectOp::create(
390 builder, loc, portClassType, portClassName,
391 ArrayRef<Value>{portPath, portDirection, portWidth});
393 ports.push_back(portObj);
399 om::ListCreateOp::create(builder, UnknownLoc::get(builder.getContext()),
400 getRtlPortsType(builder.getContext()), ports);
402 object.getActualParamsMutable().append({portsList});
408PathTracker::run(CircuitOp circuit,
InstanceGraph &instanceGraph,
411 const SymbolTable &symbolTable,
412 const DenseMap<DistinctAttr, FModuleOp> &owningModules) {
415 for (
auto *node : instanceGraph)
416 if (
auto module = node->getModule<FModuleLike>())
417 (void)namespaces.
get(module);
419 for (
auto *node : instanceGraph)
420 if (
auto module = node->getModule<FModuleLike>()) {
422 if (isa<firrtl::ClassOp, firrtl::ExtClassOp>(module))
424 PathTracker tracker(module, namespaces, instanceGraph, symbolTable,
426 if (failed(tracker.runOnModule()))
428 if (failed(tracker.updatePathInfoTable(pathInfoTable, cache)))
435LogicalResult PathTracker::runOnModule() {
436 auto processAndUpdateAnnoTarget = [&](AnnoTarget target) -> LogicalResult {
437 auto anno = processPathTrackers(target);
440 target.setAnnotations(*anno);
445 if (failed(processAndUpdateAnnoTarget(OpAnnoTarget(module))))
449 SmallVector<Attribute> portAnnotations;
450 portAnnotations.reserve(module.getNumPorts());
451 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
452 auto annos = processPathTrackers(PortAnnoTarget(module, i));
455 portAnnotations.push_back(annos->getArrayAttr());
458 module.setPortAnnotationsAttr(
459 ArrayAttr::get(module.getContext(), portAnnotations));
462 auto result =
module.walk([&](hw::InnerSymbolOpInterface op) {
463 if (failed(processAndUpdateAnnoTarget(OpAnnoTarget(op))))
464 return WalkResult::interrupt();
465 return WalkResult::advance();
468 if (result.wasInterrupted())
476PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
477 FModuleOp owningModule,
480 auto it = needsAltBasePathCache.find({moduleName, owningModule});
481 if (it != needsAltBasePathCache.end())
483 bool needsAltBasePath =
false;
484 auto *node = instanceGraph.lookup(moduleName);
487 if (node->getModule() == owningModule)
493 if (node->noUses()) {
494 needsAltBasePath =
true;
499 if (isNonLocal && !node->hasOneUse()) {
500 auto diag = mlir::emitError(loc)
501 <<
"unable to uniquely resolve target due "
502 "to multiple instantiation";
503 for (
auto *use : node->uses())
504 diag.attachNote(use->getInstance().getLoc()) <<
"instance here";
507 node = (*node->usesBegin())->getParent();
509 needsAltBasePathCache[{moduleName, owningModule}] = needsAltBasePath;
510 return needsAltBasePath;
513FailureOr<AnnotationSet>
514PathTracker::processPathTrackers(
const AnnoTarget &target) {
517 auto *op = target.
getOp();
518 annotations.removeAnnotations([&](Annotation anno) {
524 if (!anno.
isClass(
"circt.tracker"))
528 auto id = anno.
getMember<DistinctAttr>(
"id");
530 op->emitError(
"circt.tracker annotation missing id field");
540 if (
auto portTarget = dyn_cast<PortAnnoTarget>(target)) {
542 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), fieldID},
543 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
544 return moduleNamespace;
546 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
547 assert(!fieldID &&
"field not valid for modules");
548 targetSym = FlatSymbolRefAttr::get(module.getModuleNameAttr());
552 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
553 return moduleNamespace;
558 SmallVector<Attribute> path;
561 path.push_back(targetSym);
563 auto moduleName = target.
getModule().getModuleNameAttr();
566 hw::HierPathOp hierPathOp;
567 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
569 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
571 op->emitError(
"annotation does not point at a HierPathOp");
579 auto owningModule = owningModules.lookup(
id);
586 auto oldPath = hierPathOp.getNamepath().getValue();
591 bool pathContainsOwningModule =
false;
592 size_t owningModuleIndex = 0;
593 for (
auto [idx, pathFramgent] : llvm::enumerate(oldPath)) {
594 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(pathFramgent)) {
595 if (innerRef.getModule() == owningModule.getModuleNameAttr()) {
596 pathContainsOwningModule =
true;
597 owningModuleIndex = idx;
599 }
else if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(pathFramgent)) {
600 if (symRef.getAttr() == owningModule.getModuleNameAttr()) {
601 pathContainsOwningModule =
true;
602 owningModuleIndex = idx;
607 if (pathContainsOwningModule) {
609 moduleName = owningModule.getModuleNameAttr();
613 llvm::append_range(path, llvm::reverse(oldPath.drop_back().drop_front(
614 owningModuleIndex)));
617 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
620 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
625 auto needsAltBasePath = getOrComputeNeedsAltBasePath(
626 op->getLoc(), moduleName, owningModule, hierPathOp);
627 if (failed(needsAltBasePath)) {
639 if (!hierPathOp && !needsAltBasePath.value())
657 std::reverse(path.begin(), path.end());
658 auto pathAttr = ArrayAttr::get(op->getContext(), path);
662 StringAttr altBasePathModule;
663 if (*needsAltBasePath) {
665 TypeSwitch<Attribute, StringAttr>(path.front())
666 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
667 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
669 altBasePathRoots.insert(altBasePathModule);
673 entries.push_back({op, id, altBasePathModule, pathAttr});
685LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
686 HierPathCache &cache)
const {
687 for (
auto root : altBasePathRoots)
688 pathInfoTable.addAltBasePathRoot(root);
690 for (
const auto &entry : entries) {
692 "expected all PathInfoTableEntries to have a pathAttr");
695 auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
696 auto &pathInfo = it->second;
704 assert(pathInfo.symRef &&
"expected all PathInfos to have a symRef");
705 auto existingHierpath =
706 symbolTable.lookup<hw::HierPathOp>(pathInfo.symRef.getValue());
707 auto existingPathAttr = existingHierpath.getNamepath();
708 if (existingPathAttr == entry.pathAttr)
711 assert(pathInfo.loc.has_value() &&
"all PathInfo should have a Location");
712 auto diag = emitError(pathInfo.loc.value(),
713 "path identifier already found, paths must resolve "
714 "to a unique target");
715 diag.attachNote(entry.op->getLoc()) <<
"other path identifier here";
721 bool canBeInstanceTarget = isa<InstanceOp, FModuleLike>(entry.op);
723 pathInfo = {entry.op->getLoc(), canBeInstanceTarget,
724 cache.
getRefFor(entry.pathAttr), entry.altBasePathModule};
735LogicalResult LowerClassesPass::processPaths(
736 InstanceGraph &instanceGraph,
737 hw::InnerSymbolNamespaceCollection &namespaces, HierPathCache &cache,
738 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
739 auto circuit = getOperation();
749 OwningModuleCache owningModuleCache(instanceGraph);
750 DenseMap<DistinctAttr, FModuleOp> owningModules;
751 DenseMap<DistinctAttr, StringAttr> containingModules;
752 DenseMap<StringAttr, PathOp> containingModuleToPathOp;
753 std::vector<Operation *> declarations;
754 auto result = circuit.walk([&](Operation *op) {
755 if (
auto pathOp = dyn_cast<PathOp>(op)) {
757 auto owningModule = owningModuleCache.lookup(pathOp);
760 pathOp->emitError(
"path does not have a single owning module");
761 return WalkResult::interrupt();
763 auto target = pathOp.getTargetAttr();
764 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
767 if (!inserted && it->second != owningModule) {
769 <<
"path reference " << target <<
" has conflicting owning modules "
770 << it->second.getModuleNameAttr() <<
" and "
771 << owningModule.getModuleNameAttr();
772 return WalkResult::interrupt();
775 auto container = pathOp->getParentOfType<FModuleLike>();
776 assert(container &&
"path op with a non-null owning module must be "
777 "inside an FModuleLike");
778 auto containerName = container.getModuleNameAttr();
779 containingModules.try_emplace(target, containerName);
780 containingModuleToPathOp.try_emplace(containerName, pathOp);
782 return WalkResult::advance();
785 if (result.wasInterrupted())
788 if (failed(PathTracker::run(circuit, instanceGraph, namespaces, cache,
789 pathInfoTable, symbolTable, owningModules)))
823 llvm::MapVector<StringAttr, SetVector<StringAttr>> rootToContainingMods;
824 for (
const auto &[distinctAttr, pathInfo] : pathInfoTable.table) {
825 if (!pathInfo.altBasePathModule)
827 auto it = containingModules.find(distinctAttr);
828 assert(it != containingModules.end() &&
829 "path info entry with non-null altBasePathModule must have a "
830 "recorded containing FModuleLike");
831 rootToContainingMods[pathInfo.altBasePathModule].insert(it->second);
837 for (
auto &[altRoot, containingMods] : rootToContainingMods) {
843 SetVector<InstanceGraphNode *> markedNodes;
844 markedNodes.insert(rootNode);
847 PathOp reachablePathOp;
848 for (StringAttr start : containingMods) {
850 auto paths = instancePathCache.getRelativePaths(startMod, rootNode);
854 PathOp pathOp = containingModuleToPathOp.lookup(start);
855 assert(pathOp &&
"every containing module name recorded in "
856 "rootToContainingMods must have a representative "
858 auto diag = pathOp->emitOpError()
859 <<
"in module " << start
860 <<
" cannot be lowered because the module is not reachable "
862 << altRoot <<
" which contains the target";
863 auto *pathInfoIt = pathInfoTable.table.find(pathOp.getTargetAttr());
864 assert(pathInfoIt != pathInfoTable.table.end() &&
865 pathInfoIt->second.loc.has_value() &&
866 "a path op being processed here must have a PathInfo entry "
867 "with a recorded tracked-op location");
868 diag.attachNote(pathInfoIt->second.loc.value())
869 <<
"path targets this operation in module " << altRoot;
875 if (!reachablePathOp)
876 reachablePathOp = containingModuleToPathOp.lookup(start);
881 if (markedNodes.insert(startNode))
882 pathInfoTable.addAltBasePathPassthrough(
883 startNode->
getModule().getModuleNameAttr(), altRoot);
884 for (
auto path : paths) {
885 for (
auto inst : path) {
886 auto *node = instanceGraph.
lookup(
887 inst->getParentOfType<FModuleLike>().getModuleNameAttr());
888 if (markedNodes.insert(node))
889 pathInfoTable.addAltBasePathPassthrough(
890 node->
getModule().getModuleNameAttr(), altRoot);
897 if (!reachablePathOp)
900 pathInfoTable.table.find(reachablePathOp.getTargetAttr());
901 assert(pathInfoIt != pathInfoTable.table.end() &&
902 pathInfoIt->second.loc.has_value() &&
903 "a path op being processed here must have a PathInfo entry "
904 "with a recorded tracked-op location");
905 StringAttr containing =
906 reachablePathOp->getParentOfType<FModuleLike>().getModuleNameAttr();
908 if (node == rootNode)
911 if (markedNodes.contains(use->getParent()))
913 auto diag = reachablePathOp->emitOpError()
914 <<
"in module " << containing
915 <<
" cannot be lowered because there is an instantiation "
917 << use->getTarget()->getModule().getModuleNameAttr()
919 << use->getParent()->getModule().getModuleNameAttr()
920 <<
" which is not instantiated by module " << altRoot
921 <<
" which contains the target";
922 diag.attachNote(pathInfoIt->second.loc.value())
923 <<
"the path op targets this operation in module " << altRoot;
924 diag.attachNote(use->getInstance()->getLoc())
925 <<
"the problematic instantiation of module "
926 << use->getTarget()->getModule().getModuleNameAttr()
928 << use->getParent()->getModule().getModuleNameAttr() <<
" is here";
938void LowerClassesPass::runOnOperation() {
939 MLIRContext *ctx = &getContext();
940 auto intraPassMutex = std::mutex();
943 CircuitOp circuit = getOperation();
946 instanceGraph = &getAnalysis<InstanceGraph>();
947 instanceInfo = &getAnalysis<InstanceInfo>();
948 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
950 hw::InnerSymbolNamespaceCollection namespaces;
951 HierPathCache cache(circuit, symbolTable);
954 PathInfoTable pathInfoTable;
955 if (failed(processPaths(*instanceGraph, namespaces, cache, pathInfoTable,
961 LoweringState loweringState;
965 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
966 SmallVector<FModuleLike> modulesToErasePortsFrom;
967 for (
auto *node : *instanceGraph) {
968 auto moduleLike = node->
getModule<firrtl::FModuleLike>();
972 if (shouldCreateClass(moduleLike)) {
973 auto omClass = createClass(moduleLike, pathInfoTable, intraPassMutex);
974 auto &classLoweringState = loweringState.classLoweringStateTable[omClass];
977 if (!classLoweringState.moduleLike)
978 classLoweringState.moduleLike = moduleLike;
981 if (!isa<firrtl::ClassLike>(moduleLike.getOperation()))
982 modulesToErasePortsFrom.push_back(moduleLike);
989 for (
auto *instance : *node) {
990 auto inst = instance->
getInstance<firrtl::InstanceOp>();
994 auto module = instance->getTarget()->getModule<FModuleLike>();
995 if (module && shouldCreateClass(module)) {
997 {inst, 0}, [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
998 return namespaces[module];
1000 SmallVector<Attribute> path = {targetSym};
1001 auto pathAttr = ArrayAttr::get(ctx, path);
1002 auto hierPath = cache.
getOpFor(pathAttr);
1003 classLoweringState.paths.push_back(hierPath);
1007 if (
auto classLike =
1008 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
1009 classTypeTable[classLike.getModuleNameAttr()] =
1010 classLike.getInstanceType();
1015 mlir::parallelForEach(ctx, loweringState.classLoweringStateTable,
1016 [
this, &pathInfoTable](
auto &entry) {
1017 const auto &[classLike, state] = entry;
1018 lowerClassLike(state.moduleLike, classLike,
1025 for (
auto moduleLike : modulesToErasePortsFrom) {
1026 BitVector portsToErase(moduleLike.getNumPorts());
1027 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i)
1028 if (isa<PropertyType>(moduleLike.getPortType(i)))
1029 portsToErase.set(i);
1030 moduleLike.erasePorts(portsToErase);
1034 for (
auto &[omClass, state] : loweringState.classLoweringStateTable) {
1035 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
1037 for (
auto *use : llvm::make_early_inc_range(node->
uses()))
1039 instanceGraph->erase(node);
1040 state.moduleLike.erase();
1045 SmallVector<Operation *> objectContainers;
1046 for (
auto &op : circuit.getOps())
1047 if (isa<FModuleOp, om::ClassLike>(op))
1048 objectContainers.push_back(&op);
1052 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
1053 return updateInstances(op, *instanceGraph, loweringState,
1054 pathInfoTable, intraPassMutex);
1056 return signalPassFailure();
1059 if (!rtlPortsToCreate.empty())
1060 createAllRtlPorts(pathInfoTable, namespaces, cache);
1064 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
1065 return dialectConversion(op, pathInfoTable, classTypeTable);
1067 return signalPassFailure();
1070 markAnalysesPreserved<InstanceGraph>();
1073 externalClassMap.clear();
1074 rtlPortsToCreate.clear();
1078bool LowerClassesPass::shouldCreateClass(igraph::ModuleOpInterface modOp) {
1082bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
1083 return shouldCreateClass(instanceGraph->lookup(modName)->getModule());
1087 SmallVector<Attribute> &fieldNames,
1088 SmallVector<NamedAttribute> &fieldTypes) {
1089 if (hasContainingModule) {
1090 auto name = builder.getStringAttr(kPortsName);
1091 fieldNames.push_back(name);
1092 fieldTypes.push_back(NamedAttribute(
1093 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
1099 ArrayRef<StringRef> formalParamNames,
1100 bool hasContainingModule) {
1101 SmallVector<Attribute> fieldNames;
1102 SmallVector<NamedAttribute> fieldTypes;
1103 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1104 auto type = moduleLike.getPortType(i);
1105 if (!isa<PropertyType>(type))
1108 auto direction = moduleLike.getPortDirection(i);
1110 auto name = moduleLike.getPortNameAttr(i);
1111 fieldNames.push_back(name);
1112 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1117 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1118 formalParamNames, fieldNames, fieldTypes);
1121static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1123 ArrayRef<StringRef> formalParamNames,
1124 bool hasContainingModule) {
1126 SmallVector<Attribute> fieldNames;
1127 SmallVector<NamedAttribute> fieldTypes;
1128 for (
auto op : llvm::make_early_inc_range(
1129 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1130 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1134 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1136 fieldNames.push_back(name);
1137 fieldTypes.push_back(
1138 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1143 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1144 formalParamNames, fieldNames, fieldTypes);
1148om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1149 const PathInfoTable &pathInfoTable,
1150 std::mutex &intraPassMutex) {
1152 SmallVector<StringRef> formalParamNames;
1154 formalParamNames.emplace_back(
"basepath");
1157 size_t nAltBasePaths =
1158 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1159 for (
size_t i = 0; i < nAltBasePaths; ++i)
1160 formalParamNames.push_back(StringAttr::get(
1161 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1164 bool hasContainingModule =
false;
1165 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts())) {
1166 if (port.isInput() && isa<PropertyType>(port.type)) {
1167 formalParamNames.push_back(port.name);
1170 if (port.name.strref().contains(kContainingModuleName))
1171 hasContainingModule =
true;
1175 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1178 if (hasContainingModule)
1179 formalParamNames.push_back(kPortsName);
1182 StringRef className = moduleLike.getModuleName();
1183 StringAttr baseClassNameAttr = moduleLike.getModuleNameAttr();
1186 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation())) {
1187 className = externMod.getExtModuleName();
1188 baseClassNameAttr = externMod.getExtModuleNameAttr();
1194 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1197 om::ClassLike loweredClassOp;
1198 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1199 moduleLike.getOperation())) {
1202 auto [it, inserted] = externalClassMap.insert({baseClassNameAttr, {}});
1204 it->getSecond() =
convertExtClass(moduleLike, builder, className + suffix,
1205 formalParamNames, hasContainingModule);
1206 loweredClassOp = it->getSecond();
1208 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1209 formalParamNames, hasContainingModule);
1212 SymbolTable::setSymbolVisibility(
1214 cast<mlir::SymbolOpInterface>(moduleLike.getOperation()).getVisibility());
1216 return loweredClassOp;
1219void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1220 om::ClassLike classLike,
1221 const PathInfoTable &pathInfoTable) {
1223 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1224 return lowerClass(classOp, moduleLike, pathInfoTable);
1226 if (
auto classExternOp =
1227 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1228 return lowerClassExtern(classExternOp, moduleLike);
1230 llvm_unreachable(
"unhandled class-like op");
1233void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1234 const PathInfoTable &pathInfoTable) {
1236 SmallVector<Property> inputProperties;
1237 BitVector portsToErase(moduleLike.getNumPorts());
1238 bool hasContainingModule =
false;
1239 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts())) {
1241 if (!isa<PropertyType>(port.type))
1245 if (port.isInput()) {
1246 inputProperties.push_back({index, port.name, port.type, port.loc});
1249 if (port.name.strref().contains(kContainingModuleName))
1250 hasContainingModule =
true;
1254 portsToErase.set(index);
1259 Block *moduleBody = &moduleLike->getRegion(0).front();
1260 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1262 auto basePathType = om::BasePathType::get(&getContext());
1263 auto unknownLoc = UnknownLoc::get(&getContext());
1264 classBody->addArgument(basePathType, unknownLoc);
1267 size_t nAltBasePaths =
1268 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1269 for (
size_t i = 0; i < nAltBasePaths; ++i)
1270 classBody->addArgument(basePathType, unknownLoc);
1273 for (
auto &op : llvm::make_early_inc_range(llvm::reverse(*moduleBody))) {
1274 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1275 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1277 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1278 for (
auto result : instance.getResults()) {
1279 if (isa<PropertyType>(result.getType()))
1280 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1285 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1286 if (llvm::any_of(op.getOperands(), isProperty) ||
1287 llvm::any_of(op.getResults(), isProperty))
1288 op.moveBefore(classBody, classBody->begin());
1292 for (
auto input : inputProperties) {
1293 auto arg = classBody->addArgument(input.type, input.loc);
1294 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1297 llvm::SmallVector<mlir::Location> fieldLocs;
1298 llvm::SmallVector<mlir::Value> fieldValues;
1299 for (Operation &op :
1300 llvm::make_early_inc_range(classOp.getBodyBlock()->getOperations())) {
1301 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1302 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1304 fieldLocs.push_back(op.getLoc());
1305 fieldValues.push_back(propAssign.getSrc());
1312 if (hasContainingModule) {
1313 BlockArgument argumentValue = classBody->addArgument(
1314 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1315 fieldLocs.push_back(argumentValue.getLoc());
1316 fieldValues.push_back(argumentValue);
1319 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1320 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1325void LowerClassesPass::lowerClassExtern(om::ClassExternOp classExternOp,
1326 FModuleLike moduleLike) {
1330 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1333 classBody->addArgument(om::BasePathType::get(&getContext()),
1334 UnknownLoc::get(&getContext()));
1336 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1337 auto type = moduleLike.getPortType(i);
1338 if (!isa<PropertyType>(type))
1341 auto loc = moduleLike.getPortLocation(i);
1342 auto direction = moduleLike.getPortDirection(i);
1343 if (direction == Direction::In)
1344 classBody->addArgument(type, loc);
1354 firrtl::PathOp containingModuleRef,
1355 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate,
1356 std::mutex &intraPassMutex,
1357 SmallVectorImpl<Operation *> &opsToErase) {
1358 auto basePath = firrtlObject->getBlock()->getArgument(0);
1359 auto classType = om::ClassType::get(firrtlObject->getContext(),
1360 firrtlObject.getClassSymbolAttr());
1361 OpBuilder builder(firrtlObject.getOperation());
1362 auto object = om::ObjectOp::create(builder, firrtlObject->getLoc(), classType,
1363 firrtlObject.getClassNameAttr(), args);
1365 if (containingModuleRef) {
1366 std::lock_guard<std::mutex> guard(intraPassMutex);
1367 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1370 auto cast = UnrealizedConversionCastOp::create(builder,
object.
getLoc(),
1371 firrtlObject.getClassType(),
1372 object.getResult());
1373 firrtlObject.getResult().replaceAllUsesWith(cast.getResult(0));
1374 opsToErase.push_back(firrtlObject.getOperation());
1381 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1382 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1383 SmallVectorImpl<Operation *> &opsToErase) {
1385 auto basePath = firrtlObject->getBlock()->getArgument(0);
1388 auto firrtlClassType = firrtlObject.getType();
1389 auto numElements = firrtlClassType.getNumElements();
1390 llvm::SmallVector<unsigned> argIndexTable;
1394 SmallVector<Value> altBasePaths;
1395 pathInfoTable.collectAltBasePaths(
1396 firrtlObject, firrtlObject.getClassNameAttr(), altBasePaths);
1399 unsigned nextArgIndex = 1 + altBasePaths.size();
1402 auto direction = firrtlClassType.getElement(i).direction;
1404 argIndexTable[i] = nextArgIndex++;
1410 llvm::SmallVector<Value> args;
1411 args.resize(nextArgIndex);
1415 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1416 args[1 + i] = altBasePath;
1418 firrtl::PathOp containingModuleRef;
1419 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1420 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1421 auto index = subfield.getIndex();
1422 auto direction = firrtlClassType.getElement(index).direction;
1429 for (
auto *subfieldUser :
1430 llvm::make_early_inc_range(subfield->getUsers())) {
1431 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1435 auto dst = propassign.getOperand(0);
1436 auto src = propassign.getOperand(1);
1437 if (dst == subfield.getResult()) {
1438 args[argIndexTable[index]] = src;
1439 opsToErase.push_back(propassign);
1442 if (firrtlClassType.getElement(index).name.strref().contains(
1443 kContainingModuleName)) {
1444 assert(!containingModuleRef &&
1445 "expected exactly one containingModule");
1446 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1447 "expected containingModule to be a PathOp");
1448 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1454 opsToErase.push_back(subfield);
1460 auto element = firrtlClassType.getElement(i);
1464 auto argIndex = argIndexTable[i];
1465 if (!args[argIndex])
1466 return emitError(firrtlObject.getLoc())
1467 <<
"uninitialized input port " << element.name;
1471 args, containingModuleRef, rtlPortsToCreate,
1472 intraPassMutex, opsToErase);
1477 firrtl::ObjectCreateOp firrtlObject,
const PathInfoTable &pathInfoTable,
1478 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1479 SmallVectorImpl<Operation *> &opsToErase) {
1480 auto basePath = firrtlObject->getBlock()->getArgument(0);
1481 SmallVector<Value> args{basePath};
1482 pathInfoTable.collectAltBasePaths(firrtlObject,
1483 firrtlObject.getClassNameAttr(), args);
1484 llvm::append_range(args, firrtlObject.getInputs());
1486 firrtl::PathOp containingModuleRef;
1487 for (
auto [inputIndex, element] :
1488 llvm::enumerate(firrtlObject.getType().getInputElements())) {
1489 auto input = firrtlObject.getInputs()[inputIndex];
1490 if (element.name.strref().contains(kContainingModuleName)) {
1491 assert(!containingModuleRef &&
"expected exactly one containingModule");
1492 containingModuleRef = input.getDefiningOp<firrtl::PathOp>();
1493 assert(containingModuleRef &&
"expected containingModule to be a PathOp");
1498 args, containingModuleRef, rtlPortsToCreate,
1499 intraPassMutex, opsToErase);
1508 const PathInfoTable &pathInfoTable,
1509 SmallVectorImpl<Operation *> &opsToErase) {
1512 OpBuilder builder(firrtlInstance);
1517 SmallVector<Value> actualParameters;
1519 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1520 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1521 auto rebasedPath = om::BasePathCreateOp::create(
1522 builder, firrtlInstance->getLoc(), basePath, symRef);
1524 actualParameters.push_back(rebasedPath);
1527 pathInfoTable.collectAltBasePaths(
1528 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1531 for (
auto result : firrtlInstance.getResults()) {
1533 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1538 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1539 if (!propertyResult)
1545 assert(propertyAssignment &&
"properties require single assignment");
1546 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1549 opsToErase.push_back(propertyAssignment);
1553 auto referencedModule =
1554 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1556 StringRef moduleName = referencedModule.getModuleName();
1559 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1560 moduleName = externMod.getExtModuleName();
1563 auto className = FlatSymbolRefAttr::get(
1564 builder.getStringAttr(moduleName + kClassNameSuffix));
1566 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1570 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1571 className.getAttr(), actualParameters);
1576 for (
auto result : firrtlInstance.getResults()) {
1578 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1583 if (!isa<PropertyType>(result.getType()))
1587 auto objectField = om::ObjectFieldOp::create(
1588 builder,
object.
getLoc(), result.getType(),
object,
1589 firrtlInstance.getPortNameAttr(result.getResultNumber()));
1591 result.replaceAllUsesWith(objectField);
1595 opsToErase.push_back(firrtlInstance);
1603 SmallVectorImpl<Operation *> &opsToErase) {
1605 BitVector portsToErase(firrtlInstance.getNumResults());
1606 for (
auto result : firrtlInstance.getResults())
1607 if (isa<PropertyType>(result.getType()))
1608 portsToErase.set(result.getResultNumber());
1611 if (portsToErase.none())
1616 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1625 opsToErase.push_back(firrtlInstance);
1631 SmallVectorImpl<Operation *> &opsToErase) {
1632 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1634 assert(!isa<firrtl::FObjectLike>(op) &&
"should be no objects in modules");
1635 if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1645 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1646 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1647 SmallVectorImpl<Operation *> &opsToErase) {
1648 OpBuilder builder(classOp);
1649 auto &classState = state.classLoweringStateTable.at(classOp);
1650 auto it = classState.paths.begin();
1651 for (
auto &op : classOp->getRegion(0).getOps()) {
1652 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1654 intraPassMutex, opsToErase)))
1656 }
else if (
auto objectOp = dyn_cast<firrtl::ObjectCreateOp>(op)) {
1658 intraPassMutex, opsToErase)))
1660 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1662 pathInfoTable, opsToErase)))
1670LogicalResult LowerClassesPass::updateInstances(
1671 Operation *op, InstanceGraph &instanceGraph,
const LoweringState &state,
1672 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1677 SmallVector<Operation *> opsToErase;
1679 TypeSwitch<Operation *, LogicalResult>(op)
1681 .Case([&](FModuleOp moduleOp) {
1686 .Case([&](om::ClassOp classOp) {
1690 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1691 intraPassMutex, opsToErase);
1693 .Default([](
auto *op) {
return success(); });
1697 for (
auto *op : opsToErase)
1704void LowerClassesPass::createAllRtlPorts(
1705 const PathInfoTable &pathInfoTable,
1706 hw::InnerSymbolNamespaceCollection &namespaces,
1707 HierPathCache &hierPathCache) {
1708 MLIRContext *ctx = &getContext();
1711 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1714 om::ClassOp::buildSimpleClassOp(
1715 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1716 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1717 {om::PathType::get(ctx), om::StringType::get(ctx),
1718 om::OMIntegerType::get(ctx)});
1721 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1722 return lhs.object.getClassName() < rhs.object.getClassName();
1726 for (
auto rtlPortToCreate : rtlPortsToCreate)
1727 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1737struct FIntegerConstantOpConversion
1738 :
public OpConversionPattern<FIntegerConstantOp> {
1739 using OpConversionPattern::OpConversionPattern;
1742 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1743 ConversionPatternRewriter &rewriter)
const override {
1744 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1745 op, om::OMIntegerType::get(op.getContext()),
1746 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1751struct BoolConstantOpConversion :
public OpConversionPattern<BoolConstantOp> {
1752 using OpConversionPattern::OpConversionPattern;
1755 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1756 ConversionPatternRewriter &rewriter)
const override {
1757 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1758 op, rewriter.getBoolAttr(adaptor.getValue()));
1763struct PropertyAssertOpConversion
1764 :
public OpConversionPattern<firrtl::PropertyAssertOp> {
1765 using OpConversionPattern::OpConversionPattern;
1768 matchAndRewrite(firrtl::PropertyAssertOp op, OpAdaptor adaptor,
1769 ConversionPatternRewriter &rewriter)
const override {
1770 rewriter.replaceOpWithNewOp<om::PropertyAssertOp>(
1771 op, adaptor.getCondition(), adaptor.getMessage());
1776struct DoubleConstantOpConversion
1777 :
public OpConversionPattern<DoubleConstantOp> {
1778 using OpConversionPattern::OpConversionPattern;
1781 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1782 ConversionPatternRewriter &rewriter)
const override {
1783 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1788struct StringConstantOpConversion
1789 :
public OpConversionPattern<StringConstantOp> {
1790 using OpConversionPattern::OpConversionPattern;
1793 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1794 ConversionPatternRewriter &rewriter)
const override {
1795 auto stringType = om::StringType::get(op.getContext());
1796 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1797 op, stringType, StringAttr::get(op.getValue(), stringType));
1802struct ListCreateOpConversion
1803 :
public OpConversionPattern<firrtl::ListCreateOp> {
1804 using OpConversionPattern::OpConversionPattern;
1807 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1808 ConversionPatternRewriter &rewriter)
const override {
1809 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1812 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1813 adaptor.getElements());
1818struct ListConcatOpConversion
1819 :
public OpConversionPattern<firrtl::ListConcatOp> {
1820 using OpConversionPattern::OpConversionPattern;
1823 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1824 ConversionPatternRewriter &rewriter)
const override {
1825 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1828 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1829 adaptor.getSubLists());
1834struct IntegerAddOpConversion
1835 :
public OpConversionPattern<firrtl::IntegerAddOp> {
1836 using OpConversionPattern::OpConversionPattern;
1839 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1840 ConversionPatternRewriter &rewriter)
const override {
1841 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1847struct IntegerMulOpConversion
1848 :
public OpConversionPattern<firrtl::IntegerMulOp> {
1849 using OpConversionPattern::OpConversionPattern;
1852 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1853 ConversionPatternRewriter &rewriter)
const override {
1854 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1860struct IntegerShrOpConversion
1861 :
public OpConversionPattern<firrtl::IntegerShrOp> {
1862 using OpConversionPattern::OpConversionPattern;
1865 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1866 ConversionPatternRewriter &rewriter)
const override {
1867 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1873struct IntegerShlOpConversion
1874 :
public OpConversionPattern<firrtl::IntegerShlOp> {
1875 using OpConversionPattern::OpConversionPattern;
1878 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1879 ConversionPatternRewriter &rewriter)
const override {
1880 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1886struct StringConcatOpConversion
1887 :
public OpConversionPattern<firrtl::StringConcatOp> {
1888 using OpConversionPattern::OpConversionPattern;
1891 matchAndRewrite(firrtl::StringConcatOp op, OpAdaptor adaptor,
1892 ConversionPatternRewriter &rewriter)
const override {
1893 rewriter.replaceOpWithNewOp<om::StringConcatOp>(op, adaptor.getOperands());
1898struct ListContainsOpConversion
1899 :
public OpConversionPattern<firrtl::ListContainsOp> {
1900 using OpConversionPattern::OpConversionPattern;
1903 matchAndRewrite(firrtl::ListContainsOp op, OpAdaptor adaptor,
1904 ConversionPatternRewriter &rewriter)
const override {
1905 rewriter.replaceOpWithNewOp<om::ListContainsOp>(op, adaptor.getList(),
1906 adaptor.getElement());
1911struct PropEqOpConversion :
public OpConversionPattern<firrtl::PropEqOp> {
1912 using OpConversionPattern::OpConversionPattern;
1915 matchAndRewrite(firrtl::PropEqOp op, OpAdaptor adaptor,
1916 ConversionPatternRewriter &rewriter)
const override {
1917 rewriter.replaceOpWithNewOp<om::PropEqOp>(op, adaptor.getLhs(),
1923template <
typename FIRRTLOp,
typename OMOp>
1924static LogicalResult binaryOpConversion(FIRRTLOp op,
1925 typename FIRRTLOp::Adaptor adaptor,
1926 ConversionPatternRewriter &rewriter) {
1927 rewriter.replaceOpWithNewOp<OMOp>(op, adaptor.getLhs(), adaptor.getRhs());
1931struct PathOpConversion :
public OpConversionPattern<firrtl::PathOp> {
1933 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1934 const PathInfoTable &pathInfoTable,
1935 PatternBenefit benefit = 1)
1936 : OpConversionPattern(typeConverter,
context, benefit),
1937 pathInfoTable(pathInfoTable) {}
1940 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1941 ConversionPatternRewriter &rewriter)
const override {
1942 auto *
context = op->getContext();
1943 auto pathType = om::PathType::get(
context);
1944 const auto *pathInfoIt = pathInfoTable.table.find(op.getTarget());
1947 auto basePath = op->getBlock()->getArgument(0);
1951 if (pathInfoIt == pathInfoTable.table.end()) {
1952 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1953 return emitError(op.getLoc(),
"DontTouch target was deleted");
1954 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1955 return emitError(op.getLoc(),
"Instance target was deleted");
1956 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1960 auto pathInfo = pathInfoIt->second;
1961 auto symbol = pathInfo.symRef;
1965 om::TargetKind targetKind;
1966 switch (op.getTargetKind()) {
1967 case firrtl::TargetKind::DontTouch:
1968 targetKind = om::TargetKind::DontTouch;
1970 case firrtl::TargetKind::Reference:
1971 targetKind = om::TargetKind::Reference;
1973 case firrtl::TargetKind::Instance:
1974 if (!pathInfo.canBeInstanceTarget)
1975 return emitError(op.getLoc(),
"invalid target for instance path")
1976 .attachNote(pathInfo.loc)
1977 <<
"target not instance or module";
1978 targetKind = om::TargetKind::Instance;
1980 case firrtl::TargetKind::MemberInstance:
1981 case firrtl::TargetKind::MemberReference:
1982 if (pathInfo.canBeInstanceTarget)
1983 targetKind = om::TargetKind::MemberInstance;
1985 targetKind = om::TargetKind::MemberReference;
1991 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1995 auto parent = op->getParentOfType<om::ClassOp>();
1996 auto parentName = parent.getName();
1997 if (parentName.ends_with(kClassNameSuffix))
1998 parentName = parentName.drop_back(kClassNameSuffix.size());
1999 auto originalParentName = StringAttr::get(op->getContext(), parentName);
2003 pathInfoTable.getRootsForPassthrough(originalParentName);
2004 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
2007 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
2008 if (altBasePathModule == altBasePath) {
2010 auto basePathArg = op->getBlock()->getArgument(1 + i);
2011 assert(isa<om::BasePathType>(basePathArg.getType()) &&
2012 "expected a passthrough base path");
2013 basePath = basePathArg;
2018 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
2019 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
2024 const PathInfoTable &pathInfoTable;
2027struct WireOpConversion :
public OpConversionPattern<WireOp> {
2028 using OpConversionPattern::OpConversionPattern;
2031 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
2032 ConversionPatternRewriter &rewriter)
const override {
2033 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
2042 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
2043 if (!regionKindInterface)
2045 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
2054 rewriter.replaceOp(wireOp, propAssign.getSrc());
2057 rewriter.eraseOp(propAssign);
2063struct AnyCastOpConversion :
public OpConversionPattern<ObjectAnyRefCastOp> {
2064 using OpConversionPattern::OpConversionPattern;
2067 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
2068 ConversionPatternRewriter &rewriter)
const override {
2069 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
2074struct ObjectSubfieldOpConversion
2075 :
public OpConversionPattern<firrtl::ObjectSubfieldOp> {
2076 using OpConversionPattern::OpConversionPattern;
2078 ObjectSubfieldOpConversion(
2079 const TypeConverter &typeConverter, MLIRContext *
context,
2080 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
2081 : OpConversionPattern(typeConverter,
context),
2082 classTypeTable(classTypeTable) {}
2085 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
2086 ConversionPatternRewriter &rewriter)
const override {
2087 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
2093 auto firrtlClassType =
2094 classTypeTable.lookup(omClassType.getClassName().getAttr());
2095 if (!firrtlClassType)
2098 const auto &element = firrtlClassType.getElement(op.getIndex());
2100 if (element.direction == Direction::In)
2103 auto type = typeConverter->convertType(element.type);
2104 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
2109 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
2112struct ClassFieldsOpConversion :
public OpConversionPattern<om::ClassFieldsOp> {
2113 using OpConversionPattern::OpConversionPattern;
2116 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
2117 ConversionPatternRewriter &rewriter)
const override {
2118 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
2119 adaptor.getFieldLocsAttr());
2124struct ObjectOpConversion :
public OpConversionPattern<om::ObjectOp> {
2125 using OpConversionPattern::OpConversionPattern;
2128 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
2129 ConversionPatternRewriter &rewriter)
const override {
2132 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
2133 adaptor.getClassNameAttr(),
2134 adaptor.getActualParams());
2139static LogicalResult convertClassLike(om::ClassLike classOp,
2140 TypeConverter typeConverter,
2141 ConversionPatternRewriter &rewriter) {
2142 Block *body = classOp.getBodyBlock();
2143 TypeConverter::SignatureConversion result(body->getNumArguments());
2147 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
2151 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
2155 rewriter.modifyOpInPlace(classOp, [&]() {
2156 mlir::AttrTypeReplacer replacer;
2157 replacer.addReplacement([&](TypeAttr typeAttr) {
2158 return mlir::TypeAttr::get(
2159 typeConverter.convertType(typeAttr.getValue()));
2161 classOp.replaceFieldTypes(replacer);
2167struct ClassOpSignatureConversion :
public OpConversionPattern<om::ClassOp> {
2168 using OpConversionPattern::OpConversionPattern;
2171 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
2172 ConversionPatternRewriter &rewriter)
const override {
2173 return convertClassLike(classOp, *typeConverter, rewriter);
2177struct ClassExternOpSignatureConversion
2178 :
public OpConversionPattern<om::ClassExternOp> {
2179 using OpConversionPattern::OpConversionPattern;
2182 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2183 ConversionPatternRewriter &rewriter)
const override {
2184 return convertClassLike(classOp, *typeConverter, rewriter);
2188struct ObjectFieldOpConversion :
public OpConversionPattern<om::ObjectFieldOp> {
2189 using OpConversionPattern::OpConversionPattern;
2192 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2193 ConversionPatternRewriter &rewriter)
const override {
2196 auto type = typeConverter->convertType(op.getType());
2200 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2201 op, type, adaptor.getObject(), adaptor.getFieldAttr());
2208struct UnrealizedConversionCastOpConversion
2209 :
public OpConversionPattern<UnrealizedConversionCastOp> {
2210 using OpConversionPattern::OpConversionPattern;
2213 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2214 ConversionPatternRewriter &rewriter)
const override {
2215 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2217 auto type = typeConverter->convertType(op.getResult(0));
2218 if (!type || type != adaptor.getOperands()[0].getType())
2220 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2225struct UnknownValueOpConversion :
public OpConversionPattern<UnknownValueOp> {
2226 using OpConversionPattern::OpConversionPattern;
2229 matchAndRewrite(UnknownValueOp op, OpAdaptor adaptor,
2230 ConversionPatternRewriter &rewriter)
const override {
2231 auto convertedType = typeConverter->convertType(op.getType());
2234 rewriter.replaceOpWithNewOp<om::UnknownValueOp>(op, convertedType);
2248 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2249 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2252 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2253 auto containsFIRRTLType = [](Type type) {
2255 .walk([](Type type) {
2256 return failure(isa<FIRRTLDialect>(type.getDialect()));
2260 auto noFIRRTLOperands =
2261 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2262 return containsFIRRTLType(type);
2264 auto noFIRRTLResults =
2265 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2266 return containsFIRRTLType(type);
2268 return noFIRRTLOperands && noFIRRTLResults;
2272 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2273 [](Operation *op) -> std::optional<bool> {
2274 auto classLike = dyn_cast<om::ClassLike>(op);
2276 return std::nullopt;
2277 auto fieldNames = classLike.getFieldNames();
2278 if (!llvm::all_of(fieldNames, [&](
auto field) {
2279 std::optional<Type> type =
2280 classLike.getFieldType(cast<StringAttr>(field));
2281 return type.has_value() && !isa<FIRRTLType>(type.value());
2285 return llvm::none_of(
2286 classLike.getBodyBlock()->getArgumentTypes(),
2287 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2293 converter.addConversion([](IntegerType type) {
2294 return om::OMIntegerType::get(type.getContext());
2296 converter.addConversion([](FIntegerType type) {
2300 return om::OMIntegerType::get(type.getContext());
2304 converter.addConversion([](om::StringType type) {
return type; });
2305 converter.addConversion([](firrtl::StringType type) {
2306 return om::StringType::get(type.getContext());
2310 converter.addConversion([](om::PathType type) {
return type; });
2311 converter.addConversion([](om::BasePathType type) {
return type; });
2312 converter.addConversion([](om::FrozenPathType type) {
return type; });
2313 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2314 converter.addConversion([](firrtl::PathType type) {
2315 return om::PathType::get(type.getContext());
2319 converter.addConversion([](om::ClassType type) {
return type; });
2320 converter.addConversion([](firrtl::ClassType type) {
2321 return om::ClassType::get(type.getContext(), type.getNameAttr());
2325 converter.addConversion([](om::AnyType type) {
return type; });
2326 converter.addConversion([](firrtl::AnyRefType type) {
2327 return om::AnyType::get(type.getContext());
2331 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2333 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2335 auto elementType = converter.convertType(type.getElementType());
2341 converter.addConversion(
2342 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2344 return convertListType(type);
2347 converter.addConversion(
2348 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2350 return convertListType(type);
2354 converter.addConversion(
2355 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2358 converter.addConversion(
2359 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2363 converter.addTargetMaterialization(
2364 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2365 assert(values.size() == 1);
2366 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2372 converter.addSourceMaterialization(
2373 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2374 assert(values.size() == 1);
2375 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2382 const PathInfoTable &pathInfoTable,
2383 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2384 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2385 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2393 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2394 patterns.add<ClassExternOpSignatureConversion>(converter,
2401 patterns.add<PropertyAssertOpConversion>(converter,
patterns.getContext());
2402 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2410 patterns.add(binaryOpConversion<firrtl::ObjectEqOp, om::ObjectEqOp>);
2411 patterns.add(binaryOpConversion<firrtl::BoolAndOp, om::IntegerAndOp>);
2412 patterns.add(binaryOpConversion<firrtl::BoolOrOp, om::IntegerOrOp>);
2413 patterns.add(binaryOpConversion<firrtl::BoolXorOp, om::IntegerXorOp>);
2414 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2420LogicalResult LowerClassesPass::dialectConversion(
2421 Operation *op,
const PathInfoTable &pathInfoTable,
2422 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2423 ConversionTarget target(getContext());
2426 TypeConverter typeConverter;
2429 ConversionPatternSet patterns(&getContext(), typeConverter);
2433 return applyPartialConversion(op, target, std::move(patterns));
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
static std::unique_ptr< Context > context
static LogicalResult updateInstancesInModule(FModuleOp moduleOp, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static void populateConversionTarget(ConversionTarget &target)
static om::ClassLike convertClass(FModuleLike moduleLike, OpBuilder builder, Twine name, ArrayRef< StringRef > formalParamNames, bool hasContainingModule)
static LogicalResult updateInstanceInClass(InstanceOp firrtlInstance, hw::HierPathOp hierPath, InstanceGraph &instanceGraph, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static void populateRewritePatterns(ConversionPatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
static void populateTypeConverter(TypeConverter &converter)
static LogicalResult updateInstanceInModule(InstanceOp firrtlInstance, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateObjectInClass(firrtl::ObjectOp firrtlObject, const PathInfoTable &pathInfoTable, SmallVectorImpl< RtlPortsInfo > &rtlPortsToCreate, std::mutex &intraPassMutex, SmallVectorImpl< Operation * > &opsToErase)
void checkAddContainingModulePorts(bool hasContainingModule, OpBuilder builder, SmallVector< Attribute > &fieldNames, SmallVector< NamedAttribute > &fieldTypes)
static om::ClassLike convertExtClass(FModuleLike moduleLike, OpBuilder builder, Twine name, ArrayRef< StringRef > formalParamNames, bool hasContainingModule)
static LogicalResult updateObjectsAndInstancesInClass(om::ClassOp classOp, InstanceGraph &instanceGraph, const LoweringState &state, const PathInfoTable &pathInfoTable, SmallVectorImpl< RtlPortsInfo > &rtlPortsToCreate, std::mutex &intraPassMutex, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult lowerObjectInClass(FObjectLike firrtlObject, ArrayRef< Value > args, firrtl::PathOp containingModuleRef, SmallVectorImpl< RtlPortsInfo > &rtlPortsToCreate, std::mutex &intraPassMutex, SmallVectorImpl< Operation * > &opsToErase)
static Location getLoc(DefSlot slot)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static Block * getBodyBlock(FModuleLike mod)
Extension of RewritePatternSet that allows adding matchAndRewrite functions with op adaptors and Conv...
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.
bool moduleContainsProperties(igraph::ModuleOpInterface op)
Return true if this module contains (or its children transitively contain) any property operations,...
llvm::iterator_range< UseIterator > uses()
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.
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.
InstanceGraphNode * getParent() const
Get the module where the instantiation lives.
auto getInstance()
Get the instance-like op that this is tracking.
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.
igraph::InstanceRecord InstanceRecord
PropAssignOp getPropertyAssignment(FIRRTLPropertyValue value)
Return the single assignment to a Property value.
igraph::InstancePathCache InstancePathCache
BaseTy type_dyn_cast(Type type)
igraph::InstanceGraphNode InstanceGraphNode
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
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)
const SymbolTable & getSymbolTable() const
This represents an annotation targeting a specific port of a module, memory, or instance.
InnerSymbolNamespace & get(Operation *op)