CIRCT 20.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")
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
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.
llvm::function_ref< LogicalResult(StringAttr, const InnerSymTarget &)> InnerSymCallbackFn
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.
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