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.getNameAttr()] = classLike.getInstanceType();
1014 mlir::parallelForEach(ctx,
loweringState.classLoweringStateTable,
1015 [
this, &pathInfoTable](
auto &entry) {
1016 const auto &[classLike, state] = entry;
1017 lowerClassLike(state.moduleLike, classLike,
1024 for (
auto moduleLike : modulesToErasePortsFrom) {
1025 BitVector portsToErase(moduleLike.getNumPorts());
1026 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i)
1027 if (isa<PropertyType>(moduleLike.getPortType(i)))
1028 portsToErase.set(i);
1029 moduleLike.erasePorts(portsToErase);
1033 for (
auto &[omClass, state] :
loweringState.classLoweringStateTable) {
1034 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
1036 for (
auto *use :
llvm::make_early_inc_range(node->uses()))
1038 instanceGraph->
erase(node);
1039 state.moduleLike.erase();
1044 SmallVector<Operation *> objectContainers;
1045 for (
auto &op : circuit.getOps())
1046 if (isa<FModuleOp,
om::ClassLike>(op))
1047 objectContainers.push_back(&op);
1051 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
1053 pathInfoTable, intraPassMutex);
1055 return signalPassFailure();
1058 if (!rtlPortsToCreate.empty())
1059 createAllRtlPorts(pathInfoTable, namespaces, cache);
1063 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
1064 return dialectConversion(op, pathInfoTable, classTypeTable);
1066 return signalPassFailure();
1069 markAnalysesPreserved<InstanceGraph>();
1072 rtlPortsToCreate.clear();
1076bool LowerClassesPass::shouldCreateClass(igraph::ModuleOpInterface modOp) {
1077 return instanceInfo->moduleContainsProperties(modOp);
1080bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
1081 return shouldCreateClass(instanceGraph->
lookup(modName)->
getModule());
1085 SmallVector<Attribute> &fieldNames,
1086 SmallVector<NamedAttribute> &fieldTypes) {
1087 if (hasContainingModule) {
1088 auto name = builder.getStringAttr(kPortsName);
1089 fieldNames.push_back(name);
1090 fieldTypes.push_back(NamedAttribute(
1091 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
1097 ArrayRef<StringRef> formalParamNames,
1098 bool hasContainingModule) {
1099 SmallVector<Attribute> fieldNames;
1100 SmallVector<NamedAttribute> fieldTypes;
1101 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1102 auto type = moduleLike.getPortType(i);
1103 if (!isa<PropertyType>(type))
1106 auto direction = moduleLike.getPortDirection(i);
1107 if (direction != Direction::In) {
1108 auto name = moduleLike.getPortNameAttr(i);
1109 fieldNames.push_back(name);
1110 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1115 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1116 formalParamNames, fieldNames, fieldTypes);
1119static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1121 ArrayRef<StringRef> formalParamNames,
1122 bool hasContainingModule) {
1124 SmallVector<Attribute> fieldNames;
1125 SmallVector<NamedAttribute> fieldTypes;
1126 for (
auto op : llvm::make_early_inc_range(
1127 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1128 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1132 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1134 fieldNames.push_back(name);
1135 fieldTypes.push_back(
1136 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1141 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1142 formalParamNames, fieldNames, fieldTypes);
1146om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1147 const PathInfoTable &pathInfoTable,
1148 std::mutex &intraPassMutex) {
1150 SmallVector<StringRef> formalParamNames;
1152 formalParamNames.emplace_back(
"basepath");
1155 size_t nAltBasePaths =
1156 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1157 for (
size_t i = 0; i < nAltBasePaths; ++i)
1158 formalParamNames.push_back(StringAttr::get(
1159 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1162 bool hasContainingModule =
false;
1163 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1164 if (port.isInput() && isa<PropertyType>(port.type)) {
1165 formalParamNames.push_back(port.name);
1168 if (port.name.strref().contains(kContainingModuleName))
1169 hasContainingModule =
true;
1173 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1176 if (hasContainingModule)
1177 formalParamNames.push_back(kPortsName);
1180 StringRef className = moduleLike.getName();
1181 StringAttr baseClassNameAttr = moduleLike.getNameAttr();
1184 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation())) {
1185 className = externMod.getExtModuleName();
1186 baseClassNameAttr = externMod.getExtModuleNameAttr();
1192 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1195 om::ClassLike loweredClassOp;
1196 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1197 moduleLike.getOperation())) {
1200 auto [it, inserted] = externalClassMap.insert({baseClassNameAttr, {}});
1202 it->getSecond() =
convertExtClass(moduleLike, builder, className + suffix,
1203 formalParamNames, hasContainingModule);
1204 loweredClassOp = it->getSecond();
1206 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1207 formalParamNames, hasContainingModule);
1210 SymbolTable::setSymbolVisibility(loweredClassOp, moduleLike.getVisibility());
1212 return loweredClassOp;
1215void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1216 om::ClassLike classLike,
1217 const PathInfoTable &pathInfoTable) {
1219 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1220 return lowerClass(classOp, moduleLike, pathInfoTable);
1222 if (
auto classExternOp =
1223 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1224 return lowerClassExtern(classExternOp, moduleLike);
1226 llvm_unreachable(
"unhandled class-like op");
1229void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1230 const PathInfoTable &pathInfoTable) {
1232 SmallVector<Property> inputProperties;
1233 BitVector portsToErase(moduleLike.getNumPorts());
1234 bool hasContainingModule =
false;
1235 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1237 if (!isa<PropertyType>(port.type))
1241 if (port.isInput()) {
1242 inputProperties.push_back({index, port.name, port.type, port.loc});
1245 if (port.name.strref().contains(kContainingModuleName))
1246 hasContainingModule =
true;
1250 portsToErase.set(index);
1255 Block *moduleBody = &moduleLike->getRegion(0).front();
1256 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1258 auto basePathType = om::BasePathType::get(&getContext());
1259 auto unknownLoc = UnknownLoc::get(&getContext());
1260 classBody->addArgument(basePathType, unknownLoc);
1263 size_t nAltBasePaths =
1264 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1265 for (
size_t i = 0; i < nAltBasePaths; ++i)
1266 classBody->addArgument(basePathType, unknownLoc);
1269 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1270 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1271 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1273 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1274 for (
auto result : instance.getResults()) {
1275 if (isa<PropertyType>(result.getType()))
1276 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1281 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1282 if (llvm::any_of(op.getOperands(), isProperty) ||
1283 llvm::any_of(op.getResults(), isProperty))
1284 op.moveBefore(classBody, classBody->begin());
1288 for (
auto input : inputProperties) {
1289 auto arg = classBody->addArgument(input.type, input.loc);
1290 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1293 llvm::SmallVector<mlir::Location> fieldLocs;
1294 llvm::SmallVector<mlir::Value> fieldValues;
1295 for (Operation &op :
1297 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1298 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1300 fieldLocs.push_back(op.getLoc());
1301 fieldValues.push_back(propAssign.getSrc());
1308 if (hasContainingModule) {
1309 BlockArgument argumentValue = classBody->addArgument(
1310 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1311 fieldLocs.push_back(argumentValue.getLoc());
1312 fieldValues.push_back(argumentValue);
1315 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1316 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1321void LowerClassesPass::lowerClassExtern(om::ClassExternOp classExternOp,
1322 FModuleLike moduleLike) {
1326 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1329 classBody->addArgument(om::BasePathType::get(&getContext()),
1330 UnknownLoc::get(&getContext()));
1332 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1333 auto type = moduleLike.getPortType(i);
1334 if (!isa<PropertyType>(type))
1337 auto loc = moduleLike.getPortLocation(i);
1338 auto direction = moduleLike.getPortDirection(i);
1339 if (direction == Direction::In)
1340 classBody->addArgument(type, loc);
1349 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1350 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1351 SmallVectorImpl<Operation *> &opsToErase) {
1353 auto basePath = firrtlObject->getBlock()->getArgument(0);
1356 auto firrtlClassType = firrtlObject.getType();
1357 auto numElements = firrtlClassType.getNumElements();
1358 llvm::SmallVector<unsigned> argIndexTable;
1362 SmallVector<Value> altBasePaths;
1363 pathInfoTable.collectAltBasePaths(
1364 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1367 unsigned nextArgIndex = 1 + altBasePaths.size();
1370 auto direction = firrtlClassType.getElement(i).direction;
1371 if (direction == Direction::In)
1372 argIndexTable[i] = nextArgIndex++;
1378 llvm::SmallVector<Value> args;
1379 args.resize(nextArgIndex);
1383 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1384 args[1 + i] = altBasePath;
1386 firrtl::PathOp containingModuleRef;
1387 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1388 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1389 auto index = subfield.getIndex();
1390 auto direction = firrtlClassType.getElement(index).direction;
1394 if (direction == Direction::Out)
1397 for (
auto *subfieldUser :
1398 llvm::make_early_inc_range(subfield->getUsers())) {
1399 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1403 auto dst = propassign.getOperand(0);
1404 auto src = propassign.getOperand(1);
1405 if (dst == subfield.getResult()) {
1406 args[argIndexTable[index]] = src;
1407 opsToErase.push_back(propassign);
1410 if (firrtlClassType.getElement(index).name.strref().contains(
1411 kContainingModuleName)) {
1412 assert(!containingModuleRef &&
1413 "expected exactly one containingModule");
1414 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1415 "expected containingModule to be a PathOp");
1416 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1422 opsToErase.push_back(subfield);
1428 auto element = firrtlClassType.getElement(i);
1429 if (element.direction == Direction::Out)
1432 auto argIndex = argIndexTable[i];
1433 if (!args[argIndex])
1434 return emitError(firrtlObject.getLoc())
1435 <<
"uninitialized input port " << element.name;
1439 auto className = firrtlObject.getType().getNameAttr();
1440 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1443 OpBuilder builder(firrtlObject);
1445 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1446 firrtlObject.getClassNameAttr(), args);
1449 if (containingModuleRef) {
1450 std::lock_guard<std::mutex> guard(intraPassMutex);
1451 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1456 auto cast = UnrealizedConversionCastOp::create(
1457 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1458 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1461 opsToErase.push_back(firrtlObject);
1471 const PathInfoTable &pathInfoTable,
1472 SmallVectorImpl<Operation *> &opsToErase) {
1475 OpBuilder builder(firrtlInstance);
1480 SmallVector<Value> actualParameters;
1482 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1483 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1484 auto rebasedPath = om::BasePathCreateOp::create(
1485 builder, firrtlInstance->getLoc(), basePath, symRef);
1487 actualParameters.push_back(rebasedPath);
1490 pathInfoTable.collectAltBasePaths(
1491 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1494 for (
auto result : firrtlInstance.getResults()) {
1496 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1501 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1502 if (!propertyResult)
1508 assert(propertyAssignment &&
"properties require single assignment");
1509 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1512 opsToErase.push_back(propertyAssignment);
1516 auto referencedModule =
1517 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1519 StringRef moduleName = referencedModule.getName();
1522 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1523 moduleName = externMod.getExtModuleName();
1526 auto className = FlatSymbolRefAttr::get(
1527 builder.getStringAttr(moduleName + kClassNameSuffix));
1529 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1533 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1534 className.getAttr(), actualParameters);
1539 for (
auto result : firrtlInstance.getResults()) {
1541 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1546 if (!isa<PropertyType>(result.getType()))
1550 auto objectField = om::ObjectFieldOp::create(
1551 builder,
object.
getLoc(), result.getType(),
object,
1552 firrtlInstance.getPortNameAttr(result.getResultNumber()));
1554 result.replaceAllUsesWith(objectField);
1558 opsToErase.push_back(firrtlInstance);
1566 SmallVectorImpl<Operation *> &opsToErase) {
1568 BitVector portsToErase(firrtlInstance.getNumResults());
1569 for (
auto result : firrtlInstance.getResults())
1570 if (isa<PropertyType>(result.getType()))
1571 portsToErase.set(result.getResultNumber());
1574 if (portsToErase.none())
1579 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1588 opsToErase.push_back(firrtlInstance);
1594 SmallVectorImpl<Operation *> &opsToErase) {
1595 OpBuilder builder(moduleOp);
1596 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1597 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1598 assert(0 &&
"should be no objects in modules");
1599 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1609 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1610 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1611 SmallVectorImpl<Operation *> &opsToErase) {
1612 OpBuilder builder(classOp);
1613 auto &classState = state.classLoweringStateTable.at(classOp);
1614 auto it = classState.paths.begin();
1615 for (
auto &op : classOp->getRegion(0).getOps()) {
1616 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1618 intraPassMutex, opsToErase)))
1620 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1622 pathInfoTable, opsToErase)))
1630LogicalResult LowerClassesPass::updateInstances(
1631 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1632 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1637 SmallVector<Operation *> opsToErase;
1639 TypeSwitch<Operation *, LogicalResult>(op)
1641 .Case([&](FModuleOp moduleOp) {
1646 .Case([&](om::ClassOp classOp) {
1650 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1651 intraPassMutex, opsToErase);
1653 .Default([](
auto *op) {
return success(); });
1657 for (
auto *op : opsToErase)
1664void LowerClassesPass::createAllRtlPorts(
1665 const PathInfoTable &pathInfoTable,
1668 MLIRContext *ctx = &getContext();
1671 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1674 om::ClassOp::buildSimpleClassOp(
1675 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1676 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1677 {om::PathType::get(ctx), om::StringType::get(ctx),
1678 om::OMIntegerType::get(ctx)});
1681 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1682 return lhs.object.getClassName() < rhs.object.getClassName();
1686 for (
auto rtlPortToCreate : rtlPortsToCreate)
1687 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1697struct FIntegerConstantOpConversion
1699 using OpConversionPattern::OpConversionPattern;
1702 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1703 ConversionPatternRewriter &rewriter)
const override {
1704 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1705 op, om::OMIntegerType::get(op.getContext()),
1706 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1712 using OpConversionPattern::OpConversionPattern;
1715 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1716 ConversionPatternRewriter &rewriter)
const override {
1717 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1718 op, rewriter.getBoolAttr(adaptor.getValue()));
1723struct PropertyAssertOpConversion
1725 using OpConversionPattern::OpConversionPattern;
1728 matchAndRewrite(firrtl::PropertyAssertOp op, OpAdaptor adaptor,
1729 ConversionPatternRewriter &rewriter)
const override {
1730 rewriter.replaceOpWithNewOp<om::PropertyAssertOp>(
1731 op, adaptor.getCondition(), op.getMessageAttr());
1736struct DoubleConstantOpConversion
1738 using OpConversionPattern::OpConversionPattern;
1741 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1742 ConversionPatternRewriter &rewriter)
const override {
1743 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1748struct StringConstantOpConversion
1750 using OpConversionPattern::OpConversionPattern;
1753 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1754 ConversionPatternRewriter &rewriter)
const override {
1755 auto stringType = om::StringType::get(op.getContext());
1756 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1757 op, stringType, StringAttr::get(op.getValue(), stringType));
1762struct ListCreateOpConversion
1764 using OpConversionPattern::OpConversionPattern;
1767 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1768 ConversionPatternRewriter &rewriter)
const override {
1769 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1772 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1773 adaptor.getElements());
1778struct ListConcatOpConversion
1780 using OpConversionPattern::OpConversionPattern;
1783 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1784 ConversionPatternRewriter &rewriter)
const override {
1785 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1788 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1789 adaptor.getSubLists());
1794struct IntegerAddOpConversion
1796 using OpConversionPattern::OpConversionPattern;
1799 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1800 ConversionPatternRewriter &rewriter)
const override {
1801 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1807struct IntegerMulOpConversion
1809 using OpConversionPattern::OpConversionPattern;
1812 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1813 ConversionPatternRewriter &rewriter)
const override {
1814 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1820struct IntegerShrOpConversion
1822 using OpConversionPattern::OpConversionPattern;
1825 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1826 ConversionPatternRewriter &rewriter)
const override {
1827 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1833struct IntegerShlOpConversion
1835 using OpConversionPattern::OpConversionPattern;
1838 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1839 ConversionPatternRewriter &rewriter)
const override {
1840 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1846struct StringConcatOpConversion
1848 using OpConversionPattern::OpConversionPattern;
1851 matchAndRewrite(firrtl::StringConcatOp op, OpAdaptor adaptor,
1852 ConversionPatternRewriter &rewriter)
const override {
1853 rewriter.replaceOpWithNewOp<om::StringConcatOp>(op, adaptor.getOperands());
1859 using OpConversionPattern::OpConversionPattern;
1862 matchAndRewrite(firrtl::PropEqOp op, OpAdaptor adaptor,
1863 ConversionPatternRewriter &rewriter)
const override {
1864 rewriter.replaceOpWithNewOp<om::PropEqOp>(op, adaptor.getLhs(),
1870template <
typename FIRRTLOp,
typename OMOp>
1871static LogicalResult binaryOpConversion(FIRRTLOp op,
1872 typename FIRRTLOp::Adaptor adaptor,
1873 ConversionPatternRewriter &rewriter) {
1874 rewriter.replaceOpWithNewOp<OMOp>(op, adaptor.getLhs(), adaptor.getRhs());
1880 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1881 const PathInfoTable &pathInfoTable,
1882 PatternBenefit benefit = 1)
1884 pathInfoTable(pathInfoTable) {}
1887 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1888 ConversionPatternRewriter &rewriter)
const override {
1889 auto *
context = op->getContext();
1890 auto pathType = om::PathType::get(
context);
1891 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1894 auto basePath = op->getBlock()->getArgument(0);
1898 if (pathInfoIt == pathInfoTable.table.end()) {
1899 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1900 return emitError(op.getLoc(),
"DontTouch target was deleted");
1901 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1902 return emitError(op.getLoc(),
"Instance target was deleted");
1903 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1907 auto pathInfo = pathInfoIt->second;
1908 auto symbol = pathInfo.symRef;
1912 om::TargetKind targetKind;
1913 switch (op.getTargetKind()) {
1914 case firrtl::TargetKind::DontTouch:
1915 targetKind = om::TargetKind::DontTouch;
1917 case firrtl::TargetKind::Reference:
1918 targetKind = om::TargetKind::Reference;
1920 case firrtl::TargetKind::Instance:
1921 if (!pathInfo.canBeInstanceTarget)
1922 return emitError(op.getLoc(),
"invalid target for instance path")
1923 .attachNote(pathInfo.loc)
1924 <<
"target not instance or module";
1925 targetKind = om::TargetKind::Instance;
1927 case firrtl::TargetKind::MemberInstance:
1928 case firrtl::TargetKind::MemberReference:
1929 if (pathInfo.canBeInstanceTarget)
1930 targetKind = om::TargetKind::MemberInstance;
1932 targetKind = om::TargetKind::MemberReference;
1938 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1942 auto parent = op->getParentOfType<om::ClassOp>();
1943 auto parentName = parent.getName();
1944 if (parentName.ends_with(kClassNameSuffix))
1945 parentName = parentName.drop_back(kClassNameSuffix.size());
1946 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1950 pathInfoTable.getRootsForPassthrough(originalParentName);
1951 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1954 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1955 if (altBasePathModule == altBasePath) {
1957 auto basePathArg = op->getBlock()->getArgument(1 + i);
1958 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1959 "expected a passthrough base path");
1960 basePath = basePathArg;
1965 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1966 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1971 const PathInfoTable &pathInfoTable;
1975 using OpConversionPattern::OpConversionPattern;
1978 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1979 ConversionPatternRewriter &rewriter)
const override {
1980 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1989 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1990 if (!regionKindInterface)
1992 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
2001 rewriter.replaceOp(wireOp, propAssign.getSrc());
2004 rewriter.eraseOp(propAssign);
2011 using OpConversionPattern::OpConversionPattern;
2014 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
2015 ConversionPatternRewriter &rewriter)
const override {
2016 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
2021struct ObjectSubfieldOpConversion
2023 using OpConversionPattern::OpConversionPattern;
2025 ObjectSubfieldOpConversion(
2026 const TypeConverter &typeConverter, MLIRContext *
context,
2027 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
2029 classTypeTable(classTypeTable) {}
2032 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
2033 ConversionPatternRewriter &rewriter)
const override {
2034 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
2040 auto firrtlClassType =
2041 classTypeTable.lookup(omClassType.getClassName().getAttr());
2042 if (!firrtlClassType)
2045 const auto &element = firrtlClassType.getElement(op.getIndex());
2047 if (element.direction == Direction::In)
2050 auto type = typeConverter->convertType(element.type);
2051 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
2056 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
2060 using OpConversionPattern::OpConversionPattern;
2063 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
2064 ConversionPatternRewriter &rewriter)
const override {
2065 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
2066 adaptor.getFieldLocsAttr());
2072 using OpConversionPattern::OpConversionPattern;
2075 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
2076 ConversionPatternRewriter &rewriter)
const override {
2079 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
2080 adaptor.getClassNameAttr(),
2081 adaptor.getActualParams());
2086static LogicalResult convertClassLike(om::ClassLike classOp,
2087 TypeConverter typeConverter,
2088 ConversionPatternRewriter &rewriter) {
2089 Block *body = classOp.getBodyBlock();
2090 TypeConverter::SignatureConversion result(body->getNumArguments());
2094 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
2098 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
2102 rewriter.modifyOpInPlace(classOp, [&]() {
2103 mlir::AttrTypeReplacer replacer;
2104 replacer.addReplacement([&](TypeAttr typeAttr) {
2105 return mlir::TypeAttr::get(
2106 typeConverter.convertType(typeAttr.getValue()));
2108 classOp.replaceFieldTypes(replacer);
2115 using OpConversionPattern::OpConversionPattern;
2118 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
2119 ConversionPatternRewriter &rewriter)
const override {
2120 return convertClassLike(classOp, *typeConverter, rewriter);
2124struct ClassExternOpSignatureConversion
2126 using OpConversionPattern::OpConversionPattern;
2129 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2130 ConversionPatternRewriter &rewriter)
const override {
2131 return convertClassLike(classOp, *typeConverter, rewriter);
2136 using OpConversionPattern::OpConversionPattern;
2139 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2140 ConversionPatternRewriter &rewriter)
const override {
2143 auto type = typeConverter->convertType(op.getType());
2147 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2148 op, type, adaptor.getObject(), adaptor.getFieldAttr());
2155struct UnrealizedConversionCastOpConversion
2157 using OpConversionPattern::OpConversionPattern;
2160 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2161 ConversionPatternRewriter &rewriter)
const override {
2162 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2164 auto type = typeConverter->convertType(op.getResult(0));
2165 if (!type || type != adaptor.getOperands()[0].getType())
2167 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2173 using OpConversionPattern::OpConversionPattern;
2176 matchAndRewrite(UnknownValueOp op, OpAdaptor adaptor,
2177 ConversionPatternRewriter &rewriter)
const override {
2178 auto convertedType = typeConverter->convertType(op.getType());
2181 rewriter.replaceOpWithNewOp<om::UnknownValueOp>(op, convertedType);
2195 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2196 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2199 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2200 auto containsFIRRTLType = [](Type type) {
2202 .walk([](Type type) {
2203 return failure(isa<FIRRTLDialect>(type.getDialect()));
2207 auto noFIRRTLOperands =
2208 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2209 return containsFIRRTLType(type);
2211 auto noFIRRTLResults =
2212 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2213 return containsFIRRTLType(type);
2215 return noFIRRTLOperands && noFIRRTLResults;
2219 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2220 [](Operation *op) -> std::optional<bool> {
2221 auto classLike = dyn_cast<om::ClassLike>(op);
2223 return std::nullopt;
2224 auto fieldNames = classLike.getFieldNames();
2225 if (!llvm::all_of(fieldNames, [&](
auto field) {
2226 std::optional<Type> type =
2227 classLike.getFieldType(cast<StringAttr>(field));
2228 return type.has_value() && !isa<FIRRTLType>(type.value());
2232 return llvm::none_of(
2233 classLike.getBodyBlock()->getArgumentTypes(),
2234 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2240 converter.addConversion([](IntegerType type) {
2241 return om::OMIntegerType::get(type.getContext());
2243 converter.addConversion([](FIntegerType type) {
2247 return om::OMIntegerType::get(type.getContext());
2251 converter.addConversion([](om::StringType type) {
return type; });
2252 converter.addConversion([](firrtl::StringType type) {
2253 return om::StringType::get(type.getContext());
2257 converter.addConversion([](om::PathType type) {
return type; });
2258 converter.addConversion([](om::BasePathType type) {
return type; });
2259 converter.addConversion([](om::FrozenPathType type) {
return type; });
2260 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2261 converter.addConversion([](firrtl::PathType type) {
2262 return om::PathType::get(type.getContext());
2266 converter.addConversion([](om::ClassType type) {
return type; });
2267 converter.addConversion([](firrtl::ClassType type) {
2268 return om::ClassType::get(type.getContext(), type.getNameAttr());
2272 converter.addConversion([](om::AnyType type) {
return type; });
2273 converter.addConversion([](firrtl::AnyRefType type) {
2274 return om::AnyType::get(type.getContext());
2278 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2280 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2282 auto elementType = converter.convertType(type.getElementType());
2288 converter.addConversion(
2289 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2291 return convertListType(type);
2294 converter.addConversion(
2295 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2297 return convertListType(type);
2301 converter.addConversion(
2302 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2305 converter.addConversion(
2306 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2310 converter.addTargetMaterialization(
2311 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2312 assert(values.size() == 1);
2313 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2319 converter.addSourceMaterialization(
2320 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2321 assert(values.size() == 1);
2322 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2329 const PathInfoTable &pathInfoTable,
2330 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2331 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2332 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2340 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2341 patterns.add<ClassExternOpSignatureConversion>(converter,
2348 patterns.add<PropertyAssertOpConversion>(converter,
patterns.getContext());
2349 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2356 patterns.add(binaryOpConversion<firrtl::BoolAndOp, om::IntegerAndOp>);
2357 patterns.add(binaryOpConversion<firrtl::BoolOrOp, om::IntegerOrOp>);
2358 patterns.add(binaryOpConversion<firrtl::BoolXorOp, om::IntegerXorOp>);
2359 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2365LogicalResult LowerClassesPass::dialectConversion(
2366 Operation *op,
const PathInfoTable &pathInfoTable,
2367 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2368 ConversionTarget target(getContext());
2371 TypeConverter typeConverter;
2378 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, 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.