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 firrtlObject.replaceAllUsesWith(
object.getResult());
1349 opsToErase.push_back(firrtlObject);
1359 const PathInfoTable &pathInfoTable,
1360 SmallVectorImpl<Operation *> &opsToErase) {
1363 OpBuilder builder(firrtlInstance);
1368 SmallVector<Value> actualParameters;
1370 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1371 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1372 auto rebasedPath = om::BasePathCreateOp::create(
1373 builder, firrtlInstance->getLoc(), basePath, symRef);
1375 actualParameters.push_back(rebasedPath);
1378 pathInfoTable.collectAltBasePaths(
1379 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1382 for (
auto result : firrtlInstance.getResults()) {
1384 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1389 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1390 if (!propertyResult)
1396 assert(propertyAssignment &&
"properties require single assignment");
1397 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1400 opsToErase.push_back(propertyAssignment);
1404 auto referencedModule =
1405 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1407 StringRef moduleName = referencedModule.getName();
1410 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1411 if (
auto defname = externMod.getDefname())
1412 moduleName = defname.value();
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.getPortName(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 OpBuilder builder(firrtlInstance);
1471 InstanceOp newInstance = firrtlInstance.erasePorts(builder, 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,
1587 using OpConversionPattern::OpConversionPattern;
1591 ConversionPatternRewriter &rewriter)
const override {
1592 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1593 op, om::OMIntegerType::get(op.getContext()),
1594 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1600 using OpConversionPattern::OpConversionPattern;
1604 ConversionPatternRewriter &rewriter)
const override {
1605 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1606 op, rewriter.getBoolAttr(adaptor.getValue()));
1613 using OpConversionPattern::OpConversionPattern;
1617 ConversionPatternRewriter &rewriter)
const override {
1618 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1625 using OpConversionPattern::OpConversionPattern;
1629 ConversionPatternRewriter &rewriter)
const override {
1630 auto stringType = om::StringType::get(op.getContext());
1631 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1632 op, stringType, StringAttr::get(op.getValue(), stringType));
1639 using OpConversionPattern::OpConversionPattern;
1643 ConversionPatternRewriter &rewriter)
const override {
1644 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1647 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1648 adaptor.getElements());
1655 using OpConversionPattern::OpConversionPattern;
1659 ConversionPatternRewriter &rewriter)
const override {
1660 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1663 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1664 adaptor.getSubLists());
1671 using OpConversionPattern::OpConversionPattern;
1675 ConversionPatternRewriter &rewriter)
const override {
1676 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1684 using OpConversionPattern::OpConversionPattern;
1688 ConversionPatternRewriter &rewriter)
const override {
1689 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1697 using OpConversionPattern::OpConversionPattern;
1701 ConversionPatternRewriter &rewriter)
const override {
1702 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1710 using OpConversionPattern::OpConversionPattern;
1714 ConversionPatternRewriter &rewriter)
const override {
1715 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1725 PatternBenefit benefit = 1)
1731 ConversionPatternRewriter &rewriter)
const override {
1732 auto *context = op->getContext();
1733 auto pathType = om::PathType::get(context);
1737 auto basePath = op->getBlock()->getArgument(0);
1742 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1743 return emitError(op.getLoc(),
"DontTouch target was deleted");
1744 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1745 return emitError(op.getLoc(),
"Instance target was deleted");
1746 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1750 auto pathInfo = pathInfoIt->second;
1751 auto symbol = pathInfo.symRef;
1755 om::TargetKind targetKind;
1756 switch (op.getTargetKind()) {
1757 case firrtl::TargetKind::DontTouch:
1758 targetKind = om::TargetKind::DontTouch;
1760 case firrtl::TargetKind::Reference:
1761 targetKind = om::TargetKind::Reference;
1763 case firrtl::TargetKind::Instance:
1764 if (!pathInfo.canBeInstanceTarget)
1765 return emitError(op.getLoc(),
"invalid target for instance path")
1766 .attachNote(pathInfo.loc)
1767 <<
"target not instance or module";
1768 targetKind = om::TargetKind::Instance;
1770 case firrtl::TargetKind::MemberInstance:
1771 case firrtl::TargetKind::MemberReference:
1772 if (pathInfo.canBeInstanceTarget)
1773 targetKind = om::TargetKind::MemberInstance;
1775 targetKind = om::TargetKind::MemberReference;
1781 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1785 auto parent = op->getParentOfType<om::ClassOp>();
1786 auto parentName = parent.getName();
1787 if (parentName.ends_with(kClassNameSuffix))
1788 parentName = parentName.drop_back(kClassNameSuffix.size());
1789 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1794 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1797 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
1798 if (altBasePathModule == altBasePath) {
1800 auto basePathArg = op->getBlock()->getArgument(1 + i);
1801 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1802 "expected a passthrough base path");
1803 basePath = basePathArg;
1808 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1809 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1818 using OpConversionPattern::OpConversionPattern;
1822 ConversionPatternRewriter &rewriter)
const override {
1823 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1832 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1833 if (!regionKindInterface)
1835 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1842 rewriter.replaceOp(wireOp, propAssign.getSrc());
1845 rewriter.eraseOp(propAssign);
1852 using OpConversionPattern::OpConversionPattern;
1856 ConversionPatternRewriter &rewriter)
const override {
1857 rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
1864 using OpConversionPattern::OpConversionPattern;
1867 const TypeConverter &typeConverter, MLIRContext *context,
1874 ConversionPatternRewriter &rewriter)
const override {
1875 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1881 auto firrtlClassType =
1883 if (!firrtlClassType)
1886 const auto &element = firrtlClassType.getElement(op.getIndex());
1888 if (element.direction == Direction::In)
1891 auto field = FlatSymbolRefAttr::get(element.name);
1892 auto path = rewriter.getArrayAttr({field});
1893 auto type = typeConverter->convertType(element.type);
1894 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1903 using OpConversionPattern::OpConversionPattern;
1907 ConversionPatternRewriter &rewriter)
const override {
1908 rewriter.replaceOpWithNewOp<ClassFieldsOp>(op, adaptor.getOperands(),
1909 adaptor.getFieldLocsAttr());
1915 using OpConversionPattern::OpConversionPattern;
1919 ConversionPatternRewriter &rewriter)
const override {
1922 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1923 adaptor.getClassNameAttr(),
1924 adaptor.getActualParams());
1930 TypeConverter typeConverter,
1931 ConversionPatternRewriter &rewriter) {
1932 Block *body = classOp.getBodyBlock();
1933 TypeConverter::SignatureConversion result(body->getNumArguments());
1937 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
1941 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
1945 rewriter.modifyOpInPlace(classOp, [&]() {
1946 mlir::AttrTypeReplacer replacer;
1947 replacer.addReplacement([&](TypeAttr typeAttr) {
1948 return mlir::TypeAttr::get(
1949 typeConverter.convertType(typeAttr.getValue()));
1951 classOp.replaceFieldTypes(replacer);
1958 using OpConversionPattern::OpConversionPattern;
1962 ConversionPatternRewriter &rewriter)
const override {
1969 using OpConversionPattern::OpConversionPattern;
1973 ConversionPatternRewriter &rewriter)
const override {
1979 using OpConversionPattern::OpConversionPattern;
1983 ConversionPatternRewriter &rewriter)
const override {
1986 auto type = typeConverter->convertType(op.getType());
1990 rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1991 adaptor.getFieldPathAttr());
2002 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2003 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2006 target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
2007 auto containsFIRRTLType = [](Type type) {
2009 .walk([](Type type) {
2010 return failure(isa<FIRRTLDialect>(type.getDialect()));
2014 auto noFIRRTLOperands =
2015 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2016 return containsFIRRTLType(type);
2018 auto noFIRRTLResults =
2019 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2020 return containsFIRRTLType(type);
2022 return noFIRRTLOperands && noFIRRTLResults;
2026 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2027 [](Operation *op) -> std::optional<bool> {
2028 auto classLike = dyn_cast<om::ClassLike>(op);
2030 return std::nullopt;
2031 auto fieldNames = classLike.getFieldNames();
2032 if (!llvm::all_of(fieldNames, [&](
auto field) {
2033 std::optional<Type> type =
2034 classLike.getFieldType(cast<StringAttr>(field));
2035 return type.has_value() && !isa<FIRRTLType>(type.value());
2039 return llvm::none_of(
2040 classLike.getBodyBlock()->getArgumentTypes(),
2041 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2047 converter.addConversion(
2048 [](IntegerType type) {
return OMIntegerType::get(type.getContext()); });
2049 converter.addConversion([](FIntegerType type) {
2053 return OMIntegerType::get(type.getContext());
2057 converter.addConversion([](om::StringType type) {
return type; });
2058 converter.addConversion([](firrtl::StringType type) {
2059 return om::StringType::get(type.getContext());
2063 converter.addConversion([](om::PathType type) {
return type; });
2064 converter.addConversion([](om::BasePathType type) {
return type; });
2065 converter.addConversion([](om::FrozenPathType type) {
return type; });
2066 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2067 converter.addConversion([](firrtl::PathType type) {
2068 return om::PathType::get(type.getContext());
2072 converter.addConversion([](om::ClassType type) {
return type; });
2073 converter.addConversion([](firrtl::ClassType type) {
2074 return om::ClassType::get(type.getContext(), type.getNameAttr());
2078 converter.addConversion([](om::AnyType type) {
return type; });
2079 converter.addConversion([](firrtl::AnyRefType type) {
2080 return om::AnyType::get(type.getContext());
2084 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2086 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2088 auto elementType = converter.convertType(type.getElementType());
2094 converter.addConversion(
2095 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2097 return convertListType(type);
2100 converter.addConversion(
2101 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2103 return convertListType(type);
2107 converter.addConversion(
2108 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2111 converter.addConversion(
2112 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2116 converter.addTargetMaterialization(
2117 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2118 assert(values.size() == 1);
2119 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2125 converter.addSourceMaterialization(
2126 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2127 assert(values.size() == 1);
2128 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2134 RewritePatternSet &
patterns, TypeConverter &converter,
2135 const PathInfoTable &pathInfoTable,
2136 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2162LogicalResult LowerClassesPass::dialectConversion(
2163 Operation *op,
const PathInfoTable &pathInfoTable,
2164 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2165 ConversionTarget target(getContext());
2168 TypeConverter typeConverter;
2171 RewritePatternSet
patterns(&getContext());
2175 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 LogicalResult convertClassLike(om::ClassLike classOp, TypeConverter typeConverter, ConversionPatternRewriter &rewriter)
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)
LogicalResult matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassFieldsOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ObjectFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
ObjectSubfieldOpConversion(const TypeConverter &typeConverter, MLIRContext *context, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
const DenseMap< StringAttr, firrtl::ClassType > & classTypeTable
LogicalResult matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
PathOpConversion(TypeConverter &typeConverter, MLIRContext *context, const PathInfoTable &pathInfoTable, PatternBenefit benefit=1)
const PathInfoTable & pathInfoTable
LogicalResult matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(StringConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(WireOp wireOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
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.