CIRCT 20.0.0git
Loading...
Searching...
No Matches
ResolvePaths.cpp
Go to the documentation of this file.
1//===- ResolvePaths.cpp - Resolve path operations ---------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains the ResolvePathsPass.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/Pass/Pass.h"
18
19namespace circt {
20namespace firrtl {
21#define GEN_PASS_DEF_RESOLVEPATHS
22#include "circt/Dialect/FIRRTL/Passes.h.inc"
23} // namespace firrtl
24} // namespace circt
25
26using namespace circt;
27using namespace firrtl;
28
29namespace {
30struct PathResolver {
31 PathResolver(CircuitOp circuit, InstanceGraph &instanceGraph)
32 : circuit(circuit), symbolTable(circuit), instanceGraph(instanceGraph),
33 instancePathCache(instanceGraph), hierPathCache(circuit, symbolTable),
34 builder(OpBuilder::atBlockBegin(circuit->getBlock())) {}
35
36 /// This function will find the operation targeted and create a hierarchical
37 /// path operation if needed. If the target is resolved, the op will either
38 /// be a reference to the HierPathOp, or null if no HierPathOp was needed.
39 LogicalResult resolveHierPath(Location loc, FModuleOp owningModule,
40 const AnnoPathValue &target,
41 FlatSymbolRefAttr &result) {
42
43 // If the path is empty then this is a local reference and we should not
44 // construct a HierPathOp.
45 if (target.instances.empty())
46 return success();
47
48 // We want to root this path at the top level module, or in the case of an
49 // unreachable module, we settle for as high as we can get.
50 auto module = target.ref.getModule();
51 if (!target.instances.empty())
52 module = target.instances.front()->getParentOfType<FModuleLike>();
53 auto *node = instanceGraph[module];
54 while (true) {
55 // If the path is rooted at the owning module, we're done.
56 if (node->getModule() == owningModule)
57 break;
58 // If there are no more parents, then the path op lives in a different
59 // hierarchy than the HW object it references, which is an error.
60 if (node->noUses())
61 return emitError(loc)
62 << "unable to resolve path relative to owning module "
63 << owningModule.getModuleNameAttr();
64 // If there is more than one instance of this module, then the path
65 // operation is ambiguous, which is an error.
66 if (!node->hasOneUse()) {
67 auto diag = emitError(loc) << "unable to uniquely resolve target due "
68 "to multiple instantiation";
69 for (auto *use : node->uses())
70 diag.attachNote(use->getInstance().getLoc()) << "instance here";
71 return diag;
72 }
73 node = (*node->usesBegin())->getParent();
74 }
75
76 // Transform the instances into a list of FlatSymbolRefs.
77 SmallVector<Attribute> insts;
78 insts.reserve(target.instances.size());
79 std::transform(target.instances.begin(), target.instances.end(),
80 std::back_inserter(insts), [&](InstanceOp instance) {
81 return OpAnnoTarget(instance).getNLAReference(
82 namespaces[instance->getParentOfType<FModuleLike>()]);
83 });
84
85 // Push a reference to the current module.
86 insts.push_back(
87 FlatSymbolRefAttr::get(target.ref.getModule().getModuleNameAttr()));
88
89 // Return the hierchical path.
90 auto instAttr = ArrayAttr::get(circuit.getContext(), insts);
91
92 result = hierPathCache.getRefFor(instAttr);
93
94 return success();
95 }
96
97 LogicalResult resolve(OwningModuleCache &cache, UnresolvedPathOp unresolved) {
98 auto loc = unresolved.getLoc();
99 ImplicitLocOpBuilder b(loc, unresolved);
100 auto *context = b.getContext();
101
102 /// Spelling takes the form
103 /// "OMReferenceTarget:~Circuit|Foo/bar:Bar>member".
104 auto target = unresolved.getTarget();
105
106 // OMDeleted nodes do not have a target, so it is impossible to resolve
107 // them to a real path. We create a special constant for these path
108 // values.
109 if (target.consume_front("OMDeleted:")) {
110 if (!target.empty())
111 return emitError(loc, "OMDeleted references can not have targets");
112 // Deleted targets are turned into OMReference targets with a dangling
113 // id
114 // - i.e. the id is not attached to any target.
115 auto targetKind = TargetKindAttr::get(context, TargetKind::Reference);
116 auto id = DistinctAttr::create(UnitAttr::get(context));
117 auto resolved = b.create<PathOp>(targetKind, id);
118 unresolved->replaceAllUsesWith(resolved);
119 unresolved.erase();
120 return success();
121 }
122
123 // Parse the OM target kind.
124 TargetKind targetKind;
125 if (target.consume_front("OMDontTouchedReferenceTarget")) {
126 targetKind = TargetKind::DontTouch;
127 } else if (target.consume_front("OMInstanceTarget")) {
128 targetKind = TargetKind::Instance;
129 } else if (target.consume_front("OMMemberInstanceTarget")) {
130 targetKind = TargetKind::MemberInstance;
131 } else if (target.consume_front("OMMemberReferenceTarget")) {
132 targetKind = TargetKind::MemberReference;
133 } else if (target.consume_front("OMReferenceTarget")) {
134 targetKind = TargetKind::Reference;
135 } else {
136 return emitError(loc)
137 << "unknown or missing OM reference type in target string: \""
138 << target << "\"";
139 }
140 auto targetKindAttr = TargetKindAttr::get(context, targetKind);
141
142 // Parse the target.
143 if (!target.consume_front(":"))
144 return emitError(loc, "expected ':' in target string");
145
146 auto token = tokenizePath(target);
147 if (!token)
148 return emitError(loc)
149 << "cannot tokenize annotation path \"" << target << "\"";
150
151 // Resolve the target to a target.
152 auto path = resolveEntities(*token, circuit, symbolTable, targetCache);
153 if (!path)
154 return failure();
155
156 // Make sure that we are targeting a leaf of the operation. That way lower
157 // types can't split a single reference into many, and cause ambiguity. If
158 // we are targeting a module, the type will be null.
159 if (Type targetType = path->ref.getType()) {
160 auto fieldId = path->fieldIdx;
161 auto baseType = type_dyn_cast<FIRRTLBaseType>(targetType);
162 if (!baseType)
163 return emitError(loc, "unable to target non-hardware type ")
164 << targetType;
165 targetType = hw::FieldIdImpl::getFinalTypeByFieldID(baseType, fieldId);
166 if (type_isa<BundleType, FVectorType>(targetType))
167 return emitError(loc, "unable to target aggregate type ") << targetType;
168 }
169
170 auto owningModule = cache.lookup(unresolved);
171 StringRef moduleName = "nullptr";
172 if (owningModule)
173 moduleName = owningModule.getModuleName();
174 if (!owningModule)
175 return unresolved->emitError("path does not have a single owning module");
176
177 // Resolve a path to the operation in question.
178 FlatSymbolRefAttr hierPathName;
179 if (failed(resolveHierPath(loc, owningModule, *path, hierPathName)))
180 return failure();
181
182 auto createAnnotation = [&](FlatSymbolRefAttr hierPathName) {
183 // Create a unique ID.
184 auto id = DistinctAttr::create(UnitAttr::get(context));
185
186 // Create the annotation.
187 NamedAttrList fields;
188 fields.append("id", id);
189 fields.append("class", StringAttr::get(context, "circt.tracker"));
190 if (hierPathName)
191 fields.append("circt.nonlocal", hierPathName);
192 if (path->fieldIdx != 0)
193 fields.append("circt.fieldID", b.getI64IntegerAttr(path->fieldIdx));
194
195 return DictionaryAttr::get(context, fields);
196 };
197
198 // Create the annotation(s).
199 Attribute annotation = createAnnotation(hierPathName);
200
201 // Attach the annotation(s) to the target.
202 auto annoTarget = path->ref;
203 auto targetAnnotations = annoTarget.getAnnotations();
204 targetAnnotations.addAnnotations({annotation});
205 if (targetKindAttr.getValue() == TargetKind::DontTouch)
206 targetAnnotations.addDontTouch();
207 annoTarget.setAnnotations(targetAnnotations);
208
209 // Create a PathOp using the id in the annotation we added to the target.
210 auto dictAttr = cast<DictionaryAttr>(annotation);
211 auto id = cast<DistinctAttr>(dictAttr.get("id"));
212 auto resolved = b.create<PathOp>(targetKindAttr, id);
213
214 // Replace the unresolved path with the PathOp.
215 unresolved->replaceAllUsesWith(resolved);
216 unresolved.erase();
217
218 return success();
219 }
220
221 CircuitOp circuit;
222 SymbolTable symbolTable;
223 CircuitTargetCache targetCache;
224 InstanceGraph &instanceGraph;
225 InstancePathCache instancePathCache;
227 HierPathCache hierPathCache;
228 OpBuilder builder;
229};
230} // end anonymous namespace
231
232//===----------------------------------------------------------------------===//
233// Pass Infrastructure
234//===----------------------------------------------------------------------===//
235
236namespace {
237struct ResolvePathsPass
238 : public circt::firrtl::impl::ResolvePathsBase<ResolvePathsPass> {
239 void runOnOperation() override;
240};
241} // end anonymous namespace
242
243void ResolvePathsPass::runOnOperation() {
244 auto circuit = getOperation();
245 auto &instanceGraph = getAnalysis<InstanceGraph>();
246 PathResolver resolver(circuit, instanceGraph);
247 OwningModuleCache cache(instanceGraph);
248 auto result = circuit.walk([&](UnresolvedPathOp unresolved) {
249 if (failed(resolver.resolve(cache, unresolved))) {
250 signalPassFailure();
251 return WalkResult::interrupt();
252 }
253 return WalkResult::advance();
254 });
255 if (result.wasInterrupted())
256 signalPassFailure();
257 markAnalysesPreserved<InstanceGraph>();
258}
259
260std::unique_ptr<mlir::Pass> circt::firrtl::createResolvePathsPass() {
261 return std::make_unique<ResolvePathsPass>();
262}
This graph tracks modules and where they are instantiated.
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.