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"
47 auto moduleLike = node->
getModule<FModuleLike>();
51 if (isa<firrtl::ClassLike>(moduleLike.getOperation()))
55 if (moduleLike.isPublic())
59 bool hasClassPorts = llvm::any_of(moduleLike.getPorts(), [](
PortInfo port) {
60 return isa<PropertyType>(port.type);
68 for (
auto *instance : *node) {
69 if (
auto op = instance->getInstance<FInstanceLike>())
70 for (
auto result : op->getResults())
71 if (type_isa<PropertyType>(result.getType()))
82 PathInfo(Location loc,
bool canBeInstanceTarget, FlatSymbolRefAttr symRef,
83 StringAttr altBasePathModule)
84 : loc(loc), canBeInstanceTarget(canBeInstanceTarget), symRef(symRef),
85 altBasePathModule(altBasePathModule) {
86 assert(symRef &&
"symRef must not be null");
90 std::optional<Location> loc = std::nullopt;
93 bool canBeInstanceTarget =
false;
96 FlatSymbolRefAttr symRef =
nullptr;
100 StringAttr altBasePathModule =
nullptr;
104struct PathInfoTable {
107 void addAltBasePathRoot(StringAttr rootModuleName) {
108 altBasePathRoots.insert(rootModuleName);
113 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
114 StringAttr rootModuleName) {
115 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
116 rootSequence.push_back(rootModuleName);
120 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
121 getAltBasePathRoots()
const {
122 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
127 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
128 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
133 llvm::iterator_range<const StringAttr *>
134 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
135 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
136 assert(it != altBasePathsPassthroughs.end() &&
137 "expected passthrough module to already exist");
138 return llvm::make_range(it->second.begin(), it->second.end());
143 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
144 SmallVectorImpl<Value> &result)
const {
145 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
146 auto parent = instance->getParentOfType<om::ClassOp>();
149 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
150 if (parent.getName().starts_with(altBasePath)) {
152 result.push_back(instance->getBlock()->getArgument(0));
156 auto basePath = instance->getBlock()->getArgument(1 + i);
157 assert(isa<om::BasePathType>(basePath.getType()) &&
158 "expected a passthrough base path");
159 result.push_back(basePath);
165 DenseMap<DistinctAttr, PathInfo> table;
170 SmallPtrSet<StringAttr, 16> altBasePathRoots;
174 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
178static constexpr StringRef kClassNameSuffix =
"_Class";
189struct ClassLoweringState {
190 FModuleLike moduleLike;
191 std::vector<hw::HierPathOp> paths;
194struct LoweringState {
195 PathInfoTable pathInfoTable;
196 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
201 firrtl::PathOp containingModuleRef;
206struct LowerClassesPass
207 :
public circt::firrtl::impl::LowerClassesBase<LowerClassesPass> {
208 void runOnOperation()
override;
214 SymbolTable &symbolTable);
217 bool shouldCreateClass(StringAttr modName);
220 om::ClassLike createClass(FModuleLike moduleLike,
221 const PathInfoTable &pathInfoTable,
222 std::mutex &intraPassMutex);
225 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
226 const PathInfoTable &pathInfoTable);
227 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
228 const PathInfoTable &pathInfoTable);
229 void lowerClassExtern(ClassExternOp classExternOp, FModuleLike moduleLike);
232 LogicalResult updateInstances(Operation *op,
InstanceGraph &instanceGraph,
233 const LoweringState &state,
234 const PathInfoTable &pathInfoTable,
235 std::mutex &intraPassMutex);
238 void createAllRtlPorts(
const PathInfoTable &pathInfoTable,
243 LogicalResult dialectConversion(
244 Operation *op,
const PathInfoTable &pathInfoTable,
245 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
248 DenseMap<StringAttr, bool> shouldCreateClassMemo;
251 SmallVector<RtlPortsInfo> rtlPortsToCreate;
260 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
261 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
263 PathTracker(FModuleLike module,
265 InstanceGraph &instanceGraph,
const SymbolTable &symbolTable,
266 const DenseMap<DistinctAttr, FModuleOp> &owningModules)
267 : module(module), moduleNamespace(namespaces[module]),
268 namespaces(namespaces), instanceGraph(instanceGraph),
269 symbolTable(symbolTable), owningModules(owningModules) {}
272 struct PathInfoTableEntry {
275 StringAttr altBasePathModule;
281 LogicalResult runOnModule();
284 FailureOr<AnnotationSet> processPathTrackers(
const AnnoTarget &target);
286 LogicalResult updatePathInfoTable(PathInfoTable &pathInfoTable,
291 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
292 StringAttr moduleName,
293 FModuleOp owningModule,
300 DenseMap<std::pair<StringAttr, FModuleOp>,
bool> needsAltBasePathCache;
304 const SymbolTable &symbolTable;
305 const DenseMap<DistinctAttr, FModuleOp> &owningModules;
308 SmallVector<PathInfoTableEntry> entries;
309 SetVector<StringAttr> altBasePathRoots;
314static constexpr StringRef kContainingModuleName =
"containingModule";
315static constexpr StringRef kPortsName =
"ports";
316static constexpr StringRef kRtlPortClassName =
"RtlPort";
318static Type getRtlPortsType(MLIRContext *context) {
319 return om::ListType::get(om::ClassType::get(
320 context, FlatSymbolRefAttr::get(context, kRtlPortClassName)));
324static void createRtlPorts(
const RtlPortsInfo &rtlPortToCreate,
325 const PathInfoTable &pathInfoTable,
328 firrtl::PathOp containingModuleRef = rtlPortToCreate.containingModuleRef;
329 Value basePath = rtlPortToCreate.basePath;
330 om::ObjectOp
object = rtlPortToCreate.object;
333 OpBuilder::InsertionGuard guard(builder);
334 builder.setInsertionPoint(
object);
338 FlatSymbolRefAttr containingModulePathRef =
339 pathInfoTable.table.at(containingModuleRef.getTarget()).symRef;
343 hw::HierPathOp containingModulePath =
344 symbolTable.lookup<hw::HierPathOp>(containingModulePathRef.getAttr());
346 assert(containingModulePath.isModule() &&
347 "expected containing module path to target a module");
349 StringAttr moduleName = containingModulePath.leafMod();
351 FModuleLike mod = symbolTable.lookup<FModuleLike>(moduleName);
352 MLIRContext *ctx = mod.getContext();
353 Location loc = mod.getLoc();
357 auto portClassName = StringAttr::get(ctx, kRtlPortClassName);
359 om::ClassType::get(ctx, FlatSymbolRefAttr::get(portClassName));
361 SmallVector<Value> ports;
362 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
364 auto portType = type_dyn_cast<FIRRTLBaseType>(mod.getPortType(i));
365 if (!portType || portType.getBitWidthOrSentinel() == 0)
374 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), 0},
376 return namespaces[m];
379 FlatSymbolRefAttr portPathRef =
380 hierPathCache.
getRefFor(ArrayAttr::get(ctx, {portSym}));
382 auto portPath = om::PathCreateOp::create(
383 builder, loc, om::PathType::get(ctx),
384 om::TargetKindAttr::get(ctx, om::TargetKind::DontTouch), basePath,
389 StringRef portDirectionName =
390 mod.getPortDirection(i) == Direction::Out ?
"Output" :
"Input";
392 auto portDirection = om::ConstantOp::create(
393 builder, loc, om::StringType::get(ctx),
394 StringAttr::get(portDirectionName, om::StringType::get(ctx)));
398 auto portWidth = om::ConstantOp::create(
399 builder, loc, om::OMIntegerType::get(ctx),
400 om::IntegerAttr::get(
401 ctx, mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
402 portType.getBitWidthOrSentinel())));
406 auto portObj = om::ObjectOp::create(
407 builder, loc, portClassType, portClassName,
408 ArrayRef<Value>{portPath, portDirection, portWidth});
410 ports.push_back(portObj);
416 om::ListCreateOp::create(builder, UnknownLoc::get(builder.getContext()),
417 getRtlPortsType(builder.getContext()), ports);
419 object.getActualParamsMutable().append({portsList});
425PathTracker::run(CircuitOp circuit,
InstanceGraph &instanceGraph,
428 const SymbolTable &symbolTable,
429 const DenseMap<DistinctAttr, FModuleOp> &owningModules) {
432 for (
auto *node : instanceGraph)
433 if (auto module = node->getModule<FModuleLike>())
434 (void)namespaces.
get(module);
436 for (
auto *node : instanceGraph)
437 if (auto module = node->getModule<FModuleLike>()) {
439 if (isa<firrtl::ClassOp, firrtl::ExtClassOp>(module))
441 PathTracker tracker(module, namespaces, instanceGraph, symbolTable,
443 if (failed(tracker.runOnModule()))
445 if (failed(tracker.updatePathInfoTable(pathInfoTable, cache)))
452LogicalResult PathTracker::runOnModule() {
453 auto processAndUpdateAnnoTarget = [&](
AnnoTarget target) -> LogicalResult {
454 auto anno = processPathTrackers(target);
457 target.setAnnotations(*anno);
462 if (failed(processAndUpdateAnnoTarget(
OpAnnoTarget(module))))
466 SmallVector<Attribute> portAnnotations;
467 portAnnotations.reserve(module.getNumPorts());
468 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
472 portAnnotations.push_back(annos->getArrayAttr());
475 module.setPortAnnotationsAttr(
476 ArrayAttr::get(module.getContext(), portAnnotations));
479 auto result =
module.walk([&](hw::InnerSymbolOpInterface op) {
480 if (failed(processAndUpdateAnnoTarget(OpAnnoTarget(op))))
481 return WalkResult::interrupt();
482 return WalkResult::advance();
485 if (result.wasInterrupted())
493PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
494 FModuleOp owningModule,
497 auto it = needsAltBasePathCache.find({moduleName, owningModule});
498 if (it != needsAltBasePathCache.end())
500 bool needsAltBasePath =
false;
501 auto *node = instanceGraph.
lookup(moduleName);
511 needsAltBasePath =
true;
517 auto diag = mlir::emitError(loc)
518 <<
"unable to uniquely resolve target due "
519 "to multiple instantiation";
520 for (
auto *use : node->uses())
521 diag.attachNote(use->getInstance().
getLoc()) <<
"instance here";
524 node = (*node->
usesBegin())->getParent();
526 needsAltBasePathCache[{moduleName, owningModule}] = needsAltBasePath;
527 return needsAltBasePath;
530FailureOr<AnnotationSet>
531PathTracker::processPathTrackers(
const AnnoTarget &target) {
534 auto *op = target.
getOp();
535 annotations.removeAnnotations([&](
Annotation anno) {
541 if (!anno.
isClass(
"circt.tracker"))
545 auto id = anno.
getMember<DistinctAttr>(
"id");
547 op->emitError(
"circt.tracker annotation missing id field");
557 if (
auto portTarget = dyn_cast<PortAnnoTarget>(target)) {
559 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), fieldID},
561 return moduleNamespace;
563 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
564 assert(!fieldID &&
"field not valid for modules");
565 targetSym = FlatSymbolRefAttr::get(module.getModuleNameAttr());
570 return moduleNamespace;
575 SmallVector<Attribute> path;
578 path.push_back(targetSym);
580 auto moduleName = target.
getModule().getModuleNameAttr();
583 hw::HierPathOp hierPathOp;
584 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
586 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
588 op->emitError(
"annotation does not point at a HierPathOp");
596 auto owningModule = owningModules.lookup(
id);
603 auto oldPath = hierPathOp.getNamepath().getValue();
608 bool pathContainsOwningModule =
false;
609 size_t owningModuleIndex = 0;
610 for (
auto [idx, pathFramgent] :
llvm::enumerate(oldPath)) {
611 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(pathFramgent)) {
612 if (innerRef.getModule() == owningModule.getModuleNameAttr()) {
613 pathContainsOwningModule =
true;
614 owningModuleIndex = idx;
616 }
else if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(pathFramgent)) {
617 if (symRef.getAttr() == owningModule.getModuleNameAttr()) {
618 pathContainsOwningModule =
true;
619 owningModuleIndex = idx;
624 if (pathContainsOwningModule) {
626 moduleName = owningModule.getModuleNameAttr();
630 llvm::append_range(path, llvm::reverse(oldPath.drop_back().drop_front(
631 owningModuleIndex)));
634 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
637 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
642 auto needsAltBasePath = getOrComputeNeedsAltBasePath(
643 op->getLoc(), moduleName, owningModule, hierPathOp);
644 if (failed(needsAltBasePath)) {
656 if (!hierPathOp && !needsAltBasePath.value())
674 std::reverse(path.begin(), path.end());
675 auto pathAttr = ArrayAttr::get(op->getContext(), path);
679 StringAttr altBasePathModule;
680 if (*needsAltBasePath) {
682 TypeSwitch<Attribute, StringAttr>(path.front())
683 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
684 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
686 altBasePathRoots.insert(altBasePathModule);
690 entries.push_back({op, id, altBasePathModule, pathAttr});
702LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
704 for (
auto root : altBasePathRoots)
705 pathInfoTable.addAltBasePathRoot(root);
707 for (
const auto &entry : entries) {
709 "expected all PathInfoTableEntries to have a pathAttr");
712 auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
713 auto &pathInfo = it->second;
721 assert(pathInfo.symRef &&
"expected all PathInfos to have a symRef");
722 auto existingHierpath =
723 symbolTable.lookup<hw::HierPathOp>(pathInfo.symRef.getValue());
724 auto existingPathAttr = existingHierpath.getNamepath();
725 if (existingPathAttr == entry.pathAttr)
728 assert(pathInfo.loc.has_value() &&
"all PathInfo should have a Location");
729 auto diag = emitError(pathInfo.loc.value(),
730 "path identifier already found, paths must resolve "
731 "to a unique target");
732 diag.attachNote(entry.op->getLoc()) <<
"other path identifier here";
738 bool canBeInstanceTarget = isa<InstanceOp, FModuleLike>(entry.op);
740 pathInfo = {entry.op->getLoc(), canBeInstanceTarget,
741 cache.
getRefFor(entry.pathAttr), entry.altBasePathModule};
752LogicalResult LowerClassesPass::processPaths(
755 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
756 auto circuit = getOperation();
760 DenseMap<DistinctAttr, FModuleOp> owningModules;
761 std::vector<Operation *> declarations;
762 auto result = circuit.walk([&](Operation *op) {
763 if (
auto pathOp = dyn_cast<PathOp>(op)) {
765 auto owningModule = owningModuleCache.lookup(pathOp);
768 pathOp->emitError(
"path does not have a single owning module");
769 return WalkResult::interrupt();
771 auto target = pathOp.getTargetAttr();
772 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
775 if (!inserted && it->second != owningModule) {
777 <<
"path reference " << target <<
" has conflicting owning modules "
778 << it->second.getModuleNameAttr() <<
" and "
779 << owningModule.getModuleNameAttr();
780 return WalkResult::interrupt();
783 return WalkResult::advance();
786 if (result.wasInterrupted())
789 if (failed(PathTracker::run(circuit, instanceGraph, namespaces, cache,
790 pathInfoTable, symbolTable, owningModules)))
795 for (
auto rootModule : pathInfoTable.getAltBasePathRoots()) {
800 auto start = llvm::df_begin(node);
801 auto end = llvm::df_end(node);
811 if (!shouldCreateClass(
812 it->getModule<FModuleLike>().getModuleNameAttr())) {
813 it = it.skipChildren();
818 if (it->begin() == it->end()) {
824 StringAttr passthroughModule = it->getModule().getModuleNameAttr();
825 pathInfoTable.addAltBasePathPassthrough(passthroughModule, rootModule);
835void LowerClassesPass::runOnOperation() {
836 MLIRContext *ctx = &getContext();
837 auto intraPassMutex = std::mutex();
840 CircuitOp circuit = getOperation();
844 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
850 for (
auto *node : instanceGraph)
851 if (auto moduleLike = node->getModule<firrtl::FModuleLike>())
852 shouldCreateClassMemo.insert({moduleLike.getModuleNameAttr(),
false});
854 parallelForEach(circuit.getContext(), instanceGraph,
856 if (auto moduleLike = node->getModule<FModuleLike>())
857 shouldCreateClassMemo[moduleLike.getModuleNameAttr()] =
858 shouldCreateClassImpl(node);
862 PathInfoTable pathInfoTable;
863 if (failed(processPaths(instanceGraph, namespaces, cache, pathInfoTable,
872 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
873 for (
auto *node : instanceGraph) {
874 auto moduleLike = node->
getModule<firrtl::FModuleLike>();
878 if (shouldCreateClass(moduleLike.getModuleNameAttr())) {
879 auto omClass = createClass(moduleLike, pathInfoTable, intraPassMutex);
880 auto &classLoweringState =
loweringState.classLoweringStateTable[omClass];
881 classLoweringState.moduleLike = moduleLike;
888 for (
auto *instance : *node) {
889 auto inst = instance->
getInstance<firrtl::InstanceOp>();
893 auto module = instance->getTarget()->getModule<FModuleLike>();
894 if (module && shouldCreateClass(module.getModuleNameAttr())) {
897 return namespaces[module];
899 SmallVector<Attribute> path = {targetSym};
900 auto pathAttr = ArrayAttr::get(ctx, path);
901 auto hierPath = cache.
getOpFor(pathAttr);
902 classLoweringState.paths.push_back(hierPath);
907 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
908 classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
913 mlir::parallelForEach(ctx,
loweringState.classLoweringStateTable,
914 [
this, &pathInfoTable](
auto &entry) {
915 const auto &[classLike, state] = entry;
916 lowerClassLike(state.moduleLike, classLike,
921 for (
auto &[omClass, state] :
loweringState.classLoweringStateTable) {
922 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
924 for (
auto *use :
llvm::make_early_inc_range(node->uses()))
926 instanceGraph.
erase(node);
927 state.moduleLike.erase();
932 SmallVector<Operation *> objectContainers;
933 for (
auto &op : circuit.getOps())
934 if (isa<FModuleOp,
om::ClassLike>(op))
935 objectContainers.push_back(&op);
939 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
941 pathInfoTable, intraPassMutex);
943 return signalPassFailure();
946 if (!rtlPortsToCreate.empty())
947 createAllRtlPorts(pathInfoTable, namespaces, cache);
951 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
952 return dialectConversion(op, pathInfoTable, classTypeTable);
954 return signalPassFailure();
957 markAnalysesPreserved<InstanceGraph>();
960 rtlPortsToCreate.clear();
964bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
966 return shouldCreateClassMemo.at(modName);
970 SmallVector<Attribute> &fieldNames,
971 SmallVector<NamedAttribute> &fieldTypes) {
972 if (hasContainingModule) {
973 auto name = builder.getStringAttr(kPortsName);
974 fieldNames.push_back(name);
975 fieldTypes.push_back(NamedAttribute(
976 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
982 ArrayRef<StringRef> formalParamNames,
983 bool hasContainingModule) {
984 SmallVector<Attribute> fieldNames;
985 SmallVector<NamedAttribute> fieldTypes;
986 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
987 auto type = moduleLike.getPortType(i);
988 if (!isa<PropertyType>(type))
991 auto direction = moduleLike.getPortDirection(i);
992 if (direction != Direction::In) {
993 auto name = moduleLike.getPortNameAttr(i);
994 fieldNames.push_back(name);
995 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1000 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1001 formalParamNames, fieldNames, fieldTypes);
1004static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1006 ArrayRef<StringRef> formalParamNames,
1007 bool hasContainingModule) {
1009 SmallVector<Attribute> fieldNames;
1010 SmallVector<NamedAttribute> fieldTypes;
1011 for (
auto op : llvm::make_early_inc_range(
1012 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1013 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1017 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1019 fieldNames.push_back(name);
1020 fieldTypes.push_back(
1021 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1026 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1027 formalParamNames, fieldNames, fieldTypes);
1031om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1032 const PathInfoTable &pathInfoTable,
1033 std::mutex &intraPassMutex) {
1035 SmallVector<StringRef> formalParamNames;
1037 formalParamNames.emplace_back(
"basepath");
1040 size_t nAltBasePaths =
1041 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1042 for (
size_t i = 0; i < nAltBasePaths; ++i)
1043 formalParamNames.push_back(StringAttr::get(
1044 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1047 bool hasContainingModule =
false;
1048 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1049 if (port.isInput() && isa<PropertyType>(port.type)) {
1050 formalParamNames.push_back(port.name);
1053 if (port.name.strref().starts_with(kContainingModuleName))
1054 hasContainingModule =
true;
1058 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1061 if (hasContainingModule)
1062 formalParamNames.push_back(kPortsName);
1065 StringRef className = moduleLike.getName();
1068 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation()))
1069 if (
auto defname = externMod.getDefname())
1070 className = defname.value();
1075 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1078 om::ClassLike loweredClassOp;
1079 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1080 moduleLike.getOperation())) {
1081 loweredClassOp =
convertExtClass(moduleLike, builder, className + suffix,
1082 formalParamNames, hasContainingModule);
1084 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1085 formalParamNames, hasContainingModule);
1088 return loweredClassOp;
1091void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1092 om::ClassLike classLike,
1093 const PathInfoTable &pathInfoTable) {
1095 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1096 return lowerClass(classOp, moduleLike, pathInfoTable);
1098 if (
auto classExternOp =
1099 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1100 return lowerClassExtern(classExternOp, moduleLike);
1102 llvm_unreachable(
"unhandled class-like op");
1105void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1106 const PathInfoTable &pathInfoTable) {
1108 SmallVector<Property> inputProperties;
1109 BitVector portsToErase(moduleLike.getNumPorts());
1110 bool hasContainingModule =
false;
1111 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1113 if (!isa<PropertyType>(port.type))
1117 if (port.isInput()) {
1118 inputProperties.push_back({index, port.name, port.type, port.loc});
1121 if (port.name.strref().starts_with(kContainingModuleName))
1122 hasContainingModule =
true;
1126 portsToErase.set(index);
1131 Block *moduleBody = &moduleLike->getRegion(0).front();
1132 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1134 auto basePathType = BasePathType::get(&getContext());
1135 auto unknownLoc = UnknownLoc::get(&getContext());
1136 classBody->addArgument(basePathType, unknownLoc);
1139 size_t nAltBasePaths =
1140 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1141 for (
size_t i = 0; i < nAltBasePaths; ++i)
1142 classBody->addArgument(basePathType, unknownLoc);
1145 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1146 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1147 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1149 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1150 for (
auto result : instance.getResults()) {
1151 if (isa<PropertyType>(result.getType()))
1152 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1157 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1158 if (llvm::any_of(op.getOperands(), isProperty) ||
1159 llvm::any_of(op.getResults(), isProperty))
1160 op.moveBefore(classBody, classBody->begin());
1164 for (
auto input : inputProperties) {
1165 auto arg = classBody->addArgument(input.type, input.loc);
1166 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1169 llvm::SmallVector<mlir::Location> fieldLocs;
1170 llvm::SmallVector<mlir::Value> fieldValues;
1171 for (Operation &op :
1173 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1174 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1176 fieldLocs.push_back(op.getLoc());
1177 fieldValues.push_back(propAssign.getSrc());
1184 if (hasContainingModule) {
1185 BlockArgument argumentValue = classBody->addArgument(
1186 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1187 fieldLocs.push_back(argumentValue.getLoc());
1188 fieldValues.push_back(argumentValue);
1191 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1192 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1196 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
1198 moduleLike.erasePorts(portsToErase);
1202void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
1203 FModuleLike moduleLike) {
1207 BitVector portsToErase(moduleLike.getNumPorts());
1208 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1211 classBody->addArgument(BasePathType::get(&getContext()),
1212 UnknownLoc::get(&getContext()));
1214 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1215 auto type = moduleLike.getPortType(i);
1216 if (!isa<PropertyType>(type))
1219 auto loc = moduleLike.getPortLocation(i);
1220 auto direction = moduleLike.getPortDirection(i);
1221 if (direction == Direction::In)
1222 classBody->addArgument(type, loc);
1225 portsToErase.set(i);
1230 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
1232 moduleLike.erasePorts(portsToErase);
1239 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1240 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1241 SmallVectorImpl<Operation *> &opsToErase) {
1243 auto basePath = firrtlObject->getBlock()->getArgument(0);
1246 auto firrtlClassType = firrtlObject.getType();
1247 auto numElements = firrtlClassType.getNumElements();
1248 llvm::SmallVector<unsigned> argIndexTable;
1252 SmallVector<Value> altBasePaths;
1253 pathInfoTable.collectAltBasePaths(
1254 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1257 unsigned nextArgIndex = 1 + altBasePaths.size();
1260 auto direction = firrtlClassType.getElement(i).direction;
1261 if (direction == Direction::In)
1262 argIndexTable[i] = nextArgIndex++;
1268 llvm::SmallVector<Value> args;
1269 args.resize(nextArgIndex);
1273 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1274 args[1 + i] = altBasePath;
1276 firrtl::PathOp containingModuleRef;
1277 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1278 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1279 auto index = subfield.getIndex();
1280 auto direction = firrtlClassType.getElement(index).direction;
1284 if (direction == Direction::Out)
1287 for (
auto *subfieldUser :
1288 llvm::make_early_inc_range(subfield->getUsers())) {
1289 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1293 auto dst = propassign.getOperand(0);
1294 auto src = propassign.getOperand(1);
1295 if (dst == subfield.getResult()) {
1296 args[argIndexTable[index]] = src;
1297 opsToErase.push_back(propassign);
1300 if (firrtlClassType.getElement(index).name.strref().starts_with(
1301 kContainingModuleName)) {
1302 assert(!containingModuleRef &&
1303 "expected exactly one containingModule");
1304 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1305 "expected containingModule to be a PathOp");
1306 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1312 opsToErase.push_back(subfield);
1318 auto element = firrtlClassType.getElement(i);
1319 if (element.direction == Direction::Out)
1322 auto argIndex = argIndexTable[i];
1323 if (!args[argIndex])
1324 return emitError(firrtlObject.getLoc())
1325 <<
"uninitialized input port " << element.name;
1329 auto className = firrtlObject.getType().getNameAttr();
1330 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1333 OpBuilder builder(firrtlObject);
1335 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1336 firrtlObject.getClassNameAttr(), args);
1339 if (containingModuleRef) {
1340 std::lock_guard<std::mutex> guard(intraPassMutex);
1341 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1346 auto cast = UnrealizedConversionCastOp::create(
1347 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1348 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1351 opsToErase.push_back(firrtlObject);
1361 const PathInfoTable &pathInfoTable,
1362 SmallVectorImpl<Operation *> &opsToErase) {
1365 OpBuilder builder(firrtlInstance);
1370 SmallVector<Value> actualParameters;
1372 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1373 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1374 auto rebasedPath = om::BasePathCreateOp::create(
1375 builder, firrtlInstance->getLoc(), basePath, symRef);
1377 actualParameters.push_back(rebasedPath);
1380 pathInfoTable.collectAltBasePaths(
1381 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1384 for (
auto result : firrtlInstance.getResults()) {
1386 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1391 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1392 if (!propertyResult)
1398 assert(propertyAssignment &&
"properties require single assignment");
1399 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1402 opsToErase.push_back(propertyAssignment);
1406 auto referencedModule =
1407 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1409 StringRef moduleName = referencedModule.getName();
1412 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1413 if (
auto defname = externMod.getDefname())
1414 moduleName = defname.value();
1417 auto className = FlatSymbolRefAttr::get(
1418 builder.getStringAttr(moduleName + kClassNameSuffix));
1420 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1424 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1425 className.getAttr(), actualParameters);
1430 for (
auto result : firrtlInstance.getResults()) {
1432 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1437 if (!isa<PropertyType>(result.getType()))
1441 auto objectFieldPath = builder.getArrayAttr({FlatSymbolRefAttr::get(
1442 firrtlInstance.getPortName(result.getResultNumber()))});
1445 auto objectField = ObjectFieldOp::create(
1446 builder,
object.
getLoc(), result.getType(),
object, objectFieldPath);
1448 result.replaceAllUsesWith(objectField);
1452 opsToErase.push_back(firrtlInstance);
1460 SmallVectorImpl<Operation *> &opsToErase) {
1462 BitVector portsToErase(firrtlInstance.getNumResults());
1463 for (
auto result : firrtlInstance.getResults())
1464 if (isa<PropertyType>(result.getType()))
1465 portsToErase.set(result.getResultNumber());
1468 if (portsToErase.none())
1472 OpBuilder builder(firrtlInstance);
1473 InstanceOp newInstance = firrtlInstance.erasePorts(builder, portsToErase);
1482 opsToErase.push_back(firrtlInstance);
1488 SmallVectorImpl<Operation *> &opsToErase) {
1489 OpBuilder builder(moduleOp);
1490 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1491 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1492 assert(0 &&
"should be no objects in modules");
1493 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1503 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1504 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1505 SmallVectorImpl<Operation *> &opsToErase) {
1506 OpBuilder builder(classOp);
1507 auto &classState = state.classLoweringStateTable.at(classOp);
1508 auto it = classState.paths.begin();
1509 for (
auto &op : classOp->getRegion(0).getOps()) {
1510 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1512 intraPassMutex, opsToErase)))
1514 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1516 pathInfoTable, opsToErase)))
1524LogicalResult LowerClassesPass::updateInstances(
1525 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1526 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1531 SmallVector<Operation *> opsToErase;
1533 TypeSwitch<Operation *, LogicalResult>(op)
1535 .Case([&](FModuleOp moduleOp) {
1540 .Case([&](om::ClassOp classOp) {
1544 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1545 intraPassMutex, opsToErase);
1547 .Default([](
auto *op) {
return success(); });
1551 for (
auto *op : opsToErase)
1558void LowerClassesPass::createAllRtlPorts(
1559 const PathInfoTable &pathInfoTable,
1562 MLIRContext *ctx = &getContext();
1565 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1568 om::ClassOp::buildSimpleClassOp(
1569 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1570 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1571 {om::PathType::get(ctx), om::StringType::get(ctx),
1572 om::OMIntegerType::get(ctx)});
1575 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1576 return lhs.object.getClassName() < rhs.object.getClassName();
1580 for (
auto rtlPortToCreate : rtlPortsToCreate)
1581 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1591struct FIntegerConstantOpConversion
1593 using OpConversionPattern::OpConversionPattern;
1596 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1597 ConversionPatternRewriter &rewriter)
const override {
1598 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1599 op, om::OMIntegerType::get(op.getContext()),
1600 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1606 using OpConversionPattern::OpConversionPattern;
1609 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1610 ConversionPatternRewriter &rewriter)
const override {
1611 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1612 op, rewriter.getBoolAttr(adaptor.getValue()));
1617struct DoubleConstantOpConversion
1619 using OpConversionPattern::OpConversionPattern;
1622 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1623 ConversionPatternRewriter &rewriter)
const override {
1624 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1629struct StringConstantOpConversion
1631 using OpConversionPattern::OpConversionPattern;
1634 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1635 ConversionPatternRewriter &rewriter)
const override {
1636 auto stringType = om::StringType::get(op.getContext());
1637 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1638 op, stringType, StringAttr::get(op.getValue(), stringType));
1643struct ListCreateOpConversion
1645 using OpConversionPattern::OpConversionPattern;
1648 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1649 ConversionPatternRewriter &rewriter)
const override {
1650 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1653 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1654 adaptor.getElements());
1659struct ListConcatOpConversion
1661 using OpConversionPattern::OpConversionPattern;
1664 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1665 ConversionPatternRewriter &rewriter)
const override {
1666 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1669 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1670 adaptor.getSubLists());
1675struct IntegerAddOpConversion
1677 using OpConversionPattern::OpConversionPattern;
1680 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1681 ConversionPatternRewriter &rewriter)
const override {
1682 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1688struct IntegerMulOpConversion
1690 using OpConversionPattern::OpConversionPattern;
1693 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1694 ConversionPatternRewriter &rewriter)
const override {
1695 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1701struct IntegerShrOpConversion
1703 using OpConversionPattern::OpConversionPattern;
1706 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1707 ConversionPatternRewriter &rewriter)
const override {
1708 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1714struct IntegerShlOpConversion
1716 using OpConversionPattern::OpConversionPattern;
1719 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1720 ConversionPatternRewriter &rewriter)
const override {
1721 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1729 PathOpConversion(TypeConverter &typeConverter, MLIRContext *context,
1730 const PathInfoTable &pathInfoTable,
1731 PatternBenefit benefit = 1)
1733 pathInfoTable(pathInfoTable) {}
1736 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1737 ConversionPatternRewriter &rewriter)
const override {
1738 auto *context = op->getContext();
1739 auto pathType = om::PathType::get(context);
1740 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1743 auto basePath = op->getBlock()->getArgument(0);
1747 if (pathInfoIt == pathInfoTable.table.end()) {
1748 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1749 return emitError(op.getLoc(),
"DontTouch target was deleted");
1750 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1751 return emitError(op.getLoc(),
"Instance target was deleted");
1752 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1756 auto pathInfo = pathInfoIt->second;
1757 auto symbol = pathInfo.symRef;
1761 om::TargetKind targetKind;
1762 switch (op.getTargetKind()) {
1763 case firrtl::TargetKind::DontTouch:
1764 targetKind = om::TargetKind::DontTouch;
1766 case firrtl::TargetKind::Reference:
1767 targetKind = om::TargetKind::Reference;
1769 case firrtl::TargetKind::Instance:
1770 if (!pathInfo.canBeInstanceTarget)
1771 return emitError(op.getLoc(),
"invalid target for instance path")
1772 .attachNote(pathInfo.loc)
1773 <<
"target not instance or module";
1774 targetKind = om::TargetKind::Instance;
1776 case firrtl::TargetKind::MemberInstance:
1777 case firrtl::TargetKind::MemberReference:
1778 if (pathInfo.canBeInstanceTarget)
1779 targetKind = om::TargetKind::MemberInstance;
1781 targetKind = om::TargetKind::MemberReference;
1787 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1791 auto parent = op->getParentOfType<om::ClassOp>();
1792 auto parentName = parent.getName();
1793 if (parentName.ends_with(kClassNameSuffix))
1794 parentName = parentName.drop_back(kClassNameSuffix.size());
1795 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1799 pathInfoTable.getRootsForPassthrough(originalParentName);
1800 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1803 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1804 if (altBasePathModule == altBasePath) {
1806 auto basePathArg = op->getBlock()->getArgument(1 + i);
1807 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1808 "expected a passthrough base path");
1809 basePath = basePathArg;
1814 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1815 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1820 const PathInfoTable &pathInfoTable;
1824 using OpConversionPattern::OpConversionPattern;
1827 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1828 ConversionPatternRewriter &rewriter)
const override {
1829 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1838 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1839 if (!regionKindInterface)
1841 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1850 rewriter.replaceOp(wireOp, propAssign.getSrc());
1853 rewriter.eraseOp(propAssign);
1860 using OpConversionPattern::OpConversionPattern;
1863 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
1864 ConversionPatternRewriter &rewriter)
const override {
1865 rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
1870struct ObjectSubfieldOpConversion
1872 using OpConversionPattern::OpConversionPattern;
1874 ObjectSubfieldOpConversion(
1875 const TypeConverter &typeConverter, MLIRContext *context,
1876 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1878 classTypeTable(classTypeTable) {}
1881 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
1882 ConversionPatternRewriter &rewriter)
const override {
1883 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1889 auto firrtlClassType =
1890 classTypeTable.lookup(omClassType.getClassName().getAttr());
1891 if (!firrtlClassType)
1894 const auto &element = firrtlClassType.getElement(op.getIndex());
1896 if (element.direction == Direction::In)
1899 auto field = FlatSymbolRefAttr::get(element.name);
1900 auto path = rewriter.getArrayAttr({field});
1901 auto type = typeConverter->convertType(element.type);
1902 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1907 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
1911 using OpConversionPattern::OpConversionPattern;
1914 matchAndRewrite(ClassFieldsOp op, OpAdaptor adaptor,
1915 ConversionPatternRewriter &rewriter)
const override {
1916 rewriter.replaceOpWithNewOp<ClassFieldsOp>(op, adaptor.getOperands(),
1917 adaptor.getFieldLocsAttr());
1923 using OpConversionPattern::OpConversionPattern;
1926 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
1927 ConversionPatternRewriter &rewriter)
const override {
1930 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1931 adaptor.getClassNameAttr(),
1932 adaptor.getActualParams());
1937static LogicalResult convertClassLike(om::ClassLike classOp,
1938 TypeConverter typeConverter,
1939 ConversionPatternRewriter &rewriter) {
1940 Block *body = classOp.getBodyBlock();
1941 TypeConverter::SignatureConversion result(body->getNumArguments());
1945 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
1949 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
1953 rewriter.modifyOpInPlace(classOp, [&]() {
1954 mlir::AttrTypeReplacer replacer;
1955 replacer.addReplacement([&](TypeAttr typeAttr) {
1956 return mlir::TypeAttr::get(
1957 typeConverter.convertType(typeAttr.getValue()));
1959 classOp.replaceFieldTypes(replacer);
1966 using OpConversionPattern::OpConversionPattern;
1969 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
1970 ConversionPatternRewriter &rewriter)
const override {
1971 return convertClassLike(classOp, *typeConverter, rewriter);
1975struct ClassExternOpSignatureConversion
1977 using OpConversionPattern::OpConversionPattern;
1980 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
1981 ConversionPatternRewriter &rewriter)
const override {
1982 return convertClassLike(classOp, *typeConverter, rewriter);
1987 using OpConversionPattern::OpConversionPattern;
1990 matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor,
1991 ConversionPatternRewriter &rewriter)
const override {
1994 auto type = typeConverter->convertType(op.getType());
1998 rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1999 adaptor.getFieldPathAttr());
2006struct UnrealizedConversionCastOpConversion
2008 using OpConversionPattern::OpConversionPattern;
2011 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2012 ConversionPatternRewriter &rewriter)
const override {
2013 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2015 auto type = typeConverter->convertType(op.getResult(0));
2016 if (!type || type != adaptor.getOperands()[0].getType())
2018 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2032 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2033 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2036 target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
2037 auto containsFIRRTLType = [](Type type) {
2039 .walk([](Type type) {
2040 return failure(isa<FIRRTLDialect>(type.getDialect()));
2044 auto noFIRRTLOperands =
2045 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2046 return containsFIRRTLType(type);
2048 auto noFIRRTLResults =
2049 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2050 return containsFIRRTLType(type);
2052 return noFIRRTLOperands && noFIRRTLResults;
2056 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2057 [](Operation *op) -> std::optional<bool> {
2058 auto classLike = dyn_cast<om::ClassLike>(op);
2060 return std::nullopt;
2061 auto fieldNames = classLike.getFieldNames();
2062 if (!llvm::all_of(fieldNames, [&](
auto field) {
2063 std::optional<Type> type =
2064 classLike.getFieldType(cast<StringAttr>(field));
2065 return type.has_value() && !isa<FIRRTLType>(type.value());
2069 return llvm::none_of(
2070 classLike.getBodyBlock()->getArgumentTypes(),
2071 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2077 converter.addConversion(
2078 [](IntegerType type) {
return OMIntegerType::get(type.getContext()); });
2079 converter.addConversion([](FIntegerType type) {
2083 return OMIntegerType::get(type.getContext());
2087 converter.addConversion([](om::StringType type) {
return type; });
2088 converter.addConversion([](firrtl::StringType type) {
2089 return om::StringType::get(type.getContext());
2093 converter.addConversion([](om::PathType type) {
return type; });
2094 converter.addConversion([](om::BasePathType type) {
return type; });
2095 converter.addConversion([](om::FrozenPathType type) {
return type; });
2096 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2097 converter.addConversion([](firrtl::PathType type) {
2098 return om::PathType::get(type.getContext());
2102 converter.addConversion([](om::ClassType type) {
return type; });
2103 converter.addConversion([](firrtl::ClassType type) {
2104 return om::ClassType::get(type.getContext(), type.getNameAttr());
2108 converter.addConversion([](om::AnyType type) {
return type; });
2109 converter.addConversion([](firrtl::AnyRefType type) {
2110 return om::AnyType::get(type.getContext());
2114 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2116 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2118 auto elementType = converter.convertType(type.getElementType());
2124 converter.addConversion(
2125 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2127 return convertListType(type);
2130 converter.addConversion(
2131 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2133 return convertListType(type);
2137 converter.addConversion(
2138 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2141 converter.addConversion(
2142 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2146 converter.addTargetMaterialization(
2147 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2148 assert(values.size() == 1);
2149 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2155 converter.addSourceMaterialization(
2156 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2157 assert(values.size() == 1);
2158 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2164 RewritePatternSet &
patterns, TypeConverter &converter,
2165 const PathInfoTable &pathInfoTable,
2166 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2167 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2168 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2176 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2177 patterns.add<ClassExternOpSignatureConversion>(converter,
2184 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2189 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2194LogicalResult LowerClassesPass::dialectConversion(
2195 Operation *op,
const PathInfoTable &pathInfoTable,
2196 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2197 ConversionTarget target(getContext());
2200 TypeConverter typeConverter;
2203 RewritePatternSet
patterns(&getContext());
2207 return applyPartialConversion(op, target, std::move(
patterns));
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
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.