CIRCT 24.0.0git
Loading...
Searching...
No Matches
InnerSymbolTable.cpp
Go to the documentation of this file.
1//===- InnerSymbolTable.cpp - InnerSymbolTable and InnerRef verification --===//
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 implements InnerSymbolTable and verification for InnerRef's.
10//
11//===----------------------------------------------------------------------===//
12
15#include "mlir/IR/Threading.h"
16#include "llvm/Support/Debug.h"
17
18using namespace circt;
19using namespace hw;
20
21namespace circt {
22namespace hw {
23
24//===----------------------------------------------------------------------===//
25// InnerSymbolTable
26//===----------------------------------------------------------------------===//
28 assert(op->hasTrait<OpTrait::InnerSymbolTable>());
29 // Save the operation this table is for.
30 this->innerSymTblOp = op;
31
32 walkSymbols(op, [&](StringAttr name, const InnerSymTarget &target) {
33 auto it = symbolTable.try_emplace(name, target);
34 (void)it;
35 assert(it.second && "repeated symbol found");
36 });
37}
38
39FailureOr<InnerSymbolTable> InnerSymbolTable::get(Operation *op) {
40 assert(op);
41 if (!op->hasTrait<OpTrait::InnerSymbolTable>())
42 return op->emitError("expected operation to have InnerSymbolTable trait");
43
44 TableTy table;
45 auto result = walkSymbols(
46 op, [&](StringAttr name, const InnerSymTarget &target) -> LogicalResult {
47 auto it = table.try_emplace(name, target);
48 if (it.second)
49 return success();
50 auto existing = it.first->second;
51 return target.getOp()
52 ->emitError()
53 .append("redefinition of inner symbol named '", name.strref(), "'")
54 .attachNote(existing.getOp()->getLoc())
55 .append("see existing inner symbol definition here");
56 });
57 if (failed(result))
58 return failure();
59 return InnerSymbolTable(op, std::move(table));
60}
61
62LogicalResult InnerSymbolTable::walkSymbols(Operation *op,
63 InnerSymCallbackFn callback) {
64 auto walkSym = [&](StringAttr name, const InnerSymTarget &target) {
65 assert(name && !name.getValue().empty());
66 return callback(name, target);
67 };
68
69 auto walkSyms = [&](hw::InnerSymAttr symAttr,
70 const InnerSymTarget &baseTarget) -> LogicalResult {
71 assert(baseTarget.getField() == 0);
72 for (auto symProp : symAttr) {
73 if (failed(walkSym(symProp.getName(),
75 baseTarget, symProp.getFieldID()))))
76 return failure();
77 }
78 return success();
79 };
80
81 // Check for ports
82 if (auto mod = dyn_cast<PortList>(op)) {
83 for (auto [i, port] : llvm::enumerate(mod.getPortList())) {
84 if (auto symAttr = port.getSym())
85 if (failed(walkSyms(symAttr, InnerSymTarget(i, mod))))
86 return failure();
87 }
88 }
89
90 // Walk the operation and add InnerSymbolTarget's to the table.
91 return success(
92 !op->walk<mlir::WalkOrder::PreOrder>([&](Operation *curOp) -> WalkResult {
93 if (auto symOp = dyn_cast<InnerSymbolOpInterface>(curOp))
94 if (auto symAttr = symOp.getInnerSymAttr())
95 if (failed(walkSyms(symAttr, InnerSymTarget(symOp))))
96 return WalkResult::interrupt();
97
98 return WalkResult::advance();
99 }).wasInterrupted());
100}
101
102/// Look up a symbol with the specified name, returning empty InnerSymTarget if
103/// no such name exists. Names never include the @ on them.
105 return lookup(StringAttr::get(innerSymTblOp->getContext(), name));
106}
108 return symbolTable.lookup(name);
109}
110
111/// Look up a symbol with the specified name, returning null if no such
112/// name exists or doesn't target just an operation.
113Operation *InnerSymbolTable::lookupOp(StringRef name) const {
114 return lookupOp(StringAttr::get(innerSymTblOp->getContext(), name));
115}
116Operation *InnerSymbolTable::lookupOp(StringAttr name) const {
117 auto result = lookup(name);
118 if (result.isOpOnly())
119 return result.getOp();
120 return nullptr;
121}
122
123/// Get InnerSymbol for an operation.
124StringAttr InnerSymbolTable::getInnerSymbol(Operation *op) {
125 if (auto innerSymOp = dyn_cast<InnerSymbolOpInterface>(op))
126 return innerSymOp.getInnerNameAttr();
127 return {};
128}
129
130/// Get InnerSymbol for a target. Be robust to queries on unexpected
131/// operations to avoid users needing to know the details.
133 // Assert on misuse, but try to handle queries otherwise.
134 assert(target);
135
136 // Obtain the base InnerSymAttr for the specified target.
137 auto getBase = [](auto &target) -> hw::InnerSymAttr {
138 if (target.isPort()) {
139 if (auto mod = dyn_cast<PortList>(target.getOp())) {
140 assert(target.getPort() < mod.getNumPorts());
141 return mod.getPort(target.getPort()).getSym();
142 }
143 } else {
144 // InnerSymbols only supported if op implements the interface.
145 if (auto symOp = dyn_cast<InnerSymbolOpInterface>(target.getOp()))
146 return symOp.getInnerSymAttr();
147 }
148 return {};
149 };
150
151 if (auto base = getBase(target))
152 return base.getSymIfExists(target.getField());
153 return {};
154}
155
156//===----------------------------------------------------------------------===//
157// InnerSymbolTableCollection
158//===----------------------------------------------------------------------===//
159
162 auto it = symbolTables.try_emplace(op, nullptr);
163 if (it.second)
164 it.first->second = ::std::make_unique<InnerSymbolTable>(op);
165 return *it.first->second;
166}
167
168LogicalResult
170 // Gather top-level operations that have the InnerSymbolTable trait.
171 SmallVector<Operation *> innerSymTableOps(llvm::make_filter_range(
172 llvm::make_pointer_range(innerRefNSOp->getRegion(0).front()),
173 [&](Operation *op) {
174 return op->hasTrait<OpTrait::InnerSymbolTable>();
175 }));
176
177 // Ensure entries exist for each operation.
178 llvm::for_each(innerSymTableOps,
179 [&](auto *op) { symbolTables.try_emplace(op, nullptr); });
180
181 // Construct the tables in parallel (if context allows it).
182 return mlir::failableParallelForEach(
183 innerRefNSOp->getContext(), innerSymTableOps, [&](auto *op) {
184 auto it = symbolTables.find(op);
185 assert(it != symbolTables.end());
186 if (!it->second) {
187 auto result = InnerSymbolTable::get(op);
188 if (failed(result))
189 return failure();
190 it->second = std::make_unique<InnerSymbolTable>(std::move(*result));
191 return success();
192 }
193 return failure();
194 });
195}
196
197//===----------------------------------------------------------------------===//
198// InnerRefNamespace
199//===----------------------------------------------------------------------===//
200
201InnerSymTarget InnerRefNamespace::lookup(hw::InnerRefAttr inner) const {
202 auto *mod = symTable.lookup(inner.getModule());
203 if (!mod)
204 return {};
205 assert(mod->hasTrait<mlir::OpTrait::InnerSymbolTable>());
206 return innerSymTables.getInnerSymbolTable(mod).lookup(inner.getName());
207}
208
209Operation *InnerRefNamespace::lookupOp(hw::InnerRefAttr inner) const {
210 auto *mod = symTable.lookup(inner.getModule());
211 if (!mod)
212 return nullptr;
213 assert(mod->hasTrait<mlir::OpTrait::InnerSymbolTable>());
214 return innerSymTables.getInnerSymbolTable(mod).lookupOp(inner.getName());
215}
216
217//===----------------------------------------------------------------------===//
218// InnerRefNamespace verification
219//===----------------------------------------------------------------------===//
220
221namespace detail {
222
223LogicalResult verifyInnerRefNamespace(Operation *op) {
224 // Construct the symbol tables.
225 InnerSymbolTableCollection innerSymTables;
226 if (failed(innerSymTables.populateAndVerifyTables(op)))
227 return failure();
228
229 SymbolTable symbolTable(op);
230 InnerRefNamespace ns{symbolTable, innerSymTables};
231
232 // Conduct parallel walks of the top-level children of this
233 // InnerRefNamespace, verifying all InnerRefUserOp's discovered within.
234 auto verifySymbolUserFn = [&](Operation *op) -> WalkResult {
235 if (auto user = dyn_cast<InnerRefUserOpInterface>(op))
236 return WalkResult(user.verifyInnerRefs(ns));
237 return WalkResult::advance();
238 };
239
240 SmallVector<Operation *> topLevelOps;
241 for (auto &op : op->getRegion(0).front()) {
242 // Gather operations with regions for parallel processing.
243 if (op.getNumRegions() != 0) {
244 topLevelOps.push_back(&op);
245 continue;
246 }
247 // Otherwise, handle right now -- not worth the cost.
248 if (verifySymbolUserFn(&op).wasInterrupted())
249 return failure();
250 }
251 return mlir::failableParallelForEach(
252 op->getContext(), topLevelOps, [&](Operation *op) {
253 return success(!op->walk(verifySymbolUserFn).wasInterrupted());
254 });
255}
256
257} // namespace detail
258
259bool InnerRefNamespaceLike::classof(mlir::Operation *op) {
260 return op->hasTrait<mlir::OpTrait::InnerRefNamespace>() ||
261 op->hasTrait<mlir::OpTrait::SymbolTable>();
262}
263
265 const mlir::RegisteredOperationName *opInfo) {
266 return opInfo->hasTrait<mlir::OpTrait::InnerRefNamespace>() ||
267 opInfo->hasTrait<mlir::OpTrait::SymbolTable>();
268}
269
270} // namespace hw
271} // namespace circt
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
The target of an inner symbol, the entity the symbol is a handle for.
auto getField() const
Return the target's fieldID.
static InnerSymTarget getTargetForSubfield(const InnerSymTarget &base, size_t fieldID)
Return a target to the specified field within the given base.
auto getPort() const
Return the target's port, if valid. Check "isPort()".
bool isPort() const
Return if this targets a port.
Operation * getOp() const
Return the target's base operation. For ports, this is the module.
This class represents a collection of InnerSymbolTable's.
DenseMap< Operation *, std::unique_ptr< InnerSymbolTable > > symbolTables
This maps Operations to their InnnerSymbolTable's.
LogicalResult populateAndVerifyTables(Operation *innerRefNSOp)
Populate tables in parallel for all InnerSymbolTable operations in the given InnerRefNamespace operat...
InnerSymbolTable & getInnerSymbolTable(Operation *op)
Get or create the InnerSymbolTable for the specified operation.
A table of inner symbols and their resolutions.
Operation * innerSymTblOp
This is the operation this table is constructed for, which must have the InnerSymbolTable trait.
InnerSymTarget lookup(StringRef name) const
Look up a symbol with the specified name, returning empty InnerSymTarget if no such name exists.
DenseMap< StringAttr, InnerSymTarget > TableTy
static StringAttr getInnerSymbol(Operation *op)
Get InnerSymbol for an operation.
InnerSymbolTable(Operation *op)
Build an inner symbol table for the given operation.
static RetTy walkSymbols(Operation *op, FuncTy &&callback)
Walk the given IST operation and invoke the callback for all encountered inner symbols.
Operation * lookupOp(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists or doesn't target jus...
static FailureOr< InnerSymbolTable > get(Operation *op)
Construct an InnerSymbolTable, checking for verification failure.
TableTy symbolTable
This maps inner symbol names to their targets.
llvm::function_ref< LogicalResult(StringAttr, const InnerSymTarget &)> InnerSymCallbackFn
This trait is for operations that define a scope for resolving InnerRef's, and provides verification ...
A trait for inner symbol table functionality on an operation.
LogicalResult verifyInnerRefNamespace(Operation *op)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
static bool classof(mlir::Operation *op)
Return if this operation is explicitly an IRN or appears compatible.
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
Operation * lookupOp(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
InnerSymbolTableCollection & innerSymTables