MLIR  16.0.0git
SymbolTable.cpp
Go to the documentation of this file.
1 //===- SymbolTable.cpp - MLIR Symbol Table Class --------------------------===//
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 #include "mlir/IR/SymbolTable.h"
10 #include "mlir/IR/Builders.h"
12 #include "llvm/ADT/SetVector.h"
13 #include "llvm/ADT/SmallPtrSet.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/StringSwitch.h"
16 
17 using namespace mlir;
18 
19 /// Return true if the given operation is unknown and may potentially define a
20 /// symbol table.
22  return op->getNumRegions() == 1 && !op->getDialect();
23 }
24 
25 /// Returns the string name of the given symbol, or null if this is not a
26 /// symbol.
27 static StringAttr getNameIfSymbol(Operation *op) {
28  return op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
29 }
30 static StringAttr getNameIfSymbol(Operation *op, StringAttr symbolAttrNameId) {
31  return op->getAttrOfType<StringAttr>(symbolAttrNameId);
32 }
33 
34 /// Computes the nested symbol reference attribute for the symbol 'symbolName'
35 /// that are usable within the symbol table operations from 'symbol' as far up
36 /// to the given operation 'within', where 'within' is an ancestor of 'symbol'.
37 /// Returns success if all references up to 'within' could be computed.
38 static LogicalResult
39 collectValidReferencesFor(Operation *symbol, StringAttr symbolName,
40  Operation *within,
42  assert(within->isAncestor(symbol) && "expected 'within' to be an ancestor");
43  MLIRContext *ctx = symbol->getContext();
44 
45  auto leafRef = FlatSymbolRefAttr::get(symbolName);
46  results.push_back(leafRef);
47 
48  // Early exit for when 'within' is the parent of 'symbol'.
49  Operation *symbolTableOp = symbol->getParentOp();
50  if (within == symbolTableOp)
51  return success();
52 
53  // Collect references until 'symbolTableOp' reaches 'within'.
54  SmallVector<FlatSymbolRefAttr, 1> nestedRefs(1, leafRef);
55  StringAttr symbolNameId =
56  StringAttr::get(ctx, SymbolTable::getSymbolAttrName());
57  do {
58  // Each parent of 'symbol' should define a symbol table.
59  if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>())
60  return failure();
61  // Each parent of 'symbol' should also be a symbol.
62  StringAttr symbolTableName = getNameIfSymbol(symbolTableOp, symbolNameId);
63  if (!symbolTableName)
64  return failure();
65  results.push_back(SymbolRefAttr::get(symbolTableName, nestedRefs));
66 
67  symbolTableOp = symbolTableOp->getParentOp();
68  if (symbolTableOp == within)
69  break;
70  nestedRefs.insert(nestedRefs.begin(),
71  FlatSymbolRefAttr::get(symbolTableName));
72  } while (true);
73  return success();
74 }
75 
76 /// Walk all of the operations within the given set of regions, without
77 /// traversing into any nested symbol tables. Stops walking if the result of the
78 /// callback is anything other than `WalkResult::advance`.
82  SmallVector<Region *, 1> worklist(llvm::make_pointer_range(regions));
83  while (!worklist.empty()) {
84  for (Operation &op : worklist.pop_back_val()->getOps()) {
85  Optional<WalkResult> result = callback(&op);
86  if (result != WalkResult::advance())
87  return result;
88 
89  // If this op defines a new symbol table scope, we can't traverse. Any
90  // symbol references nested within 'op' are different semantically.
91  if (!op.hasTrait<OpTrait::SymbolTable>()) {
92  for (Region &region : op.getRegions())
93  worklist.push_back(&region);
94  }
95  }
96  }
97  return WalkResult::advance();
98 }
99 
100 /// Walk all of the operations nested under, and including, the given operation,
101 /// without traversing into any nested symbol tables. Stops walking if the
102 /// result of the callback is anything other than `WalkResult::advance`.
106  Optional<WalkResult> result = callback(op);
107  if (result != WalkResult::advance() || op->hasTrait<OpTrait::SymbolTable>())
108  return result;
109  return walkSymbolTable(op->getRegions(), callback);
110 }
111 
112 //===----------------------------------------------------------------------===//
113 // SymbolTable
114 //===----------------------------------------------------------------------===//
115 
116 /// Build a symbol table with the symbols within the given operation.
118  : symbolTableOp(symbolTableOp) {
119  assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>() &&
120  "expected operation to have SymbolTable trait");
121  assert(symbolTableOp->getNumRegions() == 1 &&
122  "expected operation to have a single region");
123  assert(llvm::hasSingleElement(symbolTableOp->getRegion(0)) &&
124  "expected operation to have a single block");
125 
126  StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
128  for (auto &op : symbolTableOp->getRegion(0).front()) {
129  StringAttr name = getNameIfSymbol(&op, symbolNameId);
130  if (!name)
131  continue;
132 
133  auto inserted = symbolTable.insert({name, &op});
134  (void)inserted;
135  assert(inserted.second &&
136  "expected region to contain uniquely named symbol operations");
137  }
138 }
139 
140 /// Look up a symbol with the specified name, returning null if no such name
141 /// exists. Names never include the @ on them.
142 Operation *SymbolTable::lookup(StringRef name) const {
143  return lookup(StringAttr::get(symbolTableOp->getContext(), name));
144 }
145 Operation *SymbolTable::lookup(StringAttr name) const {
146  return symbolTable.lookup(name);
147 }
148 
150  StringAttr name = getNameIfSymbol(op);
151  assert(name && "expected valid 'name' attribute");
152  assert(op->getParentOp() == symbolTableOp &&
153  "expected this operation to be inside of the operation with this "
154  "SymbolTable");
155 
156  auto it = symbolTable.find(name);
157  if (it != symbolTable.end() && it->second == op)
158  symbolTable.erase(it);
159 }
160 
162  remove(symbol);
163  symbol->erase();
164 }
165 
166 // TODO: Consider if this should be renamed to something like insertOrUpdate
167 /// Insert a new symbol into the table and associated operation if not already
168 /// there and rename it as necessary to avoid collisions. Return the name of
169 /// the symbol after insertion as attribute.
170 StringAttr SymbolTable::insert(Operation *symbol, Block::iterator insertPt) {
171  // The symbol cannot be the child of another op and must be the child of the
172  // symbolTableOp after this.
173  //
174  // TODO: consider if SymbolTable's constructor should behave the same.
175  if (!symbol->getParentOp()) {
176  auto &body = symbolTableOp->getRegion(0).front();
177  if (insertPt == Block::iterator()) {
178  insertPt = Block::iterator(body.end());
179  } else {
180  assert((insertPt == body.end() ||
181  insertPt->getParentOp() == symbolTableOp) &&
182  "expected insertPt to be in the associated module operation");
183  }
184  // Insert before the terminator, if any.
185  if (insertPt == Block::iterator(body.end()) && !body.empty() &&
186  std::prev(body.end())->hasTrait<OpTrait::IsTerminator>())
187  insertPt = std::prev(body.end());
188 
189  body.getOperations().insert(insertPt, symbol);
190  }
191  assert(symbol->getParentOp() == symbolTableOp &&
192  "symbol is already inserted in another op");
193 
194  // Add this symbol to the symbol table, uniquing the name if a conflict is
195  // detected.
196  StringAttr name = getSymbolName(symbol);
197  if (symbolTable.insert({name, symbol}).second)
198  return name;
199  // If the symbol was already in the table, also return.
200  if (symbolTable.lookup(name) == symbol)
201  return name;
202  // If a conflict was detected, then the symbol will not have been added to
203  // the symbol table. Try suffixes until we get to a unique name that works.
204  SmallString<128> nameBuffer(name.getValue());
205  unsigned originalLength = nameBuffer.size();
206 
207  MLIRContext *context = symbol->getContext();
208 
209  // Iteratively try suffixes until we find one that isn't used.
210  do {
211  nameBuffer.resize(originalLength);
212  nameBuffer += '_';
213  nameBuffer += std::to_string(uniquingCounter++);
214  } while (!symbolTable.insert({StringAttr::get(context, nameBuffer), symbol})
215  .second);
216  setSymbolName(symbol, nameBuffer);
217  return getSymbolName(symbol);
218 }
219 
220 /// Returns the name of the given symbol operation.
222  StringAttr name = getNameIfSymbol(symbol);
223  assert(name && "expected valid symbol name");
224  return name;
225 }
226 
227 /// Sets the name of the given symbol operation.
228 void SymbolTable::setSymbolName(Operation *symbol, StringAttr name) {
229  symbol->setAttr(getSymbolAttrName(), name);
230 }
231 
232 /// Returns the visibility of the given symbol operation.
234  // If the attribute doesn't exist, assume public.
235  StringAttr vis = symbol->getAttrOfType<StringAttr>(getVisibilityAttrName());
236  if (!vis)
237  return Visibility::Public;
238 
239  // Otherwise, switch on the string value.
240  return StringSwitch<Visibility>(vis.getValue())
241  .Case("private", Visibility::Private)
242  .Case("nested", Visibility::Nested)
243  .Case("public", Visibility::Public);
244 }
245 /// Sets the visibility of the given symbol operation.
247  MLIRContext *ctx = symbol->getContext();
248 
249  // If the visibility is public, just drop the attribute as this is the
250  // default.
251  if (vis == Visibility::Public) {
252  symbol->removeAttr(StringAttr::get(ctx, getVisibilityAttrName()));
253  return;
254  }
255 
256  // Otherwise, update the attribute.
257  assert((vis == Visibility::Private || vis == Visibility::Nested) &&
258  "unknown symbol visibility kind");
259 
260  StringRef visName = vis == Visibility::Private ? "private" : "nested";
261  symbol->setAttr(getVisibilityAttrName(), StringAttr::get(ctx, visName));
262 }
263 
264 /// Returns the nearest symbol table from a given operation `from`. Returns
265 /// nullptr if no valid parent symbol table could be found.
267  assert(from && "expected valid operation");
269  return nullptr;
270 
271  while (!from->hasTrait<OpTrait::SymbolTable>()) {
272  from = from->getParentOp();
273 
274  // Check that this is a valid op and isn't an unknown symbol table.
275  if (!from || isPotentiallyUnknownSymbolTable(from))
276  return nullptr;
277  }
278  return from;
279 }
280 
281 /// Walks all symbol table operations nested within, and including, `op`. For
282 /// each symbol table operation, the provided callback is invoked with the op
283 /// and a boolean signifying if the symbols within that symbol table can be
284 /// treated as if all uses are visible. `allSymUsesVisible` identifies whether
285 /// all of the symbol uses of symbols within `op` are visible.
287  Operation *op, bool allSymUsesVisible,
288  function_ref<void(Operation *, bool)> callback) {
289  bool isSymbolTable = op->hasTrait<OpTrait::SymbolTable>();
290  if (isSymbolTable) {
291  SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op);
292  allSymUsesVisible |= !symbol || symbol.isPrivate();
293  } else {
294  // Otherwise if 'op' is not a symbol table, any nested symbols are
295  // guaranteed to be hidden.
296  allSymUsesVisible = true;
297  }
298 
299  for (Region &region : op->getRegions())
300  for (Block &block : region)
301  for (Operation &nestedOp : block)
302  walkSymbolTables(&nestedOp, allSymUsesVisible, callback);
303 
304  // If 'op' had the symbol table trait, visit it after any nested symbol
305  // tables.
306  if (isSymbolTable)
307  callback(op, allSymUsesVisible);
308 }
309 
310 /// Returns the operation registered with the given symbol name with the
311 /// regions of 'symbolTableOp'. 'symbolTableOp' is required to be an operation
312 /// with the 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol
313 /// was found.
315  StringAttr symbol) {
316  assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
317  Region &region = symbolTableOp->getRegion(0);
318  if (region.empty())
319  return nullptr;
320 
321  // Look for a symbol with the given name.
322  StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
324  for (auto &op : region.front())
325  if (getNameIfSymbol(&op, symbolNameId) == symbol)
326  return &op;
327  return nullptr;
328 }
330  SymbolRefAttr symbol) {
331  SmallVector<Operation *, 4> resolvedSymbols;
332  if (failed(lookupSymbolIn(symbolTableOp, symbol, resolvedSymbols)))
333  return nullptr;
334  return resolvedSymbols.back();
335 }
336 
337 /// Internal implementation of `lookupSymbolIn` that allows for specialized
338 /// implementations of the lookup function.
340  Operation *symbolTableOp, SymbolRefAttr symbol,
342  function_ref<Operation *(Operation *, StringAttr)> lookupSymbolFn) {
343  assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
344 
345  // Lookup the root reference for this symbol.
346  symbolTableOp = lookupSymbolFn(symbolTableOp, symbol.getRootReference());
347  if (!symbolTableOp)
348  return failure();
349  symbols.push_back(symbolTableOp);
350 
351  // If there are no nested references, just return the root symbol directly.
352  ArrayRef<FlatSymbolRefAttr> nestedRefs = symbol.getNestedReferences();
353  if (nestedRefs.empty())
354  return success();
355 
356  // Verify that the root is also a symbol table.
357  if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>())
358  return failure();
359 
360  // Otherwise, lookup each of the nested non-leaf references and ensure that
361  // each corresponds to a valid symbol table.
362  for (FlatSymbolRefAttr ref : nestedRefs.drop_back()) {
363  symbolTableOp = lookupSymbolFn(symbolTableOp, ref.getAttr());
364  if (!symbolTableOp || !symbolTableOp->hasTrait<OpTrait::SymbolTable>())
365  return failure();
366  symbols.push_back(symbolTableOp);
367  }
368  symbols.push_back(lookupSymbolFn(symbolTableOp, symbol.getLeafReference()));
369  return success(symbols.back());
370 }
371 
373 SymbolTable::lookupSymbolIn(Operation *symbolTableOp, SymbolRefAttr symbol,
374  SmallVectorImpl<Operation *> &symbols) {
375  auto lookupFn = [](Operation *symbolTableOp, StringAttr symbol) {
376  return lookupSymbolIn(symbolTableOp, symbol);
377  };
378  return lookupSymbolInImpl(symbolTableOp, symbol, symbols, lookupFn);
379 }
380 
381 /// Returns the operation registered with the given symbol name within the
382 /// closes parent operation with the 'OpTrait::SymbolTable' trait. Returns
383 /// nullptr if no valid symbol was found.
385  StringAttr symbol) {
386  Operation *symbolTableOp = getNearestSymbolTable(from);
387  return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
388 }
390  SymbolRefAttr symbol) {
391  Operation *symbolTableOp = getNearestSymbolTable(from);
392  return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
393 }
394 
395 raw_ostream &mlir::operator<<(raw_ostream &os,
396  SymbolTable::Visibility visibility) {
397  switch (visibility) {
399  return os << "public";
401  return os << "private";
403  return os << "nested";
404  }
405  llvm_unreachable("Unexpected visibility");
406 }
407 
408 //===----------------------------------------------------------------------===//
409 // SymbolTable Trait Types
410 //===----------------------------------------------------------------------===//
411 
413  if (op->getNumRegions() != 1)
414  return op->emitOpError()
415  << "Operations with a 'SymbolTable' must have exactly one region";
416  if (!llvm::hasSingleElement(op->getRegion(0)))
417  return op->emitOpError()
418  << "Operations with a 'SymbolTable' must have exactly one block";
419 
420  // Check that all symbols are uniquely named within child regions.
421  DenseMap<Attribute, Location> nameToOrigLoc;
422  for (auto &block : op->getRegion(0)) {
423  for (auto &op : block) {
424  // Check for a symbol name attribute.
425  auto nameAttr =
427  if (!nameAttr)
428  continue;
429 
430  // Try to insert this symbol into the table.
431  auto it = nameToOrigLoc.try_emplace(nameAttr, op.getLoc());
432  if (!it.second)
433  return op.emitError()
434  .append("redefinition of symbol named '", nameAttr.getValue(), "'")
435  .attachNote(it.first->second)
436  .append("see existing symbol definition here");
437  }
438  }
439 
440  // Verify any nested symbol user operations.
441  SymbolTableCollection symbolTable;
442  auto verifySymbolUserFn = [&](Operation *op) -> Optional<WalkResult> {
443  if (SymbolUserOpInterface user = dyn_cast<SymbolUserOpInterface>(op))
444  return WalkResult(user.verifySymbolUses(symbolTable));
445  return WalkResult::advance();
446  };
447 
448  Optional<WalkResult> result =
449  walkSymbolTable(op->getRegions(), verifySymbolUserFn);
450  return success(result && !result->wasInterrupted());
451 }
452 
454  // Verify the name attribute.
455  if (!op->getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName()))
456  return op->emitOpError() << "requires string attribute '"
458 
459  // Verify the visibility attribute.
461  StringAttr visStrAttr = vis.dyn_cast<StringAttr>();
462  if (!visStrAttr)
463  return op->emitOpError() << "requires visibility attribute '"
465  << "' to be a string attribute, but got " << vis;
466 
467  if (!llvm::is_contained(ArrayRef<StringRef>{"public", "private", "nested"},
468  visStrAttr.getValue()))
469  return op->emitOpError()
470  << "visibility expected to be one of [\"public\", \"private\", "
471  "\"nested\"], but got "
472  << visStrAttr;
473  }
474  return success();
475 }
476 
477 //===----------------------------------------------------------------------===//
478 // Symbol Use Lists
479 //===----------------------------------------------------------------------===//
480 
481 /// Walk all of the symbol references within the given operation, invoking the
482 /// provided callback for each found use. The callbacks takes the use of the
483 /// symbol.
484 static WalkResult
487  // Check to see if the operation has any attributes.
488  DictionaryAttr attrDict = op->getAttrDictionary();
489  if (attrDict.empty())
490  return WalkResult::advance();
491 
492  // A worklist of a container attribute and the current index into the held
493  // attribute list.
494  struct WorklistItem {
495  SubElementAttrInterface container;
496  SmallVector<Attribute> immediateSubElements;
497 
498  explicit WorklistItem(SubElementAttrInterface container) {
499  SmallVector<Attribute> subElements;
500  container.walkImmediateSubElements(
501  [&](Attribute attr) { subElements.push_back(attr); }, [](Type) {});
502  immediateSubElements = std::move(subElements);
503  }
504  };
505 
506  SmallVector<WorklistItem, 1> attrWorklist(1, WorklistItem(attrDict));
507  SmallVector<int, 1> curAccessChain(1, /*Value=*/-1);
508 
509  // Process the symbol references within the given nested attribute range.
510  auto processAttrs = [&](int &index,
511  WorklistItem &worklistItem) -> WalkResult {
512  for (Attribute attr :
513  llvm::drop_begin(worklistItem.immediateSubElements, index)) {
514  // Invoke the provided callback if we find a symbol use and check for a
515  // requested interrupt.
516  if (auto symbolRef = attr.dyn_cast<SymbolRefAttr>()) {
517  if (callback({op, symbolRef}).wasInterrupted())
518  return WalkResult::interrupt();
519 
520  /// Check for a nested container attribute, these will also need to be
521  /// walked.
522  } else if (auto interface = attr.dyn_cast<SubElementAttrInterface>()) {
523  attrWorklist.emplace_back(interface);
524  curAccessChain.push_back(-1);
525  return WalkResult::advance();
526  }
527  // Make sure to keep the index counter in sync.
528  ++index;
529  }
530 
531  // Pop this container attribute from the worklist.
532  attrWorklist.pop_back();
533  curAccessChain.pop_back();
534  return WalkResult::advance();
535  };
536 
537  WalkResult result = WalkResult::advance();
538  do {
539  WorklistItem &item = attrWorklist.back();
540  int &index = curAccessChain.back();
541  ++index;
542 
543  // Process the given attribute, which is guaranteed to be a container.
544  result = processAttrs(index, item);
545  } while (!attrWorklist.empty() && !result.wasInterrupted());
546  return result;
547 }
548 
549 /// Walk all of the uses, for any symbol, that are nested within the given
550 /// regions, invoking the provided callback for each. This does not traverse
551 /// into any nested symbol tables.
555  return walkSymbolTable(regions, [&](Operation *op) -> Optional<WalkResult> {
556  // Check that this isn't a potentially unknown symbol table.
558  return std::nullopt;
559 
560  return walkSymbolRefs(op, callback);
561  });
562 }
563 /// Walk all of the uses, for any symbol, that are nested within the given
564 /// operation 'from', invoking the provided callback for each. This does not
565 /// traverse into any nested symbol tables.
569  // If this operation has regions, and it, as well as its dialect, isn't
570  // registered then conservatively fail. The operation may define a
571  // symbol table, so we can't opaquely know if we should traverse to find
572  // nested uses.
574  return std::nullopt;
575 
576  // Walk the uses on this operation.
577  if (walkSymbolRefs(from, callback).wasInterrupted())
578  return WalkResult::interrupt();
579 
580  // Only recurse if this operation is not a symbol table. A symbol table
581  // defines a new scope, so we can't walk the attributes from within the symbol
582  // table op.
583  if (!from->hasTrait<OpTrait::SymbolTable>())
584  return walkSymbolUses(from->getRegions(), callback);
585  return WalkResult::advance();
586 }
587 
588 namespace {
589 /// This class represents a single symbol scope. A symbol scope represents the
590 /// set of operations nested within a symbol table that may reference symbols
591 /// within that table. A symbol scope does not contain the symbol table
592 /// operation itself, just its contained operations. A scope ends at leaf
593 /// operations or another symbol table operation.
594 struct SymbolScope {
595  /// Walk the symbol uses within this scope, invoking the given callback.
596  /// This variant is used when the callback type matches that expected by
597  /// 'walkSymbolUses'.
598  template <typename CallbackT,
599  std::enable_if_t<!std::is_same<
600  typename llvm::function_traits<CallbackT>::result_t,
601  void>::value> * = nullptr>
602  Optional<WalkResult> walk(CallbackT cback) {
603  if (Region *region = limit.dyn_cast<Region *>())
604  return walkSymbolUses(*region, cback);
605  return walkSymbolUses(limit.get<Operation *>(), cback);
606  }
607  /// This variant is used when the callback type matches a stripped down type:
608  /// void(SymbolTable::SymbolUse use)
609  template <typename CallbackT,
610  std::enable_if_t<std::is_same<
611  typename llvm::function_traits<CallbackT>::result_t,
612  void>::value> * = nullptr>
613  Optional<WalkResult> walk(CallbackT cback) {
614  return walk([=](SymbolTable::SymbolUse use) {
615  return cback(use), WalkResult::advance();
616  });
617  }
618 
619  /// Walk all of the operations nested under the current scope without
620  /// traversing into any nested symbol tables.
621  template <typename CallbackT>
622  Optional<WalkResult> walkSymbolTable(CallbackT &&cback) {
623  if (Region *region = limit.dyn_cast<Region *>())
624  return ::walkSymbolTable(*region, cback);
625  return ::walkSymbolTable(limit.get<Operation *>(), cback);
626  }
627 
628  /// The representation of the symbol within this scope.
629  SymbolRefAttr symbol;
630 
631  /// The IR unit representing this scope.
633 };
634 } // namespace
635 
636 /// Collect all of the symbol scopes from 'symbol' to (inclusive) 'limit'.
638  Operation *limit) {
639  StringAttr symName = SymbolTable::getSymbolName(symbol);
640  assert(!symbol->hasTrait<OpTrait::SymbolTable>() || symbol != limit);
641 
642  // Compute the ancestors of 'limit'.
645  limitAncestors;
646  Operation *limitAncestor = limit;
647  do {
648  // Check to see if 'symbol' is an ancestor of 'limit'.
649  if (limitAncestor == symbol) {
650  // Check that the nearest symbol table is 'symbol's parent. SymbolRefAttr
651  // doesn't support parent references.
653  symbol->getParentOp())
654  return {{SymbolRefAttr::get(symName), limit}};
655  return {};
656  }
657 
658  limitAncestors.insert(limitAncestor);
659  } while ((limitAncestor = limitAncestor->getParentOp()));
660 
661  // Try to find the first ancestor of 'symbol' that is an ancestor of 'limit'.
662  Operation *commonAncestor = symbol->getParentOp();
663  do {
664  if (limitAncestors.count(commonAncestor))
665  break;
666  } while ((commonAncestor = commonAncestor->getParentOp()));
667  assert(commonAncestor && "'limit' and 'symbol' have no common ancestor");
668 
669  // Compute the set of valid nested references for 'symbol' as far up to the
670  // common ancestor as possible.
672  bool collectedAllReferences = succeeded(
673  collectValidReferencesFor(symbol, symName, commonAncestor, references));
674 
675  // Handle the case where the common ancestor is 'limit'.
676  if (commonAncestor == limit) {
678 
679  // Walk each of the ancestors of 'symbol', calling the compute function for
680  // each one.
681  Operation *limitIt = symbol->getParentOp();
682  for (size_t i = 0, e = references.size(); i != e;
683  ++i, limitIt = limitIt->getParentOp()) {
684  assert(limitIt->hasTrait<OpTrait::SymbolTable>());
685  scopes.push_back({references[i], &limitIt->getRegion(0)});
686  }
687  return scopes;
688  }
689 
690  // Otherwise, we just need the symbol reference for 'symbol' that will be
691  // used within 'limit'. This is the last reference in the list we computed
692  // above if we were able to collect all references.
693  if (!collectedAllReferences)
694  return {};
695  return {{references.back(), limit}};
696 }
698  Region *limit) {
699  auto scopes = collectSymbolScopes(symbol, limit->getParentOp());
700 
701  // If we collected some scopes to walk, make sure to constrain the one for
702  // limit to the specific region requested.
703  if (!scopes.empty())
704  scopes.back().limit = limit;
705  return scopes;
706 }
707 template <typename IRUnit>
709  IRUnit *limit) {
710  return {{SymbolRefAttr::get(symbol), limit}};
711 }
712 
713 /// Returns true if the given reference 'SubRef' is a sub reference of the
714 /// reference 'ref', i.e. 'ref' is a further qualified reference.
715 static bool isReferencePrefixOf(SymbolRefAttr subRef, SymbolRefAttr ref) {
716  if (ref == subRef)
717  return true;
718 
719  // If the references are not pointer equal, check to see if `subRef` is a
720  // prefix of `ref`.
721  if (ref.isa<FlatSymbolRefAttr>() ||
722  ref.getRootReference() != subRef.getRootReference())
723  return false;
724 
725  auto refLeafs = ref.getNestedReferences();
726  auto subRefLeafs = subRef.getNestedReferences();
727  return subRefLeafs.size() < refLeafs.size() &&
728  subRefLeafs == refLeafs.take_front(subRefLeafs.size());
729 }
730 
731 //===----------------------------------------------------------------------===//
732 // SymbolTable::getSymbolUses
733 
734 /// The implementation of SymbolTable::getSymbolUses below.
735 template <typename FromT>
737  std::vector<SymbolTable::SymbolUse> uses;
738  auto walkFn = [&](SymbolTable::SymbolUse symbolUse) {
739  uses.push_back(symbolUse);
740  return WalkResult::advance();
741  };
742  auto result = walkSymbolUses(from, walkFn);
743  return result ? Optional<SymbolTable::UseRange>(std::move(uses))
744  : std::nullopt;
745 }
746 
747 /// Get an iterator range for all of the uses, for any symbol, that are nested
748 /// within the given operation 'from'. This does not traverse into any nested
749 /// symbol tables, and will also only return uses on 'from' if it does not
750 /// also define a symbol table. This is because we treat the region as the
751 /// boundary of the symbol table, and not the op itself. This function returns
752 /// None if there are any unknown operations that may potentially be symbol
753 /// tables.
755  return getSymbolUsesImpl(from);
756 }
759 }
760 
761 //===----------------------------------------------------------------------===//
762 // SymbolTable::getSymbolUses
763 
764 /// The implementation of SymbolTable::getSymbolUses below.
765 template <typename SymbolT, typename IRUnitT>
767  IRUnitT *limit) {
768  std::vector<SymbolTable::SymbolUse> uses;
769  for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
770  if (!scope.walk([&](SymbolTable::SymbolUse symbolUse) {
771  if (isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef()))
772  uses.push_back(symbolUse);
773  }))
774  return std::nullopt;
775  }
776  return SymbolTable::UseRange(std::move(uses));
777 }
778 
779 /// Get all of the uses of the given symbol that are nested within the given
780 /// operation 'from', invoking the provided callback for each. This does not
781 /// traverse into any nested symbol tables. This function returns std::nullopt
782 /// if there are any unknown operations that may potentially be symbol tables.
783 auto SymbolTable::getSymbolUses(StringAttr symbol, Operation *from)
784  -> Optional<UseRange> {
785  return getSymbolUsesImpl(symbol, from);
786 }
788  -> Optional<UseRange> {
789  return getSymbolUsesImpl(symbol, from);
790 }
791 auto SymbolTable::getSymbolUses(StringAttr symbol, Region *from)
792  -> Optional<UseRange> {
793  return getSymbolUsesImpl(symbol, from);
794 }
796  -> Optional<UseRange> {
797  return getSymbolUsesImpl(symbol, from);
798 }
799 
800 //===----------------------------------------------------------------------===//
801 // SymbolTable::symbolKnownUseEmpty
802 
803 /// The implementation of SymbolTable::symbolKnownUseEmpty below.
804 template <typename SymbolT, typename IRUnitT>
805 static bool symbolKnownUseEmptyImpl(SymbolT symbol, IRUnitT *limit) {
806  for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
807  // Walk all of the symbol uses looking for a reference to 'symbol'.
808  if (scope.walk([&](SymbolTable::SymbolUse symbolUse) {
809  return isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef())
810  ? WalkResult::interrupt()
811  : WalkResult::advance();
812  }) != WalkResult::advance())
813  return false;
814  }
815  return true;
816 }
817 
818 /// Return if the given symbol is known to have no uses that are nested within
819 /// the given operation 'from'. This does not traverse into any nested symbol
820 /// tables. This function will also return false if there are any unknown
821 /// operations that may potentially be symbol tables.
822 bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Operation *from) {
823  return symbolKnownUseEmptyImpl(symbol, from);
824 }
826  return symbolKnownUseEmptyImpl(symbol, from);
827 }
828 bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Region *from) {
829  return symbolKnownUseEmptyImpl(symbol, from);
830 }
832  return symbolKnownUseEmptyImpl(symbol, from);
833 }
834 
835 //===----------------------------------------------------------------------===//
836 // SymbolTable::replaceAllSymbolUses
837 
838 /// Generates a new symbol reference attribute with a new leaf reference.
839 static SymbolRefAttr generateNewRefAttr(SymbolRefAttr oldAttr,
840  FlatSymbolRefAttr newLeafAttr) {
841  if (oldAttr.isa<FlatSymbolRefAttr>())
842  return newLeafAttr;
843  auto nestedRefs = llvm::to_vector<2>(oldAttr.getNestedReferences());
844  nestedRefs.back() = newLeafAttr;
845  return SymbolRefAttr::get(oldAttr.getRootReference(), nestedRefs);
846 }
847 
848 /// The implementation of SymbolTable::replaceAllSymbolUses below.
849 template <typename SymbolT, typename IRUnitT>
850 static LogicalResult
851 replaceAllSymbolUsesImpl(SymbolT symbol, StringAttr newSymbol, IRUnitT *limit) {
852  // Generate a new attribute to replace the given attribute.
853  FlatSymbolRefAttr newLeafAttr = FlatSymbolRefAttr::get(newSymbol);
854  for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
855  SymbolRefAttr oldAttr = scope.symbol;
856  SymbolRefAttr newAttr = generateNewRefAttr(scope.symbol, newLeafAttr);
857  AttrTypeReplacer replacer;
858  replacer.addReplacement(
859  [&](SymbolRefAttr attr) -> std::pair<Attribute, WalkResult> {
860  // Regardless of the match, don't walk nested SymbolRefAttrs, we don't
861  // want to accidentally replace an inner reference.
862  if (attr == oldAttr)
863  return {newAttr, WalkResult::skip()};
864  // Handle prefix matches.
865  if (isReferencePrefixOf(oldAttr, attr)) {
866  auto oldNestedRefs = oldAttr.getNestedReferences();
867  auto nestedRefs = attr.getNestedReferences();
868  if (oldNestedRefs.empty())
869  return {SymbolRefAttr::get(newSymbol, nestedRefs),
870  WalkResult::skip()};
871 
872  auto newNestedRefs = llvm::to_vector<4>(nestedRefs);
873  newNestedRefs[oldNestedRefs.size() - 1] = newLeafAttr;
874  return {SymbolRefAttr::get(attr.getRootReference(), newNestedRefs),
875  WalkResult::skip()};
876  }
877  return {attr, WalkResult::skip()};
878  });
879 
880  auto walkFn = [&](Operation *op) -> Optional<WalkResult> {
881  replacer.replaceElementsIn(op);
882  return WalkResult::advance();
883  };
884  if (!scope.walkSymbolTable(walkFn))
885  return failure();
886  }
887  return success();
888 }
889 
890 /// Attempt to replace all uses of the given symbol 'oldSymbol' with the
891 /// provided symbol 'newSymbol' that are nested within the given operation
892 /// 'from'. This does not traverse into any nested symbol tables. If there are
893 /// any unknown operations that may potentially be symbol tables, no uses are
894 /// replaced and failure is returned.
896  StringAttr newSymbol,
897  Operation *from) {
898  return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
899 }
901  StringAttr newSymbol,
902  Operation *from) {
903  return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
904 }
906  StringAttr newSymbol,
907  Region *from) {
908  return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
909 }
911  StringAttr newSymbol,
912  Region *from) {
913  return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
914 }
915 
916 //===----------------------------------------------------------------------===//
917 // SymbolTableCollection
918 //===----------------------------------------------------------------------===//
919 
921  StringAttr symbol) {
922  return getSymbolTable(symbolTableOp).lookup(symbol);
923 }
925  SymbolRefAttr name) {
927  if (failed(lookupSymbolIn(symbolTableOp, name, symbols)))
928  return nullptr;
929  return symbols.back();
930 }
931 /// A variant of 'lookupSymbolIn' that returns all of the symbols referenced by
932 /// a given SymbolRefAttr. Returns failure if any of the nested references could
933 /// not be resolved.
936  SymbolRefAttr name,
937  SmallVectorImpl<Operation *> &symbols) {
938  auto lookupFn = [this](Operation *symbolTableOp, StringAttr symbol) {
939  return lookupSymbolIn(symbolTableOp, symbol);
940  };
941  return lookupSymbolInImpl(symbolTableOp, name, symbols, lookupFn);
942 }
943 
944 /// Returns the operation registered with the given symbol name within the
945 /// closest parent operation of, or including, 'from' with the
946 /// 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol was
947 /// found.
949  StringAttr symbol) {
950  Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
951  return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
952 }
953 Operation *
955  SymbolRefAttr symbol) {
956  Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
957  return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
958 }
959 
960 /// Lookup, or create, a symbol table for an operation.
962  auto it = symbolTables.try_emplace(op, nullptr);
963  if (it.second)
964  it.first->second = std::make_unique<SymbolTable>(op);
965  return *it.first->second;
966 }
967 
968 //===----------------------------------------------------------------------===//
969 // SymbolUserMap
970 //===----------------------------------------------------------------------===//
971 
973  Operation *symbolTableOp)
974  : symbolTable(symbolTable) {
975  // Walk each of the symbol tables looking for discardable callgraph nodes.
976  SmallVector<Operation *> symbols;
977  auto walkFn = [&](Operation *symbolTableOp, bool allUsesVisible) {
978  for (Operation &nestedOp : symbolTableOp->getRegion(0).getOps()) {
979  auto symbolUses = SymbolTable::getSymbolUses(&nestedOp);
980  assert(symbolUses && "expected uses to be valid");
981 
982  for (const SymbolTable::SymbolUse &use : *symbolUses) {
983  symbols.clear();
984  (void)symbolTable.lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
985  symbols);
986  for (Operation *symbolOp : symbols)
987  symbolToUsers[symbolOp].insert(use.getUser());
988  }
989  }
990  };
991  // We just set `allSymUsesVisible` to false here because it isn't necessary
992  // for building the user map.
993  SymbolTable::walkSymbolTables(symbolTableOp, /*allSymUsesVisible=*/false,
994  walkFn);
995 }
996 
998  StringAttr newSymbolName) {
999  auto it = symbolToUsers.find(symbol);
1000  if (it == symbolToUsers.end())
1001  return;
1002 
1003  // Replace the uses within the users of `symbol`.
1004  for (Operation *user : it->second)
1005  (void)SymbolTable::replaceAllSymbolUses(symbol, newSymbolName, user);
1006 
1007  // Move the current users of `symbol` to the new symbol if it is in the
1008  // symbol table.
1009  Operation *newSymbol =
1010  symbolTable.lookupSymbolIn(symbol->getParentOp(), newSymbolName);
1011  if (newSymbol != symbol) {
1012  // Transfer over the users to the new symbol. The reference to the old one
1013  // is fetched again as the iterator is invalidated during the insertion.
1014  auto newIt = symbolToUsers.try_emplace(newSymbol, SetVector<Operation *>{});
1015  auto oldIt = symbolToUsers.find(symbol);
1016  assert(oldIt != symbolToUsers.end() && "missing old users list");
1017  if (newIt.second)
1018  newIt.first->second = std::move(oldIt->second);
1019  else
1020  newIt.first->second.set_union(oldIt->second);
1021  symbolToUsers.erase(oldIt);
1022  }
1023 }
1024 
1025 //===----------------------------------------------------------------------===//
1026 // Visibility parsing implementation.
1027 //===----------------------------------------------------------------------===//
1028 
1030  NamedAttrList &attrs) {
1031  StringRef visibility;
1032  if (parser.parseOptionalKeyword(&visibility, {"public", "private", "nested"}))
1033  return failure();
1034 
1035  StringAttr visibilityAttr = parser.getBuilder().getStringAttr(visibility);
1036  attrs.push_back(parser.getBuilder().getNamedAttr(
1037  SymbolTable::getVisibilityAttrName(), visibilityAttr));
1038  return success();
1039 }
1040 
1041 //===----------------------------------------------------------------------===//
1042 // Symbol Interfaces
1043 //===----------------------------------------------------------------------===//
1044 
1045 /// Include the generated symbol interfaces.
1046 #include "mlir/IR/SymbolInterfaces.cpp.inc"
static constexpr const bool value
static Optional< WalkResult > walkSymbolUses(MutableArrayRef< Region > regions, function_ref< WalkResult(SymbolTable::SymbolUse)> callback)
Walk all of the uses, for any symbol, that are nested within the given regions, invoking the provided...
static LogicalResult collectValidReferencesFor(Operation *symbol, StringAttr symbolName, Operation *within, SmallVectorImpl< SymbolRefAttr > &results)
Computes the nested symbol reference attribute for the symbol 'symbolName' that are usable within the...
Definition: SymbolTable.cpp:39
static bool symbolKnownUseEmptyImpl(SymbolT symbol, IRUnitT *limit)
The implementation of SymbolTable::symbolKnownUseEmpty below.
static SmallVector< SymbolScope, 2 > collectSymbolScopes(Operation *symbol, Operation *limit)
Collect all of the symbol scopes from 'symbol' to (inclusive) 'limit'.
static WalkResult walkSymbolRefs(Operation *op, function_ref< WalkResult(SymbolTable::SymbolUse)> callback)
Walk all of the symbol references within the given operation, invoking the provided callback for each...
static StringAttr getNameIfSymbol(Operation *op)
Returns the string name of the given symbol, or null if this is not a symbol.
Definition: SymbolTable.cpp:27
static bool isReferencePrefixOf(SymbolRefAttr subRef, SymbolRefAttr ref)
Returns true if the given reference 'SubRef' is a sub reference of the reference 'ref',...
static SymbolRefAttr generateNewRefAttr(SymbolRefAttr oldAttr, FlatSymbolRefAttr newLeafAttr)
Generates a new symbol reference attribute with a new leaf reference.
static Optional< SymbolTable::UseRange > getSymbolUsesImpl(FromT from)
The implementation of SymbolTable::getSymbolUses below.
static Optional< WalkResult > walkSymbolTable(MutableArrayRef< Region > regions, function_ref< Optional< WalkResult >(Operation *)> callback)
Walk all of the operations within the given set of regions, without traversing into any nested symbol...
Definition: SymbolTable.cpp:80
static LogicalResult replaceAllSymbolUsesImpl(SymbolT symbol, StringAttr newSymbol, IRUnitT *limit)
The implementation of SymbolTable::replaceAllSymbolUses below.
static LogicalResult lookupSymbolInImpl(Operation *symbolTableOp, SymbolRefAttr symbol, SmallVectorImpl< Operation * > &symbols, function_ref< Operation *(Operation *, StringAttr)> lookupSymbolFn)
Internal implementation of lookupSymbolIn that allows for specialized implementations of the lookup f...
static bool isPotentiallyUnknownSymbolTable(Operation *op)
Return true if the given operation is unknown and may potentially define a symbol table.
Definition: SymbolTable.cpp:21
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
void replaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation.
void addReplacement(ReplaceFn< Attribute > fn)
Register a replacement function for mapping a given attribute or type.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
Block represents an ordered list of Operations.
Definition: Block.h:30
OpListType::iterator iterator
Definition: Block.h:129
StringAttr getStringAttr(const Twine &bytes)
Definition: Builders.cpp:243
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition: Builders.cpp:95
A symbol reference with a reference path containing a single element.
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
InFlightDiagnostic & append(Args &&...args) &
Append arguments to the diagnostic.
Definition: Diagnostics.h:333
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:56
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void push_back(NamedAttribute newAttribute)
Add an attribute with the specified name.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h:701
A trait used to provide symbol table functionalities to a region operation.
Definition: SymbolTable.h:341
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:31
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:528
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition: Operation.h:151
AttrClass getAttrOfType(StringAttr name)
Definition: Operation.h:375
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition: Operation.h:371
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:147
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:477
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:154
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:165
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:225
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition: Operation.h:486
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition: Operation.h:395
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:480
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
Definition: Operation.h:359
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition: Operation.h:194
Attribute removeAttr(StringAttr name)
Remove the attribute with the specified name if it exists.
Definition: Operation.h:407
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:512
void erase()
Remove this operation from its parent block and delete it.
Definition: Operation.cpp:418
This class represents success/failure for parsing-like operations that find it important to chain tog...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
iterator_range< OpIterator > getOps()
Definition: Region.h:172
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition: Region.h:200
bool empty()
Definition: Region.h:60
Block & front()
Definition: Region.h:65
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:245
Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Operation * lookupSymbolIn(Operation *symbolTableOp, StringAttr symbol)
Look up a symbol with the specified name within the specified symbol table operation,...
SymbolTable & getSymbolTable(Operation *op)
Lookup, or create, a symbol table for an operation.
This class represents a specific symbol use.
Definition: SymbolTable.h:147
This class implements a range of SymbolRef uses.
Definition: SymbolTable.h:167
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition: SymbolTable.h:23
static Visibility getSymbolVisibility(Operation *symbol)
Returns the visibility of the given symbol operation.
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition: SymbolTable.h:58
static void setSymbolVisibility(Operation *symbol, Visibility vis)
Sets the visibility of the given symbol operation.
static LogicalResult replaceAllSymbolUses(StringAttr oldSymbol, StringAttr newSymbol, Operation *from)
Attempt to replace all uses of the given symbol 'oldSymbol' with the provided symbol 'newSymbol' that...
Visibility
An enumeration detailing the different visibility types that a symbol may have.
Definition: SymbolTable.h:72
@ Nested
The symbol is visible to the current IR, which may include operations in symbol tables above the one ...
@ Public
The symbol is public and may be referenced anywhere internal or external to the visible references in...
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
static StringRef getVisibilityAttrName()
Return the name of the attribute used for symbol visibility.
Definition: SymbolTable.h:64
void erase(Operation *symbol)
Erase the given symbol from the table and delete the operation.
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
SymbolTable(Operation *symbolTableOp)
Build a symbol table with the symbols within the given operation.
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static void setSymbolName(Operation *symbol, StringAttr name)
Sets the name of the given symbol operation.
static bool symbolKnownUseEmpty(StringAttr symbol, Operation *from)
Return if the given symbol is known to have no uses that are nested within the given operation 'from'...
static void walkSymbolTables(Operation *op, bool allSymUsesVisible, function_ref< void(Operation *, bool)> callback)
Walks all symbol table operations nested within, and including, op.
static StringAttr getSymbolName(Operation *symbol)
Returns the name of the given symbol operation, aborting if no symbol is present.
static Optional< UseRange > getSymbolUses(Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
void remove(Operation *op)
Remove the given symbol from the table, without deleting it.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
void replaceAllUsesWith(Operation *symbol, StringAttr newSymbolName)
Replace all of the uses of the given symbol with newSymbolName.
SymbolUserMap(SymbolTableCollection &symbolTable, Operation *symbolTableOp)
Build a user map for all of the symbols defined in regions nested under 'symbolTableOp'.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
A utility result that is used to signal how to proceed with an ongoing walk:
Definition: Visitors.h:34
static WalkResult skip()
Definition: Visitors.h:52
static WalkResult advance()
Definition: Visitors.h:51
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition: Visitors.h:55
static WalkResult interrupt()
Definition: Visitors.h:50
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition: Visitors.cpp:24
LogicalResult verifySymbol(Operation *op)
LogicalResult verifySymbolTable(Operation *op)
ParseResult parseOptionalVisibilityKeyword(OpAsmParser &parser, NamedAttrList &attrs)
Parse an optional visibility attribute keyword (i.e., public, private, or nested) without quotes in a...
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
Definition: LogicalResult.h:68
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
Definition: AliasAnalysis.h:78
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26