CIRCT  19.0.0git
AppID.cpp
Go to the documentation of this file.
1 //===- AppID.cpp - AppID related code -------------------------------------===//
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 
11 
13 
14 using namespace circt;
15 using namespace esi;
16 
17 AppIDAttr circt::esi::getAppID(Operation *op) {
18  if (auto appidOp = dyn_cast<esi::HasAppID>(op))
19  return appidOp.getAppID();
20  if (auto appid = op->getAttrOfType<AppIDAttr>(AppIDAttr::AppIDAttrName))
21  return appid;
22  return AppIDAttr();
23 }
24 
25 /// Helper class constructed on a per-HWModuleLike basis. Contains a map for
26 /// fast lookups to the operation involved in an appid component.
28 public:
29  /// Add an appid component to the index. 'Inherited' is true if we're
30  /// bubbling up from an instance and is used to inform the conflicting entry
31  /// error message.
32  LogicalResult add(AppIDAttr id, Operation *op, bool inherited) {
33  if (childAppIDPaths.find(id) != childAppIDPaths.end()) {
34  return op->emitOpError("Found multiple identical AppIDs in same module")
35  .attachNote(childAppIDPaths[id]->getLoc())
36  << "first AppID located here."
37  << (inherited ? " Must insert appid to differentiate one instance "
38  "branch from the other."
39  : "");
40  }
41  childAppIDPaths[id] = op;
42  childAppIDPathsOrdered.emplace_back(id, op);
43  return success();
44  }
45 
46  FailureOr<Operation *> lookup(AppIDAttr id, Location loc) const {
47  auto f = childAppIDPaths.find(id);
48  if (f == childAppIDPaths.end())
49  return emitError(loc, "could not find appid '") << id << "'";
50  return f->second;
51  }
52 
53  // Returns a range iterator to the AppID components exposed by this module.
54  auto getAppIDs() const {
55  return llvm::make_first_range(childAppIDPathsOrdered);
56  }
57 
58  // Get a read-only reference to the index.
59  ArrayRef<std::pair<AppIDAttr, Operation *>> getChildren() const {
60  return childAppIDPathsOrdered;
61  }
62 
63 private:
64  // Operations involved in appids.
65  DenseMap<AppIDAttr, Operation *> childAppIDPaths;
66  // For every entry in childAppIDPaths, we need it in the original order. Keep
67  // that order here.
68  SmallVector<std::pair<AppIDAttr, Operation *>, 8> childAppIDPathsOrdered;
69 };
70 
71 AppIDIndex::AppIDIndex(Operation *mlirTop) : valid(true), mlirTop(mlirTop) {
72  Block &topBlock = mlirTop->getRegion(0).front();
73  symCache.addDefinitions(mlirTop);
74  symCache.freeze();
75 
76  // Build the per-module cache.
77  for (auto mod : topBlock.getOps<hw::HWModuleLike>())
78  if (failed(buildIndexFor(mod))) {
79  valid = false;
80  break;
81  }
82 }
83 AppIDIndex::~AppIDIndex() {
84  for (auto [appId, childAppIDs] : containerAppIDs)
85  delete childAppIDs;
86 }
87 
88 ArrayAttr AppIDIndex::getChildAppIDsOf(hw::HWModuleLike fromMod) const {
89  auto f = containerAppIDs.find(fromMod);
90  if (f == containerAppIDs.end())
91  return ArrayAttr::get(fromMod.getContext(), {});
92 
93  const ModuleAppIDs *fromModIdx = f->getSecond();
94  SmallVector<Attribute, 8> attrs(llvm::map_range(
95  fromModIdx->getAppIDs(), [](AppIDAttr a) -> Attribute { return a; }));
96  return ArrayAttr::get(fromMod.getContext(), attrs);
97 }
98 
99 // NOLINTNEXTLINE(misc-no-recursion)
100 LogicalResult AppIDIndex::walk(
101  hw::HWModuleLike top, hw::HWModuleLike current,
102  SmallVectorImpl<AppIDAttr> &pathStack,
103  SmallVectorImpl<Operation *> &opStack,
104  function_ref<void(AppIDPathAttr, ArrayRef<Operation *>)> fn) const {
105  ModuleAppIDs *modIDs = containerAppIDs.lookup(current);
106  if (!modIDs)
107  return success();
108 
109  for (auto [appid, op] : modIDs->getChildren()) {
110  // If we encounter an instance op which isn't ID'd, iterate down the
111  // instance hierarchy until we find it.
112  AppIDAttr opAppID = getAppID(op);
113  while (!opAppID) {
114  // We make a bunch of assumptions based on correct construction of the
115  // index here. Assert on a bunch of things which would ordinarily be
116  // failures, but we can assume never to happen based on the index
117  // construction.
118 
119  auto inst = dyn_cast<hw::HWInstanceLike>(op);
120  assert(inst && "Search bottomed out. Invalid appid index.");
121 
122  auto moduleNames = inst.getReferencedModuleNamesAttr();
123  if (moduleNames.size() != 1)
124  return inst.emitError("expected an instance with a single reference");
125 
126  auto tgtMod =
127  dyn_cast<hw::HWModuleLike>(symCache.getDefinition(moduleNames[0]));
128  assert(tgtMod && "invalid module reference");
129 
130  ModuleAppIDs *ffModIds = containerAppIDs.at(tgtMod);
131  assert(ffModIds && "could not find module in index.");
132 
133  auto opF = ffModIds->lookup(appid, op->getLoc());
134  assert(succeeded(opF) &&
135  "could not find appid in module index. Invalid index.");
136 
137  // Set the iteration variables for the next iteration.
138  op = *opF;
139  opAppID = getAppID(op);
140  }
141 
142  // Push the appid and op onto the shared stacks.
143  opStack.push_back(op);
144  pathStack.push_back(appid);
145 
146  // Call the callback.
147  AppIDPathAttr path = AppIDPathAttr::get(
148  current.getContext(), FlatSymbolRefAttr::get(top.getNameAttr()),
149  pathStack);
150  fn(path, opStack);
151 
152  // We must recurse on an instance.
153  if (auto inst = dyn_cast<hw::HWInstanceLike>(op)) {
154  auto moduleNames = inst.getReferencedModuleNamesAttr();
155  if (moduleNames.size() != 1)
156  return inst.emitError("expected an instance with a single reference");
157 
158  auto tgtMod =
159  dyn_cast<hw::HWModuleLike>(symCache.getDefinition(moduleNames[0]));
160  assert(tgtMod && "invalid module reference");
161 
162  if (failed(walk(top, tgtMod, pathStack, opStack, fn)))
163  return failure();
164  }
165 
166  // Since the stacks are shared (for efficiency reasons), pop them.
167  pathStack.pop_back();
168  opStack.pop_back();
169  }
170  return success();
171 }
172 
173 LogicalResult AppIDIndex::walk(
174  hw::HWModuleLike top,
175  function_ref<void(AppIDPathAttr, ArrayRef<Operation *>)> fn) const {
176  SmallVector<AppIDAttr, 8> path;
177  SmallVector<Operation *, 8> opStack;
178  return walk(top, top, path, opStack, fn);
179 }
180 LogicalResult AppIDIndex::walk(
181  StringRef top,
182  function_ref<void(AppIDPathAttr, ArrayRef<Operation *>)> fn) const {
183  Operation *op = symCache.getDefinition(
184  FlatSymbolRefAttr::get(mlirTop->getContext(), top));
185  if (auto topMod = dyn_cast_or_null<hw::HWModuleLike>(op))
186  return walk(topMod, fn);
187  return mlirTop->emitOpError("Could not find module '") << top << "'";
188 }
189 
190 FailureOr<ArrayAttr> AppIDIndex::getAppIDPathAttr(hw::HWModuleLike fromMod,
191  AppIDAttr appid,
192  Location loc) const {
193  SmallVector<Attribute, 8> path;
194  do {
195  auto f = containerAppIDs.find(fromMod);
196  if (f == containerAppIDs.end())
197  return emitError(loc, "Could not find appid index for module '")
198  << fromMod.getName() << "'";
199 
200  const ModuleAppIDs *modIDs = f->getSecond();
201  FailureOr<hw::InnerSymbolOpInterface> op = modIDs->lookup(appid, loc);
202  if (failed(op))
203  return failure();
204  path.push_back(op->getInnerRef());
205 
206  if (getAppID(*op))
207  break;
208 
209  if (auto inst = dyn_cast<hw::HWInstanceLike>(op->getOperation())) {
210  auto moduleNames = inst.getReferencedModuleNamesAttr();
211  if (moduleNames.size() != 1)
212  return inst.emitError("expected an instance with a single reference");
213  fromMod = cast<hw::HWModuleLike>(symCache.getDefinition(moduleNames[0]));
214  } else {
215  assert(false && "Search bottomed out");
216  }
217  } while (true);
218  return ArrayAttr::get(fromMod.getContext(), path);
219 }
220 
221 /// Do a DFS of the instance hierarchy, 'bubbling up' appids.
223 AppIDIndex::buildIndexFor(hw::HWModuleLike mod) {
224  // Memoize.
225  ModuleAppIDs *&appIDs = containerAppIDs[mod];
226  if (appIDs != nullptr)
227  return appIDs;
228  appIDs = new ModuleAppIDs();
229 
230  auto done = mod.walk([&](Operation *op) {
231  // If an op has an appid attribute, add it to the index and terminate the
232  // DFS (since AppIDs only get 'bubbled up' until they encounter an ID'd
233  // instantiation).
234  if (AppIDAttr appid = getAppID(op)) {
235  if (failed(appIDs->add(appid, op, false)))
236  return WalkResult::interrupt();
237  return WalkResult::advance();
238  }
239 
240  // If we encounter an instance op which isn't ID'd...
241  if (auto inst = dyn_cast<hw::HWInstanceLike>(op)) {
242  auto moduleNames = inst.getReferencedModuleNamesAttr();
243  if (moduleNames.size() != 1) {
244  inst.emitError("expected an instance with a single reference");
245  return WalkResult::interrupt();
246  }
247  auto tgtMod =
248  dyn_cast<hw::HWModuleLike>(symCache.getDefinition(moduleNames[0]));
249  // Do the assert here to get a more precise message.
250  assert(tgtMod && "invalid module reference");
251 
252  // Recurse.
253  FailureOr<const ModuleAppIDs *> childIds = buildIndexFor(tgtMod);
254  if (failed(childIds))
255  return WalkResult::interrupt();
256 
257  // Then add the 'bubbled up' appids to the cache.
258  for (AppIDAttr appid : (*childIds)->getAppIDs())
259  if (failed(appIDs->add(appid, op, true)))
260  return WalkResult::interrupt();
261  }
262  return WalkResult::advance();
263  });
264 
265  if (done.wasInterrupted())
266  return failure();
267  return appIDs;
268 }
assert(baseType &&"element must be base type")
Helper class constructed on a per-HWModuleLike basis.
Definition: AppID.cpp:27
SmallVector< std::pair< AppIDAttr, Operation * >, 8 > childAppIDPathsOrdered
Definition: AppID.cpp:68
LogicalResult add(AppIDAttr id, Operation *op, bool inherited)
Add an appid component to the index.
Definition: AppID.cpp:32
FailureOr< Operation * > lookup(AppIDAttr id, Location loc) const
Definition: AppID.cpp:46
auto getAppIDs() const
Definition: AppID.cpp:54
DenseMap< AppIDAttr, Operation * > childAppIDPaths
Definition: AppID.cpp:65
ArrayRef< std::pair< AppIDAttr, Operation * > > getChildren() const
Definition: AppID.cpp:59
AppIDIndex(Operation *mlirTop)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
AppIDAttr getAppID(Operation *op)
Get the AppID of a particular operation.
Definition: AppID.cpp:17
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: esi.py:1