23#include "mlir/IR/BuiltinOps.h"
24#include "mlir/IR/PatternMatch.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "mlir/Support/LogicalResult.h"
28#include "mlir/Transforms/DialectConversion.h"
29#include "llvm/ADT/DepthFirstIterator.h"
30#include "llvm/ADT/STLExtras.h"
34#define GEN_PASS_DEF_LOWERCLASSES
35#include "circt/Dialect/FIRRTL/Passes.h.inc"
46 auto moduleLike = node->
getModule<FModuleLike>();
50 if (isa<firrtl::ClassLike>(moduleLike.getOperation()))
54 if (moduleLike.isPublic())
58 bool hasClassPorts = llvm::any_of(moduleLike.getPorts(), [](
PortInfo port) {
59 return isa<PropertyType>(port.type);
67 for (
auto *instance : *node) {
68 if (
auto op = instance->getInstance<FInstanceLike>())
69 for (
auto result : op->getResults())
70 if (type_isa<PropertyType>(result.getType()))
81 PathInfo(Location loc,
bool canBeInstanceTarget, FlatSymbolRefAttr symRef,
82 StringAttr altBasePathModule)
83 : loc(loc), canBeInstanceTarget(canBeInstanceTarget), symRef(symRef),
84 altBasePathModule(altBasePathModule) {
85 assert(symRef &&
"symRef must not be null");
89 std::optional<Location> loc = std::nullopt;
92 bool canBeInstanceTarget =
false;
95 FlatSymbolRefAttr symRef =
nullptr;
99 StringAttr altBasePathModule =
nullptr;
103struct PathInfoTable {
106 void addAltBasePathRoot(StringAttr rootModuleName) {
107 altBasePathRoots.insert(rootModuleName);
112 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
113 StringAttr rootModuleName) {
114 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
115 rootSequence.push_back(rootModuleName);
119 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
120 getAltBasePathRoots()
const {
121 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
126 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
127 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
132 llvm::iterator_range<const StringAttr *>
133 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
134 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
135 assert(it != altBasePathsPassthroughs.end() &&
136 "expected passthrough module to already exist");
137 return llvm::make_range(it->second.begin(), it->second.end());
142 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
143 SmallVectorImpl<Value> &result)
const {
144 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
145 auto parent = instance->getParentOfType<om::ClassOp>();
148 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
149 if (parent.getName().starts_with(altBasePath)) {
151 result.push_back(instance->getBlock()->getArgument(0));
155 auto basePath = instance->getBlock()->getArgument(1 + i);
156 assert(isa<om::BasePathType>(basePath.getType()) &&
157 "expected a passthrough base path");
158 result.push_back(basePath);
164 DenseMap<DistinctAttr, PathInfo> table;
169 SmallPtrSet<StringAttr, 16> altBasePathRoots;
173 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
177static constexpr StringRef kClassNameSuffix =
"_Class";
188struct ClassLoweringState {
189 FModuleLike moduleLike;
190 std::vector<hw::HierPathOp> paths;
193struct LoweringState {
194 PathInfoTable pathInfoTable;
195 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
200 firrtl::PathOp containingModuleRef;
205struct LowerClassesPass
206 :
public circt::firrtl::impl::LowerClassesBase<LowerClassesPass> {
207 void runOnOperation()
override;
213 SymbolTable &symbolTable);
216 bool shouldCreateClass(StringAttr modName);
219 om::ClassLike createClass(FModuleLike moduleLike,
220 const PathInfoTable &pathInfoTable,
221 std::mutex &intraPassMutex);
224 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
225 const PathInfoTable &pathInfoTable);
226 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
227 const PathInfoTable &pathInfoTable);
228 void lowerClassExtern(om::ClassExternOp classExternOp,
229 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;
258 DenseMap<StringAttr, om::ClassLike> externalClassMap;
267 PathInfoTable &pathInfoTable,
const SymbolTable &symbolTable,
268 const DenseMap<DistinctAttr, FModuleOp> &owningModules);
270 PathTracker(FModuleLike module,
272 InstanceGraph &instanceGraph,
const SymbolTable &symbolTable,
273 const DenseMap<DistinctAttr, FModuleOp> &owningModules)
274 : module(module), moduleNamespace(namespaces[module]),
275 namespaces(namespaces), instanceGraph(instanceGraph),
276 symbolTable(symbolTable), owningModules(owningModules) {}
279 struct PathInfoTableEntry {
282 StringAttr altBasePathModule;
288 LogicalResult runOnModule();
291 FailureOr<AnnotationSet> processPathTrackers(
const AnnoTarget &target);
293 LogicalResult updatePathInfoTable(PathInfoTable &pathInfoTable,
298 FailureOr<bool> getOrComputeNeedsAltBasePath(Location loc,
299 StringAttr moduleName,
300 FModuleOp owningModule,
307 DenseMap<std::pair<StringAttr, FModuleOp>,
bool> needsAltBasePathCache;
311 const SymbolTable &symbolTable;
312 const DenseMap<DistinctAttr, FModuleOp> &owningModules;
315 SmallVector<PathInfoTableEntry> entries;
316 SetVector<StringAttr> altBasePathRoots;
321static constexpr StringRef kContainingModuleName =
"containingModule";
322static constexpr StringRef kPortsName =
"ports";
323static constexpr StringRef kRtlPortClassName =
"RtlPort";
325static Type getRtlPortsType(MLIRContext *
context) {
326 return om::ListType::get(om::ClassType::get(
331static void createRtlPorts(
const RtlPortsInfo &rtlPortToCreate,
332 const PathInfoTable &pathInfoTable,
335 firrtl::PathOp containingModuleRef = rtlPortToCreate.containingModuleRef;
336 Value basePath = rtlPortToCreate.basePath;
337 om::ObjectOp
object = rtlPortToCreate.object;
340 OpBuilder::InsertionGuard guard(builder);
341 builder.setInsertionPoint(
object);
345 FlatSymbolRefAttr containingModulePathRef =
346 pathInfoTable.table.at(containingModuleRef.getTarget()).symRef;
350 hw::HierPathOp containingModulePath =
351 symbolTable.lookup<hw::HierPathOp>(containingModulePathRef.getAttr());
353 assert(containingModulePath.isModule() &&
354 "expected containing module path to target a module");
356 StringAttr moduleName = containingModulePath.leafMod();
358 FModuleLike mod = symbolTable.lookup<FModuleLike>(moduleName);
359 MLIRContext *ctx = mod.getContext();
360 Location loc = mod.getLoc();
364 auto portClassName = StringAttr::get(ctx, kRtlPortClassName);
366 om::ClassType::get(ctx, FlatSymbolRefAttr::get(portClassName));
368 SmallVector<Value> ports;
369 for (
unsigned i = 0, e = mod.getNumPorts(); i < e; ++i) {
371 auto portType = type_dyn_cast<FIRRTLBaseType>(mod.getPortType(i));
372 if (!portType || portType.getBitWidthOrSentinel() == 0)
381 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), 0},
383 return namespaces[m];
386 FlatSymbolRefAttr portPathRef =
387 hierPathCache.
getRefFor(ArrayAttr::get(ctx, {portSym}));
389 auto portPath = om::PathCreateOp::create(
390 builder, loc, om::PathType::get(ctx),
391 om::TargetKindAttr::get(ctx, om::TargetKind::DontTouch), basePath,
396 StringRef portDirectionName =
397 mod.getPortDirection(i) == Direction::Out ?
"Output" :
"Input";
399 auto portDirection = om::ConstantOp::create(
400 builder, loc, om::StringType::get(ctx),
401 StringAttr::get(portDirectionName, om::StringType::get(ctx)));
405 auto portWidth = om::ConstantOp::create(
406 builder, loc, om::OMIntegerType::get(ctx),
407 om::IntegerAttr::get(
408 ctx, mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
409 portType.getBitWidthOrSentinel())));
413 auto portObj = om::ObjectOp::create(
414 builder, loc, portClassType, portClassName,
415 ArrayRef<Value>{portPath, portDirection, portWidth});
417 ports.push_back(portObj);
423 om::ListCreateOp::create(builder, UnknownLoc::get(builder.getContext()),
424 getRtlPortsType(builder.getContext()), ports);
426 object.getActualParamsMutable().append({portsList});
432PathTracker::run(CircuitOp circuit,
InstanceGraph &instanceGraph,
435 const SymbolTable &symbolTable,
436 const DenseMap<DistinctAttr, FModuleOp> &owningModules) {
439 for (
auto *node : instanceGraph)
440 if (auto module = node->getModule<FModuleLike>())
441 (void)namespaces.
get(module);
443 for (
auto *node : instanceGraph)
444 if (auto module = node->getModule<FModuleLike>()) {
446 if (isa<firrtl::ClassOp, firrtl::ExtClassOp>(module))
448 PathTracker tracker(module, namespaces, instanceGraph, symbolTable,
450 if (failed(tracker.runOnModule()))
452 if (failed(tracker.updatePathInfoTable(pathInfoTable, cache)))
459LogicalResult PathTracker::runOnModule() {
460 auto processAndUpdateAnnoTarget = [&](
AnnoTarget target) -> LogicalResult {
461 auto anno = processPathTrackers(target);
464 target.setAnnotations(*anno);
469 if (failed(processAndUpdateAnnoTarget(
OpAnnoTarget(module))))
473 SmallVector<Attribute> portAnnotations;
474 portAnnotations.reserve(module.getNumPorts());
475 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i) {
479 portAnnotations.push_back(annos->getArrayAttr());
482 module.setPortAnnotationsAttr(
483 ArrayAttr::get(module.getContext(), portAnnotations));
486 auto result =
module.walk([&](hw::InnerSymbolOpInterface op) {
487 if (failed(processAndUpdateAnnoTarget(OpAnnoTarget(op))))
488 return WalkResult::interrupt();
489 return WalkResult::advance();
492 if (result.wasInterrupted())
500PathTracker::getOrComputeNeedsAltBasePath(Location loc, StringAttr moduleName,
501 FModuleOp owningModule,
504 auto it = needsAltBasePathCache.find({moduleName, owningModule});
505 if (it != needsAltBasePathCache.end())
507 bool needsAltBasePath =
false;
508 auto *node = instanceGraph.
lookup(moduleName);
518 needsAltBasePath =
true;
524 auto diag = mlir::emitError(loc)
525 <<
"unable to uniquely resolve target due "
526 "to multiple instantiation";
527 for (
auto *use : node->uses())
528 diag.attachNote(use->getInstance().
getLoc()) <<
"instance here";
531 node = (*node->
usesBegin())->getParent();
533 needsAltBasePathCache[{moduleName, owningModule}] = needsAltBasePath;
534 return needsAltBasePath;
537FailureOr<AnnotationSet>
538PathTracker::processPathTrackers(
const AnnoTarget &target) {
541 auto *op = target.
getOp();
542 annotations.removeAnnotations([&](
Annotation anno) {
548 if (!anno.
isClass(
"circt.tracker"))
552 auto id = anno.
getMember<DistinctAttr>(
"id");
554 op->emitError(
"circt.tracker annotation missing id field");
564 if (
auto portTarget = dyn_cast<PortAnnoTarget>(target)) {
566 getInnerRefTo({portTarget.getPortNo(), portTarget.getOp(), fieldID},
568 return moduleNamespace;
570 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
571 assert(!fieldID &&
"field not valid for modules");
572 targetSym = FlatSymbolRefAttr::get(module.getModuleNameAttr());
577 return moduleNamespace;
582 SmallVector<Attribute> path;
585 path.push_back(targetSym);
587 auto moduleName = target.
getModule().getModuleNameAttr();
590 hw::HierPathOp hierPathOp;
591 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
593 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
595 op->emitError(
"annotation does not point at a HierPathOp");
603 auto owningModule = owningModules.lookup(
id);
610 auto oldPath = hierPathOp.getNamepath().getValue();
615 bool pathContainsOwningModule =
false;
616 size_t owningModuleIndex = 0;
617 for (
auto [idx, pathFramgent] :
llvm::enumerate(oldPath)) {
618 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(pathFramgent)) {
619 if (innerRef.getModule() == owningModule.getModuleNameAttr()) {
620 pathContainsOwningModule =
true;
621 owningModuleIndex = idx;
623 }
else if (
auto symRef = dyn_cast<FlatSymbolRefAttr>(pathFramgent)) {
624 if (symRef.getAttr() == owningModule.getModuleNameAttr()) {
625 pathContainsOwningModule =
true;
626 owningModuleIndex = idx;
631 if (pathContainsOwningModule) {
633 moduleName = owningModule.getModuleNameAttr();
637 llvm::append_range(path, llvm::reverse(oldPath.drop_back().drop_front(
638 owningModuleIndex)));
641 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
644 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
649 auto needsAltBasePath = getOrComputeNeedsAltBasePath(
650 op->getLoc(), moduleName, owningModule, hierPathOp);
651 if (failed(needsAltBasePath)) {
663 if (!hierPathOp && !needsAltBasePath.value())
681 std::reverse(path.begin(), path.end());
682 auto pathAttr = ArrayAttr::get(op->getContext(), path);
686 StringAttr altBasePathModule;
687 if (*needsAltBasePath) {
689 TypeSwitch<Attribute, StringAttr>(path.front())
690 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
691 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
693 altBasePathRoots.insert(altBasePathModule);
697 entries.push_back({op, id, altBasePathModule, pathAttr});
709LogicalResult PathTracker::updatePathInfoTable(PathInfoTable &pathInfoTable,
711 for (
auto root : altBasePathRoots)
712 pathInfoTable.addAltBasePathRoot(root);
714 for (
const auto &entry : entries) {
716 "expected all PathInfoTableEntries to have a pathAttr");
719 auto [it, inserted] = pathInfoTable.table.try_emplace(entry.id);
720 auto &pathInfo = it->second;
728 assert(pathInfo.symRef &&
"expected all PathInfos to have a symRef");
729 auto existingHierpath =
730 symbolTable.lookup<hw::HierPathOp>(pathInfo.symRef.getValue());
731 auto existingPathAttr = existingHierpath.getNamepath();
732 if (existingPathAttr == entry.pathAttr)
735 assert(pathInfo.loc.has_value() &&
"all PathInfo should have a Location");
736 auto diag = emitError(pathInfo.loc.value(),
737 "path identifier already found, paths must resolve "
738 "to a unique target");
739 diag.attachNote(entry.op->getLoc()) <<
"other path identifier here";
745 bool canBeInstanceTarget = isa<InstanceOp, FModuleLike>(entry.op);
747 pathInfo = {entry.op->getLoc(), canBeInstanceTarget,
748 cache.
getRefFor(entry.pathAttr), entry.altBasePathModule};
759LogicalResult LowerClassesPass::processPaths(
762 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
763 auto circuit = getOperation();
767 DenseMap<DistinctAttr, FModuleOp> owningModules;
768 std::vector<Operation *> declarations;
769 auto result = circuit.walk([&](Operation *op) {
770 if (
auto pathOp = dyn_cast<PathOp>(op)) {
772 auto owningModule = owningModuleCache.lookup(pathOp);
775 pathOp->emitError(
"path does not have a single owning module");
776 return WalkResult::interrupt();
778 auto target = pathOp.getTargetAttr();
779 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
782 if (!inserted && it->second != owningModule) {
784 <<
"path reference " << target <<
" has conflicting owning modules "
785 << it->second.getModuleNameAttr() <<
" and "
786 << owningModule.getModuleNameAttr();
787 return WalkResult::interrupt();
790 return WalkResult::advance();
793 if (result.wasInterrupted())
796 if (failed(PathTracker::run(circuit, instanceGraph, namespaces, cache,
797 pathInfoTable, symbolTable, owningModules)))
802 for (
auto rootModule : pathInfoTable.getAltBasePathRoots()) {
807 auto start = llvm::df_begin(node);
808 auto end = llvm::df_end(node);
818 if (!shouldCreateClass(
819 it->getModule<FModuleLike>().getModuleNameAttr())) {
820 it = it.skipChildren();
825 if (it->begin() == it->end()) {
831 StringAttr passthroughModule = it->getModule().getModuleNameAttr();
832 pathInfoTable.addAltBasePathPassthrough(passthroughModule, rootModule);
842void LowerClassesPass::runOnOperation() {
843 MLIRContext *ctx = &getContext();
844 auto intraPassMutex = std::mutex();
847 CircuitOp circuit = getOperation();
851 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
857 for (
auto *node : instanceGraph)
858 if (auto moduleLike = node->getModule<firrtl::FModuleLike>())
859 shouldCreateClassMemo.insert({moduleLike.getModuleNameAttr(),
false});
861 parallelForEach(circuit.getContext(), instanceGraph,
863 if (auto moduleLike = node->getModule<FModuleLike>())
864 shouldCreateClassMemo[moduleLike.getModuleNameAttr()] =
865 shouldCreateClassImpl(node);
869 PathInfoTable pathInfoTable;
870 if (failed(processPaths(instanceGraph, namespaces, cache, pathInfoTable,
880 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
881 SmallVector<FModuleLike> modulesToErasePortsFrom;
882 for (
auto *node : instanceGraph) {
883 auto moduleLike = node->
getModule<firrtl::FModuleLike>();
887 if (shouldCreateClass(moduleLike.getModuleNameAttr())) {
888 auto omClass = createClass(moduleLike, pathInfoTable, intraPassMutex);
889 auto &classLoweringState =
loweringState.classLoweringStateTable[omClass];
892 if (!classLoweringState.moduleLike)
893 classLoweringState.moduleLike = moduleLike;
896 if (!isa<firrtl::ClassLike>(moduleLike.getOperation()))
897 modulesToErasePortsFrom.push_back(moduleLike);
904 for (
auto *instance : *node) {
905 auto inst = instance->
getInstance<firrtl::InstanceOp>();
909 auto module = instance->getTarget()->getModule<FModuleLike>();
910 if (module && shouldCreateClass(module.getModuleNameAttr())) {
913 return namespaces[module];
915 SmallVector<Attribute> path = {targetSym};
916 auto pathAttr = ArrayAttr::get(ctx, path);
917 auto hierPath = cache.
getOpFor(pathAttr);
918 classLoweringState.paths.push_back(hierPath);
923 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
924 classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
929 mlir::parallelForEach(ctx,
loweringState.classLoweringStateTable,
930 [
this, &pathInfoTable](
auto &entry) {
931 const auto &[classLike, state] = entry;
932 lowerClassLike(state.moduleLike, classLike,
939 for (
auto moduleLike : modulesToErasePortsFrom) {
940 BitVector portsToErase(moduleLike.getNumPorts());
941 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i)
942 if (isa<PropertyType>(moduleLike.getPortType(i)))
944 moduleLike.erasePorts(portsToErase);
948 for (
auto &[omClass, state] :
loweringState.classLoweringStateTable) {
949 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
951 for (
auto *use :
llvm::make_early_inc_range(node->uses()))
953 instanceGraph.
erase(node);
954 state.moduleLike.erase();
959 SmallVector<Operation *> objectContainers;
960 for (
auto &op : circuit.getOps())
961 if (isa<FModuleOp,
om::ClassLike>(op))
962 objectContainers.push_back(&op);
966 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
968 pathInfoTable, intraPassMutex);
970 return signalPassFailure();
973 if (!rtlPortsToCreate.empty())
974 createAllRtlPorts(pathInfoTable, namespaces, cache);
978 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
979 return dialectConversion(op, pathInfoTable, classTypeTable);
981 return signalPassFailure();
984 markAnalysesPreserved<InstanceGraph>();
987 rtlPortsToCreate.clear();
991bool LowerClassesPass::shouldCreateClass(StringAttr modName) {
993 return shouldCreateClassMemo.at(modName);
997 SmallVector<Attribute> &fieldNames,
998 SmallVector<NamedAttribute> &fieldTypes) {
999 if (hasContainingModule) {
1000 auto name = builder.getStringAttr(kPortsName);
1001 fieldNames.push_back(name);
1002 fieldTypes.push_back(NamedAttribute(
1003 name, TypeAttr::get(getRtlPortsType(builder.getContext()))));
1009 ArrayRef<StringRef> formalParamNames,
1010 bool hasContainingModule) {
1011 SmallVector<Attribute> fieldNames;
1012 SmallVector<NamedAttribute> fieldTypes;
1013 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1014 auto type = moduleLike.getPortType(i);
1015 if (!isa<PropertyType>(type))
1018 auto direction = moduleLike.getPortDirection(i);
1019 if (direction != Direction::In) {
1020 auto name = moduleLike.getPortNameAttr(i);
1021 fieldNames.push_back(name);
1022 fieldTypes.push_back(NamedAttribute(name, TypeAttr::get(type)));
1027 return om::ClassExternOp::create(builder, moduleLike.getLoc(), name,
1028 formalParamNames, fieldNames, fieldTypes);
1031static om::ClassLike
convertClass(FModuleLike moduleLike, OpBuilder builder,
1033 ArrayRef<StringRef> formalParamNames,
1034 bool hasContainingModule) {
1036 SmallVector<Attribute> fieldNames;
1037 SmallVector<NamedAttribute> fieldTypes;
1038 for (
auto op : llvm::make_early_inc_range(
1039 moduleLike->getRegion(0).getOps<PropAssignOp>())) {
1040 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
1044 StringAttr name = moduleLike.getPortNameAttr(outputPort.getArgNumber());
1046 fieldNames.push_back(name);
1047 fieldTypes.push_back(
1048 NamedAttribute(name, TypeAttr::get(op.getSrc().getType())));
1053 return om::ClassOp::create(builder, moduleLike.getLoc(), name,
1054 formalParamNames, fieldNames, fieldTypes);
1058om::ClassLike LowerClassesPass::createClass(FModuleLike moduleLike,
1059 const PathInfoTable &pathInfoTable,
1060 std::mutex &intraPassMutex) {
1062 SmallVector<StringRef> formalParamNames;
1064 formalParamNames.emplace_back(
"basepath");
1067 size_t nAltBasePaths =
1068 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1069 for (
size_t i = 0; i < nAltBasePaths; ++i)
1070 formalParamNames.push_back(StringAttr::get(
1071 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
1074 bool hasContainingModule =
false;
1075 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1076 if (port.isInput() && isa<PropertyType>(port.type)) {
1077 formalParamNames.push_back(port.name);
1080 if (port.name.strref().contains(kContainingModuleName))
1081 hasContainingModule =
true;
1085 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1088 if (hasContainingModule)
1089 formalParamNames.push_back(kPortsName);
1092 StringRef className = moduleLike.getName();
1093 StringAttr baseClassNameAttr = moduleLike.getNameAttr();
1096 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation())) {
1097 className = externMod.getExtModuleName();
1098 baseClassNameAttr = externMod.getExtModuleNameAttr();
1104 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
1107 om::ClassLike loweredClassOp;
1108 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(
1109 moduleLike.getOperation())) {
1112 auto [it, inserted] = externalClassMap.insert({baseClassNameAttr, {}});
1114 it->getSecond() =
convertExtClass(moduleLike, builder, className + suffix,
1115 formalParamNames, hasContainingModule);
1116 loweredClassOp = it->getSecond();
1118 loweredClassOp =
convertClass(moduleLike, builder, className + suffix,
1119 formalParamNames, hasContainingModule);
1122 return loweredClassOp;
1125void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
1126 om::ClassLike classLike,
1127 const PathInfoTable &pathInfoTable) {
1129 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
1130 return lowerClass(classOp, moduleLike, pathInfoTable);
1132 if (
auto classExternOp =
1133 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
1134 return lowerClassExtern(classExternOp, moduleLike);
1136 llvm_unreachable(
"unhandled class-like op");
1139void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
1140 const PathInfoTable &pathInfoTable) {
1142 SmallVector<Property> inputProperties;
1143 BitVector portsToErase(moduleLike.getNumPorts());
1144 bool hasContainingModule =
false;
1145 for (
auto [index, port] :
llvm::enumerate(moduleLike.getPorts())) {
1147 if (!isa<PropertyType>(port.type))
1151 if (port.isInput()) {
1152 inputProperties.push_back({index, port.name, port.type, port.loc});
1155 if (port.name.strref().contains(kContainingModuleName))
1156 hasContainingModule =
true;
1160 portsToErase.set(index);
1165 Block *moduleBody = &moduleLike->getRegion(0).front();
1166 Block *classBody = &classOp->getRegion(0).emplaceBlock();
1168 auto basePathType = om::BasePathType::get(&getContext());
1169 auto unknownLoc = UnknownLoc::get(&getContext());
1170 classBody->addArgument(basePathType, unknownLoc);
1173 size_t nAltBasePaths =
1174 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
1175 for (
size_t i = 0; i < nAltBasePaths; ++i)
1176 classBody->addArgument(basePathType, unknownLoc);
1179 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(*moduleBody))) {
1180 if (
auto instance = dyn_cast<InstanceOp>(op)) {
1181 if (!shouldCreateClass(instance.getReferencedModuleNameAttr()))
1183 auto *clone = OpBuilder::atBlockBegin(classBody).clone(op);
1184 for (
auto result : instance.getResults()) {
1185 if (isa<PropertyType>(result.getType()))
1186 result.replaceAllUsesWith(clone->getResult(result.getResultNumber()));
1191 auto isProperty = [](
auto x) {
return isa<PropertyType>(x.getType()); };
1192 if (llvm::any_of(op.getOperands(), isProperty) ||
1193 llvm::any_of(op.getResults(), isProperty))
1194 op.moveBefore(classBody, classBody->begin());
1198 for (
auto input : inputProperties) {
1199 auto arg = classBody->addArgument(input.type, input.loc);
1200 moduleBody->getArgument(input.index).replaceAllUsesWith(arg);
1203 llvm::SmallVector<mlir::Location> fieldLocs;
1204 llvm::SmallVector<mlir::Value> fieldValues;
1205 for (Operation &op :
1207 if (
auto propAssign = dyn_cast<PropAssignOp>(op)) {
1208 if (
auto blockArg = dyn_cast<BlockArgument>(propAssign.getDest())) {
1210 fieldLocs.push_back(op.getLoc());
1211 fieldValues.push_back(propAssign.getSrc());
1218 if (hasContainingModule) {
1219 BlockArgument argumentValue = classBody->addArgument(
1220 getRtlPortsType(&getContext()), UnknownLoc::get(&getContext()));
1221 fieldLocs.push_back(argumentValue.getLoc());
1222 fieldValues.push_back(argumentValue);
1225 OpBuilder builder = OpBuilder::atBlockEnd(classOp.getBodyBlock());
1226 classOp.addNewFieldsOp(builder, fieldLocs, fieldValues);
1231void LowerClassesPass::lowerClassExtern(om::ClassExternOp classExternOp,
1232 FModuleLike moduleLike) {
1236 Block *classBody = &classExternOp.getRegion().emplaceBlock();
1239 classBody->addArgument(om::BasePathType::get(&getContext()),
1240 UnknownLoc::get(&getContext()));
1242 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
1243 auto type = moduleLike.getPortType(i);
1244 if (!isa<PropertyType>(type))
1247 auto loc = moduleLike.getPortLocation(i);
1248 auto direction = moduleLike.getPortDirection(i);
1249 if (direction == Direction::In)
1250 classBody->addArgument(type, loc);
1259 firrtl::ObjectOp firrtlObject,
const PathInfoTable &pathInfoTable,
1260 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1261 SmallVectorImpl<Operation *> &opsToErase) {
1263 auto basePath = firrtlObject->getBlock()->getArgument(0);
1266 auto firrtlClassType = firrtlObject.getType();
1267 auto numElements = firrtlClassType.getNumElements();
1268 llvm::SmallVector<unsigned> argIndexTable;
1272 SmallVector<Value> altBasePaths;
1273 pathInfoTable.collectAltBasePaths(
1274 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
1277 unsigned nextArgIndex = 1 + altBasePaths.size();
1280 auto direction = firrtlClassType.getElement(i).direction;
1281 if (direction == Direction::In)
1282 argIndexTable[i] = nextArgIndex++;
1288 llvm::SmallVector<Value> args;
1289 args.resize(nextArgIndex);
1293 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
1294 args[1 + i] = altBasePath;
1296 firrtl::PathOp containingModuleRef;
1297 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
1298 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
1299 auto index = subfield.getIndex();
1300 auto direction = firrtlClassType.getElement(index).direction;
1304 if (direction == Direction::Out)
1307 for (
auto *subfieldUser :
1308 llvm::make_early_inc_range(subfield->getUsers())) {
1309 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
1313 auto dst = propassign.getOperand(0);
1314 auto src = propassign.getOperand(1);
1315 if (dst == subfield.getResult()) {
1316 args[argIndexTable[index]] = src;
1317 opsToErase.push_back(propassign);
1320 if (firrtlClassType.getElement(index).name.strref().contains(
1321 kContainingModuleName)) {
1322 assert(!containingModuleRef &&
1323 "expected exactly one containingModule");
1324 assert(isa_and_nonnull<firrtl::PathOp>(src.getDefiningOp()) &&
1325 "expected containingModule to be a PathOp");
1326 containingModuleRef = src.getDefiningOp<firrtl::PathOp>();
1332 opsToErase.push_back(subfield);
1338 auto element = firrtlClassType.getElement(i);
1339 if (element.direction == Direction::Out)
1342 auto argIndex = argIndexTable[i];
1343 if (!args[argIndex])
1344 return emitError(firrtlObject.getLoc())
1345 <<
"uninitialized input port " << element.name;
1349 auto className = firrtlObject.getType().getNameAttr();
1350 auto classType = om::ClassType::get(firrtlObject->getContext(), className);
1353 OpBuilder builder(firrtlObject);
1355 auto object = om::ObjectOp::create(builder, firrtlObject.getLoc(), classType,
1356 firrtlObject.getClassNameAttr(), args);
1359 if (containingModuleRef) {
1360 std::lock_guard<std::mutex> guard(intraPassMutex);
1361 rtlPortsToCreate.push_back({containingModuleRef, basePath,
object});
1366 auto cast = UnrealizedConversionCastOp::create(
1367 builder,
object.
getLoc(), firrtlObject.getType(),
object.getResult());
1368 firrtlObject.replaceAllUsesWith(cast.getResult(0));
1371 opsToErase.push_back(firrtlObject);
1381 const PathInfoTable &pathInfoTable,
1382 SmallVectorImpl<Operation *> &opsToErase) {
1385 OpBuilder builder(firrtlInstance);
1390 SmallVector<Value> actualParameters;
1392 auto basePath = firrtlInstance->getBlock()->getArgument(0);
1393 auto symRef = FlatSymbolRefAttr::get(hierPath.getSymNameAttr());
1394 auto rebasedPath = om::BasePathCreateOp::create(
1395 builder, firrtlInstance->getLoc(), basePath, symRef);
1397 actualParameters.push_back(rebasedPath);
1400 pathInfoTable.collectAltBasePaths(
1401 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
1404 for (
auto result : firrtlInstance.getResults()) {
1406 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
1411 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
1412 if (!propertyResult)
1418 assert(propertyAssignment &&
"properties require single assignment");
1419 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
1422 opsToErase.push_back(propertyAssignment);
1426 auto referencedModule =
1427 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
1429 StringRef moduleName = referencedModule.getName();
1432 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
1433 moduleName = externMod.getExtModuleName();
1436 auto className = FlatSymbolRefAttr::get(
1437 builder.getStringAttr(moduleName + kClassNameSuffix));
1439 auto classType = om::ClassType::get(firrtlInstance->getContext(), className);
1443 om::ObjectOp::create(builder, firrtlInstance.getLoc(), classType,
1444 className.getAttr(), actualParameters);
1449 for (
auto result : firrtlInstance.getResults()) {
1451 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
1456 if (!isa<PropertyType>(result.getType()))
1460 auto objectFieldPath = builder.getArrayAttr({FlatSymbolRefAttr::get(
1461 firrtlInstance.getPortNameAttr(result.getResultNumber()))});
1464 auto objectField = om::ObjectFieldOp::create(
1465 builder,
object.
getLoc(), result.getType(),
object, objectFieldPath);
1467 result.replaceAllUsesWith(objectField);
1471 opsToErase.push_back(firrtlInstance);
1479 SmallVectorImpl<Operation *> &opsToErase) {
1481 BitVector portsToErase(firrtlInstance.getNumResults());
1482 for (
auto result : firrtlInstance.getResults())
1483 if (isa<PropertyType>(result.getType()))
1484 portsToErase.set(result.getResultNumber());
1487 if (portsToErase.none())
1491 InstanceOp newInstance =
1492 firrtlInstance.cloneWithErasedPortsAndReplaceUses(portsToErase);
1501 opsToErase.push_back(firrtlInstance);
1507 SmallVectorImpl<Operation *> &opsToErase) {
1508 OpBuilder builder(moduleOp);
1509 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1510 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1511 assert(0 &&
"should be no objects in modules");
1512 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1522 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1523 SmallVectorImpl<RtlPortsInfo> &rtlPortsToCreate, std::mutex &intraPassMutex,
1524 SmallVectorImpl<Operation *> &opsToErase) {
1525 OpBuilder builder(classOp);
1526 auto &classState = state.classLoweringStateTable.at(classOp);
1527 auto it = classState.paths.begin();
1528 for (
auto &op : classOp->getRegion(0).getOps()) {
1529 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1531 intraPassMutex, opsToErase)))
1533 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1535 pathInfoTable, opsToErase)))
1543LogicalResult LowerClassesPass::updateInstances(
1544 Operation *op,
InstanceGraph &instanceGraph,
const LoweringState &state,
1545 const PathInfoTable &pathInfoTable, std::mutex &intraPassMutex) {
1550 SmallVector<Operation *> opsToErase;
1552 TypeSwitch<Operation *, LogicalResult>(op)
1554 .Case([&](FModuleOp moduleOp) {
1559 .Case([&](om::ClassOp classOp) {
1563 classOp, instanceGraph, state, pathInfoTable, rtlPortsToCreate,
1564 intraPassMutex, opsToErase);
1566 .Default([](
auto *op) {
return success(); });
1570 for (
auto *op : opsToErase)
1577void LowerClassesPass::createAllRtlPorts(
1578 const PathInfoTable &pathInfoTable,
1581 MLIRContext *ctx = &getContext();
1584 OpBuilder builder = OpBuilder::atBlockEnd(getOperation().
getBodyBlock());
1587 om::ClassOp::buildSimpleClassOp(
1588 builder, UnknownLoc::get(ctx), kRtlPortClassName,
1589 {
"ref",
"direction",
"width"}, {
"ref",
"direction",
"width"},
1590 {om::PathType::get(ctx), om::StringType::get(ctx),
1591 om::OMIntegerType::get(ctx)});
1594 llvm::stable_sort(rtlPortsToCreate, [](
auto lhs,
auto rhs) {
1595 return lhs.object.getClassName() < rhs.object.getClassName();
1599 for (
auto rtlPortToCreate : rtlPortsToCreate)
1600 createRtlPorts(rtlPortToCreate, pathInfoTable, namespaces, hierPathCache,
1610struct FIntegerConstantOpConversion
1612 using OpConversionPattern::OpConversionPattern;
1615 matchAndRewrite(FIntegerConstantOp op, OpAdaptor adaptor,
1616 ConversionPatternRewriter &rewriter)
const override {
1617 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1618 op, om::OMIntegerType::get(op.getContext()),
1619 om::IntegerAttr::get(op.getContext(), adaptor.getValueAttr()));
1625 using OpConversionPattern::OpConversionPattern;
1628 matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor,
1629 ConversionPatternRewriter &rewriter)
const override {
1630 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1631 op, rewriter.getBoolAttr(adaptor.getValue()));
1636struct DoubleConstantOpConversion
1638 using OpConversionPattern::OpConversionPattern;
1641 matchAndRewrite(DoubleConstantOp op, OpAdaptor adaptor,
1642 ConversionPatternRewriter &rewriter)
const override {
1643 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1648struct StringConstantOpConversion
1650 using OpConversionPattern::OpConversionPattern;
1653 matchAndRewrite(StringConstantOp op, OpAdaptor adaptor,
1654 ConversionPatternRewriter &rewriter)
const override {
1655 auto stringType = om::StringType::get(op.getContext());
1656 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1657 op, stringType, StringAttr::get(op.getValue(), stringType));
1662struct ListCreateOpConversion
1664 using OpConversionPattern::OpConversionPattern;
1667 matchAndRewrite(firrtl::ListCreateOp op, OpAdaptor adaptor,
1668 ConversionPatternRewriter &rewriter)
const override {
1669 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1672 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1673 adaptor.getElements());
1678struct ListConcatOpConversion
1680 using OpConversionPattern::OpConversionPattern;
1683 matchAndRewrite(firrtl::ListConcatOp op, OpAdaptor adaptor,
1684 ConversionPatternRewriter &rewriter)
const override {
1685 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1688 rewriter.replaceOpWithNewOp<om::ListConcatOp>(op, listType,
1689 adaptor.getSubLists());
1694struct IntegerAddOpConversion
1696 using OpConversionPattern::OpConversionPattern;
1699 matchAndRewrite(firrtl::IntegerAddOp op, OpAdaptor adaptor,
1700 ConversionPatternRewriter &rewriter)
const override {
1701 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1707struct IntegerMulOpConversion
1709 using OpConversionPattern::OpConversionPattern;
1712 matchAndRewrite(firrtl::IntegerMulOp op, OpAdaptor adaptor,
1713 ConversionPatternRewriter &rewriter)
const override {
1714 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1720struct IntegerShrOpConversion
1722 using OpConversionPattern::OpConversionPattern;
1725 matchAndRewrite(firrtl::IntegerShrOp op, OpAdaptor adaptor,
1726 ConversionPatternRewriter &rewriter)
const override {
1727 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1733struct IntegerShlOpConversion
1735 using OpConversionPattern::OpConversionPattern;
1738 matchAndRewrite(firrtl::IntegerShlOp op, OpAdaptor adaptor,
1739 ConversionPatternRewriter &rewriter)
const override {
1740 rewriter.replaceOpWithNewOp<om::IntegerShlOp>(op, adaptor.getLhs(),
1748 PathOpConversion(TypeConverter &typeConverter, MLIRContext *
context,
1749 const PathInfoTable &pathInfoTable,
1750 PatternBenefit benefit = 1)
1752 pathInfoTable(pathInfoTable) {}
1755 matchAndRewrite(firrtl::PathOp op, OpAdaptor adaptor,
1756 ConversionPatternRewriter &rewriter)
const override {
1757 auto *
context = op->getContext();
1758 auto pathType = om::PathType::get(
context);
1759 auto pathInfoIt = pathInfoTable.table.find(op.getTarget());
1762 auto basePath = op->getBlock()->getArgument(0);
1766 if (pathInfoIt == pathInfoTable.table.end()) {
1767 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1768 return emitError(op.getLoc(),
"DontTouch target was deleted");
1769 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1770 return emitError(op.getLoc(),
"Instance target was deleted");
1771 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1775 auto pathInfo = pathInfoIt->second;
1776 auto symbol = pathInfo.symRef;
1780 om::TargetKind targetKind;
1781 switch (op.getTargetKind()) {
1782 case firrtl::TargetKind::DontTouch:
1783 targetKind = om::TargetKind::DontTouch;
1785 case firrtl::TargetKind::Reference:
1786 targetKind = om::TargetKind::Reference;
1788 case firrtl::TargetKind::Instance:
1789 if (!pathInfo.canBeInstanceTarget)
1790 return emitError(op.getLoc(),
"invalid target for instance path")
1791 .attachNote(pathInfo.loc)
1792 <<
"target not instance or module";
1793 targetKind = om::TargetKind::Instance;
1795 case firrtl::TargetKind::MemberInstance:
1796 case firrtl::TargetKind::MemberReference:
1797 if (pathInfo.canBeInstanceTarget)
1798 targetKind = om::TargetKind::MemberInstance;
1800 targetKind = om::TargetKind::MemberReference;
1806 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1810 auto parent = op->getParentOfType<om::ClassOp>();
1811 auto parentName = parent.getName();
1812 if (parentName.ends_with(kClassNameSuffix))
1813 parentName = parentName.drop_back(kClassNameSuffix.size());
1814 auto originalParentName = StringAttr::get(op->getContext(), parentName);
1818 pathInfoTable.getRootsForPassthrough(originalParentName);
1819 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1822 for (
auto [i, altBasePath] :
llvm::enumerate(altBasePaths)) {
1823 if (altBasePathModule == altBasePath) {
1825 auto basePathArg = op->getBlock()->getArgument(1 + i);
1826 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1827 "expected a passthrough base path");
1828 basePath = basePathArg;
1833 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1834 op, pathType, om::TargetKindAttr::get(op.getContext(), targetKind),
1839 const PathInfoTable &pathInfoTable;
1843 using OpConversionPattern::OpConversionPattern;
1846 matchAndRewrite(WireOp wireOp, OpAdaptor adaptor,
1847 ConversionPatternRewriter &rewriter)
const override {
1848 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1857 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1858 if (!regionKindInterface)
1860 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1869 rewriter.replaceOp(wireOp, propAssign.getSrc());
1872 rewriter.eraseOp(propAssign);
1879 using OpConversionPattern::OpConversionPattern;
1882 matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor,
1883 ConversionPatternRewriter &rewriter)
const override {
1884 rewriter.replaceOpWithNewOp<om::AnyCastOp>(op, adaptor.getInput());
1889struct ObjectSubfieldOpConversion
1891 using OpConversionPattern::OpConversionPattern;
1893 ObjectSubfieldOpConversion(
1894 const TypeConverter &typeConverter, MLIRContext *
context,
1895 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1897 classTypeTable(classTypeTable) {}
1900 matchAndRewrite(firrtl::ObjectSubfieldOp op, OpAdaptor adaptor,
1901 ConversionPatternRewriter &rewriter)
const override {
1902 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1908 auto firrtlClassType =
1909 classTypeTable.lookup(omClassType.getClassName().getAttr());
1910 if (!firrtlClassType)
1913 const auto &element = firrtlClassType.getElement(op.getIndex());
1915 if (element.direction == Direction::In)
1918 auto field = FlatSymbolRefAttr::get(element.name);
1919 auto path = rewriter.getArrayAttr({field});
1920 auto type = typeConverter->convertType(element.type);
1921 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1926 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable;
1930 using OpConversionPattern::OpConversionPattern;
1933 matchAndRewrite(om::ClassFieldsOp op, OpAdaptor adaptor,
1934 ConversionPatternRewriter &rewriter)
const override {
1935 rewriter.replaceOpWithNewOp<om::ClassFieldsOp>(op, adaptor.getOperands(),
1936 adaptor.getFieldLocsAttr());
1942 using OpConversionPattern::OpConversionPattern;
1945 matchAndRewrite(om::ObjectOp objectOp, OpAdaptor adaptor,
1946 ConversionPatternRewriter &rewriter)
const override {
1949 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1950 adaptor.getClassNameAttr(),
1951 adaptor.getActualParams());
1956static LogicalResult convertClassLike(om::ClassLike classOp,
1957 TypeConverter typeConverter,
1958 ConversionPatternRewriter &rewriter) {
1959 Block *body = classOp.getBodyBlock();
1960 TypeConverter::SignatureConversion result(body->getNumArguments());
1964 typeConverter.convertSignatureArgs(body->getArgumentTypes(), result)))
1968 if (failed(rewriter.convertRegionTypes(body->getParent(), typeConverter,
1972 rewriter.modifyOpInPlace(classOp, [&]() {
1973 mlir::AttrTypeReplacer replacer;
1974 replacer.addReplacement([&](TypeAttr typeAttr) {
1975 return mlir::TypeAttr::get(
1976 typeConverter.convertType(typeAttr.getValue()));
1978 classOp.replaceFieldTypes(replacer);
1985 using OpConversionPattern::OpConversionPattern;
1988 matchAndRewrite(om::ClassOp classOp, OpAdaptor adaptor,
1989 ConversionPatternRewriter &rewriter)
const override {
1990 return convertClassLike(classOp, *typeConverter, rewriter);
1994struct ClassExternOpSignatureConversion
1996 using OpConversionPattern::OpConversionPattern;
1999 matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor,
2000 ConversionPatternRewriter &rewriter)
const override {
2001 return convertClassLike(classOp, *typeConverter, rewriter);
2006 using OpConversionPattern::OpConversionPattern;
2009 matchAndRewrite(om::ObjectFieldOp op, OpAdaptor adaptor,
2010 ConversionPatternRewriter &rewriter)
const override {
2013 auto type = typeConverter->convertType(op.getType());
2017 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(
2018 op, type, adaptor.getObject(), adaptor.getFieldPathAttr());
2025struct UnrealizedConversionCastOpConversion
2027 using OpConversionPattern::OpConversionPattern;
2030 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2031 ConversionPatternRewriter &rewriter)
const override {
2032 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
2034 auto type = typeConverter->convertType(op.getResult(0));
2035 if (!type || type != adaptor.getOperands()[0].getType())
2037 rewriter.replaceOp(op, adaptor.getOperands()[0]);
2051 target.addDynamicallyLegalDialect<FIRRTLDialect>(
2052 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
2055 target.addDynamicallyLegalDialect<om::OMDialect>([](Operation *op) {
2056 auto containsFIRRTLType = [](Type type) {
2058 .walk([](Type type) {
2059 return failure(isa<FIRRTLDialect>(type.getDialect()));
2063 auto noFIRRTLOperands =
2064 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
2065 return containsFIRRTLType(type);
2067 auto noFIRRTLResults =
2068 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
2069 return containsFIRRTLType(type);
2071 return noFIRRTLOperands && noFIRRTLResults;
2075 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
2076 [](Operation *op) -> std::optional<bool> {
2077 auto classLike = dyn_cast<om::ClassLike>(op);
2079 return std::nullopt;
2080 auto fieldNames = classLike.getFieldNames();
2081 if (!llvm::all_of(fieldNames, [&](
auto field) {
2082 std::optional<Type> type =
2083 classLike.getFieldType(cast<StringAttr>(field));
2084 return type.has_value() && !isa<FIRRTLType>(type.value());
2088 return llvm::none_of(
2089 classLike.getBodyBlock()->getArgumentTypes(),
2090 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
2096 converter.addConversion([](IntegerType type) {
2097 return om::OMIntegerType::get(type.getContext());
2099 converter.addConversion([](FIntegerType type) {
2103 return om::OMIntegerType::get(type.getContext());
2107 converter.addConversion([](om::StringType type) {
return type; });
2108 converter.addConversion([](firrtl::StringType type) {
2109 return om::StringType::get(type.getContext());
2113 converter.addConversion([](om::PathType type) {
return type; });
2114 converter.addConversion([](om::BasePathType type) {
return type; });
2115 converter.addConversion([](om::FrozenPathType type) {
return type; });
2116 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
2117 converter.addConversion([](firrtl::PathType type) {
2118 return om::PathType::get(type.getContext());
2122 converter.addConversion([](om::ClassType type) {
return type; });
2123 converter.addConversion([](firrtl::ClassType type) {
2124 return om::ClassType::get(type.getContext(), type.getNameAttr());
2128 converter.addConversion([](om::AnyType type) {
return type; });
2129 converter.addConversion([](firrtl::AnyRefType type) {
2130 return om::AnyType::get(type.getContext());
2134 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
2136 if (isa<om::OMDialect>(type.getElementType().getDialect()))
2138 auto elementType = converter.convertType(type.getElementType());
2144 converter.addConversion(
2145 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
2147 return convertListType(type);
2150 converter.addConversion(
2151 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
2153 return convertListType(type);
2157 converter.addConversion(
2158 [](BoolType type) {
return IntegerType::get(type.getContext(), 1); });
2161 converter.addConversion(
2162 [](DoubleType type) {
return Float64Type::get(type.getContext()); });
2166 converter.addTargetMaterialization(
2167 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2168 assert(values.size() == 1);
2169 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2175 converter.addSourceMaterialization(
2176 [](OpBuilder &builder, Type type, ValueRange values, Location loc) {
2177 assert(values.size() == 1);
2178 return UnrealizedConversionCastOp::create(builder, loc, type, values[0])
2184 RewritePatternSet &
patterns, TypeConverter &converter,
2185 const PathInfoTable &pathInfoTable,
2186 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2187 patterns.add<FIntegerConstantOpConversion>(converter,
patterns.getContext());
2188 patterns.add<StringConstantOpConversion>(converter,
patterns.getContext());
2196 patterns.add<ClassOpSignatureConversion>(converter,
patterns.getContext());
2197 patterns.add<ClassExternOpSignatureConversion>(converter,
2204 patterns.add<DoubleConstantOpConversion>(converter,
patterns.getContext());
2209 patterns.add<UnrealizedConversionCastOpConversion>(converter,
2214LogicalResult LowerClassesPass::dialectConversion(
2215 Operation *op,
const PathInfoTable &pathInfoTable,
2216 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
2217 ConversionTarget target(getContext());
2220 TypeConverter typeConverter;
2223 RewritePatternSet
patterns(&getContext());
2227 return applyPartialConversion(op, target, std::move(
patterns));
assert(baseType &&"element must be base type")
MlirType uint64_t numElements
static std::unique_ptr< Context > context
static LogicalResult updateInstancesInModule(FModuleOp moduleOp, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static void populateConversionTarget(ConversionTarget &target)
static om::ClassLike convertClass(FModuleLike moduleLike, OpBuilder builder, Twine name, ArrayRef< StringRef > formalParamNames, bool hasContainingModule)
static LogicalResult updateInstanceInClass(InstanceOp firrtlInstance, hw::HierPathOp hierPath, InstanceGraph &instanceGraph, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static void populateTypeConverter(TypeConverter &converter)
static LogicalResult updateInstanceInModule(InstanceOp firrtlInstance, InstanceGraph &instanceGraph, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateObjectInClass(firrtl::ObjectOp firrtlObject, const PathInfoTable &pathInfoTable, SmallVectorImpl< RtlPortsInfo > &rtlPortsToCreate, std::mutex &intraPassMutex, SmallVectorImpl< Operation * > &opsToErase)
void checkAddContainingModulePorts(bool hasContainingModule, OpBuilder builder, SmallVector< Attribute > &fieldNames, SmallVector< NamedAttribute > &fieldTypes)
static om::ClassLike convertExtClass(FModuleLike moduleLike, OpBuilder builder, Twine name, ArrayRef< StringRef > formalParamNames, bool hasContainingModule)
static LogicalResult updateObjectsAndInstancesInClass(om::ClassOp classOp, InstanceGraph &instanceGraph, const LoweringState &state, const PathInfoTable &pathInfoTable, SmallVectorImpl< RtlPortsInfo > &rtlPortsToCreate, std::mutex &intraPassMutex, SmallVectorImpl< Operation * > &opsToErase)
static void populateRewritePatterns(RewritePatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
bool noUses()
Return true if there are no more instances of this module.
auto getModule()
Get the module that this node is tracking.
UseIterator usesBegin()
Iterate the instance records which instantiate this module.
bool hasOneUse()
Return true if this module has exactly one use.
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
virtual void erase(InstanceGraphNode *node)
Remove this module from the instance graph.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
This is an edge in the InstanceGraph.
InstanceGraphNode * getParent() const
Get the module where the instantiation lives.
auto getInstance()
Get the instance-like op that this is tracking.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
PropAssignOp getPropertyAssignment(FIRRTLPropertyValue value)
Return the single assignment to a Property value.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
An annotation target is used to keep track of something that is targeted by an Annotation.
Operation * getOp() const
FModuleLike getModule() const
Get the parent module of the target.
AnnotationSet getAnnotations() const
Get the annotations associated with the target.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
hw::HierPathOp getOpFor(ArrayAttr attr)
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
const SymbolTable & getSymbolTable() const
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
This implements an analysis to determine which module owns a given path operation.
This represents an annotation targeting a specific port of a module, memory, or instance.
This holds the name and type that describes the module's ports.