22 #include "mlir/IR/BuiltinOps.h"
23 #include "mlir/IR/IRMapping.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/Threading.h"
26 #include "mlir/Support/LogicalResult.h"
27 #include "mlir/Transforms/DialectConversion.h"
28 #include "llvm/ADT/DepthFirstIterator.h"
29 #include "llvm/ADT/STLExtras.h"
30 #include "llvm/Support/raw_ostream.h"
33 using namespace circt;
43 PathInfo(Operation *op, FlatSymbolRefAttr symRef,
44 StringAttr altBasePathModule)
45 : op(op), symRef(symRef), altBasePathModule(altBasePathModule) {
46 assert(op &&
"op must not be null");
47 assert(symRef &&
"symRef must not be null");
50 operator bool()
const {
return op !=
nullptr; }
53 Operation *op =
nullptr;
56 FlatSymbolRefAttr symRef =
nullptr;
60 StringAttr altBasePathModule =
nullptr;
64 struct PathInfoTable {
67 void addAltBasePathRoot(StringAttr rootModuleName) {
68 altBasePathRoots.insert(rootModuleName);
73 void addAltBasePathPassthrough(StringAttr passthroughModuleName,
74 StringAttr rootModuleName) {
75 auto &rootSequence = altBasePathsPassthroughs[passthroughModuleName];
76 rootSequence.push_back(rootModuleName);
80 llvm::iterator_range<SmallPtrSetImpl<StringAttr>::iterator>
81 getAltBasePathRoots()
const {
82 return llvm::make_range(altBasePathRoots.begin(), altBasePathRoots.end());
87 size_t getNumAltBasePaths(StringAttr passthroughModuleName)
const {
88 return altBasePathsPassthroughs.lookup(passthroughModuleName).size();
93 llvm::iterator_range<const StringAttr *>
94 getRootsForPassthrough(StringAttr passthroughModuleName)
const {
95 auto it = altBasePathsPassthroughs.find(passthroughModuleName);
96 assert(it != altBasePathsPassthroughs.end() &&
97 "expected passthrough module to already exist");
98 return llvm::make_range(it->second.begin(), it->second.end());
103 void collectAltBasePaths(Operation *instance, StringAttr moduleNameAttr,
104 SmallVectorImpl<Value> &result)
const {
105 auto altBasePaths = altBasePathsPassthroughs.lookup(moduleNameAttr);
106 auto parent = instance->getParentOfType<om::ClassOp>();
109 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
110 if (parent.getName().starts_with(altBasePath)) {
112 result.push_back(instance->getBlock()->getArgument(0));
116 auto basePath = instance->getBlock()->getArgument(1 + i);
117 assert(isa<om::BasePathType>(basePath.getType()) &&
118 "expected a passthrough base path");
119 result.push_back(basePath);
125 DenseMap<DistinctAttr, PathInfo> table;
130 SmallPtrSet<StringAttr, 16> altBasePathRoots;
134 DenseMap<StringAttr, SmallVector<StringAttr>> altBasePathsPassthroughs;
138 static constexpr StringRef kClassNameSuffix =
"_Class";
149 struct ClassLoweringState {
150 FModuleLike moduleLike;
151 std::vector<hw::HierPathOp> paths;
154 struct LoweringState {
155 PathInfoTable pathInfoTable;
156 DenseMap<om::ClassLike, ClassLoweringState> classLoweringStateTable;
159 struct LowerClassesPass :
public LowerClassesBase<LowerClassesPass> {
160 void runOnOperation()
override;
164 hw::InnerSymbolNamespaceCollection &namespaces,
166 SymbolTable &symbolTable);
169 bool shouldCreateClass(FModuleLike moduleLike);
172 om::ClassLike createClass(FModuleLike moduleLike,
173 const PathInfoTable &pathInfoTable);
176 void lowerClassLike(FModuleLike moduleLike, om::ClassLike classLike,
177 const PathInfoTable &pathInfoTable);
178 void lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
179 const PathInfoTable &pathInfoTable);
180 void lowerClassExtern(ClassExternOp classExternOp, FModuleLike moduleLike);
183 LogicalResult updateInstances(Operation *op,
InstanceGraph &instanceGraph,
184 const LoweringState &state,
185 const PathInfoTable &pathInfoTable);
188 LogicalResult dialectConversion(
189 Operation *op,
const PathInfoTable &pathInfoTable,
190 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable);
193 DenseMap<FModuleLike, bool> shouldCreateClassMemo;
204 LogicalResult LowerClassesPass::processPaths(
206 hw::InnerSymbolNamespaceCollection &namespaces,
HierPathCache &cache,
207 PathInfoTable &pathInfoTable, SymbolTable &symbolTable) {
208 auto *context = &getContext();
209 auto circuit = getOperation();
213 DenseMap<DistinctAttr, FModuleOp> owningModules;
214 std::vector<Operation *> declarations;
215 auto result = circuit.walk([&](Operation *op) {
216 if (
auto pathOp = dyn_cast<PathOp>(op)) {
218 auto owningModule = owningModuleCache.lookup(pathOp);
221 pathOp->emitError(
"path does not have a single owning module");
222 return WalkResult::interrupt();
224 auto target = pathOp.getTargetAttr();
225 auto [it, inserted] = owningModules.try_emplace(target, owningModule);
228 if (!inserted && it->second != owningModule) {
230 <<
"path reference " << target <<
" has conflicting owning modules "
231 << it->second.getModuleNameAttr() <<
" and "
232 << owningModule.getModuleNameAttr();
233 return WalkResult::interrupt();
236 return WalkResult::advance();
238 if (result.wasInterrupted())
241 auto processPathTrackers = [&](
AnnoTarget target) -> LogicalResult {
243 auto annotations = target.getAnnotations();
244 auto *op = target.getOp();
245 annotations.removeAnnotations([&](
Annotation anno) {
251 if (!anno.
isClass(
"circt.tracker"))
255 auto id = anno.
getMember<DistinctAttr>(
"id");
257 op->emitError(
"circt.tracker annotation missing id field");
269 {portTarget.getPortNo(), portTarget.getOp(), fieldID},
270 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
271 return namespaces[module];
273 }
else if (
auto module = dyn_cast<FModuleLike>(op)) {
274 assert(!fieldID &&
"field not valid for modules");
278 {target.getOp(), fieldID},
279 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
280 return namespaces[module];
285 SmallVector<Attribute> path;
288 path.push_back(targetSym);
290 auto moduleName = target.getModule().getModuleNameAttr();
293 hw::HierPathOp hierPathOp;
294 if (
auto hierName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
296 dyn_cast<hw::HierPathOp>(symbolTable.lookup(hierName.getAttr()));
298 op->emitError(
"annotation does not point at a HierPathOp");
304 auto [it, inserted] = pathInfoTable.table.try_emplace(
id);
305 auto &pathInfo = it->second;
308 emitError(pathInfo.op->getLoc(),
"duplicate identifier found");
309 diag.attachNote(op->getLoc()) <<
"other identifier here";
317 auto owningModule = owningModules.lookup(
id);
324 auto oldPath = hierPathOp.getNamepath().getValue();
325 llvm::append_range(path, llvm::reverse(oldPath.drop_back()));
330 bool pathContainsOwningModule =
331 llvm::any_of(oldPath, [&](
auto pathFragment) {
332 return llvm::TypeSwitch<Attribute, bool>(pathFragment)
333 .Case([&](hw::InnerRefAttr innerRef) {
334 return innerRef.getModule() ==
335 owningModule.getModuleNameAttr();
337 .Case([&](FlatSymbolRefAttr symRef) {
338 return symRef.getAttr() == owningModule.getModuleNameAttr();
340 .Default([](
auto attr) {
return false; });
342 if (pathContainsOwningModule) {
343 moduleName = owningModule.getModuleNameAttr();
345 moduleName = cast<hw::InnerRefAttr>(oldPath.front()).getModule();
351 bool needsAltBasePath =
false;
352 auto *node = instanceGraph.
lookup(moduleName);
355 if (node->getModule() == owningModule)
361 if (node->noUses()) {
362 needsAltBasePath =
true;
367 if (!node->hasOneUse()) {
368 auto diag = op->emitError()
369 <<
"unable to uniquely resolve target due "
370 "to multiple instantiation";
371 for (
auto *use : node->uses())
372 diag.attachNote(use->getInstance().getLoc()) <<
"instance here";
376 node = (*node->usesBegin())->getParent();
380 std::reverse(path.begin(), path.end());
385 StringAttr altBasePathModule;
386 if (needsAltBasePath) {
388 TypeSwitch<Attribute, StringAttr>(path.front())
389 .Case<FlatSymbolRefAttr>([](
auto a) {
return a.getAttr(); })
390 .Case<hw::InnerRefAttr>([](
auto a) {
return a.getModule(); });
392 pathInfoTable.addAltBasePathRoot(altBasePathModule);
396 pathInfo = {op, cache.
getRefFor(pathAttr), altBasePathModule};
404 target.setAnnotations(annotations);
408 for (
auto module : circuit.getOps<FModuleLike>()) {
413 for (
unsigned i = 0, e = module.getNumPorts(); i < e; ++i)
417 auto result = module.walk([&](hw::InnerSymbolOpInterface op) {
419 return WalkResult::interrupt();
420 return WalkResult::advance();
422 if (result.wasInterrupted())
428 for (
auto rootModule : pathInfoTable.getAltBasePathRoots()) {
433 auto start = llvm::df_begin(node);
434 auto end = llvm::df_end(node);
444 if (!shouldCreateClass(it->getModule<FModuleLike>())) {
445 it = it.skipChildren();
450 if (std::distance(it->begin(), it->end()) == 0) {
456 StringAttr passthroughModule = it->getModule().getModuleNameAttr();
457 pathInfoTable.addAltBasePathPassthrough(passthroughModule, rootModule);
467 void LowerClassesPass::runOnOperation() {
468 MLIRContext *ctx = &getContext();
471 CircuitOp circuit = getOperation();
475 SymbolTable &symbolTable = getAnalysis<SymbolTable>();
477 hw::InnerSymbolNamespaceCollection namespaces;
481 PathInfoTable pathInfoTable;
482 if (failed(processPaths(instanceGraph, namespaces, cache, pathInfoTable,
488 LoweringState loweringState;
491 DenseMap<StringAttr, firrtl::ClassType> classTypeTable;
492 for (
auto moduleLike : circuit.getOps<FModuleLike>()) {
493 if (shouldCreateClass(moduleLike)) {
494 auto omClass = createClass(moduleLike, pathInfoTable);
495 auto &classLoweringState = loweringState.classLoweringStateTable[omClass];
496 classLoweringState.moduleLike = moduleLike;
503 moduleLike.walk([&](InstanceOp inst) {
506 symbolTable.lookup<FModuleLike>(inst.getReferencedModuleNameAttr());
507 if (shouldCreateClass(module)) {
509 {inst, 0}, [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
510 return namespaces[module];
512 SmallVector<Attribute> path = {targetSym};
514 auto hierPath = cache.
getOpFor(pathAttr);
515 classLoweringState.paths.push_back(hierPath);
520 dyn_cast<firrtl::ClassLike>(moduleLike.getOperation()))
521 classTypeTable[classLike.getNameAttr()] = classLike.getInstanceType();
526 mlir::parallelForEach(ctx, loweringState.classLoweringStateTable,
527 [
this, &pathInfoTable](
auto &entry) {
528 const auto &[classLike, state] = entry;
529 lowerClassLike(state.moduleLike, classLike,
534 for (
auto &[omClass, state] : loweringState.classLoweringStateTable) {
535 if (isa<firrtl::ClassLike>(state.moduleLike.getOperation())) {
537 for (
auto *use : llvm::make_early_inc_range(node->
uses()))
539 instanceGraph.
erase(node);
540 state.moduleLike.erase();
545 SmallVector<Operation *> objectContainers;
546 for (
auto &op : circuit.getOps())
547 if (isa<FModuleOp, om::ClassLike>(op))
548 objectContainers.push_back(&op);
552 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
553 return updateInstances(op, instanceGraph, loweringState,
556 return signalPassFailure();
560 mlir::failableParallelForEach(ctx, objectContainers, [&](
auto *op) {
561 return dialectConversion(op, pathInfoTable, classTypeTable);
563 return signalPassFailure();
566 markAnalysesPreserved<InstanceGraph>();
570 return std::make_unique<LowerClassesPass>();
574 bool LowerClassesPass::shouldCreateClass(FModuleLike moduleLike) {
576 auto it = shouldCreateClassMemo.find(moduleLike);
577 if (it != shouldCreateClassMemo.end())
580 if (isa<firrtl::ClassLike>(moduleLike.getOperation())) {
581 shouldCreateClassMemo.insert({moduleLike,
true});
586 if (moduleLike.isPublic()) {
587 shouldCreateClassMemo.insert({moduleLike,
true});
592 bool hasClassPorts = llvm::any_of(moduleLike.getPorts(), [](
PortInfo port) {
593 return isa<PropertyType>(port.type);
597 shouldCreateClassMemo.insert({moduleLike,
true});
604 moduleLike.getOperation()->getRegion(0).getOps<FInstanceLike>()) {
605 for (
auto result : op->getResults()) {
606 if (type_isa<PropertyType>(result.getType())) {
607 shouldCreateClassMemo.insert({moduleLike,
true});
613 shouldCreateClassMemo.insert({moduleLike,
false});
619 LowerClassesPass::createClass(FModuleLike moduleLike,
620 const PathInfoTable &pathInfoTable) {
622 SmallVector<StringRef> formalParamNames;
624 formalParamNames.emplace_back(
"basepath");
627 size_t nAltBasePaths =
628 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
629 for (
size_t i = 0; i < nAltBasePaths; ++i)
631 moduleLike->getContext(),
"alt_basepath_" + llvm::Twine(i)));
633 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts()))
634 if (port.isInput() && isa<PropertyType>(port.type))
635 formalParamNames.push_back(port.name);
637 OpBuilder
builder = OpBuilder::atBlockEnd(getOperation().getBodyBlock());
640 StringRef className = moduleLike.getName();
643 if (
auto externMod = dyn_cast<FExtModuleOp>(moduleLike.getOperation()))
644 if (
auto defname = externMod.getDefname())
645 className = defname.value();
650 isa<FModuleOp, FExtModuleOp>(moduleLike) ? kClassNameSuffix :
"";
653 om::ClassLike loweredClassOp;
654 if (isa<firrtl::ExtClassOp, firrtl::FExtModuleOp>(moduleLike.getOperation()))
655 loweredClassOp =
builder.create<om::ClassExternOp>(
656 moduleLike.getLoc(), className + suffix, formalParamNames);
658 loweredClassOp =
builder.create<om::ClassOp>(
659 moduleLike.getLoc(), className + suffix, formalParamNames);
661 return loweredClassOp;
664 void LowerClassesPass::lowerClassLike(FModuleLike moduleLike,
665 om::ClassLike classLike,
666 const PathInfoTable &pathInfoTable) {
668 if (
auto classOp = dyn_cast<om::ClassOp>(classLike.getOperation())) {
669 return lowerClass(classOp, moduleLike, pathInfoTable);
671 if (
auto classExternOp =
672 dyn_cast<om::ClassExternOp>(classLike.getOperation())) {
673 return lowerClassExtern(classExternOp, moduleLike);
675 llvm_unreachable(
"unhandled class-like op");
678 void LowerClassesPass::lowerClass(om::ClassOp classOp, FModuleLike moduleLike,
679 const PathInfoTable &pathInfoTable) {
684 SmallVector<Property> inputProperties;
685 BitVector portsToErase(moduleLike.getNumPorts());
686 for (
auto [index, port] : llvm::enumerate(moduleLike.getPorts())) {
688 if (!isa<PropertyType>(port.type))
693 inputProperties.push_back({index, port.name, port.type, port.loc});
696 portsToErase.set(index);
701 Block *classBody = &classOp->getRegion(0).emplaceBlock();
705 classBody->addArgument(basePathType, unknownLoc);
708 size_t nAltBasePaths =
709 pathInfoTable.getNumAltBasePaths(moduleLike.getModuleNameAttr());
710 for (
size_t i = 0; i < nAltBasePaths; ++i)
711 classBody->addArgument(basePathType, unknownLoc);
713 for (
auto inputProperty : inputProperties) {
714 BlockArgument parameterValue =
715 classBody->addArgument(inputProperty.type, inputProperty.loc);
716 BlockArgument inputValue =
717 moduleLike->getRegion(0).getArgument(inputProperty.index);
718 mapping.map(inputValue, parameterValue);
722 SmallVector<Operation *> opsToErase;
723 OpBuilder
builder = OpBuilder::atBlockBegin(classOp.getBodyBlock());
724 for (
auto &op : moduleLike->getRegion(0).getOps()) {
726 auto propertyOperands = llvm::any_of(op.getOperandTypes(), [](Type type) {
727 return isa<PropertyType>(type);
731 auto propertyResults = llvm::any_of(
732 op.getResultTypes(), [](Type type) { return isa<PropertyType>(type); });
735 if (!propertyOperands && !propertyResults)
743 if (!isa<InstanceOp>(op))
744 opsToErase.push_back(&op);
748 for (
auto op : llvm::make_early_inc_range(classOp.getOps<PropAssignOp>())) {
751 auto outputPort = dyn_cast<BlockArgument>(op.getDest());
756 auto name = moduleLike.getPortName(outputPort.getArgNumber());
757 builder.create<ClassFieldOp>(op.getLoc(), name, op.getSrc());
763 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
765 for (
auto *op : llvm::reverse(opsToErase))
769 moduleLike.erasePorts(portsToErase);
773 void LowerClassesPass::lowerClassExtern(ClassExternOp classExternOp,
774 FModuleLike moduleLike) {
778 BitVector portsToErase(moduleLike.getNumPorts());
779 Block *classBody = &classExternOp.getRegion().emplaceBlock();
780 OpBuilder
builder = OpBuilder::atBlockBegin(classBody);
786 for (
unsigned i = 0, e = moduleLike.getNumPorts(); i < e; ++i) {
787 auto type = moduleLike.getPortType(i);
788 if (!isa<PropertyType>(type))
791 auto loc = moduleLike.getPortLocation(i);
792 auto direction = moduleLike.getPortDirection(i);
793 if (direction == Direction::In)
794 classBody->addArgument(type, loc);
796 auto name = moduleLike.getPortNameAttr(i);
797 builder.create<om::ClassExternFieldOp>(loc, name, type);
806 if (!isa<firrtl::ClassLike>(moduleLike.getOperation())) {
808 moduleLike.erasePorts(portsToErase);
816 const PathInfoTable &pathInfoTable,
817 SmallVectorImpl<Operation *> &opsToErase) {
819 auto basePath = firrtlObject->getBlock()->getArgument(0);
822 auto firrtlClassType = firrtlObject.getType();
823 auto numElements = firrtlClassType.getNumElements();
824 llvm::SmallVector<unsigned> argIndexTable;
828 SmallVector<Value> altBasePaths;
829 pathInfoTable.collectAltBasePaths(
830 firrtlObject, firrtlClassType.getNameAttr().getAttr(), altBasePaths);
833 unsigned nextArgIndex = 1 + altBasePaths.size();
836 auto direction = firrtlClassType.getElement(i).direction;
837 if (direction == Direction::In)
838 argIndexTable[i] = nextArgIndex++;
844 llvm::SmallVector<Value> args;
845 args.resize(nextArgIndex);
849 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths))
850 args[1 + i] = altBasePath;
852 for (
auto *user : llvm::make_early_inc_range(firrtlObject->getUsers())) {
853 if (
auto subfield = dyn_cast<ObjectSubfieldOp>(user)) {
854 auto index = subfield.getIndex();
855 auto direction = firrtlClassType.getElement(index).direction;
859 if (direction == Direction::Out)
862 for (
auto *subfieldUser :
863 llvm::make_early_inc_range(subfield->getUsers())) {
864 if (
auto propassign = dyn_cast<PropAssignOp>(subfieldUser)) {
868 auto dst = propassign.getOperand(0);
869 auto src = propassign.getOperand(1);
870 if (dst == subfield.getResult()) {
871 args[argIndexTable[index]] = src;
872 opsToErase.push_back(propassign);
877 opsToErase.push_back(subfield);
883 auto element = firrtlClassType.getElement(i);
884 if (element.direction == Direction::Out)
887 auto argIndex = argIndexTable[i];
889 return emitError(firrtlObject.getLoc())
890 <<
"uninitialized input port " << element.name;
894 auto className = firrtlObject.getType().getNameAttr();
898 OpBuilder
builder(firrtlObject);
899 auto object =
builder.create<om::ObjectOp>(
900 firrtlObject.getLoc(), classType, firrtlObject.getClassNameAttr(), args);
904 firrtlObject.replaceAllUsesWith(
object.getResult());
907 opsToErase.push_back(firrtlObject);
917 const PathInfoTable &pathInfoTable,
918 SmallVectorImpl<Operation *> &opsToErase) {
921 OpBuilder
builder(firrtlInstance);
926 SmallVector<Value> actualParameters;
928 auto basePath = firrtlInstance->getBlock()->getArgument(0);
930 auto rebasedPath =
builder.create<om::BasePathCreateOp>(
931 firrtlInstance->getLoc(), basePath, symRef);
933 actualParameters.push_back(rebasedPath);
936 pathInfoTable.collectAltBasePaths(
937 firrtlInstance, firrtlInstance.getModuleNameAttr().getAttr(),
940 for (
auto result : firrtlInstance.getResults()) {
942 if (firrtlInstance.getPortDirection(result.getResultNumber()) ==
947 auto propertyResult = dyn_cast<FIRRTLPropertyValue>(result);
954 assert(propertyAssignment &&
"properties require single assignment");
955 actualParameters.push_back(propertyAssignment.getSrcMutable().get());
958 opsToErase.push_back(propertyAssignment);
962 auto referencedModule =
963 firrtlInstance.getReferencedModule<FModuleLike>(instanceGraph);
965 StringRef moduleName = referencedModule.getName();
968 if (
auto externMod = dyn_cast<FExtModuleOp>(referencedModule.getOperation()))
969 if (
auto defname = externMod.getDefname())
970 moduleName = defname.value();
974 builder.getStringAttr(moduleName + kClassNameSuffix));
980 builder.create<om::ObjectOp>(firrtlInstance.getLoc(), classType,
981 className.getAttr(), actualParameters);
986 for (
auto result : firrtlInstance.getResults()) {
988 if (firrtlInstance.getPortDirection(result.getResultNumber()) !=
993 if (!isa<PropertyType>(result.getType()))
998 firrtlInstance.getPortName(result.getResultNumber()))});
1001 auto objectField =
builder.create<ObjectFieldOp>(
1002 object.getLoc(), result.getType(), object, objectFieldPath);
1004 result.replaceAllUsesWith(objectField);
1008 opsToErase.push_back(firrtlInstance);
1014 static LogicalResult
1016 SmallVectorImpl<Operation *> &opsToErase) {
1018 BitVector portsToErase(firrtlInstance.getNumResults());
1019 for (
auto result : firrtlInstance.getResults())
1020 if (isa<PropertyType>(result.getType()))
1021 portsToErase.set(result.getResultNumber());
1024 if (portsToErase.none())
1028 OpBuilder
builder(firrtlInstance);
1029 InstanceOp newInstance = firrtlInstance.erasePorts(
builder, portsToErase);
1038 opsToErase.push_back(firrtlInstance);
1042 static LogicalResult
1044 SmallVectorImpl<Operation *> &opsToErase) {
1046 for (
auto &op : moduleOp->getRegion(0).getOps()) {
1047 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1048 assert(0 &&
"should be no objects in modules");
1049 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1059 const LoweringState &state,
const PathInfoTable &pathInfoTable,
1060 SmallVectorImpl<Operation *> &opsToErase) {
1062 auto &classState = state.classLoweringStateTable.at(classOp);
1063 auto it = classState.paths.begin();
1064 for (
auto &op : classOp->getRegion(0).getOps()) {
1065 if (
auto objectOp = dyn_cast<firrtl::ObjectOp>(op)) {
1068 }
else if (
auto instanceOp = dyn_cast<InstanceOp>(op)) {
1070 pathInfoTable, opsToErase)))
1079 LowerClassesPass::updateInstances(Operation *op,
InstanceGraph &instanceGraph,
1080 const LoweringState &state,
1081 const PathInfoTable &pathInfoTable) {
1086 SmallVector<Operation *> opsToErase;
1088 TypeSwitch<Operation *, LogicalResult>(op)
1090 .Case([&](FModuleOp moduleOp) {
1095 .Case([&](om::ClassOp classOp) {
1099 classOp, instanceGraph, state, pathInfoTable, opsToErase);
1101 .Default([](
auto *op) {
return success(); });
1105 for (
auto *op : opsToErase)
1115 using OpConversionPattern::OpConversionPattern;
1119 ConversionPatternRewriter &rewriter)
const override {
1120 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1128 using OpConversionPattern::OpConversionPattern;
1132 ConversionPatternRewriter &rewriter)
const override {
1133 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1134 op, rewriter.getBoolAttr(adaptor.getValue()));
1141 using OpConversionPattern::OpConversionPattern;
1145 ConversionPatternRewriter &rewriter)
const override {
1146 rewriter.replaceOpWithNewOp<om::ConstantOp>(op, adaptor.getValue());
1153 using OpConversionPattern::OpConversionPattern;
1157 ConversionPatternRewriter &rewriter)
const override {
1159 rewriter.replaceOpWithNewOp<om::ConstantOp>(
1167 using OpConversionPattern::OpConversionPattern;
1171 ConversionPatternRewriter &rewriter)
const override {
1172 auto listType = getTypeConverter()->convertType<om::ListType>(op.getType());
1175 rewriter.replaceOpWithNewOp<om::ListCreateOp>(op, listType,
1176 adaptor.getElements());
1183 using OpConversionPattern::OpConversionPattern;
1187 ConversionPatternRewriter &rewriter)
const override {
1188 rewriter.replaceOpWithNewOp<om::IntegerAddOp>(op, adaptor.getLhs(),
1196 using OpConversionPattern::OpConversionPattern;
1200 ConversionPatternRewriter &rewriter)
const override {
1201 rewriter.replaceOpWithNewOp<om::IntegerMulOp>(op, adaptor.getLhs(),
1209 using OpConversionPattern::OpConversionPattern;
1213 ConversionPatternRewriter &rewriter)
const override {
1214 rewriter.replaceOpWithNewOp<om::IntegerShrOp>(op, adaptor.getLhs(),
1223 const PathInfoTable &pathInfoTable,
1224 PatternBenefit benefit = 1)
1226 pathInfoTable(pathInfoTable) {}
1230 ConversionPatternRewriter &rewriter)
const override {
1231 auto *context = op->getContext();
1233 auto pathInfo = pathInfoTable.table.lookup(op.getTarget());
1236 auto basePath = op->getBlock()->getArgument(0);
1241 if (op.getTargetKind() == firrtl::TargetKind::DontTouch)
1242 return emitError(op.getLoc(),
"DontTouch target was deleted");
1243 if (op.getTargetKind() == firrtl::TargetKind::Instance)
1244 return emitError(op.getLoc(),
"Instance target was deleted");
1245 rewriter.replaceOpWithNewOp<om::EmptyPathOp>(op);
1249 auto symbol = pathInfo.symRef;
1253 om::TargetKind targetKind;
1254 switch (op.getTargetKind()) {
1255 case firrtl::TargetKind::DontTouch:
1256 targetKind = om::TargetKind::DontTouch;
1258 case firrtl::TargetKind::Reference:
1259 targetKind = om::TargetKind::Reference;
1261 case firrtl::TargetKind::Instance:
1262 if (!isa<InstanceOp, FModuleLike>(pathInfo.op))
1263 return emitError(op.getLoc(),
"invalid target for instance path")
1264 .attachNote(pathInfo.op->getLoc())
1265 <<
"target not instance or module";
1266 targetKind = om::TargetKind::Instance;
1268 case firrtl::TargetKind::MemberInstance:
1269 case firrtl::TargetKind::MemberReference:
1270 if (isa<InstanceOp, FModuleLike>(pathInfo.op))
1271 targetKind = om::TargetKind::MemberInstance;
1273 targetKind = om::TargetKind::MemberReference;
1279 if (
auto altBasePathModule = pathInfo.altBasePathModule) {
1283 auto parent = op->getParentOfType<om::ClassOp>();
1284 auto parentName = parent.getName();
1285 if (parentName.ends_with(kClassNameSuffix))
1286 parentName = parentName.drop_back(kClassNameSuffix.size());
1287 auto originalParentName =
StringAttr::get(op->getContext(), parentName);
1291 pathInfoTable.getRootsForPassthrough(originalParentName);
1292 assert(!altBasePaths.empty() &&
"expected passthrough base paths");
1295 for (
auto [i, altBasePath] : llvm::enumerate(altBasePaths)) {
1296 if (altBasePathModule == altBasePath) {
1298 auto basePathArg = op->getBlock()->getArgument(1 + i);
1299 assert(isa<om::BasePathType>(basePathArg.getType()) &&
1300 "expected a passthrough base path");
1301 basePath = basePathArg;
1306 rewriter.replaceOpWithNewOp<om::PathCreateOp>(
1316 using OpConversionPattern::OpConversionPattern;
1320 ConversionPatternRewriter &rewriter)
const override {
1321 auto wireValue = dyn_cast<FIRRTLPropertyValue>(wireOp.getResult());
1330 auto regionKindInterface = wireOp->getParentOfType<RegionKindInterface>();
1331 if (!regionKindInterface)
1333 if (regionKindInterface.getRegionKind(0) != RegionKind::Graph)
1340 rewriter.replaceOp(wireOp, propAssign.getSrc());
1343 rewriter.eraseOp(propAssign);
1350 using OpConversionPattern::OpConversionPattern;
1354 ConversionPatternRewriter &rewriter)
const override {
1355 rewriter.replaceOpWithNewOp<AnyCastOp>(op, adaptor.getInput());
1362 using OpConversionPattern::OpConversionPattern;
1365 const TypeConverter &typeConverter, MLIRContext *context,
1366 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable)
1368 classTypeTable(classTypeTable) {}
1372 ConversionPatternRewriter &rewriter)
const override {
1373 auto omClassType = dyn_cast<om::ClassType>(adaptor.getInput().getType());
1379 auto firrtlClassType =
1380 classTypeTable.lookup(omClassType.getClassName().getAttr());
1381 if (!firrtlClassType)
1384 const auto &element = firrtlClassType.getElement(op.getIndex());
1386 if (element.direction == Direction::In)
1390 auto path = rewriter.getArrayAttr({field});
1391 auto type = typeConverter->convertType(element.type);
1392 rewriter.replaceOpWithNewOp<om::ObjectFieldOp>(op, type, adaptor.getInput(),
1401 using OpConversionPattern::OpConversionPattern;
1405 ConversionPatternRewriter &rewriter)
const override {
1406 rewriter.replaceOpWithNewOp<ClassFieldOp>(op, adaptor.getNameAttr(),
1407 adaptor.getValue());
1414 using OpConversionPattern::OpConversionPattern;
1418 ConversionPatternRewriter &rewriter)
const override {
1419 auto type = typeConverter->convertType(adaptor.getType());
1422 rewriter.replaceOpWithNewOp<ClassExternFieldOp>(op, adaptor.getNameAttr(),
1429 using OpConversionPattern::OpConversionPattern;
1433 ConversionPatternRewriter &rewriter)
const override {
1436 rewriter.replaceOpWithNewOp<om::ObjectOp>(objectOp, objectOp.getType(),
1437 adaptor.getClassNameAttr(),
1438 adaptor.getActualParams());
1444 using OpConversionPattern::OpConversionPattern;
1448 ConversionPatternRewriter &rewriter)
const override {
1449 Block *body = classOp.getBodyBlock();
1450 TypeConverter::SignatureConversion result(body->getNumArguments());
1453 if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1458 if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1462 rewriter.modifyOpInPlace(classOp, []() {});
1470 using OpConversionPattern::OpConversionPattern;
1474 ConversionPatternRewriter &rewriter)
const override {
1475 Block *body = classOp.getBodyBlock();
1476 TypeConverter::SignatureConversion result(body->getNumArguments());
1479 if (failed(typeConverter->convertSignatureArgs(body->getArgumentTypes(),
1484 if (failed(rewriter.convertRegionTypes(body->getParent(), *typeConverter,
1488 rewriter.modifyOpInPlace(classOp, []() {});
1495 using OpConversionPattern::OpConversionPattern;
1499 ConversionPatternRewriter &rewriter)
const override {
1502 auto type = typeConverter->convertType(op.getType());
1506 rewriter.replaceOpWithNewOp<ObjectFieldOp>(op, type, adaptor.getObject(),
1507 adaptor.getFieldPathAttr());
1518 target.addDynamicallyLegalDialect<FIRRTLDialect>(
1519 [](Operation *op) {
return !op->getParentOfType<om::ClassLike>(); });
1522 target.addDynamicallyLegalDialect<OMDialect>([](Operation *op) {
1523 auto containsFIRRTLType = [](Type type) {
1525 .walk([](Type type) {
1526 return failure(isa<FIRRTLDialect>(type.getDialect()));
1530 auto noFIRRTLOperands =
1531 llvm::none_of(op->getOperandTypes(), [&containsFIRRTLType](Type type) {
1532 return containsFIRRTLType(type);
1534 auto noFIRRTLResults =
1535 llvm::none_of(op->getResultTypes(), [&containsFIRRTLType](Type type) {
1536 return containsFIRRTLType(type);
1538 return noFIRRTLOperands && noFIRRTLResults;
1543 target.addDynamicallyLegalOp<ClassExternFieldOp>(
1544 [](ClassExternFieldOp op) {
return !isa<FIRRTLType>(op.getType()); });
1547 target.addDynamicallyLegalOp<om::ClassOp, om::ClassExternOp>(
1548 [](Operation *op) -> std::optional<bool> {
1549 auto classLike = dyn_cast<om::ClassLike>(op);
1551 return std::nullopt;
1553 return llvm::none_of(
1554 classLike.getBodyBlock()->getArgumentTypes(),
1555 [](Type type) { return isa<FIRRTLDialect>(type.getDialect()); });
1561 converter.addConversion(
1563 converter.addConversion([](FIntegerType type) {
1571 converter.addConversion([](om::StringType type) {
return type; });
1572 converter.addConversion([](firrtl::StringType type) {
1577 converter.addConversion([](om::PathType type) {
return type; });
1578 converter.addConversion([](om::BasePathType type) {
return type; });
1579 converter.addConversion([](om::FrozenPathType type) {
return type; });
1580 converter.addConversion([](om::FrozenBasePathType type) {
return type; });
1581 converter.addConversion([](firrtl::PathType type) {
1586 converter.addConversion([](om::ClassType type) {
return type; });
1587 converter.addConversion([](firrtl::ClassType type) {
1592 converter.addConversion([](om::AnyType type) {
return type; });
1593 converter.addConversion([](firrtl::AnyRefType type) {
1598 auto convertListType = [&converter](
auto type) -> std::optional<mlir::Type> {
1599 auto elementType = converter.convertType(type.getElementType());
1605 converter.addConversion(
1606 [convertListType](om::ListType type) -> std::optional<mlir::Type> {
1608 return convertListType(type);
1611 converter.addConversion(
1612 [convertListType](firrtl::ListType type) -> std::optional<mlir::Type> {
1614 return convertListType(type);
1618 converter.addConversion(
1622 converter.addConversion(
1623 [](DoubleType type) {
return FloatType::getF64(type.getContext()); });
1626 converter.addTargetMaterialization(
1627 [](OpBuilder &
builder, Type type, ValueRange values, Location loc) {
1628 assert(values.size() == 1);
1633 converter.addSourceMaterialization(
1634 [](OpBuilder &
builder, Type type, ValueRange values, Location loc) {
1635 assert(values.size() == 1);
1641 RewritePatternSet &
patterns, TypeConverter &converter,
1642 const PathInfoTable &pathInfoTable,
1643 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1668 LogicalResult LowerClassesPass::dialectConversion(
1669 Operation *op,
const PathInfoTable &pathInfoTable,
1670 const DenseMap<StringAttr, firrtl::ClassType> &classTypeTable) {
1671 ConversionTarget target(getContext());
1674 TypeConverter typeConverter;
1677 RewritePatternSet
patterns(&getContext());
1681 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 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 updateObjectsAndInstancesInClass(om::ClassOp classOp, InstanceGraph &instanceGraph, const LoweringState &state, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static LogicalResult updateObjectInClass(firrtl::ObjectOp firrtlObject, const PathInfoTable &pathInfoTable, SmallVectorImpl< Operation * > &opsToErase)
static void populateRewritePatterns(RewritePatternSet &patterns, TypeConverter &converter, const PathInfoTable &pathInfoTable, const DenseMap< StringAttr, firrtl::ClassType > &classTypeTable)
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.
llvm::iterator_range< UseIterator > uses()
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.
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.
std::unique_ptr< mlir::Pass > createLowerClassesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
LogicalResult matchAndRewrite(ObjectAnyRefCastOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(BoolConstantOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassExternFieldOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(om::ClassExternOp classOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(ClassFieldOp 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::IntegerShrOp 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.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
hw::HierPathOp getOpFor(ArrayAttr attr)
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
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.