CIRCT  19.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 
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  // Walk the operation and add InnerSymbolTarget's to the table.
82  return success(
83  !op->walk<mlir::WalkOrder::PreOrder>([&](Operation *curOp) -> WalkResult {
84  if (auto symOp = dyn_cast<InnerSymbolOpInterface>(curOp))
85  if (auto symAttr = symOp.getInnerSymAttr())
86  if (failed(walkSyms(symAttr, InnerSymTarget(symOp))))
87  return WalkResult::interrupt();
88 
89  // Check for ports
90  if (auto mod = dyn_cast<PortList>(curOp)) {
91  for (auto [i, port] : llvm::enumerate(mod.getPortList())) {
92  if (auto symAttr = port.getSym())
93  if (failed(walkSyms(symAttr, InnerSymTarget(i, curOp))))
94  return WalkResult::interrupt();
95  }
96  }
97  return WalkResult::advance();
98  }).wasInterrupted());
99 }
100 
101 /// Look up a symbol with the specified name, returning empty InnerSymTarget if
102 /// no such name exists. Names never include the @ on them.
104  return lookup(StringAttr::get(innerSymTblOp->getContext(), name));
105 }
106 InnerSymTarget InnerSymbolTable::lookup(StringAttr name) const {
107  return symbolTable.lookup(name);
108 }
109 
110 /// Look up a symbol with the specified name, returning null if no such
111 /// name exists or doesn't target just an operation.
112 Operation *InnerSymbolTable::lookupOp(StringRef name) const {
113  return lookupOp(StringAttr::get(innerSymTblOp->getContext(), name));
114 }
115 Operation *InnerSymbolTable::lookupOp(StringAttr name) const {
116  auto result = lookup(name);
117  if (result.isOpOnly())
118  return result.getOp();
119  return nullptr;
120 }
121 
122 /// Get InnerSymbol for an operation.
123 StringAttr InnerSymbolTable::getInnerSymbol(Operation *op) {
124  if (auto innerSymOp = dyn_cast<InnerSymbolOpInterface>(op))
125  return innerSymOp.getInnerNameAttr();
126  return {};
127 }
128 
129 /// Get InnerSymbol for a target. Be robust to queries on unexpected
130 /// operations to avoid users needing to know the details.
132  // Assert on misuse, but try to handle queries otherwise.
133  assert(target);
134 
135  // Obtain the base InnerSymAttr for the specified target.
136  auto getBase = [](auto &target) -> hw::InnerSymAttr {
137  if (target.isPort()) {
138  if (auto mod = dyn_cast<PortList>(target.getOp())) {
139  assert(target.getPort() < mod.getNumPorts());
140  return mod.getPort(target.getPort()).getSym();
141  }
142  } else {
143  // InnerSymbols only supported if op implements the interface.
144  if (auto symOp = dyn_cast<InnerSymbolOpInterface>(target.getOp()))
145  return symOp.getInnerSymAttr();
146  }
147  return {};
148  };
149 
150  if (auto base = getBase(target))
151  return base.getSymIfExists(target.getField());
152  return {};
153 }
154 
155 //===----------------------------------------------------------------------===//
156 // InnerSymbolTableCollection
157 //===----------------------------------------------------------------------===//
158 
161  auto it = symbolTables.try_emplace(op, nullptr);
162  if (it.second)
163  it.first->second = ::std::make_unique<InnerSymbolTable>(op);
164  return *it.first->second;
165 }
166 
167 LogicalResult
169  // Gather top-level operations that have the InnerSymbolTable trait.
170  SmallVector<Operation *> innerSymTableOps(llvm::make_filter_range(
171  llvm::make_pointer_range(innerRefNSOp->getRegion(0).front()),
172  [&](Operation *op) {
173  return op->hasTrait<OpTrait::InnerSymbolTable>();
174  }));
175 
176  // Ensure entries exist for each operation.
177  llvm::for_each(innerSymTableOps,
178  [&](auto *op) { symbolTables.try_emplace(op, nullptr); });
179 
180  // Construct the tables in parallel (if context allows it).
181  return mlir::failableParallelForEach(
182  innerRefNSOp->getContext(), innerSymTableOps, [&](auto *op) {
183  auto it = symbolTables.find(op);
184  assert(it != symbolTables.end());
185  if (!it->second) {
186  auto result = InnerSymbolTable::get(op);
187  if (failed(result))
188  return failure();
189  it->second = std::make_unique<InnerSymbolTable>(std::move(*result));
190  return success();
191  }
192  return failure();
193  });
194 }
195 
196 //===----------------------------------------------------------------------===//
197 // InnerRefNamespace
198 //===----------------------------------------------------------------------===//
199 
200 InnerSymTarget InnerRefNamespace::lookup(hw::InnerRefAttr inner) const {
201  auto *mod = symTable.lookup(inner.getModule());
202  if (!mod)
203  return {};
204  assert(mod->hasTrait<mlir::OpTrait::InnerSymbolTable>());
205  return innerSymTables.getInnerSymbolTable(mod).lookup(inner.getName());
206 }
207 
208 Operation *InnerRefNamespace::lookupOp(hw::InnerRefAttr inner) const {
209  auto *mod = symTable.lookup(inner.getModule());
210  if (!mod)
211  return nullptr;
212  assert(mod->hasTrait<mlir::OpTrait::InnerSymbolTable>());
213  return innerSymTables.getInnerSymbolTable(mod).lookupOp(inner.getName());
214 }
215 
216 //===----------------------------------------------------------------------===//
217 // InnerRefNamespace verification
218 //===----------------------------------------------------------------------===//
219 
220 namespace detail {
221 
222 LogicalResult verifyInnerRefNamespace(Operation *op) {
223  // Construct the symbol tables.
224  InnerSymbolTableCollection innerSymTables;
225  if (failed(innerSymTables.populateAndVerifyTables(op)))
226  return failure();
227 
228  SymbolTable symbolTable(op);
229  InnerRefNamespace ns{symbolTable, innerSymTables};
230 
231  // Conduct parallel walks of the top-level children of this
232  // InnerRefNamespace, verifying all InnerRefUserOp's discovered within.
233  auto verifySymbolUserFn = [&](Operation *op) -> WalkResult {
234  if (auto user = dyn_cast<InnerRefUserOpInterface>(op))
235  return WalkResult(user.verifyInnerRefs(ns));
236  return WalkResult::advance();
237  };
238 
239  SmallVector<Operation *> topLevelOps;
240  for (auto &op : op->getRegion(0).front()) {
241  // Gather operations with regions for parallel processing.
242  if (op.getNumRegions() != 0) {
243  topLevelOps.push_back(&op);
244  continue;
245  }
246  // Otherwise, handle right now -- not worth the cost.
247  if (verifySymbolUserFn(&op).wasInterrupted())
248  return failure();
249  }
250  return mlir::failableParallelForEach(
251  op->getContext(), topLevelOps, [&](Operation *op) {
252  return success(!op->walk(verifySymbolUserFn).wasInterrupted());
253  });
254 }
255 
256 } // namespace detail
257 
258 bool InnerRefNamespaceLike::classof(mlir::Operation *op) {
259  return op->hasTrait<mlir::OpTrait::InnerRefNamespace>() ||
260  op->hasTrait<mlir::OpTrait::SymbolTable>();
261 }
262 
264  const mlir::RegisteredOperationName *opInfo) {
265  return opInfo->hasTrait<mlir::OpTrait::InnerRefNamespace>() ||
266  opInfo->hasTrait<mlir::OpTrait::SymbolTable>();
267 }
268 
269 } // namespace hw
270 } // 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:54
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...
InnerSymbolTableCollection & innerSymTables