12 #include "llvm/ADT/SetVector.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/ADT/StringSwitch.h"
42 assert(within->
isAncestor(symbol) &&
"expected 'within' to be an ancestor");
46 results.push_back(leafRef);
50 if (within == symbolTableOp)
55 StringAttr symbolNameId =
62 StringAttr symbolTableName =
getNameIfSymbol(symbolTableOp, symbolNameId);
68 if (symbolTableOp == within)
70 nestedRefs.insert(nestedRefs.begin(),
79 static std::optional<WalkResult>
83 while (!worklist.empty()) {
84 for (
Operation &op : worklist.pop_back_val()->getOps()) {
85 std::optional<WalkResult> result = callback(&op);
92 for (
Region ®ion : op.getRegions())
93 worklist.push_back(®ion);
103 static std::optional<WalkResult>
106 std::optional<WalkResult> result = callback(op);
118 : symbolTableOp(symbolTableOp) {
120 "expected operation to have SymbolTable trait");
122 "expected operation to have a single region");
124 "expected operation to have a single block");
133 auto inserted = symbolTable.insert({name, &op});
135 assert(inserted.second &&
136 "expected region to contain uniquely named symbol operations");
146 return symbolTable.lookup(name);
151 assert(name &&
"expected valid 'name' attribute");
153 "expected this operation to be inside of the operation with this "
156 auto it = symbolTable.find(name);
157 if (it != symbolTable.end() && it->second == op)
158 symbolTable.erase(it);
180 assert((insertPt == body.end() ||
181 insertPt->getParentOp() == symbolTableOp) &&
182 "expected insertPt to be in the associated module operation");
187 insertPt = std::prev(body.end());
189 body.getOperations().insert(insertPt, symbol);
192 "symbol is already inserted in another op");
197 if (symbolTable.insert({name, symbol}).second)
200 if (symbolTable.lookup(name) == symbol)
206 [&](StringRef candidate) {
208 .insert({StringAttr::get(context, candidate), symbol})
212 setSymbolName(symbol, nameBuffer);
213 return getSymbolName(symbol);
216 LogicalResult SymbolTable::rename(StringAttr from, StringAttr to) {
225 assert(from &&
"expected valid 'name' attribute");
227 "expected this operation to be inside of the operation with this "
229 assert(
lookup(from) == op &&
"current name does not resolve to op");
230 assert(
lookup(to) ==
nullptr &&
"new name already exists");
241 assert(
lookup(to) == op &&
"new name does not resolve to renamed op");
242 assert(
lookup(from) ==
nullptr &&
"old name still exists");
249 return rename(from, toAttr);
254 return rename(op, toAttr);
257 FailureOr<StringAttr>
267 prefix.push_back(
'_');
270 auto lookupNewName = [&](
SymbolTable *st) {
return st->lookup(newName); };
271 if (!lookupNewName(
this) && llvm::none_of(others, lookupNewName)) {
283 FailureOr<StringAttr>
286 assert(from &&
"expected valid 'name' attribute");
293 assert(name &&
"expected valid symbol name");
328 "unknown symbol visibility kind");
337 assert(from &&
"expected valid operation");
361 SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op);
362 allSymUsesVisible |= !symbol || symbol.isPrivate();
366 allSymUsesVisible =
true;
370 for (
Block &block : region)
377 callback(op, allSymUsesVisible);
394 for (
auto &op : region.
front())
400 SymbolRefAttr symbol) {
404 return resolvedSymbols.back();
410 Operation *symbolTableOp, SymbolRefAttr symbol,
416 symbolTableOp = lookupSymbolFn(symbolTableOp, symbol.getRootReference());
419 symbols.push_back(symbolTableOp);
423 if (nestedRefs.empty())
433 symbolTableOp = lookupSymbolFn(symbolTableOp, ref.
getAttr());
436 symbols.push_back(symbolTableOp);
438 symbols.push_back(lookupSymbolFn(symbolTableOp, symbol.getLeafReference()));
439 return success(symbols.back());
445 auto lookupFn = [](
Operation *symbolTableOp, StringAttr symbol) {
457 return symbolTableOp ?
lookupSymbolIn(symbolTableOp, symbol) :
nullptr;
460 SymbolRefAttr symbol) {
462 return symbolTableOp ?
lookupSymbolIn(symbolTableOp, symbol) :
nullptr;
467 switch (visibility) {
469 return os <<
"public";
471 return os <<
"private";
473 return os <<
"nested";
475 llvm_unreachable(
"Unexpected visibility");
485 <<
"Operations with a 'SymbolTable' must have exactly one region";
488 <<
"Operations with a 'SymbolTable' must have exactly one block";
493 for (
auto &op : block) {
501 auto it = nameToOrigLoc.try_emplace(nameAttr, op.
getLoc());
504 .
append(
"redefinition of symbol named '", nameAttr.getValue(),
"'")
505 .attachNote(it.first->second)
506 .
append(
"see existing symbol definition here");
512 auto verifySymbolUserFn = [&](
Operation *op) -> std::optional<WalkResult> {
513 if (SymbolUserOpInterface user = dyn_cast<SymbolUserOpInterface>(op))
514 return WalkResult(user.verifySymbolUses(symbolTable));
518 std::optional<WalkResult> result =
520 return success(result && !result->wasInterrupted());
526 return op->
emitOpError() <<
"requires string attribute '"
531 StringAttr visStrAttr = llvm::dyn_cast<StringAttr>(vis);
533 return op->
emitOpError() <<
"requires visibility attribute '"
535 <<
"' to be a string attribute, but got " << vis;
538 visStrAttr.getValue()))
540 <<
"visibility expected to be one of [\"public\", \"private\", "
541 "\"nested\"], but got "
558 [&](SymbolRefAttr symbolRef) {
559 if (callback({op, symbolRef}).wasInterrupted())
570 static std::optional<WalkResult>
574 [&](
Operation *op) -> std::optional<WalkResult> {
586 static std::optional<WalkResult>
618 template <
typename CallbackT,
619 std::enable_if_t<!std::is_same<
620 typename llvm::function_traits<CallbackT>::result_t,
621 void>::value> * =
nullptr>
622 std::optional<WalkResult>
walk(CallbackT cback) {
623 if (
Region *region = llvm::dyn_cast_if_present<Region *>(limit))
629 template <
typename CallbackT,
630 std::enable_if_t<std::is_same<
631 typename llvm::function_traits<CallbackT>::result_t,
632 void>::value> * =
nullptr>
633 std::optional<WalkResult>
walk(CallbackT cback) {
641 template <
typename CallbackT>
643 if (
Region *region = llvm::dyn_cast_if_present<Region *>(limit))
649 SymbolRefAttr symbol;
669 if (limitAncestor == symbol) {
678 limitAncestors.insert(limitAncestor);
679 }
while ((limitAncestor = limitAncestor->
getParentOp()));
684 if (limitAncestors.count(commonAncestor))
686 }
while ((commonAncestor = commonAncestor->
getParentOp()));
687 assert(commonAncestor &&
"'limit' and 'symbol' have no common ancestor");
692 bool collectedAllReferences = succeeded(
696 if (commonAncestor == limit) {
702 for (
size_t i = 0, e = references.size(); i != e;
705 scopes.push_back({references[i], &limitIt->
getRegion(0)});
713 if (!collectedAllReferences)
715 return {{references.back(), limit}};
724 scopes.back().limit = limit;
737 scopes.push_back({symbolRef, ®ion});
749 if (llvm::isa<FlatSymbolRefAttr>(ref) ||
750 ref.getRootReference() != subRef.getRootReference())
753 auto refLeafs = ref.getNestedReferences();
754 auto subRefLeafs = subRef.getNestedReferences();
755 return subRefLeafs.size() < refLeafs.size() &&
756 subRefLeafs == refLeafs.take_front(subRefLeafs.size());
764 template <
typename FromT>
766 std::vector<SymbolTable::SymbolUse> uses;
768 uses.push_back(symbolUse);
772 return result ? std::optional<SymbolTable::UseRange>(std::move(uses))
795 template <
typename SymbolT,
typename IRUnitT>
798 std::vector<SymbolTable::SymbolUse> uses;
801 if (isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef()))
802 uses.push_back(symbolUse);
814 -> std::optional<UseRange> {
818 -> std::optional<UseRange> {
822 -> std::optional<UseRange> {
826 -> std::optional<UseRange> {
835 template <
typename SymbolT,
typename IRUnitT>
840 return isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef())
841 ? WalkResult::interrupt()
842 : WalkResult::advance();
873 if (llvm::isa<FlatSymbolRefAttr>(oldAttr))
875 auto nestedRefs = llvm::to_vector<2>(oldAttr.getNestedReferences());
876 nestedRefs.back() = newLeafAttr;
881 template <
typename SymbolT,
typename IRUnitT>
887 SymbolRefAttr oldAttr = scope.symbol;
891 [&](SymbolRefAttr attr) -> std::pair<Attribute, WalkResult> {
898 auto oldNestedRefs = oldAttr.getNestedReferences();
899 auto nestedRefs = attr.getNestedReferences();
900 if (oldNestedRefs.empty())
904 auto newNestedRefs = llvm::to_vector<4>(nestedRefs);
905 newNestedRefs[oldNestedRefs.size() - 1] = newLeafAttr;
912 auto walkFn = [&](
Operation *op) -> std::optional<WalkResult> {
916 if (!scope.walkSymbolTable(walkFn))
928 StringAttr newSymbol,
933 StringAttr newSymbol,
938 StringAttr newSymbol,
943 StringAttr newSymbol,
957 SymbolRefAttr name) {
961 return symbols.back();
970 auto lookupFn = [
this](
Operation *symbolTableOp, StringAttr symbol) {
983 return symbolTableOp ?
lookupSymbolIn(symbolTableOp, symbol) :
nullptr;
987 SymbolRefAttr symbol) {
989 return symbolTableOp ?
lookupSymbolIn(symbolTableOp, symbol) :
nullptr;
994 auto it = symbolTables.try_emplace(op,
nullptr);
996 it.first->second = std::make_unique<SymbolTable>(op);
997 return *it.first->second;
1001 symbolTables.erase(op);
1009 StringAttr symbol) {
1010 return getSymbolTable(symbolTableOp).
lookup(symbol);
1020 SymbolRefAttr name) {
1024 return symbols.back();
1028 Operation *symbolTableOp, SymbolRefAttr name,
1030 auto lookupFn = [
this](
Operation *symbolTableOp, StringAttr symbol) {
1037 LockedSymbolTableCollection::getSymbolTable(
Operation *symbolTableOp) {
1041 llvm::sys::SmartScopedReader<true> lock(mutex);
1042 auto it = collection.symbolTables.find(symbolTableOp);
1043 if (it != collection.symbolTables.end())
1048 auto symbolTable = std::make_unique<SymbolTable>(symbolTableOp);
1050 llvm::sys::SmartScopedWriter<true> lock(mutex);
1051 return *collection.symbolTables
1052 .insert({symbolTableOp, std::move(symbolTable)})
1062 : symbolTable(symbolTable) {
1065 auto walkFn = [&](
Operation *symbolTableOp,
bool allUsesVisible) {
1068 assert(symbolUses &&
"expected uses to be valid");
1072 (void)symbolTable.
lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
1075 symbolToUsers[symbolOp].insert(use.getUser());
1086 StringAttr newSymbolName) {
1087 auto it = symbolToUsers.find(symbol);
1088 if (it == symbolToUsers.end())
1099 if (newSymbol != symbol) {
1102 auto newIt = symbolToUsers.try_emplace(newSymbol);
1103 auto oldIt = symbolToUsers.find(symbol);
1104 assert(oldIt != symbolToUsers.end() &&
"missing old users list");
1106 newIt.first->second = std::move(oldIt->second);
1108 newIt.first->second.set_union(oldIt->second);
1109 symbolToUsers.erase(oldIt);
1119 StringRef visibility;
1134 #include "mlir/IR/SymbolInterfaces.cpp.inc"
static MLIRContext * getContext(OpFoldResult val)
static std::optional< WalkResult > walkSymbolTable(MutableArrayRef< Region > regions, function_ref< std::optional< WalkResult >(Operation *)> callback)
Walk all of the operations within the given set of regions, without traversing into any nested symbol...
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...
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.
static bool isReferencePrefixOf(SymbolRefAttr subRef, SymbolRefAttr ref)
Returns true if the given reference 'SubRef' is a sub reference of the reference 'ref',...
static std::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 SymbolRefAttr generateNewRefAttr(SymbolRefAttr oldAttr, FlatSymbolRefAttr newLeafAttr)
Generates a new symbol reference attribute with a new leaf reference.
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 std::optional< SymbolTable::UseRange > getSymbolUsesImpl(FromT from)
The implementation of SymbolTable::getSymbolUses below.
static bool isPotentiallyUnknownSymbolTable(Operation *op)
Return true if the given operation is unknown and may potentially define a symbol table.
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.
This is an attribute/type replacer that is naively cached.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
OpListType::iterator iterator
StringAttr getStringAttr(const Twine &bytes)
NamedAttribute getNamedAttr(StringRef name, Attribute val)
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.
StringAttr getAttr() const
Returns the name of the held symbol reference as a StringAttr.
InFlightDiagnostic & append(Args &&...args) &
Append arguments to the diagnostic.
Operation * lookupSymbolIn(Operation *symbolTableOp, StringAttr symbol) override
Look up a symbol with the specified name within the specified symbol table operation,...
MLIRContext is the top-level object for a collection of MLIR operations.
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.
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
AttrClass getAttrOfType(StringAttr name)
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Attribute removeAttr(StringAttr name)
Remove the attribute with the specified name if it exists.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
iterator_range< OpIterator > getOps()
Operation * getParentOp()
Return the parent operation this region is attached to.
bool hasOneBlock()
Return true if this region has exactly one block.
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
virtual Operation * lookupSymbolIn(Operation *symbolTableOp, StringAttr symbol)
Look up a symbol with the specified name within the specified symbol table operation,...
virtual void invalidateSymbolTable(Operation *op)
Invalidate the cached symbol table for an operation.
virtual SymbolTable & getSymbolTable(Operation *op)
Lookup, or create, a symbol table for an operation.
This class represents a specific symbol use.
This class implements a range of SymbolRef uses.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
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.
static void setSymbolVisibility(Operation *symbol, Visibility vis)
Sets the visibility of the given symbol operation.
Operation * getOp() const
Returns the associated 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.
@ 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.
LogicalResult rename(StringAttr from, StringAttr to)
Renames the given op or the op refered to by the given name to the given new name and updates the sym...
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'...
FailureOr< StringAttr > renameToUnique(StringAttr from, ArrayRef< SymbolTable * > others)
Renames the given op or the op refered to by the given name to the a name that is unique within this ...
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 std::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'.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
void addReplacement(ReplaceFn< Attribute > fn)
Register a replacement function for mapping a given attribute or type.
void replaceElementsIn(Operation *op, bool replaceAttrs=true, bool replaceLocs=false, bool replaceTypes=false)
Replace the elements within the given operation.
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.
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.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)