CIRCT  18.0.0git
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 
13 #include "PassDetails.h"
15 #include "mlir/IR/ImplicitLocOpBuilder.h"
16 
17 using namespace circt;
18 using namespace firrtl;
19 
20 namespace {
21 struct PathResolver {
22  PathResolver(CircuitOp circuit, InstanceGraph &instanceGraph)
23  : circuit(circuit), symbolTable(circuit), instanceGraph(instanceGraph),
24  hierPathCache(circuit, symbolTable),
25  builder(OpBuilder::atBlockBegin(circuit->getBlock())) {}
26 
27  /// This function will find the operation targeted and create a hierarchical
28  /// path operation if needed. If the target is resolved, the op will either
29  /// be a reference to the HierPathOp, or null if no HierPathOp was needed.
30  LogicalResult resolveHierPath(Location loc, const AnnoPathValue &target,
31  FlatSymbolRefAttr &op) {
32 
33  // We want to root this path at the top level module, or in the case of an
34  // unreachable module, we settle for as high as we can get.
35  auto module = target.ref.getModule();
36  if (!target.instances.empty())
37  module = target.instances.front()->getParentOfType<FModuleLike>();
38  auto *node = instanceGraph[module];
39  while (!node->noUses()) {
40  if (!node->hasOneUse()) {
41  auto diag = emitError(loc) << "unable to uniquely resolve target due "
42  "to multiple instantiation";
43  for (auto *use : node->uses())
44  diag.attachNote(use->getInstance().getLoc()) << "instance here";
45  return diag;
46  }
47  node = (*node->usesBegin())->getParent();
48  }
49 
50  // Find the minimal uniquely-identifying path to the operation. We scan
51  // through the list of instances looking for the first module which is
52  // multiply instantiated. We will start our HierPathOp at this instance.
53  auto *it = llvm::find_if(target.instances, [&](InstanceOp instance) {
54  auto *node = instanceGraph[instanceGraph.getReferencedModule(instance)];
55  return !node->hasOneUse();
56  });
57 
58  // If the path is empty, then this is a local reference and we should not
59  // construct a HierPathOp.
60  auto pathLength = std::distance(it, target.instances.end());
61  if (pathLength == 0) {
62  op = nullptr;
63  return success();
64  }
65 
66  // Transform the instances into a list of FlatSymbolRefs.
67  SmallVector<Attribute> insts;
68  insts.reserve(pathLength);
69  std::transform(it, target.instances.end(), std::back_inserter(insts),
70  [&](InstanceOp instance) {
71  return OpAnnoTarget(instance).getNLAReference(
72  namespaces[instance->getParentOfType<FModuleLike>()]);
73  });
74 
75  // Push a reference to the current module.
76  insts.push_back(
77  FlatSymbolRefAttr::get(target.ref.getModule().getModuleNameAttr()));
78  auto instAttr = ArrayAttr::get(circuit.getContext(), insts);
79 
80  // Return the hierchical path.
81  op = hierPathCache.getRefFor(instAttr);
82  return success();
83  }
84 
85  LogicalResult resolve(UnresolvedPathOp unresolved) {
86  auto loc = unresolved.getLoc();
87  ImplicitLocOpBuilder b(loc, unresolved);
88  auto *context = b.getContext();
89 
90  /// Spelling takes the form "OMReferenceTarget:~Circuit|Foo/bar:Bar>member".
91  auto target = unresolved.getTarget();
92 
93  // OMDeleted nodes do not have a target, so it is impossible to resolve them
94  // to a real path. We create a special constant for these path values.
95  if (target.consume_front("OMDeleted")) {
96  if (!target.empty())
97  return emitError(loc, "OMDeleted references can not have targets");
98  // Deleted targets are turned into OMReference targets with a dangling id
99  // - i.e. the id is not attached to any target.
100  auto targetKind = TargetKindAttr::get(context, TargetKind::Reference);
101  auto id = DistinctAttr::create(UnitAttr::get(context));
102  auto resolved = b.create<PathOp>(targetKind, id);
103  unresolved->replaceAllUsesWith(resolved);
104  unresolved.erase();
105  return success();
106  }
107 
108  // Parse the OM target kind.
109  TargetKind targetKind;
110  if (target.consume_front("OMDontTouchedReferenceTarget")) {
111  targetKind = TargetKind::DontTouch;
112  } else if (target.consume_front("OMInstanceTarget")) {
113  targetKind = TargetKind::Instance;
114  } else if (target.consume_front("OMMemberInstanceTarget")) {
115  targetKind = TargetKind::MemberInstance;
116  } else if (target.consume_front("OMMemberReferenceTarget")) {
117  targetKind = TargetKind::MemberReference;
118  } else if (target.consume_front("OMReferenceTarget")) {
119  targetKind = TargetKind::Reference;
120  } else {
121  return emitError(loc)
122  << "unknown or missing OM reference type in target string: \""
123  << target << "\"";
124  }
125  auto targetKindAttr = TargetKindAttr::get(context, targetKind);
126 
127  // Parse the target.
128  if (!target.consume_front(":"))
129  return emitError(loc, "expected ':' in target string");
130 
131  auto token = tokenizePath(target);
132  if (!token)
133  return emitError(loc)
134  << "cannot tokenize annotation path \"" << target << "\"";
135 
136  // Resolve the target to a target.
137  auto path = resolveEntities(*token, circuit, symbolTable, targetCache);
138  if (!path)
139  return failure();
140 
141  // Make sure that we are targeting a leaf of the operation. That way lower
142  // types can't split a single reference into many, and cause ambiguity. If
143  // we are targeting a module, the type will be null.
144  if (Type targetType = path->ref.getType()) {
145  auto fieldId = path->fieldIdx;
146  auto baseType = dyn_cast<FIRRTLBaseType>(targetType);
147  if (!baseType)
148  return emitError(loc, "unable to target non-hardware type ")
149  << targetType;
150  targetType = hw::FieldIdImpl::getFinalTypeByFieldID(baseType, fieldId);
151  if (isa<BundleType, FVectorType>(targetType))
152  return emitError(loc, "unable to target aggregate type ") << targetType;
153  }
154 
155  // Create a unique ID.
156  auto id = DistinctAttr::create(UnitAttr::get(context));
157 
158  // Resolve a unique path to the operation in question.
159  FlatSymbolRefAttr hierPathName;
160  if (failed(resolveHierPath(loc, *path, hierPathName)))
161  return failure();
162 
163  // Create the annotation.
164  NamedAttrList fields;
165  fields.append("id", id);
166  fields.append("class", StringAttr::get(context, "circt.tracker"));
167  if (hierPathName)
168  fields.append("circt.nonlocal", hierPathName);
169  if (path->fieldIdx != 0)
170  fields.append("circt.fieldID", b.getI64IntegerAttr(path->fieldIdx));
171  auto annotation = DictionaryAttr::get(context, fields);
172 
173  // Attach the annotation to the target.
174  auto annoTarget = path->ref;
175  auto annotations = annoTarget.getAnnotations();
176  annotations.addAnnotations(annotation);
177  if (targetKindAttr.getValue() == TargetKind::DontTouch)
178  annotations.addDontTouch();
179  annoTarget.setAnnotations(annotations);
180 
181  // Create the path operation.
182  auto resolved = b.create<PathOp>(targetKindAttr, id);
183  unresolved->replaceAllUsesWith(resolved);
184  unresolved.erase();
185  return success();
186  }
187 
188  CircuitOp circuit;
189  SymbolTable symbolTable;
190  CircuitTargetCache targetCache;
191  InstanceGraph &instanceGraph;
192  hw::InnerSymbolNamespaceCollection namespaces;
193  HierPathCache hierPathCache;
194  OpBuilder builder;
195 };
196 } // end anonymous namespace
197 
198 //===----------------------------------------------------------------------===//
199 // Pass Infrastructure
200 //===----------------------------------------------------------------------===//
201 
202 namespace {
203 struct ResolvePathsPass : public ResolvePathsBase<ResolvePathsPass> {
204  void runOnOperation() override;
205 };
206 } // end anonymous namespace
207 
208 void ResolvePathsPass::runOnOperation() {
209  auto circuit = getOperation();
210  auto &instanceGraph = getAnalysis<InstanceGraph>();
211  PathResolver resolver(circuit, instanceGraph);
212  auto result = circuit.walk([&](UnresolvedPathOp unresolved) {
213  if (failed(resolver.resolve(unresolved))) {
214  signalPassFailure();
215  return WalkResult::interrupt();
216  }
217  return WalkResult::advance();
218  });
219  if (result.wasInterrupted())
220  signalPassFailure();
221  markAnalysesPreserved<InstanceGraph>();
222 }
223 
224 std::unique_ptr<mlir::Pass> circt::firrtl::createResolvePathsPass() {
225  return std::make_unique<ResolvePathsPass>();
226 }
Builder builder
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.
Definition: CalyxOps.cpp:53
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)
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21
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.