17 #include "mlir/IR/ImplicitLocOpBuilder.h"
18 #include "mlir/Pass/Pass.h"
22 #define GEN_PASS_DEF_RESOLVEPATHS
23 #include "circt/Dialect/FIRRTL/Passes.h.inc"
27 using namespace circt;
28 using namespace firrtl;
33 : circuit(circuit), symbolTable(circuit), instanceGraph(instanceGraph),
34 instancePathCache(instanceGraph), hierPathCache(circuit, symbolTable),
35 builder(OpBuilder::atBlockBegin(circuit->getBlock())) {}
40 LogicalResult resolveHierPath(Location loc, FModuleOp owningModule,
42 FlatSymbolRefAttr &result) {
48 module = target.
instances.front()->getParentOfType<FModuleLike>();
49 auto *node = instanceGraph[module];
52 if (node->getModule() == owningModule)
58 <<
"unable to resolve path relative to owning module "
59 << owningModule.getModuleNameAttr();
65 if (!node->hasOneUse()) {
66 auto diag = emitWarning(loc) <<
"unable to uniquely resolve target due "
67 "to multiple instantiation";
68 for (
auto *use : node->uses())
69 diag.attachNote(use->getInstance().getLoc()) <<
"instance here";
71 node = (*node->usesBegin())->getParent();
81 SmallVector<Attribute> insts;
84 std::back_inserter(insts), [&](InstanceOp instance) {
85 return OpAnnoTarget(instance).getNLAReference(
86 namespaces[instance->getParentOfType<FModuleLike>()]);
96 result = hierPathCache.getRefFor(instAttr);
102 auto loc = unresolved.getLoc();
103 ImplicitLocOpBuilder b(loc, unresolved);
104 auto *context = b.getContext();
108 auto target = unresolved.getTarget();
113 if (target.consume_front(
"OMDeleted:")) {
115 return emitError(loc,
"OMDeleted references can not have targets");
121 auto resolved = b.create<PathOp>(targetKind, id);
122 unresolved->replaceAllUsesWith(resolved);
128 TargetKind targetKind;
129 if (target.consume_front(
"OMDontTouchedReferenceTarget")) {
130 targetKind = TargetKind::DontTouch;
131 }
else if (target.consume_front(
"OMInstanceTarget")) {
132 targetKind = TargetKind::Instance;
133 }
else if (target.consume_front(
"OMMemberInstanceTarget")) {
134 targetKind = TargetKind::MemberInstance;
135 }
else if (target.consume_front(
"OMMemberReferenceTarget")) {
136 targetKind = TargetKind::MemberReference;
137 }
else if (target.consume_front(
"OMReferenceTarget")) {
138 targetKind = TargetKind::Reference;
140 return emitError(loc)
141 <<
"unknown or missing OM reference type in target string: \""
147 if (!target.consume_front(
":"))
148 return emitError(loc,
"expected ':' in target string");
152 return emitError(loc)
153 <<
"cannot tokenize annotation path \"" << target <<
"\"";
156 auto path =
resolveEntities(*token, circuit, symbolTable, targetCache);
163 if (Type targetType = path->ref.getType()) {
164 auto fieldId = path->fieldIdx;
165 auto baseType = type_dyn_cast<FIRRTLBaseType>(targetType);
167 return emitError(loc,
"unable to target non-hardware type ")
170 if (type_isa<BundleType, FVectorType>(targetType))
171 return emitError(loc,
"unable to target aggregate type ") << targetType;
174 auto owningModule = cache.
lookup(unresolved);
175 StringRef moduleName =
"nullptr";
177 moduleName = owningModule.getModuleName();
179 return unresolved->emitError(
"path does not have a single owning module");
182 FlatSymbolRefAttr hierPathName;
183 if (failed(resolveHierPath(loc, owningModule, *path, hierPathName)))
186 auto createAnnotation = [&](FlatSymbolRefAttr hierPathName) {
191 NamedAttrList fields;
192 fields.append(
"id",
id);
195 fields.append(
"circt.nonlocal", hierPathName);
196 if (path->fieldIdx != 0)
197 fields.append(
"circt.fieldID", b.getI64IntegerAttr(path->fieldIdx));
203 Attribute annotation = createAnnotation(hierPathName);
206 auto annoTarget = path->ref;
207 auto targetAnnotations = annoTarget.getAnnotations();
208 targetAnnotations.addAnnotations({annotation});
209 if (targetKindAttr.getValue() == TargetKind::DontTouch)
210 targetAnnotations.addDontTouch();
211 annoTarget.setAnnotations(targetAnnotations);
214 auto dictAttr = cast<DictionaryAttr>(annotation);
215 auto id = cast<DistinctAttr>(dictAttr.get(
"id"));
216 auto resolved = b.create<PathOp>(targetKindAttr, id);
219 unresolved->replaceAllUsesWith(resolved);
226 SymbolTable symbolTable;
230 hw::InnerSymbolNamespaceCollection namespaces;
241 struct ResolvePathsPass
242 :
public circt::firrtl::impl::ResolvePathsBase<ResolvePathsPass> {
243 void runOnOperation()
override;
247 void ResolvePathsPass::runOnOperation() {
248 auto circuit = getOperation();
249 auto &instanceGraph = getAnalysis<InstanceGraph>();
250 PathResolver resolver(circuit, instanceGraph);
252 auto result = circuit.walk([&](UnresolvedPathOp unresolved) {
253 if (failed(resolver.resolve(cache, unresolved))) {
255 return WalkResult::interrupt();
257 return WalkResult::advance();
259 if (result.wasInterrupted())
261 markAnalysesPreserved<InstanceGraph>();
265 return std::make_unique<ResolvePathsPass>();
This graph tracks modules and where they are instantiated.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::unique_ptr< mlir::Pass > createResolvePathsPass()
std::optional< AnnoPathValue > resolveEntities(TokenAnnoTarget path, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Convert a parsed target string to a resolved target structure.
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
::mlir::Type getFinalTypeByFieldID(Type type, uint64_t fieldID)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
SmallVector< InstanceOp > instances
FModuleLike getModule() const
Get the parent module of the target.
Cache AnnoTargets for a circuit's modules, walked as needed.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
This implements an analysis to determine which module owns a given path operation.
FModuleOp lookup(ClassOp classOp)
Return this operation's owning module.
A data structure that caches and provides absolute paths to module instances in the IR.