CIRCT  20.0.0git
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 
18 using namespace circt;
19 using namespace hw;
20 
21 namespace circt {
22 namespace 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 
39 FailureOr<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 
62 LogicalResult 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 }
107 InnerSymTarget InnerSymbolTable::lookup(StringAttr name) const {
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.
113 Operation *InnerSymbolTable::lookupOp(StringRef name) const {
114  return lookupOp(StringAttr::get(innerSymTblOp->getContext(), name));
115 }
116 Operation *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.
124 StringAttr 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 
168 LogicalResult
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 
201 InnerSymTarget 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 
209 Operation *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 
221 namespace detail {
222 
223 LogicalResult 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 
259 bool 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.
Operation * getOp() const
Return the target's base operation. For ports, this is the module.
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.
This class represents a collection of InnerSymbolTable'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.
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.
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
LogicalResult verifyInnerRefNamespace(Operation *op)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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...