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().contains(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 className = externMod.getExtModuleName();
1074 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1077 om::ClassLike loweredClassOp;
1078 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1079 moduleLike.getOperation())) {
1080 loweredClassOp =
convertExtClass(moduleLike, builder, className + suffix,
1081 formalParamNames, hasContainingModule);
1083 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1084 formalParamNames, hasContainingModule);
1087 return loweredClassOp;
1090void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1091 om::ClassLike classLike,
1092 const PathInfoTable &pathInfoTable) {
1094 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1095 return lowerClass(classOp, moduleLike, pathInfoTable);
1097 if (
auto classExternOp =
1098 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1099 return lowerClassExtern(classExternOp, moduleLike);
1101 llvm_unreachable(
"unhandled class-like op");
1104void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1105 const PathInfoTable &pathInfoTable) {
1107 SmallVector<Property> inputProperties;
1108 BitVector portsToErase(moduleLike.getNumPorts());
1109 bool hasContainingModule =
false;
1110 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1112 if (!isa<PropertyType>(port.type))
1116 if (port.isInput()) {
1117 inputProperties.push_back({index, port.name, port.type, port.loc});
1120 if (port.name.strref().contains(kContainingModuleName))
1121 hasContainingModule =
true;
1125 portsToErase.set(index);
1130 Block *moduleBody = &moduleLike->getRegion(0).front();
1131 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1133 auto basePathType = BasePathType::get(&getContext());
1134 auto unknownLoc = UnknownLoc::get(&getContext());
1135 classBody->addArgument(basePathType, unknownLoc);
1138 size_t nAltBasePaths =
1139 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1140 for (
size_t i = 0; i < nAltBasePaths; ++i)
1141 classBody->addArgument(basePathType, unknownLoc);
1144 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1145 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1146 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1148 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1149 for (
auto result : instance.getResults()) {
1150 if (isa<PropertyType>(result.getType()))
1151 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1156 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1157 if (llvm::any_of(op.getOperands(), isProperty) ||
1158 llvm::any_of(op.getResults(), isProperty))
1159 op.moveBefore(classBody, classBody->begin());
1163 for (
auto input : inputProperties) {
1164 auto arg = classBody->addArgument(input.type, input.loc);
1165 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1168 llvm::SmallVector<mlir::Location> fieldLocs;
1169 llvm::SmallVector<mlir::Value> fieldValues;
1170 for (Operation &op :
1172 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1173 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1175 fieldLocs.push_back(op.getLoc());
1176 fieldValues.push_back(propAssign.getSrc());
1183 if (hasContainingModule) {
1184 BlockArgument argumentValue = classBody->addArgument(
1185 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1186 fieldLocs.push_back(argumentValue.getLoc());
1187 fieldValues.push_back(argumentValue);
1190 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1191 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1195 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
1197 moduleLike.erasePorts(portsToErase);
1201void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
1202 FModuleLike moduleLike) {
1206 BitVector portsToErase(moduleLike.getNumPorts());
1207 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1210 classBody->addArgument(BasePathType::get(&getContext()),
1211 UnknownLoc::get(&getContext()));
1213 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1214 auto type = moduleLike.getPortType(i);
1215 if (!isa<PropertyType>(type))
1218 auto loc = moduleLike.getPortLocation(i);
1219 auto direction = moduleLike.getPortDirection(i);
1220 if (direction == Direction::In)
1221 classBody->addArgument(type, loc);
1224 portsToErase.set(i);
1229 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
1231 moduleLike.erasePorts(portsToErase);
1238 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1239 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1240 SmallVectorImpl<Operation *> &opsToErase) {
1242 auto basePath = firrtlObject->getBlock()->getArgument(0);
1245 auto firrtlClassType = firrtlObject.getType();
1246 auto numElements = firrtlClassType.getNumElements();
1247 llvm::SmallVector<unsigned> argIndexTable;
1251 SmallVector<Value> altBasePaths;
1252 pathInfoTable.collectAltBasePaths(
1253 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1256 unsigned nextArgIndex = 1 + altBasePaths.size();
1259 auto direction = firrtlClassType.getElement(i).direction;
1260 if (direction == Direction::In)
1261 argIndexTable[i] = nextArgIndex++;
1267 llvm::SmallVector<Value> args;
1268 args.resize(nextArgIndex);
1272 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1273 args[1 + i] = altBasePath;
1275 firrtl::PathOp containingModuleRef;
1276 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1277 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1278 auto index = subfield.getIndex();
1279 auto direction = firrtlClassType.getElement(index).direction;
1283 if (direction == Direction::Out)
1286 for (
auto *subfieldUser :
1287 llvm::make_early_inc_range(subfield->getUsers())) {
1288 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1292 auto dst = propassign.getOperand(0);
1293 auto src = propassign.getOperand(1);
1294 if (dst == subfield.getResult()) {
1295 args[argIndexTable[index]] = src;
1296 opsToErase.push_back(propassign);
1299 if (firrtlClassType.getElement(index).name.strref().contains(
1300 kContainingModuleName)) {
1301 assert(!containingModuleRef &&
1302 "expected exactly one containingModule");
1303 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1304 "expected containingModule to be a PathOp");
1305 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1311 opsToErase.push_back(subfield);
1317 auto element = firrtlClassType.getElement(i);
1318 if (element.direction == Direction::Out)
1321 auto argIndex = argIndexTable[i];
1322 if (!args[argIndex])
1323 return emitError(firrtlObject.getLoc())
1324 <<
"uninitialized input port " << element.name;
1328 auto className = firrtlObject.getType().getNameAttr();
1329 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1332 OpBuilder builder(firrtlObject);
1334 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1335 firrtlObject.getClassNameAttr(), args);
1338 if (containingModuleRef) {
1339 std::lock_guard<std::mutex> guard(intraPassMutex);
1340 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1345 auto cast = UnrealizedConversionCastOp::create(
1346 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1347 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1350 opsToErase.push_back(firrtlObject);
1360 const PathInfoTable &pathInfoTable,
1361 SmallVectorImpl<Operation *> &opsToErase) {
1364 OpBuilder builder(firrtlInstance);
1369 SmallVector<Value> actualParameters;
1371 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1372 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1373 auto rebasedPath = om::BasePathCreateOp::create(
1374 builder, firrtlInstance->getLoc(), basePath, symRef);
1376 actualParameters.push_back(rebasedPath);
1379 pathInfoTable.collectAltBasePaths(
1380 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1383 for (
auto result : firrtlInstance.getResults()) {
1385 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1390 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1391 if (!propertyResult)
1397 assert(propertyAssignment &&
"properties require single assignment");
1398 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1401 opsToErase.push_back(propertyAssignment);
1405 auto referencedModule =
1406 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1408 StringRef moduleName = referencedModule.getName();
1411 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1412 moduleName = externMod.getExtModuleName();
1415 auto className = FlatSymbolRefAttr::get(
1416 builder.getStringAttr(moduleName + kClassNameSuffix));
1418 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1422 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1423 className.getAttr(), actualParameters);
1428 for (
auto result : firrtlInstance.getResults()) {
1430 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1435 if (!isa<PropertyType>(result.getType()))
1439 auto objectFieldPath = builder.getArrayAttr({FlatSymbolRefAttr::get(
1440 firrtlInstance.getPortNameAttr(result.getResultNumber()))});
1443 auto objectField = ObjectFieldOp::create(
1444 builder,
object.
getLoc(), result.getType(),
object, objectFieldPath);
1446 result.replaceAllUsesWith(objectField);
1450 opsToErase.push_back(firrtlInstance);
1458 SmallVectorImpl<Operation *> &opsToErase) {
1460 BitVector portsToErase(firrtlInstance.getNumResults());
1461 for (
auto result : firrtlInstance.getResults())
1462 if (isa<PropertyType>(result.getType()))
1463 portsToErase.set(result.getResultNumber());
1466 if (portsToErase.none())
1470 InstanceOp newInstance =
1471 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1480 opsToErase.push_back(firrtlInstance);
1486 SmallVectorImpl<Operation *> &opsToErase) {
1487 OpBuilder builder(moduleOp);
1488 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1489 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1490 assert(0 &&
"should be no objects in modules");
1491 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1501 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1502 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1503 SmallVectorImpl<Operation *> &opsToErase) {
1504 OpBuilder builder(classOp);
1505 auto &classState = state.classLoweringStateTable.at(classOp);
1506 auto it = classState.paths.begin();
1507 for (
auto &op : classOp->getRegion(0).getOps()) {
1508 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1510 intraPassMutex, opsToErase)))
1512 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1514 pathInfoTable, opsToErase)))
1522LogicalResult LowerClassesPass::updateInstances(
1523 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1524 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1529 SmallVector<Operation *> opsToErase;
1531 TypeSwitch<Operation *, LogicalResult>(op)
1533 .Case([&](FModuleOp moduleOp) {
1538 .Case([&](om::ClassOp classOp) {
1542 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1543 intraPassMutex, opsToErase);
1545 .Default([](
auto *op) {
return success(); });
1549 for (
auto *op : opsToErase)
1556void LowerClassesPass::createAllRtlPorts(
1557 const PathInfoTable &pathInfoTable,
1560 MLIRContext *ctx = &getContext();
1563 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1566 om::ClassOp::buildSimpleClassOp(
1567 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1568 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1569 {om::PathType::get(ctx), om::StringType::get(ctx),
1570 om::OMIntegerType::get(ctx)});
1573 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1574 return lhs.object.getClassName() < rhs.object.getClassName();
1578 for (
auto rtlPortToCreate : rtlPortsToCreate)
1579 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1589struct FIntegerConstantOpConversion
1591 using OpConversionPattern::OpConversionPattern;
1594 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1595 ConversionPatternRewriter &rewriter)
const override {
1596 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1597 op, om::OMIntegerType::get(op.getContext()),
1598 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1604 using OpConversionPattern::OpConversionPattern;
1607 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1608 ConversionPatternRewriter &rewriter)
const override {
1609 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1610 op, rewriter.getBoolAttr(adaptor.getValue()));
1615struct DoubleConstantOpConversion
1617 using OpConversionPattern::OpConversionPattern;
1620 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1621 ConversionPatternRewriter &rewriter)
const override {
1622 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1627struct StringConstantOpConversion
1629 using OpConversionPattern::OpConversionPattern;
1632 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1633 ConversionPatternRewriter &rewriter)
const override {
1634 auto stringType = om::StringType::get(op.getContext());
1635 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1636 op, stringType, StringAttr::get(op.getValue(), stringType));
1641struct ListCreateOpConversion
1643 using OpConversionPattern::OpConversionPattern;
1646 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1647 ConversionPatternRewriter &rewriter)
const override {
1648 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1651 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1652 adaptor.getElements());
1657struct ListConcatOpConversion
1659 using OpConversionPattern::OpConversionPattern;
1662 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1663 ConversionPatternRewriter &rewriter)
const override {
1664 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1667 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1668 adaptor.getSubLists());
1673struct IntegerAddOpConversion
1675 using OpConversionPattern::OpConversionPattern;
1678 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1679 ConversionPatternRewriter &rewriter)
const override {
1680 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1686struct IntegerMulOpConversion
1688 using OpConversionPattern::OpConversionPattern;
1691 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1692 ConversionPatternRewriter &rewriter)
const override {
1693 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1699struct IntegerShrOpConversion
1701 using OpConversionPattern::OpConversionPattern;
1704 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1705 ConversionPatternRewriter &rewriter)
const override {
1706 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1712struct IntegerShlOpConversion
1714 using OpConversionPattern::OpConversionPattern;
1717 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1718 ConversionPatternRewriter &rewriter)
const override {
1719 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1727 PathOpConversion(TypeConverter &typeConverter, MLIRContext *context,
1728 const PathInfoTable &pathInfoTable,
1729 PatternBenefit benefit = 1)
1731 pathInfoTable(pathInfoTable) {}
1734 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1735 ConversionPatternRewriter &rewriter)
const override {
1736 auto *context = op->getContext();
1737 auto pathType = om::PathType::get(context);
1738 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1741 auto basePath = op->getBlock()->getArgument(0);
1745 if (pathInfoIt == pathInfoTable.table.end()) {
1746 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1747 return emitError(op.getLoc(),
"DontTouch target was deleted");
1748 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1749 return emitError(op.getLoc(),
"Instance target was deleted");
1750 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1754 auto pathInfo = pathInfoIt->second;
1755 auto symbol = pathInfo.symRef;
1759 om::TargetKind targetKind;
1760 switch (op.getTargetKind()) {
1761 case firrtl::TargetKind::DontTouch:
1762 targetKind = om::TargetKind::DontTouch;
1764 case firrtl::TargetKind::Reference:
1765 targetKind = om::TargetKind::Reference;
1767 case firrtl::TargetKind::Instance:
1768 if (!pathInfo.canBeInstanceTarget)
1769 return emitError(op.getLoc(),
"invalid target for instance path")
1770 .attachNote(pathInfo.loc)
1771 <<
"target not instance or module";
1772 targetKind = om::TargetKind::Instance;
1774 case firrtl::TargetKind::MemberInstance:
1775 case firrtl::TargetKind::MemberReference:
1776 if (pathInfo.canBeInstanceTarget)
1777 targetKind = om::TargetKind::MemberInstance;
1779 targetKind = om::TargetKind::MemberReference;
1785 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1789 auto parent = op->getParentOfType<om::ClassOp>();
1790 auto parentName = parent.getName();
1791 if (parentName.ends_with(kClassNameSuffix))
1792 parentName = parentName.drop_back(kClassNameSuffix.size());
1793 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1797 pathInfoTable.getRootsForPassthrough(originalParentName);
1798 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1801 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1802 if (altBasePathModule == altBasePath) {
1804 auto basePathArg = op->getBlock()->getArgument(1 + i);
1805 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1806 "expected a passthrough base path");
1807 basePath = basePathArg;
1812 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1813 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1818 const PathInfoTable &pathInfoTable;
1822 using OpConversionPattern::OpConversionPattern;
1825 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1826 ConversionPatternRewriter &rewriter)
const override {
1827 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1836 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1837 if (!regionKindInterface)
1839 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1848 rewriter.replaceOp(wireOp, propAssign.getSrc());
1851 rewriter.eraseOp(propAssign);
1858 using OpConversionPattern::OpConversionPattern;
1861 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
1862 ConversionPatternRewriter &rewriter)
const override {
1863 rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
1868struct ObjectSubfieldOpConversion
1870 using OpConversionPattern::OpConversionPattern;
1872 ObjectSubfieldOpConversion(
1873 const TypeConverter &typeConverter, MLIRContext *context,
1874 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1876 classTypeTable(classTypeTable) {}
1879 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
1880 ConversionPatternRewriter &rewriter)
const override {
1881 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1887 auto firrtlClassType =
1888 classTypeTable.lookup(omClassType.getClassName().getAttr());
1889 if (!firrtlClassType)
1892 const auto &element = firrtlClassType.getElement(op.getIndex());
1894 if (element.direction == Direction::In)
1897 auto field = FlatSymbolRefAttr::get(element.name);
1898 auto path = rewriter.getArrayAttr({field});
1899 auto type = typeConverter->convertType(element.type);
1900 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1905 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
1909 using OpConversionPattern::OpConversionPattern;
1912 matchAndRewrite(ClassFieldsOp op, OpAdaptor adaptor,
1913 ConversionPatternRewriter &rewriter)
const override {
1914 rewriter.replaceOpWithNewOp<ClassFieldsOp>(op, adaptor.getOperands(),
1915 adaptor.getFieldLocsAttr());
1921 using OpConversionPattern::OpConversionPattern;
1924 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
1925 ConversionPatternRewriter &rewriter)
const override {
1928 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1929 adaptor.getClassNameAttr(),
1930 adaptor.getActualParams());
1935static LogicalResult convertClassLike(om::ClassLike classOp,
1936 TypeConverter typeConverter,
1937 ConversionPatternRewriter &rewriter) {
1938 Block *body = classOp.getBodyBlock();
1939 TypeConverter::SignatureConversion result(body->getNumArguments());
1943 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
1947 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
1951 rewriter.modifyOpInPlace(classOp, [&]() {
1952 mlir::AttrTypeReplacer replacer;
1953 replacer.addReplacement([&](TypeAttr typeAttr) {
1954 return mlir::TypeAttr::get(
1955 typeConverter.convertType(typeAttr.getValue()));
1957 classOp.replaceFieldTypes(replacer);
1964 using OpConversionPattern::OpConversionPattern;
1967 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
1968 ConversionPatternRewriter &rewriter)
const override {
1969 return convertClassLike(classOp, *typeConverter, rewriter);
1973struct ClassExternOpSignatureConversion
1975 using OpConversionPattern::OpConversionPattern;
1978 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
1979 ConversionPatternRewriter &rewriter)
const override {
1980 return convertClassLike(classOp, *typeConverter, rewriter);
1985 using OpConversionPattern::OpConversionPattern;
1988 matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor,
1989 ConversionPatternRewriter &rewriter)
const override {
1992 auto type = typeConverter->convertType(op.getType());
1996 rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1997 adaptor.getFieldPathAttr());
2004struct UnrealizedConversionCastOpConversion
2006 using OpConversionPattern::OpConversionPattern;
2009 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2010 ConversionPatternRewriter &rewriter)
const override {
2011 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2013 auto type = typeConverter->convertType(op.getResult(0));
2014 if (!type || type != adaptor.getOperands()[0].getType())
2016 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2030 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2031 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2034 target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
2035 auto containsFIRRTLType = [](Type type) {
2037 .walk([](Type type) {
2038 return failure(isa<FIRRTLDialect>(type.getDialect()));
2042 auto noFIRRTLOperands =
2043 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2044 return containsFIRRTLType(type);
2046 auto noFIRRTLResults =
2047 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2048 return containsFIRRTLType(type);
2050 return noFIRRTLOperands && noFIRRTLResults;
2054 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2055 [](Operation *op) -> std::optional<bool> {
2056 auto classLike = dyn_cast<om::ClassLike>(op);
2058 return std::nullopt;
2059 auto fieldNames = classLike.getFieldNames();
2060 if (!llvm::all_of(fieldNames, [&](
auto field) {
2061 std::optional<Type> type =
2062 classLike.getFieldType(cast<StringAttr>(field));
2063 return type.has_value() && !isa<FIRRTLType>(type.value());
2067 return llvm::none_of(
2068 classLike.getBodyBlock()->getArgumentTypes(),
2069 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2075 converter.addConversion(
2076 [](IntegerType type) {
return OMIntegerType::get(type.getContext()); });
2077 converter.addConversion([](FIntegerType type) {
2081 return OMIntegerType::get(type.getContext());
2085 converter.addConversion([](om::StringType type) {
return type; });
2086 converter.addConversion([](firrtl::StringType type) {
2087 return om::StringType::get(type.getContext());
2091 converter.addConversion([](om::PathType type) {
return type; });
2092 converter.addConversion([](om::BasePathType type) {
return type; });
2093 converter.addConversion([](om::FrozenPathType type) {
return type; });
2094 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2095 converter.addConversion([](firrtl::PathType type) {
2096 return om::PathType::get(type.getContext());
2100 converter.addConversion([](om::ClassType type) {
return type; });
2101 converter.addConversion([](firrtl::ClassType type) {
2102 return om::ClassType::get(type.getContext(), type.getNameAttr());
2106 converter.addConversion([](om::AnyType type) {
return type; });
2107 converter.addConversion([](firrtl::AnyRefType type) {
2108 return om::AnyType::get(type.getContext());
2112 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2114 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2116 auto elementType = converter.convertType(type.getElementType());
2122 converter.addConversion(
2123 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2125 return convertListType(type);
2128 converter.addConversion(
2129 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2131 return convertListType(type);
2135 converter.addConversion(
2136 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2139 converter.addConversion(
2140 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2144 converter.addTargetMaterialization(
2145 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2146 assert(values.size() == 1);
2147 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2153 converter.addSourceMaterialization(
2154 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2155 assert(values.size() == 1);
2156 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2162 RewritePatternSet &
patterns, TypeConverter &converter,
2163 const PathInfoTable &pathInfoTable,
2164 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2165 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2166 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2174 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2175 patterns.add<ClassExternOpSignatureConversion>(converter,
2182 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2187 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2192LogicalResult LowerClassesPass::dialectConversion(
2193 Operation *op,
const PathInfoTable &pathInfoTable,
2194 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2195 ConversionTarget target(getContext());
2198 TypeConverter typeConverter;
2201 RewritePatternSet
patterns(&getContext());
2205 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.