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);
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;
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,
216 LogicalResult dialectConversion(
217 Operation *op,
const PathInfoTable &pathInfoTable,
218 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
227 SmallVector<RtlPortsInfo> rtlPortsToCreate;
234 DenseMap<StringAttr, om::ClassLike> externalClassMap;
243 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
244 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
246 PathTracker(FModuleLike module,
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,
274 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
275 StringAttr moduleName,
276 FModuleOp owningModule,
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) {
347 auto portType = type_dyn_cast<FIRRTLBaseType>(mod.getPortType(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 =
373 mod.getPortDirection(i) == Direction::Out ?
"Output" :
"Input";
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) {
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},
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());
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,
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(
738 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
739 auto circuit = getOperation();
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)))
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";
934 return failure(failed);
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>();
954 PathInfoTable pathInfoTable;
955 if (failed(processPaths(*instanceGraph, namespaces, cache, pathInfoTable,
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)) {
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) {
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 rtlPortsToCreate.clear();
1077bool LowerClassesPass::shouldCreateClass(igraph::ModuleOpInterface modOp) {
1078 return instanceInfo->moduleContainsProperties(modOp);
1081bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
1082 return shouldCreateClass(instanceGraph->
lookup(modName)->
getModule());
1086 SmallVector<Attribute> &fieldNames,
1087 SmallVector<NamedAttribute> &fieldTypes) {
1088 if (hasContainingModule) {
1089 auto name = builder.getStringAttr(kPortsName);
1090 fieldNames.push_back(name);
1091 fieldTypes.push_back(NamedAttribute(
1092 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
1098 ArrayRef<StringRef> formalParamNames,
1099 bool hasContainingModule) {
1100 SmallVector<Attribute> fieldNames;
1101 SmallVector<NamedAttribute> fieldTypes;
1102 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1103 auto type = moduleLike.getPortType(i);
1104 if (!isa<PropertyType>(type))
1107 auto direction = moduleLike.getPortDirection(i);
1108 if (direction != Direction::In) {
1109 auto name = moduleLike.getPortNameAttr(i);
1110 fieldNames.push_back(name);
1111 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1116 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1117 formalParamNames, fieldNames, fieldTypes);
1120static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1122 ArrayRef<StringRef> formalParamNames,
1123 bool hasContainingModule) {
1125 SmallVector<Attribute> fieldNames;
1126 SmallVector<NamedAttribute> fieldTypes;
1127 for (
auto op : llvm::make_early_inc_range(
1128 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1129 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1133 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1135 fieldNames.push_back(name);
1136 fieldTypes.push_back(
1137 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1142 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1143 formalParamNames, fieldNames, fieldTypes);
1147om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1148 const PathInfoTable &pathInfoTable,
1149 std::mutex &intraPassMutex) {
1151 SmallVector<StringRef> formalParamNames;
1153 formalParamNames.emplace_back(
"basepath");
1156 size_t nAltBasePaths =
1157 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1158 for (
size_t i = 0; i < nAltBasePaths; ++i)
1159 formalParamNames.push_back(StringAttr::get(
1160 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1163 bool hasContainingModule =
false;
1164 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1165 if (port.isInput() && isa<PropertyType>(port.type)) {
1166 formalParamNames.push_back(port.name);
1169 if (port.name.strref().contains(kContainingModuleName))
1170 hasContainingModule =
true;
1174 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1177 if (hasContainingModule)
1178 formalParamNames.push_back(kPortsName);
1181 StringRef className = moduleLike.getModuleName();
1182 StringAttr baseClassNameAttr = moduleLike.getModuleNameAttr();
1185 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation())) {
1186 className = externMod.getExtModuleName();
1187 baseClassNameAttr = externMod.getExtModuleNameAttr();
1193 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1196 om::ClassLike loweredClassOp;
1197 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1198 moduleLike.getOperation())) {
1201 auto [it, inserted] = externalClassMap.insert({baseClassNameAttr, {}});
1203 it->getSecond() =
convertExtClass(moduleLike, builder, className + suffix,
1204 formalParamNames, hasContainingModule);
1205 loweredClassOp = it->getSecond();
1207 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1208 formalParamNames, hasContainingModule);
1211 SymbolTable::setSymbolVisibility(
1213 cast<mlir::SymbolOpInterface>(moduleLike.getOperation()).getVisibility());
1215 return loweredClassOp;
1218void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1219 om::ClassLike classLike,
1220 const PathInfoTable &pathInfoTable) {
1222 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1223 return lowerClass(classOp, moduleLike, pathInfoTable);
1225 if (
auto classExternOp =
1226 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1227 return lowerClassExtern(classExternOp, moduleLike);
1229 llvm_unreachable(
"unhandled class-like op");
1232void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1233 const PathInfoTable &pathInfoTable) {
1235 SmallVector<Property> inputProperties;
1236 BitVector portsToErase(moduleLike.getNumPorts());
1237 bool hasContainingModule =
false;
1238 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1240 if (!isa<PropertyType>(port.type))
1244 if (port.isInput()) {
1245 inputProperties.push_back({index, port.name, port.type, port.loc});
1248 if (port.name.strref().contains(kContainingModuleName))
1249 hasContainingModule =
true;
1253 portsToErase.set(index);
1258 Block *moduleBody = &moduleLike->getRegion(0).front();
1259 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1261 auto basePathType = om::BasePathType::get(&getContext());
1262 auto unknownLoc = UnknownLoc::get(&getContext());
1263 classBody->addArgument(basePathType, unknownLoc);
1266 size_t nAltBasePaths =
1267 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1268 for (
size_t i = 0; i < nAltBasePaths; ++i)
1269 classBody->addArgument(basePathType, unknownLoc);
1272 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1273 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1274 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1276 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1277 for (
auto result : instance.getResults()) {
1278 if (isa<PropertyType>(result.getType()))
1279 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1284 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1285 if (llvm::any_of(op.getOperands(), isProperty) ||
1286 llvm::any_of(op.getResults(), isProperty))
1287 op.moveBefore(classBody, classBody->begin());
1291 for (
auto input : inputProperties) {
1292 auto arg = classBody->addArgument(input.type, input.loc);
1293 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1296 llvm::SmallVector<mlir::Location> fieldLocs;
1297 llvm::SmallVector<mlir::Value> fieldValues;
1298 for (Operation &op :
1300 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1301 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1303 fieldLocs.push_back(op.getLoc());
1304 fieldValues.push_back(propAssign.getSrc());
1311 if (hasContainingModule) {
1312 BlockArgument argumentValue = classBody->addArgument(
1313 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1314 fieldLocs.push_back(argumentValue.getLoc());
1315 fieldValues.push_back(argumentValue);
1318 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1319 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1324void LowerClassesPass::lowerClassExtern(om::ClassExternOp classExternOp,
1325 FModuleLike moduleLike) {
1329 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1332 classBody->addArgument(om::BasePathType::get(&getContext()),
1333 UnknownLoc::get(&getContext()));
1335 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1336 auto type = moduleLike.getPortType(i);
1337 if (!isa<PropertyType>(type))
1340 auto loc = moduleLike.getPortLocation(i);
1341 auto direction = moduleLike.getPortDirection(i);
1342 if (direction == Direction::In)
1343 classBody->addArgument(type, loc);
1352 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1353 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1354 SmallVectorImpl<Operation *> &opsToErase) {
1356 auto basePath = firrtlObject->getBlock()->getArgument(0);
1359 auto firrtlClassType = firrtlObject.getType();
1360 auto numElements = firrtlClassType.getNumElements();
1361 llvm::SmallVector<unsigned> argIndexTable;
1365 SmallVector<Value> altBasePaths;
1366 pathInfoTable.collectAltBasePaths(
1367 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1370 unsigned nextArgIndex = 1 + altBasePaths.size();
1373 auto direction = firrtlClassType.getElement(i).direction;
1374 if (direction == Direction::In)
1375 argIndexTable[i] = nextArgIndex++;
1381 llvm::SmallVector<Value> args;
1382 args.resize(nextArgIndex);
1386 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1387 args[1 + i] = altBasePath;
1389 firrtl::PathOp containingModuleRef;
1390 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1391 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1392 auto index = subfield.getIndex();
1393 auto direction = firrtlClassType.getElement(index).direction;
1397 if (direction == Direction::Out)
1400 for (
auto *subfieldUser :
1401 llvm::make_early_inc_range(subfield->getUsers())) {
1402 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1406 auto dst = propassign.getOperand(0);
1407 auto src = propassign.getOperand(1);
1408 if (dst == subfield.getResult()) {
1409 args[argIndexTable[index]] = src;
1410 opsToErase.push_back(propassign);
1413 if (firrtlClassType.getElement(index).name.strref().contains(
1414 kContainingModuleName)) {
1415 assert(!containingModuleRef &&
1416 "expected exactly one containingModule");
1417 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1418 "expected containingModule to be a PathOp");
1419 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1425 opsToErase.push_back(subfield);
1431 auto element = firrtlClassType.getElement(i);
1432 if (element.direction == Direction::Out)
1435 auto argIndex = argIndexTable[i];
1436 if (!args[argIndex])
1437 return emitError(firrtlObject.getLoc())
1438 <<
"uninitialized input port " << element.name;
1442 auto className = firrtlObject.getType().getNameAttr();
1443 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1446 OpBuilder builder(firrtlObject);
1448 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1449 firrtlObject.getClassNameAttr(), args);
1452 if (containingModuleRef) {
1453 std::lock_guard<std::mutex> guard(intraPassMutex);
1454 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1459 auto cast = UnrealizedConversionCastOp::create(
1460 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1461 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1464 opsToErase.push_back(firrtlObject);
1474 const PathInfoTable &pathInfoTable,
1475 SmallVectorImpl<Operation *> &opsToErase) {
1478 OpBuilder builder(firrtlInstance);
1483 SmallVector<Value> actualParameters;
1485 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1486 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1487 auto rebasedPath = om::BasePathCreateOp::create(
1488 builder, firrtlInstance->getLoc(), basePath, symRef);
1490 actualParameters.push_back(rebasedPath);
1493 pathInfoTable.collectAltBasePaths(
1494 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1497 for (
auto result : firrtlInstance.getResults()) {
1499 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1504 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1505 if (!propertyResult)
1511 assert(propertyAssignment &&
"properties require single assignment");
1512 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1515 opsToErase.push_back(propertyAssignment);
1519 auto referencedModule =
1520 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1522 StringRef moduleName = referencedModule.getModuleName();
1525 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1526 moduleName = externMod.getExtModuleName();
1529 auto className = FlatSymbolRefAttr::get(
1530 builder.getStringAttr(moduleName + kClassNameSuffix));
1532 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1536 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1537 className.getAttr(), actualParameters);
1542 for (
auto result : firrtlInstance.getResults()) {
1544 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1549 if (!isa<PropertyType>(result.getType()))
1553 auto objectField = om::ObjectFieldOp::create(
1554 builder,
object.
getLoc(), result.getType(),
object,
1555 firrtlInstance.getPortNameAttr(result.getResultNumber()));
1557 result.replaceAllUsesWith(objectField);
1561 opsToErase.push_back(firrtlInstance);
1569 SmallVectorImpl<Operation *> &opsToErase) {
1571 BitVector portsToErase(firrtlInstance.getNumResults());
1572 for (
auto result : firrtlInstance.getResults())
1573 if (isa<PropertyType>(result.getType()))
1574 portsToErase.set(result.getResultNumber());
1577 if (portsToErase.none())
1582 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1591 opsToErase.push_back(firrtlInstance);
1597 SmallVectorImpl<Operation *> &opsToErase) {
1598 OpBuilder builder(moduleOp);
1599 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1600 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1601 assert(0 &&
"should be no objects in modules");
1602 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1612 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1613 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1614 SmallVectorImpl<Operation *> &opsToErase) {
1615 OpBuilder builder(classOp);
1616 auto &classState = state.classLoweringStateTable.at(classOp);
1617 auto it = classState.paths.begin();
1618 for (
auto &op : classOp->getRegion(0).getOps()) {
1619 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1621 intraPassMutex, opsToErase)))
1623 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1625 pathInfoTable, opsToErase)))
1633LogicalResult LowerClassesPass::updateInstances(
1634 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1635 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1640 SmallVector<Operation *> opsToErase;
1642 TypeSwitch<Operation *, LogicalResult>(op)
1644 .Case([&](FModuleOp moduleOp) {
1649 .Case([&](om::ClassOp classOp) {
1653 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1654 intraPassMutex, opsToErase);
1656 .Default([](
auto *op) {
return success(); });
1660 for (
auto *op : opsToErase)
1667void LowerClassesPass::createAllRtlPorts(
1668 const PathInfoTable &pathInfoTable,
1671 MLIRContext *ctx = &getContext();
1674 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1677 om::ClassOp::buildSimpleClassOp(
1678 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1679 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1680 {om::PathType::get(ctx), om::StringType::get(ctx),
1681 om::OMIntegerType::get(ctx)});
1684 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1685 return lhs.object.getClassName() < rhs.object.getClassName();
1689 for (
auto rtlPortToCreate : rtlPortsToCreate)
1690 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1700struct FIntegerConstantOpConversion
1702 using OpConversionPattern::OpConversionPattern;
1705 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1706 ConversionPatternRewriter &rewriter)
const override {
1707 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1708 op, om::OMIntegerType::get(op.getContext()),
1709 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1715 using OpConversionPattern::OpConversionPattern;
1718 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1719 ConversionPatternRewriter &rewriter)
const override {
1720 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1721 op, rewriter.getBoolAttr(adaptor.getValue()));
1726struct PropertyAssertOpConversion
1728 using OpConversionPattern::OpConversionPattern;
1731 matchAndRewrite(firrtl::PropertyAssertOp op, OpAdaptor adaptor,
1732 ConversionPatternRewriter &rewriter)
const override {
1733 rewriter.replaceOpWithNewOp<om::PropertyAssertOp>(
1734 op, adaptor.getCondition(), adaptor.getMessage());
1739struct DoubleConstantOpConversion
1741 using OpConversionPattern::OpConversionPattern;
1744 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1745 ConversionPatternRewriter &rewriter)
const override {
1746 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1751struct StringConstantOpConversion
1753 using OpConversionPattern::OpConversionPattern;
1756 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1757 ConversionPatternRewriter &rewriter)
const override {
1758 auto stringType = om::StringType::get(op.getContext());
1759 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1760 op, stringType, StringAttr::get(op.getValue(), stringType));
1765struct ListCreateOpConversion
1767 using OpConversionPattern::OpConversionPattern;
1770 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1771 ConversionPatternRewriter &rewriter)
const override {
1772 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1775 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1776 adaptor.getElements());
1781struct ListConcatOpConversion
1783 using OpConversionPattern::OpConversionPattern;
1786 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1787 ConversionPatternRewriter &rewriter)
const override {
1788 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1791 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1792 adaptor.getSubLists());
1797struct IntegerAddOpConversion
1799 using OpConversionPattern::OpConversionPattern;
1802 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1803 ConversionPatternRewriter &rewriter)
const override {
1804 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1810struct IntegerMulOpConversion
1812 using OpConversionPattern::OpConversionPattern;
1815 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1816 ConversionPatternRewriter &rewriter)
const override {
1817 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1823struct IntegerShrOpConversion
1825 using OpConversionPattern::OpConversionPattern;
1828 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1829 ConversionPatternRewriter &rewriter)
const override {
1830 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1836struct IntegerShlOpConversion
1838 using OpConversionPattern::OpConversionPattern;
1841 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1842 ConversionPatternRewriter &rewriter)
const override {
1843 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1849struct StringConcatOpConversion
1851 using OpConversionPattern::OpConversionPattern;
1854 matchAndRewrite(firrtl::StringConcatOp op, OpAdaptor adaptor,
1855 ConversionPatternRewriter &rewriter)
const override {
1856 rewriter.replaceOpWithNewOp<om::StringConcatOp>(op, adaptor.getOperands());
1862 using OpConversionPattern::OpConversionPattern;
1865 matchAndRewrite(firrtl::PropEqOp op, OpAdaptor adaptor,
1866 ConversionPatternRewriter &rewriter)
const override {
1867 rewriter.replaceOpWithNewOp<om::PropEqOp>(op, adaptor.getLhs(),
1873template <
typename FIRRTLOp,
typename OMOp>
1874static LogicalResult binaryOpConversion(FIRRTLOp op,
1875 typename FIRRTLOp::Adaptor adaptor,
1876 ConversionPatternRewriter &rewriter) {
1877 rewriter.replaceOpWithNewOp<OMOp>(op, adaptor.getLhs(), adaptor.getRhs());
1883 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1884 const PathInfoTable &pathInfoTable,
1885 PatternBenefit benefit = 1)
1887 pathInfoTable(pathInfoTable) {}
1890 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1891 ConversionPatternRewriter &rewriter)
const override {
1892 auto *
context = op->getContext();
1893 auto pathType = om::PathType::get(
context);
1894 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1897 auto basePath = op->getBlock()->getArgument(0);
1901 if (pathInfoIt == pathInfoTable.table.end()) {
1902 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1903 return emitError(op.getLoc(),
"DontTouch target was deleted");
1904 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1905 return emitError(op.getLoc(),
"Instance target was deleted");
1906 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1910 auto pathInfo = pathInfoIt->second;
1911 auto symbol = pathInfo.symRef;
1915 om::TargetKind targetKind;
1916 switch (op.getTargetKind()) {
1917 case firrtl::TargetKind::DontTouch:
1918 targetKind = om::TargetKind::DontTouch;
1920 case firrtl::TargetKind::Reference:
1921 targetKind = om::TargetKind::Reference;
1923 case firrtl::TargetKind::Instance:
1924 if (!pathInfo.canBeInstanceTarget)
1925 return emitError(op.getLoc(),
"invalid target for instance path")
1926 .attachNote(pathInfo.loc)
1927 <<
"target not instance or module";
1928 targetKind = om::TargetKind::Instance;
1930 case firrtl::TargetKind::MemberInstance:
1931 case firrtl::TargetKind::MemberReference:
1932 if (pathInfo.canBeInstanceTarget)
1933 targetKind = om::TargetKind::MemberInstance;
1935 targetKind = om::TargetKind::MemberReference;
1941 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1945 auto parent = op->getParentOfType<om::ClassOp>();
1946 auto parentName = parent.getName();
1947 if (parentName.ends_with(kClassNameSuffix))
1948 parentName = parentName.drop_back(kClassNameSuffix.size());
1949 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1953 pathInfoTable.getRootsForPassthrough(originalParentName);
1954 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1957 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1958 if (altBasePathModule == altBasePath) {
1960 auto basePathArg = op->getBlock()->getArgument(1 + i);
1961 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1962 "expected a passthrough base path");
1963 basePath = basePathArg;
1968 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1969 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1974 const PathInfoTable &pathInfoTable;
1978 using OpConversionPattern::OpConversionPattern;
1981 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1982 ConversionPatternRewriter &rewriter)
const override {
1983 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1992 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1993 if (!regionKindInterface)
1995 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
2004 rewriter.replaceOp(wireOp, propAssign.getSrc());
2007 rewriter.eraseOp(propAssign);
2014 using OpConversionPattern::OpConversionPattern;
2017 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
2018 ConversionPatternRewriter &rewriter)
const override {
2019 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
2024struct ObjectSubfieldOpConversion
2026 using OpConversionPattern::OpConversionPattern;
2028 ObjectSubfieldOpConversion(
2029 const TypeConverter &typeConverter, MLIRContext *
context,
2030 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
2032 classTypeTable(classTypeTable) {}
2035 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
2036 ConversionPatternRewriter &rewriter)
const override {
2037 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
2043 auto firrtlClassType =
2044 classTypeTable.lookup(omClassType.getClassName().getAttr());
2045 if (!firrtlClassType)
2048 const auto &element = firrtlClassType.getElement(op.getIndex());
2050 if (element.direction == Direction::In)
2053 auto type = typeConverter->convertType(element.type);
2054 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
2059 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
2063 using OpConversionPattern::OpConversionPattern;
2066 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
2067 ConversionPatternRewriter &rewriter)
const override {
2068 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
2069 adaptor.getFieldLocsAttr());
2075 using OpConversionPattern::OpConversionPattern;
2078 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
2079 ConversionPatternRewriter &rewriter)
const override {
2082 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
2083 adaptor.getClassNameAttr(),
2084 adaptor.getActualParams());
2089static LogicalResult convertClassLike(om::ClassLike classOp,
2090 TypeConverter typeConverter,
2091 ConversionPatternRewriter &rewriter) {
2092 Block *body = classOp.getBodyBlock();
2093 TypeConverter::SignatureConversion result(body->getNumArguments());
2097 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
2101 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
2105 rewriter.modifyOpInPlace(classOp, [&]() {
2106 mlir::AttrTypeReplacer replacer;
2107 replacer.addReplacement([&](TypeAttr typeAttr) {
2108 return mlir::TypeAttr::get(
2109 typeConverter.convertType(typeAttr.getValue()));
2111 classOp.replaceFieldTypes(replacer);
2118 using OpConversionPattern::OpConversionPattern;
2121 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
2122 ConversionPatternRewriter &rewriter)
const override {
2123 return convertClassLike(classOp, *typeConverter, rewriter);
2127struct ClassExternOpSignatureConversion
2129 using OpConversionPattern::OpConversionPattern;
2132 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2133 ConversionPatternRewriter &rewriter)
const override {
2134 return convertClassLike(classOp, *typeConverter, rewriter);
2139 using OpConversionPattern::OpConversionPattern;
2142 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2143 ConversionPatternRewriter &rewriter)
const override {
2146 auto type = typeConverter->convertType(op.getType());
2150 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2151 op, type, adaptor.getObject(), adaptor.getFieldAttr());
2158struct UnrealizedConversionCastOpConversion
2160 using OpConversionPattern::OpConversionPattern;
2163 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2164 ConversionPatternRewriter &rewriter)
const override {
2165 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2167 auto type = typeConverter->convertType(op.getResult(0));
2168 if (!type || type != adaptor.getOperands()[0].getType())
2170 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2176 using OpConversionPattern::OpConversionPattern;
2179 matchAndRewrite(UnknownValueOp op, OpAdaptor adaptor,
2180 ConversionPatternRewriter &rewriter)
const override {
2181 auto convertedType = typeConverter->convertType(op.getType());
2184 rewriter.replaceOpWithNewOp<om::UnknownValueOp>(op, convertedType);
2198 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2199 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2202 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2203 auto containsFIRRTLType = [](Type type) {
2205 .walk([](Type type) {
2206 return failure(isa<FIRRTLDialect>(type.getDialect()));
2210 auto noFIRRTLOperands =
2211 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2212 return containsFIRRTLType(type);
2214 auto noFIRRTLResults =
2215 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2216 return containsFIRRTLType(type);
2218 return noFIRRTLOperands && noFIRRTLResults;
2222 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2223 [](Operation *op) -> std::optional<bool> {
2224 auto classLike = dyn_cast<om::ClassLike>(op);
2226 return std::nullopt;
2227 auto fieldNames = classLike.getFieldNames();
2228 if (!llvm::all_of(fieldNames, [&](
auto field) {
2229 std::optional<Type> type =
2230 classLike.getFieldType(cast<StringAttr>(field));
2231 return type.has_value() && !isa<FIRRTLType>(type.value());
2235 return llvm::none_of(
2236 classLike.getBodyBlock()->getArgumentTypes(),
2237 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2243 converter.addConversion([](IntegerType type) {
2244 return om::OMIntegerType::get(type.getContext());
2246 converter.addConversion([](FIntegerType type) {
2250 return om::OMIntegerType::get(type.getContext());
2254 converter.addConversion([](om::StringType type) {
return type; });
2255 converter.addConversion([](firrtl::StringType type) {
2256 return om::StringType::get(type.getContext());
2260 converter.addConversion([](om::PathType type) {
return type; });
2261 converter.addConversion([](om::BasePathType type) {
return type; });
2262 converter.addConversion([](om::FrozenPathType type) {
return type; });
2263 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2264 converter.addConversion([](firrtl::PathType type) {
2265 return om::PathType::get(type.getContext());
2269 converter.addConversion([](om::ClassType type) {
return type; });
2270 converter.addConversion([](firrtl::ClassType type) {
2271 return om::ClassType::get(type.getContext(), type.getNameAttr());
2275 converter.addConversion([](om::AnyType type) {
return type; });
2276 converter.addConversion([](firrtl::AnyRefType type) {
2277 return om::AnyType::get(type.getContext());
2281 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2283 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2285 auto elementType = converter.convertType(type.getElementType());
2291 converter.addConversion(
2292 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2294 return convertListType(type);
2297 converter.addConversion(
2298 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2300 return convertListType(type);
2304 converter.addConversion(
2305 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2308 converter.addConversion(
2309 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2313 converter.addTargetMaterialization(
2314 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2315 assert(values.size() == 1);
2316 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2322 converter.addSourceMaterialization(
2323 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2324 assert(values.size() == 1);
2325 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2332 const PathInfoTable &pathInfoTable,
2333 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2334 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2335 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2343 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2344 patterns.add<ClassExternOpSignatureConversion>(converter,
2351 patterns.add<PropertyAssertOpConversion>(converter,
patterns.getContext());
2352 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2359 patterns.add(binaryOpConversion<firrtl::BoolAndOp, om::IntegerAndOp>);
2360 patterns.add(binaryOpConversion<firrtl::BoolOrOp, om::IntegerOrOp>);
2361 patterns.add(binaryOpConversion<firrtl::BoolXorOp, om::IntegerXorOp>);
2362 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2368LogicalResult LowerClassesPass::dialectConversion(
2369 Operation *op,
const PathInfoTable &pathInfoTable,
2370 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2371 ConversionTarget target(getContext());
2374 TypeConverter typeConverter;
2381 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 Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Extension of RewritePatternSet that allows adding matchAndRewrite functions with op adaptors and Conv...
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.
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
virtual void erase(InstanceGraphNode *node)
Remove this module from the instance graph.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
This is an edge in the InstanceGraph.
InstanceGraphNode * getParent() const
Get the module where the instantiation lives.
auto getInstance()
Get the instance-like op that this is tracking.
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.
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)
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)
const SymbolTable & getSymbolTable() const
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.
A data structure that caches and provides paths to module instances in the IR.