16 #include "mlir/IR/ImplicitLocOpBuilder.h"
18 using namespace circt;
19 using namespace firrtl;
24 : circuit(circuit), symbolTable(circuit), instanceGraph(instanceGraph),
25 instancePathCache(instanceGraph), hierPathCache(circuit, symbolTable),
26 builder(OpBuilder::atBlockBegin(circuit->getBlock())) {}
31 LogicalResult resolveHierPath(Location loc, FModuleOp owningModule,
34 SmallVectorImpl<FlatSymbolRefAttr> &results) {
40 module = target.
instances.front()->getParentOfType<FModuleLike>();
41 auto *node = instanceGraph[module];
42 bool needsDisambiguation =
false;
45 if (node->getModule() == owningModule)
51 <<
"unable to resolve path relative to owning module "
52 << owningModule.getModuleNameAttr();
55 if (!node->hasOneUse()) {
58 if (canDisambiguate) {
59 needsDisambiguation =
true;
63 auto diag = emitError(loc) <<
"unable to uniquely resolve target due "
64 "to multiple instantiation";
65 for (
auto *use : node->uses())
66 diag.attachNote(use->getInstance().getLoc()) <<
"instance here";
69 node = (*node->usesBegin())->getParent();
75 auto *it = llvm::find_if(target.
instances, [&](InstanceOp instance) {
76 auto *node = instanceGraph.lookup(instance.getReferencedModuleNameAttr());
77 return !node->hasOneUse();
82 auto pathLength = std::distance(it, target.
instances.end());
83 if (pathLength == 0 && !needsDisambiguation) {
88 SmallVector<Attribute> insts;
89 insts.reserve(pathLength);
90 std::transform(it, target.
instances.end(), std::back_inserter(insts),
91 [&](InstanceOp instance) {
92 return OpAnnoTarget(instance).getNLAReference(
93 namespaces[instance->getParentOfType<FModuleLike>()]);
102 if (needsDisambiguation) {
103 ArrayRef<igraph::InstancePath> instancePaths =
104 instancePathCache.getAbsolutePaths(node->getModule(),
105 instanceGraph[owningModule]);
106 for (
auto instancePath : instancePaths) {
108 SmallVector<Attribute> fullInsts;
109 fullInsts.reserve(instancePath.size() + target.
instances.size() + 1);
111 instancePath.begin(), instancePath.end(),
112 std::back_inserter(fullInsts),
113 [&](igraph::InstanceOpInterface inst) {
114 return OpAnnoTarget(cast<InstanceOp>(inst))
116 namespaces[inst->getParentOfType<FModuleLike>()]);
124 namespaces[inst->getParentOfType<FModuleOp>()]));
128 fullInsts.append(insts);
132 results.push_back(hierPathCache.getRefFor(instAttr));
137 results.push_back(hierPathCache.getRefFor(instAttr));
144 auto loc = unresolved.getLoc();
145 ImplicitLocOpBuilder b(loc, unresolved);
146 auto *context = b.getContext();
150 auto target = unresolved.getTarget();
155 if (target.consume_front(
"OMDeleted:")) {
157 return emitError(loc,
"OMDeleted references can not have targets");
163 auto resolved = b.create<PathOp>(targetKind, id);
164 unresolved->replaceAllUsesWith(resolved);
170 TargetKind targetKind;
171 if (target.consume_front(
"OMDontTouchedReferenceTarget")) {
172 targetKind = TargetKind::DontTouch;
173 }
else if (target.consume_front(
"OMInstanceTarget")) {
174 targetKind = TargetKind::Instance;
175 }
else if (target.consume_front(
"OMMemberInstanceTarget")) {
176 targetKind = TargetKind::MemberInstance;
177 }
else if (target.consume_front(
"OMMemberReferenceTarget")) {
178 targetKind = TargetKind::MemberReference;
179 }
else if (target.consume_front(
"OMReferenceTarget")) {
180 targetKind = TargetKind::Reference;
182 return emitError(loc)
183 <<
"unknown or missing OM reference type in target string: \""
189 if (!target.consume_front(
":"))
190 return emitError(loc,
"expected ':' in target string");
194 return emitError(loc)
195 <<
"cannot tokenize annotation path \"" << target <<
"\"";
198 auto path =
resolveEntities(*token, circuit, symbolTable, targetCache);
205 if (Type targetType = path->ref.getType()) {
206 auto fieldId = path->fieldIdx;
207 auto baseType = dyn_cast<FIRRTLBaseType>(targetType);
209 return emitError(loc,
"unable to target non-hardware type ")
212 if (isa<BundleType, FVectorType>(targetType))
213 return emitError(loc,
"unable to target aggregate type ") << targetType;
216 auto owningModule = cache.
lookup(unresolved);
217 StringRef moduleName =
"nullptr";
219 moduleName = owningModule.getModuleName();
221 return unresolved->emitError(
"path does not have a single owning module");
226 bool canDisambiguate =
227 !unresolved->use_empty() &&
228 llvm::all_of(unresolved->getUsers(),
229 [](Operation *user) { return isa<ListCreateOp>(user); });
233 SmallVector<FlatSymbolRefAttr> hierPathNames;
234 if (failed(resolveHierPath(loc, owningModule, *path, canDisambiguate,
238 auto createAnnotation = [&](std::optional<FlatSymbolRefAttr> hierPathName) {
243 NamedAttrList fields;
244 fields.append(
"id",
id);
247 fields.append(
"circt.nonlocal", hierPathName.value());
248 if (path->fieldIdx != 0)
249 fields.append(
"circt.fieldID", b.getI64IntegerAttr(path->fieldIdx));
255 SmallVector<Attribute> annotations;
256 if (hierPathNames.empty()) {
257 annotations.push_back(createAnnotation(std::nullopt));
259 for (
auto hierPathName : hierPathNames)
260 annotations.push_back(createAnnotation(hierPathName));
264 auto annoTarget = path->ref;
265 auto targetAnnotations = annoTarget.getAnnotations();
266 targetAnnotations.addAnnotations(annotations);
267 if (targetKindAttr.getValue() == TargetKind::DontTouch)
268 targetAnnotations.addDontTouch();
269 annoTarget.setAnnotations(targetAnnotations);
272 size_t lastAnnotationIdx = annotations.size() - 1;
273 for (
auto [i, annotation] : llvm::enumerate(annotations)) {
275 auto dictAttr = cast<DictionaryAttr>(annotation);
276 auto id = cast<DistinctAttr>(dictAttr.get(
"id"));
277 auto resolved = b.create<PathOp>(targetKindAttr, id);
279 if (!canDisambiguate || i == lastAnnotationIdx) {
285 unresolved->replaceAllUsesWith(resolved);
290 for (
auto &use : unresolved->getUses()) {
291 assert(isa<ListCreateOp>(use.getOwner()));
292 use.getOwner()->insertOperands(use.getOperandNumber(),
293 resolved.getResult());
301 SymbolTable symbolTable;
305 hw::InnerSymbolNamespaceCollection namespaces;
316 struct ResolvePathsPass :
public ResolvePathsBase<ResolvePathsPass> {
317 void runOnOperation()
override;
321 void ResolvePathsPass::runOnOperation() {
322 auto circuit = getOperation();
323 auto &instanceGraph = getAnalysis<InstanceGraph>();
324 PathResolver resolver(circuit, instanceGraph);
326 auto result = circuit.walk([&](UnresolvedPathOp unresolved) {
327 if (failed(resolver.resolve(cache, unresolved))) {
329 return WalkResult::interrupt();
331 return WalkResult::advance();
333 if (result.wasInterrupted())
335 markAnalysesPreserved<InstanceGraph>();
339 return std::make_unique<ResolvePathsPass>();
assert(baseType &&"element must be base type")
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 represents an annotation targeting a specific operation.
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.