CIRCT 20.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 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 {
61 }
62
63private:
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
71AppIDIndex::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}
83AppIDIndex::~AppIDIndex() {
84 for (auto [appId, childAppIDs] : containerAppIDs)
85 delete childAppIDs;
86}
87
88ArrayAttr 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)
100LogicalResult 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
173LogicalResult 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}
180LogicalResult 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
190FailureOr<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.
222FailureOr<const AppIDIndex::ModuleAppIDs *>
223AppIDIndex::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
FailureOr< Operation * > lookup(AppIDAttr id, Location loc) const
Definition AppID.cpp:46
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
ArrayRef< std::pair< AppIDAttr, Operation * > > getChildren() const
Definition AppID.cpp:59
auto getAppIDs() const
Definition AppID.cpp:54
DenseMap< AppIDAttr, Operation * > childAppIDPaths
Definition AppID.cpp:65
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