CIRCT 24.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.getModuleNameAttr()),
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.getModuleName() << "'";
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")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
return success()
NamedAttrList attrs
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