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