23#include "mlir/IR/BuiltinOps.h"
24#include "mlir/IR/PatternMatch.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "mlir/Support/LogicalResult.h"
28#include "mlir/Transforms/DialectConversion.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/STLExtras.h"
34#define GEN_PASS_DEF_LOWERCLASSES
35#include "circt/Dialect/FIRRTL/Passes.h.inc"
46 auto moduleLike = node->
getModule<FModuleLike>();
50 if (isa<firrtl::ClassLike>(moduleLike.getOperation()))
54 if (moduleLike.isPublic())
58 bool hasClassPorts = llvm::any_of(moduleLike.getPorts(), [](
PortInfo port) {
59 return isa<PropertyType>(port.type);
67 for (
auto *instance : *node) {
71 if (instance->getInstance<firrtl::ObjectOp>())
74 if (
auto op = instance->getInstance<FInstanceLike>())
75 for (
auto result : op->getResults())
76 if (type_isa<PropertyType>(result.getType()))
87 PathInfo(Location loc,
bool canBeInstanceTarget, FlatSymbolRefAttr symRef,
88 StringAttr altBasePathModule)
89 : loc(loc), canBeInstanceTarget(canBeInstanceTarget), symRef(symRef),
90 altBasePathModule(altBasePathModule) {
91 assert(symRef &&
"symRef must not be null");
95 std::optional<Location> loc = std::nullopt;
98 bool canBeInstanceTarget =
false;
101 FlatSymbolRefAttr symRef =
nullptr;
105 StringAttr altBasePathModule =
nullptr;
109struct PathInfoTable {
112 void addAltBasePathRoot(StringAttr rootModuleName) {
113 altBasePathRoots.insert(rootModuleName);
118 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
119 StringAttr rootModuleName) {
120 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
121 rootSequence.push_back(rootModuleName);
125 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
126 getAltBasePathRoots()
const {
127 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
132 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
133 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
138 llvm::iterator_range<const StringAttr *>
139 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
140 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
141 assert(it != altBasePathsPassthroughs.end() &&
142 "expected passthrough module to already exist");
143 return llvm::make_range(it->second.begin(), it->second.end());
148 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
149 SmallVectorImpl<Value> &result)
const {
150 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
151 auto parent = instance->getParentOfType<om::ClassOp>();
154 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
155 if (parent.getName().starts_with(altBasePath)) {
157 result.push_back(instance->getBlock()->getArgument(0));
161 auto basePath = instance->getBlock()->getArgument(1 + i);
162 assert(isa<om::BasePathType>(basePath.getType()) &&
163 "expected a passthrough base path");
164 result.push_back(basePath);
170 DenseMap<DistinctAttr, PathInfo> table;
175 SmallPtrSet<StringAttr, 16> altBasePathRoots;
179 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
183static constexpr StringRef kClassNameSuffix =
"_Class";
194struct ClassLoweringState {
195 FModuleLike moduleLike;
196 std::vector<hw::HierPathOp> paths;
199struct LoweringState {
200 PathInfoTable pathInfoTable;
201 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
206 firrtl::PathOp containingModuleRef;
211struct LowerClassesPass
212 :
public circt::firrtl::impl::LowerClassesBase<LowerClassesPass> {
213 void runOnOperation()
override;
219 SymbolTable &symbolTable);
222 bool shouldCreateClass(StringAttr modName);
225 om::ClassLike createClass(FModuleLike moduleLike,
226 const PathInfoTable &pathInfoTable,
227 std::mutex &intraPassMutex);
230 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
231 const PathInfoTable &pathInfoTable);
232 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
233 const PathInfoTable &pathInfoTable);
234 void lowerClassExtern(om::ClassExternOp classExternOp,
235 FModuleLike moduleLike);
238 LogicalResult updateInstances(Operation *op,
InstanceGraph &instanceGraph,
239 const LoweringState &state,
240 const PathInfoTable &pathInfoTable,
241 std::mutex &intraPassMutex);
244 void createAllRtlPorts(
const PathInfoTable &pathInfoTable,
249 LogicalResult dialectConversion(
250 Operation *op,
const PathInfoTable &pathInfoTable,
251 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
254 DenseMap<StringAttr, bool> shouldCreateClassMemo;
257 SmallVector<RtlPortsInfo> rtlPortsToCreate;
264 DenseMap<StringAttr, om::ClassLike> externalClassMap;
273 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
274 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
276 PathTracker(FModuleLike module,
278 InstanceGraph &instanceGraph,
const SymbolTable &symbolTable,
279 const DenseMap<DistinctAttr, FModuleOp> &owningModules)
280 : module(module), moduleNamespace(namespaces[module]),
281 namespaces(namespaces), instanceGraph(instanceGraph),
282 symbolTable(symbolTable), owningModules(owningModules) {}
285 struct PathInfoTableEntry {
288 StringAttr altBasePathModule;
294 LogicalResult runOnModule();
297 FailureOr<AnnotationSet> processPathTrackers(
const AnnoTarget &target);
299 LogicalResult updatePathInfoTable(PathInfoTable &pathInfoTable,
304 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
305 StringAttr moduleName,
306 FModuleOp owningModule,
313 DenseMap<std::pair<StringAttr, FModuleOp>,
bool> needsAltBasePathCache;
317 const SymbolTable &symbolTable;
318 const DenseMap<DistinctAttr, FModuleOp> &owningModules;
321 SmallVector<PathInfoTableEntry> entries;
322 SetVector<StringAttr> altBasePathRoots;
327static constexpr StringRef kContainingModuleName =
"containingModule";
328static constexpr StringRef kPortsName =
"ports";
329static constexpr StringRef kRtlPortClassName =
"RtlPort";
331static Type getRtlPortsType(MLIRContext *
context) {
332 return om::ListType::get(om::ClassType::get(
337static void createRtlPorts(
const RtlPortsInfo &rtlPortToCreate,
338 const PathInfoTable &pathInfoTable,
341 firrtl::PathOp containingModuleRef = rtlPortToCreate.containingModuleRef;
342 Value basePath = rtlPortToCreate.basePath;
343 om::ObjectOp
object = rtlPortToCreate.object;
346 OpBuilder::InsertionGuard guard(builder);
347 builder.setInsertionPoint(
object);
351 FlatSymbolRefAttr containingModulePathRef =
352 pathInfoTable.table.at(containingModuleRef.getTarget()).symRef;
356 hw::HierPathOp containingModulePath =
357 symbolTable.lookup<hw::HierPathOp>(containingModulePathRef.getAttr());
359 assert(containingModulePath.isModule() &&
360 "expected containing module path to target a module");
362 StringAttr moduleName = containingModulePath.leafMod();
364 FModuleLike mod = symbolTable.lookup<FModuleLike>(moduleName);
365 MLIRContext *ctx = mod.getContext();
366 Location loc = mod.getLoc();
370 auto portClassName = StringAttr::get(ctx, kRtlPortClassName);
372 om::ClassType::get(ctx, FlatSymbolRefAttr::get(portClassName));
374 SmallVector<Value> ports;
375 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
377 auto portType = type_dyn_cast<FIRRTLBaseType>(mod.getPortType(i));
378 if (!portType || portType.getBitWidthOrSentinel() == 0)
387 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), 0},
389 return namespaces[m];
392 FlatSymbolRefAttr portPathRef =
393 hierPathCache.
getRefFor(ArrayAttr::get(ctx, {portSym}));
395 auto portPath = om::PathCreateOp::create(
396 builder, loc, om::PathType::get(ctx),
397 om::TargetKindAttr::get(ctx, om::TargetKind::DontTouch), basePath,
402 StringRef portDirectionName =
403 mod.getPortDirection(i) == Direction::Out ?
"Output" :
"Input";
405 auto portDirection = om::ConstantOp::create(
406 builder, loc, om::StringType::get(ctx),
407 StringAttr::get(portDirectionName, om::StringType::get(ctx)));
411 auto portWidth = om::ConstantOp::create(
412 builder, loc, om::OMIntegerType::get(ctx),
413 om::IntegerAttr::get(
414 ctx, mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
415 portType.getBitWidthOrSentinel())));
419 auto portObj = om::ObjectOp::create(
420 builder, loc, portClassType, portClassName,
421 ArrayRef<Value>{portPath, portDirection, portWidth});
423 ports.push_back(portObj);
429 om::ListCreateOp::create(builder, UnknownLoc::get(builder.getContext()),
430 getRtlPortsType(builder.getContext()), ports);
432 object.getActualParamsMutable().append({portsList});
438PathTracker::run(CircuitOp circuit,
InstanceGraph &instanceGraph,
441 const SymbolTable &symbolTable,
442 const DenseMap<DistinctAttr, FModuleOp> &owningModules) {
445 for (
auto *node : instanceGraph)
446 if (auto module = node->getModule<FModuleLike>())
447 (void)namespaces.
get(module);
449 for (
auto *node : instanceGraph)
450 if (auto module = node->getModule<FModuleLike>()) {
452 if (isa<firrtl::ClassOp, firrtl::ExtClassOp>(module))
454 PathTracker tracker(module, namespaces, instanceGraph, symbolTable,
456 if (failed(tracker.runOnModule()))
458 if (failed(tracker.updatePathInfoTable(pathInfoTable, cache)))
465LogicalResult PathTracker::runOnModule() {
466 auto processAndUpdateAnnoTarget = [&](
AnnoTarget target) -> LogicalResult {
467 auto anno = processPathTrackers(target);
470 target.setAnnotations(*anno);
475 if (failed(processAndUpdateAnnoTarget(
OpAnnoTarget(module))))
479 SmallVector<Attribute> portAnnotations;
480 portAnnotations.reserve(module.getNumPorts());
481 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
485 portAnnotations.push_back(annos->getArrayAttr());
488 module.setPortAnnotationsAttr(
489 ArrayAttr::get(module.getContext(), portAnnotations));
492 auto result =
module.walk([&](hw::InnerSymbolOpInterface op) {
493 if (failed(processAndUpdateAnnoTarget(OpAnnoTarget(op))))
494 return WalkResult::interrupt();
495 return WalkResult::advance();
498 if (result.wasInterrupted())
506PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
507 FModuleOp owningModule,
510 auto it = needsAltBasePathCache.find({moduleName, owningModule});
511 if (it != needsAltBasePathCache.end())
513 bool needsAltBasePath =
false;
514 auto *node = instanceGraph.
lookup(moduleName);
524 needsAltBasePath =
true;
530 auto diag = mlir::emitError(loc)
531 <<
"unable to uniquely resolve target due "
532 "to multiple instantiation";
533 for (
auto *use : node->uses())
534 diag.attachNote(use->getInstance().
getLoc()) <<
"instance here";
537 node = (*node->
usesBegin())->getParent();
539 needsAltBasePathCache[{moduleName, owningModule}] = needsAltBasePath;
540 return needsAltBasePath;
543FailureOr<AnnotationSet>
544PathTracker::processPathTrackers(
const AnnoTarget &target) {
547 auto *op = target.
getOp();
548 annotations.removeAnnotations([&](
Annotation anno) {
554 if (!anno.
isClass(
"circt.tracker"))
558 auto id = anno.
getMember<DistinctAttr>(
"id");
560 op->emitError(
"circt.tracker annotation missing id field");
570 if (
auto portTarget = dyn_cast<PortAnnoTarget>(target)) {
572 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), fieldID},
574 return moduleNamespace;
576 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
577 assert(!fieldID &&
"field not valid for modules");
578 targetSym = FlatSymbolRefAttr::get(module.getModuleNameAttr());
583 return moduleNamespace;
588 SmallVector<Attribute> path;
591 path.push_back(targetSym);
593 auto moduleName = target.
getModule().getModuleNameAttr();
596 hw::HierPathOp hierPathOp;
597 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
599 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
601 op->emitError(
"annotation does not point at a HierPathOp");
609 auto owningModule = owningModules.lookup(
id);
616 auto oldPath = hierPathOp.getNamepath().getValue();
621 bool pathContainsOwningModule =
false;
622 size_t owningModuleIndex = 0;
623 for (
auto [idx, pathFramgent] :
llvm::enumerate(oldPath)) {
624 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(pathFramgent)) {
625 if (innerRef.getModule() == owningModule.getModuleNameAttr()) {
626 pathContainsOwningModule =
true;
627 owningModuleIndex = idx;
629 }
else if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(pathFramgent)) {
630 if (symRef.getAttr() == owningModule.getModuleNameAttr()) {
631 pathContainsOwningModule =
true;
632 owningModuleIndex = idx;
637 if (pathContainsOwningModule) {
639 moduleName = owningModule.getModuleNameAttr();
643 llvm::append_range(path, llvm::reverse(oldPath.drop_back().drop_front(
644 owningModuleIndex)));
647 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
650 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
655 auto needsAltBasePath = getOrComputeNeedsAltBasePath(
656 op->getLoc(), moduleName, owningModule, hierPathOp);
657 if (failed(needsAltBasePath)) {
669 if (!hierPathOp && !needsAltBasePath.value())
687 std::reverse(path.begin(), path.end());
688 auto pathAttr = ArrayAttr::get(op->getContext(), path);
692 StringAttr altBasePathModule;
693 if (*needsAltBasePath) {
695 TypeSwitch<Attribute, StringAttr>(path.front())
696 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
697 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
699 altBasePathRoots.insert(altBasePathModule);
703 entries.push_back({op, id, altBasePathModule, pathAttr});
715LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
717 for (
auto root : altBasePathRoots)
718 pathInfoTable.addAltBasePathRoot(root);
720 for (
const auto &entry : entries) {
722 "expected all PathInfoTableEntries to have a pathAttr");
725 auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
726 auto &pathInfo = it->second;
734 assert(pathInfo.symRef &&
"expected all PathInfos to have a symRef");
735 auto existingHierpath =
736 symbolTable.lookup<hw::HierPathOp>(pathInfo.symRef.getValue());
737 auto existingPathAttr = existingHierpath.getNamepath();
738 if (existingPathAttr == entry.pathAttr)
741 assert(pathInfo.loc.has_value() &&
"all PathInfo should have a Location");
742 auto diag = emitError(pathInfo.loc.value(),
743 "path identifier already found, paths must resolve "
744 "to a unique target");
745 diag.attachNote(entry.op->getLoc()) <<
"other path identifier here";
751 bool canBeInstanceTarget = isa<InstanceOp, FModuleLike>(entry.op);
753 pathInfo = {entry.op->getLoc(), canBeInstanceTarget,
754 cache.
getRefFor(entry.pathAttr), entry.altBasePathModule};
765LogicalResult LowerClassesPass::processPaths(
768 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
769 auto circuit = getOperation();
773 DenseMap<DistinctAttr, FModuleOp> owningModules;
774 std::vector<Operation *> declarations;
775 auto result = circuit.walk([&](Operation *op) {
776 if (
auto pathOp = dyn_cast<PathOp>(op)) {
778 auto owningModule = owningModuleCache.lookup(pathOp);
781 pathOp->emitError(
"path does not have a single owning module");
782 return WalkResult::interrupt();
784 auto target = pathOp.getTargetAttr();
785 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
788 if (!inserted && it->second != owningModule) {
790 <<
"path reference " << target <<
" has conflicting owning modules "
791 << it->second.getModuleNameAttr() <<
" and "
792 << owningModule.getModuleNameAttr();
793 return WalkResult::interrupt();
796 return WalkResult::advance();
799 if (result.wasInterrupted())
802 if (failed(PathTracker::run(circuit, instanceGraph, namespaces, cache,
803 pathInfoTable, symbolTable, owningModules)))
808 for (
auto rootModule : pathInfoTable.getAltBasePathRoots()) {
813 auto start = llvm::df_begin(node);
814 auto end = llvm::df_end(node);
824 if (!shouldCreateClass(
825 it->getModule<FModuleLike>().getModuleNameAttr())) {
826 it = it.skipChildren();
831 if (it->begin() == it->end()) {
837 StringAttr passthroughModule = it->getModule().getModuleNameAttr();
838 pathInfoTable.addAltBasePathPassthrough(passthroughModule, rootModule);
848void LowerClassesPass::runOnOperation() {
849 MLIRContext *ctx = &getContext();
850 auto intraPassMutex = std::mutex();
853 CircuitOp circuit = getOperation();
857 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
863 for (
auto *node : instanceGraph)
864 if (auto moduleLike = node->getModule<firrtl::FModuleLike>())
865 shouldCreateClassMemo.insert({moduleLike.getModuleNameAttr(),
false});
867 parallelForEach(circuit.getContext(), instanceGraph,
869 if (auto moduleLike = node->getModule<FModuleLike>())
870 shouldCreateClassMemo[moduleLike.getModuleNameAttr()] =
871 shouldCreateClassImpl(node);
875 PathInfoTable pathInfoTable;
876 if (failed(processPaths(instanceGraph, namespaces, cache, pathInfoTable,
886 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
887 SmallVector<FModuleLike> modulesToErasePortsFrom;
888 for (
auto *node : instanceGraph) {
889 auto moduleLike = node->
getModule<firrtl::FModuleLike>();
893 if (shouldCreateClass(moduleLike.getModuleNameAttr())) {
894 auto omClass = createClass(moduleLike, pathInfoTable, intraPassMutex);
895 auto &classLoweringState =
loweringState.classLoweringStateTable[omClass];
898 if (!classLoweringState.moduleLike)
899 classLoweringState.moduleLike = moduleLike;
902 if (!isa<firrtl::ClassLike>(moduleLike.getOperation()))
903 modulesToErasePortsFrom.push_back(moduleLike);
910 for (
auto *instance : *node) {
911 auto inst = instance->
getInstance<firrtl::InstanceOp>();
915 auto module = instance->getTarget()->getModule<FModuleLike>();
916 if (module && shouldCreateClass(module.getModuleNameAttr())) {
919 return namespaces[module];
921 SmallVector<Attribute> path = {targetSym};
922 auto pathAttr = ArrayAttr::get(ctx, path);
923 auto hierPath = cache.
getOpFor(pathAttr);
924 classLoweringState.paths.push_back(hierPath);
929 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
930 classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
935 mlir::parallelForEach(ctx,
loweringState.classLoweringStateTable,
936 [
this, &pathInfoTable](
auto &entry) {
937 const auto &[classLike, state] = entry;
938 lowerClassLike(state.moduleLike, classLike,
945 for (
auto moduleLike : modulesToErasePortsFrom) {
946 BitVector portsToErase(moduleLike.getNumPorts());
947 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i)
948 if (isa<PropertyType>(moduleLike.getPortType(i)))
950 moduleLike.erasePorts(portsToErase);
954 for (
auto &[omClass, state] :
loweringState.classLoweringStateTable) {
955 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
957 for (
auto *use :
llvm::make_early_inc_range(node->uses()))
959 instanceGraph.
erase(node);
960 state.moduleLike.erase();
965 SmallVector<Operation *> objectContainers;
966 for (
auto &op : circuit.getOps())
967 if (isa<FModuleOp,
om::ClassLike>(op))
968 objectContainers.push_back(&op);
972 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
974 pathInfoTable, intraPassMutex);
976 return signalPassFailure();
979 if (!rtlPortsToCreate.empty())
980 createAllRtlPorts(pathInfoTable, namespaces, cache);
984 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
985 return dialectConversion(op, pathInfoTable, classTypeTable);
987 return signalPassFailure();
990 markAnalysesPreserved<InstanceGraph>();
993 rtlPortsToCreate.clear();
997bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
999 return shouldCreateClassMemo.at(modName);
1003 SmallVector<Attribute> &fieldNames,
1004 SmallVector<NamedAttribute> &fieldTypes) {
1005 if (hasContainingModule) {
1006 auto name = builder.getStringAttr(kPortsName);
1007 fieldNames.push_back(name);
1008 fieldTypes.push_back(NamedAttribute(
1009 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
1015 ArrayRef<StringRef> formalParamNames,
1016 bool hasContainingModule) {
1017 SmallVector<Attribute> fieldNames;
1018 SmallVector<NamedAttribute> fieldTypes;
1019 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1020 auto type = moduleLike.getPortType(i);
1021 if (!isa<PropertyType>(type))
1024 auto direction = moduleLike.getPortDirection(i);
1025 if (direction != Direction::In) {
1026 auto name = moduleLike.getPortNameAttr(i);
1027 fieldNames.push_back(name);
1028 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1033 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1034 formalParamNames, fieldNames, fieldTypes);
1037static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1039 ArrayRef<StringRef> formalParamNames,
1040 bool hasContainingModule) {
1042 SmallVector<Attribute> fieldNames;
1043 SmallVector<NamedAttribute> fieldTypes;
1044 for (
auto op : llvm::make_early_inc_range(
1045 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1046 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1050 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1052 fieldNames.push_back(name);
1053 fieldTypes.push_back(
1054 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1059 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1060 formalParamNames, fieldNames, fieldTypes);
1064om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1065 const PathInfoTable &pathInfoTable,
1066 std::mutex &intraPassMutex) {
1068 SmallVector<StringRef> formalParamNames;
1070 formalParamNames.emplace_back(
"basepath");
1073 size_t nAltBasePaths =
1074 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1075 for (
size_t i = 0; i < nAltBasePaths; ++i)
1076 formalParamNames.push_back(StringAttr::get(
1077 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1080 bool hasContainingModule =
false;
1081 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1082 if (port.isInput() && isa<PropertyType>(port.type)) {
1083 formalParamNames.push_back(port.name);
1086 if (port.name.strref().contains(kContainingModuleName))
1087 hasContainingModule =
true;
1091 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1094 if (hasContainingModule)
1095 formalParamNames.push_back(kPortsName);
1098 StringRef className = moduleLike.getName();
1099 StringAttr baseClassNameAttr = moduleLike.getNameAttr();
1102 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation())) {
1103 className = externMod.getExtModuleName();
1104 baseClassNameAttr = externMod.getExtModuleNameAttr();
1110 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1113 om::ClassLike loweredClassOp;
1114 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1115 moduleLike.getOperation())) {
1118 auto [it, inserted] = externalClassMap.insert({baseClassNameAttr, {}});
1120 it->getSecond() =
convertExtClass(moduleLike, builder, className + suffix,
1121 formalParamNames, hasContainingModule);
1122 loweredClassOp = it->getSecond();
1124 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1125 formalParamNames, hasContainingModule);
1128 return loweredClassOp;
1131void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1132 om::ClassLike classLike,
1133 const PathInfoTable &pathInfoTable) {
1135 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1136 return lowerClass(classOp, moduleLike, pathInfoTable);
1138 if (
auto classExternOp =
1139 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1140 return lowerClassExtern(classExternOp, moduleLike);
1142 llvm_unreachable(
"unhandled class-like op");
1145void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1146 const PathInfoTable &pathInfoTable) {
1148 SmallVector<Property> inputProperties;
1149 BitVector portsToErase(moduleLike.getNumPorts());
1150 bool hasContainingModule =
false;
1151 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1153 if (!isa<PropertyType>(port.type))
1157 if (port.isInput()) {
1158 inputProperties.push_back({index, port.name, port.type, port.loc});
1161 if (port.name.strref().contains(kContainingModuleName))
1162 hasContainingModule =
true;
1166 portsToErase.set(index);
1171 Block *moduleBody = &moduleLike->getRegion(0).front();
1172 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1174 auto basePathType = om::BasePathType::get(&getContext());
1175 auto unknownLoc = UnknownLoc::get(&getContext());
1176 classBody->addArgument(basePathType, unknownLoc);
1179 size_t nAltBasePaths =
1180 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1181 for (
size_t i = 0; i < nAltBasePaths; ++i)
1182 classBody->addArgument(basePathType, unknownLoc);
1185 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1186 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1187 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1189 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1190 for (
auto result : instance.getResults()) {
1191 if (isa<PropertyType>(result.getType()))
1192 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1197 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1198 if (llvm::any_of(op.getOperands(), isProperty) ||
1199 llvm::any_of(op.getResults(), isProperty))
1200 op.moveBefore(classBody, classBody->begin());
1204 for (
auto input : inputProperties) {
1205 auto arg = classBody->addArgument(input.type, input.loc);
1206 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1209 llvm::SmallVector<mlir::Location> fieldLocs;
1210 llvm::SmallVector<mlir::Value> fieldValues;
1211 for (Operation &op :
1213 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1214 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1216 fieldLocs.push_back(op.getLoc());
1217 fieldValues.push_back(propAssign.getSrc());
1224 if (hasContainingModule) {
1225 BlockArgument argumentValue = classBody->addArgument(
1226 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1227 fieldLocs.push_back(argumentValue.getLoc());
1228 fieldValues.push_back(argumentValue);
1231 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1232 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1237void LowerClassesPass::lowerClassExtern(om::ClassExternOp classExternOp,
1238 FModuleLike moduleLike) {
1242 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1245 classBody->addArgument(om::BasePathType::get(&getContext()),
1246 UnknownLoc::get(&getContext()));
1248 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1249 auto type = moduleLike.getPortType(i);
1250 if (!isa<PropertyType>(type))
1253 auto loc = moduleLike.getPortLocation(i);
1254 auto direction = moduleLike.getPortDirection(i);
1255 if (direction == Direction::In)
1256 classBody->addArgument(type, loc);
1265 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1266 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1267 SmallVectorImpl<Operation *> &opsToErase) {
1269 auto basePath = firrtlObject->getBlock()->getArgument(0);
1272 auto firrtlClassType = firrtlObject.getType();
1273 auto numElements = firrtlClassType.getNumElements();
1274 llvm::SmallVector<unsigned> argIndexTable;
1278 SmallVector<Value> altBasePaths;
1279 pathInfoTable.collectAltBasePaths(
1280 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1283 unsigned nextArgIndex = 1 + altBasePaths.size();
1286 auto direction = firrtlClassType.getElement(i).direction;
1287 if (direction == Direction::In)
1288 argIndexTable[i] = nextArgIndex++;
1294 llvm::SmallVector<Value> args;
1295 args.resize(nextArgIndex);
1299 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1300 args[1 + i] = altBasePath;
1302 firrtl::PathOp containingModuleRef;
1303 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1304 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1305 auto index = subfield.getIndex();
1306 auto direction = firrtlClassType.getElement(index).direction;
1310 if (direction == Direction::Out)
1313 for (
auto *subfieldUser :
1314 llvm::make_early_inc_range(subfield->getUsers())) {
1315 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1319 auto dst = propassign.getOperand(0);
1320 auto src = propassign.getOperand(1);
1321 if (dst == subfield.getResult()) {
1322 args[argIndexTable[index]] = src;
1323 opsToErase.push_back(propassign);
1326 if (firrtlClassType.getElement(index).name.strref().contains(
1327 kContainingModuleName)) {
1328 assert(!containingModuleRef &&
1329 "expected exactly one containingModule");
1330 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1331 "expected containingModule to be a PathOp");
1332 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1338 opsToErase.push_back(subfield);
1344 auto element = firrtlClassType.getElement(i);
1345 if (element.direction == Direction::Out)
1348 auto argIndex = argIndexTable[i];
1349 if (!args[argIndex])
1350 return emitError(firrtlObject.getLoc())
1351 <<
"uninitialized input port " << element.name;
1355 auto className = firrtlObject.getType().getNameAttr();
1356 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1359 OpBuilder builder(firrtlObject);
1361 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1362 firrtlObject.getClassNameAttr(), args);
1365 if (containingModuleRef) {
1366 std::lock_guard<std::mutex> guard(intraPassMutex);
1367 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1372 auto cast = UnrealizedConversionCastOp::create(
1373 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1374 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1377 opsToErase.push_back(firrtlObject);
1387 const PathInfoTable &pathInfoTable,
1388 SmallVectorImpl<Operation *> &opsToErase) {
1391 OpBuilder builder(firrtlInstance);
1396 SmallVector<Value> actualParameters;
1398 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1399 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1400 auto rebasedPath = om::BasePathCreateOp::create(
1401 builder, firrtlInstance->getLoc(), basePath, symRef);
1403 actualParameters.push_back(rebasedPath);
1406 pathInfoTable.collectAltBasePaths(
1407 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1410 for (
auto result : firrtlInstance.getResults()) {
1412 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1417 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1418 if (!propertyResult)
1424 assert(propertyAssignment &&
"properties require single assignment");
1425 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1428 opsToErase.push_back(propertyAssignment);
1432 auto referencedModule =
1433 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1435 StringRef moduleName = referencedModule.getName();
1438 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1439 moduleName = externMod.getExtModuleName();
1442 auto className = FlatSymbolRefAttr::get(
1443 builder.getStringAttr(moduleName + kClassNameSuffix));
1445 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1449 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1450 className.getAttr(), actualParameters);
1455 for (
auto result : firrtlInstance.getResults()) {
1457 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1462 if (!isa<PropertyType>(result.getType()))
1466 auto objectFieldPath = builder.getArrayAttr({FlatSymbolRefAttr::get(
1467 firrtlInstance.getPortNameAttr(result.getResultNumber()))});
1470 auto objectField = om::ObjectFieldOp::create(
1471 builder,
object.
getLoc(), result.getType(),
object, objectFieldPath);
1473 result.replaceAllUsesWith(objectField);
1477 opsToErase.push_back(firrtlInstance);
1485 SmallVectorImpl<Operation *> &opsToErase) {
1487 BitVector portsToErase(firrtlInstance.getNumResults());
1488 for (
auto result : firrtlInstance.getResults())
1489 if (isa<PropertyType>(result.getType()))
1490 portsToErase.set(result.getResultNumber());
1493 if (portsToErase.none())
1498 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1507 opsToErase.push_back(firrtlInstance);
1513 SmallVectorImpl<Operation *> &opsToErase) {
1514 OpBuilder builder(moduleOp);
1515 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1516 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1517 assert(0 &&
"should be no objects in modules");
1518 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1528 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1529 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1530 SmallVectorImpl<Operation *> &opsToErase) {
1531 OpBuilder builder(classOp);
1532 auto &classState = state.classLoweringStateTable.at(classOp);
1533 auto it = classState.paths.begin();
1534 for (
auto &op : classOp->getRegion(0).getOps()) {
1535 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1537 intraPassMutex, opsToErase)))
1539 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1541 pathInfoTable, opsToErase)))
1549LogicalResult LowerClassesPass::updateInstances(
1550 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1551 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1556 SmallVector<Operation *> opsToErase;
1558 TypeSwitch<Operation *, LogicalResult>(op)
1560 .Case([&](FModuleOp moduleOp) {
1565 .Case([&](om::ClassOp classOp) {
1569 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1570 intraPassMutex, opsToErase);
1572 .Default([](
auto *op) {
return success(); });
1576 for (
auto *op : opsToErase)
1583void LowerClassesPass::createAllRtlPorts(
1584 const PathInfoTable &pathInfoTable,
1587 MLIRContext *ctx = &getContext();
1590 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1593 om::ClassOp::buildSimpleClassOp(
1594 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1595 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1596 {om::PathType::get(ctx), om::StringType::get(ctx),
1597 om::OMIntegerType::get(ctx)});
1600 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1601 return lhs.object.getClassName() < rhs.object.getClassName();
1605 for (
auto rtlPortToCreate : rtlPortsToCreate)
1606 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1616struct FIntegerConstantOpConversion
1618 using OpConversionPattern::OpConversionPattern;
1621 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1622 ConversionPatternRewriter &rewriter)
const override {
1623 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1624 op, om::OMIntegerType::get(op.getContext()),
1625 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1631 using OpConversionPattern::OpConversionPattern;
1634 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1635 ConversionPatternRewriter &rewriter)
const override {
1636 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1637 op, rewriter.getBoolAttr(adaptor.getValue()));
1642struct DoubleConstantOpConversion
1644 using OpConversionPattern::OpConversionPattern;
1647 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1648 ConversionPatternRewriter &rewriter)
const override {
1649 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1654struct StringConstantOpConversion
1656 using OpConversionPattern::OpConversionPattern;
1659 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1660 ConversionPatternRewriter &rewriter)
const override {
1661 auto stringType = om::StringType::get(op.getContext());
1662 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1663 op, stringType, StringAttr::get(op.getValue(), stringType));
1668struct ListCreateOpConversion
1670 using OpConversionPattern::OpConversionPattern;
1673 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1674 ConversionPatternRewriter &rewriter)
const override {
1675 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1678 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1679 adaptor.getElements());
1684struct ListConcatOpConversion
1686 using OpConversionPattern::OpConversionPattern;
1689 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1690 ConversionPatternRewriter &rewriter)
const override {
1691 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1694 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1695 adaptor.getSubLists());
1700struct IntegerAddOpConversion
1702 using OpConversionPattern::OpConversionPattern;
1705 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1706 ConversionPatternRewriter &rewriter)
const override {
1707 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1713struct IntegerMulOpConversion
1715 using OpConversionPattern::OpConversionPattern;
1718 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1719 ConversionPatternRewriter &rewriter)
const override {
1720 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1726struct IntegerShrOpConversion
1728 using OpConversionPattern::OpConversionPattern;
1731 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1732 ConversionPatternRewriter &rewriter)
const override {
1733 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1739struct IntegerShlOpConversion
1741 using OpConversionPattern::OpConversionPattern;
1744 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1745 ConversionPatternRewriter &rewriter)
const override {
1746 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1752struct StringConcatOpConversion
1754 using OpConversionPattern::OpConversionPattern;
1757 matchAndRewrite(firrtl::StringConcatOp op, OpAdaptor adaptor,
1758 ConversionPatternRewriter &rewriter)
const override {
1759 rewriter.replaceOpWithNewOp<om::StringConcatOp>(op, adaptor.getOperands());
1766 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1767 const PathInfoTable &pathInfoTable,
1768 PatternBenefit benefit = 1)
1770 pathInfoTable(pathInfoTable) {}
1773 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1774 ConversionPatternRewriter &rewriter)
const override {
1775 auto *
context = op->getContext();
1776 auto pathType = om::PathType::get(
context);
1777 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1780 auto basePath = op->getBlock()->getArgument(0);
1784 if (pathInfoIt == pathInfoTable.table.end()) {
1785 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1786 return emitError(op.getLoc(),
"DontTouch target was deleted");
1787 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1788 return emitError(op.getLoc(),
"Instance target was deleted");
1789 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1793 auto pathInfo = pathInfoIt->second;
1794 auto symbol = pathInfo.symRef;
1798 om::TargetKind targetKind;
1799 switch (op.getTargetKind()) {
1800 case firrtl::TargetKind::DontTouch:
1801 targetKind = om::TargetKind::DontTouch;
1803 case firrtl::TargetKind::Reference:
1804 targetKind = om::TargetKind::Reference;
1806 case firrtl::TargetKind::Instance:
1807 if (!pathInfo.canBeInstanceTarget)
1808 return emitError(op.getLoc(),
"invalid target for instance path")
1809 .attachNote(pathInfo.loc)
1810 <<
"target not instance or module";
1811 targetKind = om::TargetKind::Instance;
1813 case firrtl::TargetKind::MemberInstance:
1814 case firrtl::TargetKind::MemberReference:
1815 if (pathInfo.canBeInstanceTarget)
1816 targetKind = om::TargetKind::MemberInstance;
1818 targetKind = om::TargetKind::MemberReference;
1824 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1828 auto parent = op->getParentOfType<om::ClassOp>();
1829 auto parentName = parent.getName();
1830 if (parentName.ends_with(kClassNameSuffix))
1831 parentName = parentName.drop_back(kClassNameSuffix.size());
1832 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1836 pathInfoTable.getRootsForPassthrough(originalParentName);
1837 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1840 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1841 if (altBasePathModule == altBasePath) {
1843 auto basePathArg = op->getBlock()->getArgument(1 + i);
1844 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1845 "expected a passthrough base path");
1846 basePath = basePathArg;
1851 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1852 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1857 const PathInfoTable &pathInfoTable;
1861 using OpConversionPattern::OpConversionPattern;
1864 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1865 ConversionPatternRewriter &rewriter)
const override {
1866 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1875 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1876 if (!regionKindInterface)
1878 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1887 rewriter.replaceOp(wireOp, propAssign.getSrc());
1890 rewriter.eraseOp(propAssign);
1897 using OpConversionPattern::OpConversionPattern;
1900 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
1901 ConversionPatternRewriter &rewriter)
const override {
1902 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
1907struct ObjectSubfieldOpConversion
1909 using OpConversionPattern::OpConversionPattern;
1911 ObjectSubfieldOpConversion(
1912 const TypeConverter &typeConverter, MLIRContext *
context,
1913 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1915 classTypeTable(classTypeTable) {}
1918 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
1919 ConversionPatternRewriter &rewriter)
const override {
1920 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1926 auto firrtlClassType =
1927 classTypeTable.lookup(omClassType.getClassName().getAttr());
1928 if (!firrtlClassType)
1931 const auto &element = firrtlClassType.getElement(op.getIndex());
1933 if (element.direction == Direction::In)
1936 auto field = FlatSymbolRefAttr::get(element.name);
1937 auto path = rewriter.getArrayAttr({field});
1938 auto type = typeConverter->convertType(element.type);
1939 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1944 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
1948 using OpConversionPattern::OpConversionPattern;
1951 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
1952 ConversionPatternRewriter &rewriter)
const override {
1953 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
1954 adaptor.getFieldLocsAttr());
1960 using OpConversionPattern::OpConversionPattern;
1963 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
1964 ConversionPatternRewriter &rewriter)
const override {
1967 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1968 adaptor.getClassNameAttr(),
1969 adaptor.getActualParams());
1974static LogicalResult convertClassLike(om::ClassLike classOp,
1975 TypeConverter typeConverter,
1976 ConversionPatternRewriter &rewriter) {
1977 Block *body = classOp.getBodyBlock();
1978 TypeConverter::SignatureConversion result(body->getNumArguments());
1982 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
1986 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
1990 rewriter.modifyOpInPlace(classOp, [&]() {
1991 mlir::AttrTypeReplacer replacer;
1992 replacer.addReplacement([&](TypeAttr typeAttr) {
1993 return mlir::TypeAttr::get(
1994 typeConverter.convertType(typeAttr.getValue()));
1996 classOp.replaceFieldTypes(replacer);
2003 using OpConversionPattern::OpConversionPattern;
2006 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
2007 ConversionPatternRewriter &rewriter)
const override {
2008 return convertClassLike(classOp, *typeConverter, rewriter);
2012struct ClassExternOpSignatureConversion
2014 using OpConversionPattern::OpConversionPattern;
2017 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2018 ConversionPatternRewriter &rewriter)
const override {
2019 return convertClassLike(classOp, *typeConverter, rewriter);
2024 using OpConversionPattern::OpConversionPattern;
2027 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2028 ConversionPatternRewriter &rewriter)
const override {
2031 auto type = typeConverter->convertType(op.getType());
2035 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2036 op, type, adaptor.getObject(), adaptor.getFieldPathAttr());
2043struct UnrealizedConversionCastOpConversion
2045 using OpConversionPattern::OpConversionPattern;
2048 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2049 ConversionPatternRewriter &rewriter)
const override {
2050 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2052 auto type = typeConverter->convertType(op.getResult(0));
2053 if (!type || type != adaptor.getOperands()[0].getType())
2055 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2061 using OpConversionPattern::OpConversionPattern;
2064 matchAndRewrite(UnknownValueOp op, OpAdaptor adaptor,
2065 ConversionPatternRewriter &rewriter)
const override {
2066 auto convertedType = typeConverter->convertType(op.getType());
2069 rewriter.replaceOpWithNewOp<om::UnknownValueOp>(op, convertedType);
2083 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2084 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2087 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2088 auto containsFIRRTLType = [](Type type) {
2090 .walk([](Type type) {
2091 return failure(isa<FIRRTLDialect>(type.getDialect()));
2095 auto noFIRRTLOperands =
2096 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2097 return containsFIRRTLType(type);
2099 auto noFIRRTLResults =
2100 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2101 return containsFIRRTLType(type);
2103 return noFIRRTLOperands && noFIRRTLResults;
2107 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2108 [](Operation *op) -> std::optional<bool> {
2109 auto classLike = dyn_cast<om::ClassLike>(op);
2111 return std::nullopt;
2112 auto fieldNames = classLike.getFieldNames();
2113 if (!llvm::all_of(fieldNames, [&](
auto field) {
2114 std::optional<Type> type =
2115 classLike.getFieldType(cast<StringAttr>(field));
2116 return type.has_value() && !isa<FIRRTLType>(type.value());
2120 return llvm::none_of(
2121 classLike.getBodyBlock()->getArgumentTypes(),
2122 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2128 converter.addConversion([](IntegerType type) {
2129 return om::OMIntegerType::get(type.getContext());
2131 converter.addConversion([](FIntegerType type) {
2135 return om::OMIntegerType::get(type.getContext());
2139 converter.addConversion([](om::StringType type) {
return type; });
2140 converter.addConversion([](firrtl::StringType type) {
2141 return om::StringType::get(type.getContext());
2145 converter.addConversion([](om::PathType type) {
return type; });
2146 converter.addConversion([](om::BasePathType type) {
return type; });
2147 converter.addConversion([](om::FrozenPathType type) {
return type; });
2148 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2149 converter.addConversion([](firrtl::PathType type) {
2150 return om::PathType::get(type.getContext());
2154 converter.addConversion([](om::ClassType type) {
return type; });
2155 converter.addConversion([](firrtl::ClassType type) {
2156 return om::ClassType::get(type.getContext(), type.getNameAttr());
2160 converter.addConversion([](om::AnyType type) {
return type; });
2161 converter.addConversion([](firrtl::AnyRefType type) {
2162 return om::AnyType::get(type.getContext());
2166 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2168 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2170 auto elementType = converter.convertType(type.getElementType());
2176 converter.addConversion(
2177 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2179 return convertListType(type);
2182 converter.addConversion(
2183 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2185 return convertListType(type);
2189 converter.addConversion(
2190 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2193 converter.addConversion(
2194 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2198 converter.addTargetMaterialization(
2199 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2200 assert(values.size() == 1);
2201 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2207 converter.addSourceMaterialization(
2208 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2209 assert(values.size() == 1);
2210 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2216 RewritePatternSet &
patterns, TypeConverter &converter,
2217 const PathInfoTable &pathInfoTable,
2218 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2219 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2220 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2228 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2229 patterns.add<ClassExternOpSignatureConversion>(converter,
2236 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2242 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2248LogicalResult LowerClassesPass::dialectConversion(
2249 Operation *op,
const PathInfoTable &pathInfoTable,
2250 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2251 ConversionTarget target(getContext());
2254 TypeConverter typeConverter;
2257 RewritePatternSet
patterns(&getContext());
2261 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 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 void populateRewritePatterns(RewritePatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
UseIterator usesBegin()
Iterate the instance records which instantiate this module.
bool hasOneUse()
Return true if this module has exactly one use.
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.
This holds the name and type that describes the module's ports.