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 PropertyAssertOpConversion
1644 using OpConversionPattern::OpConversionPattern;
1647 matchAndRewrite(firrtl::PropertyAssertOp op, OpAdaptor adaptor,
1648 ConversionPatternRewriter &rewriter)
const override {
1649 rewriter.replaceOpWithNewOp<om::PropertyAssertOp>(
1650 op, adaptor.getCondition(), op.getMessageAttr());
1655struct DoubleConstantOpConversion
1657 using OpConversionPattern::OpConversionPattern;
1660 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1661 ConversionPatternRewriter &rewriter)
const override {
1662 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1667struct StringConstantOpConversion
1669 using OpConversionPattern::OpConversionPattern;
1672 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1673 ConversionPatternRewriter &rewriter)
const override {
1674 auto stringType = om::StringType::get(op.getContext());
1675 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1676 op, stringType, StringAttr::get(op.getValue(), stringType));
1681struct ListCreateOpConversion
1683 using OpConversionPattern::OpConversionPattern;
1686 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1687 ConversionPatternRewriter &rewriter)
const override {
1688 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1691 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1692 adaptor.getElements());
1697struct ListConcatOpConversion
1699 using OpConversionPattern::OpConversionPattern;
1702 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1703 ConversionPatternRewriter &rewriter)
const override {
1704 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1707 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1708 adaptor.getSubLists());
1713struct IntegerAddOpConversion
1715 using OpConversionPattern::OpConversionPattern;
1718 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1719 ConversionPatternRewriter &rewriter)
const override {
1720 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1726struct IntegerMulOpConversion
1728 using OpConversionPattern::OpConversionPattern;
1731 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1732 ConversionPatternRewriter &rewriter)
const override {
1733 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1739struct IntegerShrOpConversion
1741 using OpConversionPattern::OpConversionPattern;
1744 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1745 ConversionPatternRewriter &rewriter)
const override {
1746 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1752struct IntegerShlOpConversion
1754 using OpConversionPattern::OpConversionPattern;
1757 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1758 ConversionPatternRewriter &rewriter)
const override {
1759 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1765struct StringConcatOpConversion
1767 using OpConversionPattern::OpConversionPattern;
1770 matchAndRewrite(firrtl::StringConcatOp op, OpAdaptor adaptor,
1771 ConversionPatternRewriter &rewriter)
const override {
1772 rewriter.replaceOpWithNewOp<om::StringConcatOp>(op, adaptor.getOperands());
1778 using OpConversionPattern::OpConversionPattern;
1781 matchAndRewrite(firrtl::PropEqOp op, OpAdaptor adaptor,
1782 ConversionPatternRewriter &rewriter)
const override {
1783 rewriter.replaceOpWithNewOp<om::PropEqOp>(op, adaptor.getLhs(),
1791 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1792 const PathInfoTable &pathInfoTable,
1793 PatternBenefit benefit = 1)
1795 pathInfoTable(pathInfoTable) {}
1798 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1799 ConversionPatternRewriter &rewriter)
const override {
1800 auto *
context = op->getContext();
1801 auto pathType = om::PathType::get(
context);
1802 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1805 auto basePath = op->getBlock()->getArgument(0);
1809 if (pathInfoIt == pathInfoTable.table.end()) {
1810 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1811 return emitError(op.getLoc(),
"DontTouch target was deleted");
1812 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1813 return emitError(op.getLoc(),
"Instance target was deleted");
1814 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1818 auto pathInfo = pathInfoIt->second;
1819 auto symbol = pathInfo.symRef;
1823 om::TargetKind targetKind;
1824 switch (op.getTargetKind()) {
1825 case firrtl::TargetKind::DontTouch:
1826 targetKind = om::TargetKind::DontTouch;
1828 case firrtl::TargetKind::Reference:
1829 targetKind = om::TargetKind::Reference;
1831 case firrtl::TargetKind::Instance:
1832 if (!pathInfo.canBeInstanceTarget)
1833 return emitError(op.getLoc(),
"invalid target for instance path")
1834 .attachNote(pathInfo.loc)
1835 <<
"target not instance or module";
1836 targetKind = om::TargetKind::Instance;
1838 case firrtl::TargetKind::MemberInstance:
1839 case firrtl::TargetKind::MemberReference:
1840 if (pathInfo.canBeInstanceTarget)
1841 targetKind = om::TargetKind::MemberInstance;
1843 targetKind = om::TargetKind::MemberReference;
1849 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1853 auto parent = op->getParentOfType<om::ClassOp>();
1854 auto parentName = parent.getName();
1855 if (parentName.ends_with(kClassNameSuffix))
1856 parentName = parentName.drop_back(kClassNameSuffix.size());
1857 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1861 pathInfoTable.getRootsForPassthrough(originalParentName);
1862 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1865 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1866 if (altBasePathModule == altBasePath) {
1868 auto basePathArg = op->getBlock()->getArgument(1 + i);
1869 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1870 "expected a passthrough base path");
1871 basePath = basePathArg;
1876 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1877 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1882 const PathInfoTable &pathInfoTable;
1886 using OpConversionPattern::OpConversionPattern;
1889 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1890 ConversionPatternRewriter &rewriter)
const override {
1891 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1900 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1901 if (!regionKindInterface)
1903 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1912 rewriter.replaceOp(wireOp, propAssign.getSrc());
1915 rewriter.eraseOp(propAssign);
1922 using OpConversionPattern::OpConversionPattern;
1925 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
1926 ConversionPatternRewriter &rewriter)
const override {
1927 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
1932struct ObjectSubfieldOpConversion
1934 using OpConversionPattern::OpConversionPattern;
1936 ObjectSubfieldOpConversion(
1937 const TypeConverter &typeConverter, MLIRContext *
context,
1938 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1940 classTypeTable(classTypeTable) {}
1943 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
1944 ConversionPatternRewriter &rewriter)
const override {
1945 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1951 auto firrtlClassType =
1952 classTypeTable.lookup(omClassType.getClassName().getAttr());
1953 if (!firrtlClassType)
1956 const auto &element = firrtlClassType.getElement(op.getIndex());
1958 if (element.direction == Direction::In)
1961 auto field = FlatSymbolRefAttr::get(element.name);
1962 auto path = rewriter.getArrayAttr({field});
1963 auto type = typeConverter->convertType(element.type);
1964 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1969 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
1973 using OpConversionPattern::OpConversionPattern;
1976 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
1977 ConversionPatternRewriter &rewriter)
const override {
1978 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
1979 adaptor.getFieldLocsAttr());
1985 using OpConversionPattern::OpConversionPattern;
1988 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
1989 ConversionPatternRewriter &rewriter)
const override {
1992 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1993 adaptor.getClassNameAttr(),
1994 adaptor.getActualParams());
1999static LogicalResult convertClassLike(om::ClassLike classOp,
2000 TypeConverter typeConverter,
2001 ConversionPatternRewriter &rewriter) {
2002 Block *body = classOp.getBodyBlock();
2003 TypeConverter::SignatureConversion result(body->getNumArguments());
2007 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
2011 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
2015 rewriter.modifyOpInPlace(classOp, [&]() {
2016 mlir::AttrTypeReplacer replacer;
2017 replacer.addReplacement([&](TypeAttr typeAttr) {
2018 return mlir::TypeAttr::get(
2019 typeConverter.convertType(typeAttr.getValue()));
2021 classOp.replaceFieldTypes(replacer);
2028 using OpConversionPattern::OpConversionPattern;
2031 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
2032 ConversionPatternRewriter &rewriter)
const override {
2033 return convertClassLike(classOp, *typeConverter, rewriter);
2037struct ClassExternOpSignatureConversion
2039 using OpConversionPattern::OpConversionPattern;
2042 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2043 ConversionPatternRewriter &rewriter)
const override {
2044 return convertClassLike(classOp, *typeConverter, rewriter);
2049 using OpConversionPattern::OpConversionPattern;
2052 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2053 ConversionPatternRewriter &rewriter)
const override {
2056 auto type = typeConverter->convertType(op.getType());
2060 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2061 op, type, adaptor.getObject(), adaptor.getFieldPathAttr());
2068struct UnrealizedConversionCastOpConversion
2070 using OpConversionPattern::OpConversionPattern;
2073 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2074 ConversionPatternRewriter &rewriter)
const override {
2075 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2077 auto type = typeConverter->convertType(op.getResult(0));
2078 if (!type || type != adaptor.getOperands()[0].getType())
2080 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2086 using OpConversionPattern::OpConversionPattern;
2089 matchAndRewrite(UnknownValueOp op, OpAdaptor adaptor,
2090 ConversionPatternRewriter &rewriter)
const override {
2091 auto convertedType = typeConverter->convertType(op.getType());
2094 rewriter.replaceOpWithNewOp<om::UnknownValueOp>(op, convertedType);
2108 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2109 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2112 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2113 auto containsFIRRTLType = [](Type type) {
2115 .walk([](Type type) {
2116 return failure(isa<FIRRTLDialect>(type.getDialect()));
2120 auto noFIRRTLOperands =
2121 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2122 return containsFIRRTLType(type);
2124 auto noFIRRTLResults =
2125 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2126 return containsFIRRTLType(type);
2128 return noFIRRTLOperands && noFIRRTLResults;
2132 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2133 [](Operation *op) -> std::optional<bool> {
2134 auto classLike = dyn_cast<om::ClassLike>(op);
2136 return std::nullopt;
2137 auto fieldNames = classLike.getFieldNames();
2138 if (!llvm::all_of(fieldNames, [&](
auto field) {
2139 std::optional<Type> type =
2140 classLike.getFieldType(cast<StringAttr>(field));
2141 return type.has_value() && !isa<FIRRTLType>(type.value());
2145 return llvm::none_of(
2146 classLike.getBodyBlock()->getArgumentTypes(),
2147 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2153 converter.addConversion([](IntegerType type) {
2154 return om::OMIntegerType::get(type.getContext());
2156 converter.addConversion([](FIntegerType type) {
2160 return om::OMIntegerType::get(type.getContext());
2164 converter.addConversion([](om::StringType type) {
return type; });
2165 converter.addConversion([](firrtl::StringType type) {
2166 return om::StringType::get(type.getContext());
2170 converter.addConversion([](om::PathType type) {
return type; });
2171 converter.addConversion([](om::BasePathType type) {
return type; });
2172 converter.addConversion([](om::FrozenPathType type) {
return type; });
2173 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2174 converter.addConversion([](firrtl::PathType type) {
2175 return om::PathType::get(type.getContext());
2179 converter.addConversion([](om::ClassType type) {
return type; });
2180 converter.addConversion([](firrtl::ClassType type) {
2181 return om::ClassType::get(type.getContext(), type.getNameAttr());
2185 converter.addConversion([](om::AnyType type) {
return type; });
2186 converter.addConversion([](firrtl::AnyRefType type) {
2187 return om::AnyType::get(type.getContext());
2191 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2193 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2195 auto elementType = converter.convertType(type.getElementType());
2201 converter.addConversion(
2202 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2204 return convertListType(type);
2207 converter.addConversion(
2208 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2210 return convertListType(type);
2214 converter.addConversion(
2215 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2218 converter.addConversion(
2219 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2223 converter.addTargetMaterialization(
2224 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2225 assert(values.size() == 1);
2226 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2232 converter.addSourceMaterialization(
2233 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2234 assert(values.size() == 1);
2235 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2241 RewritePatternSet &
patterns, TypeConverter &converter,
2242 const PathInfoTable &pathInfoTable,
2243 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2244 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2245 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2253 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2254 patterns.add<ClassExternOpSignatureConversion>(converter,
2261 patterns.add<PropertyAssertOpConversion>(converter,
patterns.getContext());
2262 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2269 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2275LogicalResult LowerClassesPass::dialectConversion(
2276 Operation *op,
const PathInfoTable &pathInfoTable,
2277 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2278 ConversionTarget target(getContext());
2281 TypeConverter typeConverter;
2284 RewritePatternSet
patterns(&getContext());
2288 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.