MLIR 24.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
10#include "mlir/IR/Builders.h"
12#include "llvm/ADT/SetVector.h"
13#include "llvm/ADT/SmallString.h"
14#include <optional>
15
16using namespace mlir;
17
18/// Return true if the given operation is unknown and may potentially define a
19/// symbol table.
21 return op->getNumRegions() == 1 && !op->getDialect();
22}
23
24/// Returns the string name of the given symbol, or null if this is not a
25/// symbol.
26static StringAttr getNameIfSymbol(Operation *op) {
27 auto symbol = dyn_cast<SymbolOpInterface>(op);
28 if (!symbol)
29 return {};
30 return symbol.getNameAttr();
31}
32
33/// Computes the nested symbol reference attribute for the symbol 'symbolName'
34/// that are usable within the symbol table operations from 'symbol' as far up
35/// to the given operation 'within', where 'within' is an ancestor of 'symbol'.
36/// Returns success if all references up to 'within' could be computed.
37static LogicalResult
38collectValidReferencesFor(Operation *symbol, StringAttr symbolName,
39 Operation *within,
41 assert(within->isAncestor(symbol) && "expected 'within' to be an ancestor");
42
43 auto leafRef = FlatSymbolRefAttr::get(symbolName);
44 results.push_back(leafRef);
45
46 // Early exit for when 'within' is the parent of 'symbol'.
47 Operation *symbolTableOp = symbol->getParentOp();
48 if (within == symbolTableOp)
49 return success();
50
51 // Collect references until 'symbolTableOp' reaches 'within'.
52 SmallVector<FlatSymbolRefAttr, 1> nestedRefs(1, leafRef);
53 do {
54 // Each parent of 'symbol' should define a symbol table.
55 if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>())
56 return failure();
57 // Each parent of 'symbol' should also be a symbol.
58 StringAttr symbolTableName = getNameIfSymbol(symbolTableOp);
59 if (!symbolTableName)
60 return failure();
61 results.push_back(SymbolRefAttr::get(symbolTableName, nestedRefs));
62
63 symbolTableOp = symbolTableOp->getParentOp();
64 if (symbolTableOp == within)
65 break;
66 nestedRefs.insert(nestedRefs.begin(),
67 FlatSymbolRefAttr::get(symbolTableName));
68 } while (true);
69 return success();
70}
71
72/// Walk all of the operations within the given set of regions, without
73/// traversing into any nested symbol tables. Stops walking if the result of the
74/// callback is anything other than `WalkResult::advance`.
75static std::optional<WalkResult>
77 function_ref<std::optional<WalkResult>(Operation *)> callback) {
78 SmallVector<Region *, 1> worklist(llvm::make_pointer_range(regions));
79 while (!worklist.empty()) {
80 for (Operation &op : worklist.pop_back_val()->getOps()) {
81 std::optional<WalkResult> result = callback(&op);
83 return result;
84
85 // If this op defines a new symbol table scope, we can't traverse. Any
86 // symbol references nested within 'op' are different semantically.
87 if (!op.hasTrait<OpTrait::SymbolTable>()) {
88 for (Region &region : op.getRegions())
89 worklist.push_back(&region);
90 }
91 }
92 }
93 return WalkResult::advance();
94}
95
96/// Walk all of the operations nested under, and including, the given operation,
97/// without traversing into any nested symbol tables. Stops walking if the
98/// result of the callback is anything other than `WalkResult::advance`.
99static std::optional<WalkResult>
101 function_ref<std::optional<WalkResult>(Operation *)> callback) {
102 std::optional<WalkResult> result = callback(op);
104 return result;
105 return walkSymbolTable(op->getRegions(), callback);
106}
107
108//===----------------------------------------------------------------------===//
109// SymbolTable
110//===----------------------------------------------------------------------===//
111
112/// Build a symbol table with the symbols within the given operation.
114 : symbolTableOp(symbolTableOp) {
115 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>() &&
116 "expected operation to have SymbolTable trait");
117 assert(symbolTableOp->getNumRegions() == 1 &&
118 "expected operation to have a single region");
119 assert(symbolTableOp->getRegion(0).hasOneBlock() &&
120 "expected operation to have a single block");
121
122 for (auto &op : symbolTableOp->getRegion(0).front()) {
123 StringAttr name = getNameIfSymbol(&op);
124 if (!name)
125 continue;
126
127 // Silently skip duplicate symbol names. Duplicate symbols are an
128 // invalid IR condition diagnosed by the SymbolTable trait's
129 // verifyRegionTrait. The constructor may be called before verification
130 // completes (e.g., when IsolatedFromAbove ops look up symbols in an
131 // ancestor symbol table during verification), so an assert here would
132 // crash instead of producing a proper diagnostic.
133 symbolTable.try_emplace(name, &op);
134 }
135}
136
137/// Look up a symbol with the specified name, returning null if no such name
138/// exists. Names never include the @ on them.
139Operation *SymbolTable::lookup(StringRef name) const {
140 return lookup(StringAttr::get(symbolTableOp->getContext(), name));
141}
142Operation *SymbolTable::lookup(StringAttr name) const {
143 return symbolTable.lookup(name);
144}
145
147 StringAttr name = getNameIfSymbol(op);
148 assert(name && "expected valid 'name' attribute");
149 assert(op->getParentOp() == symbolTableOp &&
150 "expected this operation to be inside of the operation with this "
151 "SymbolTable");
152
153 auto it = symbolTable.find(name);
154 if (it != symbolTable.end() && it->second == op)
155 symbolTable.erase(it);
156}
157
159 remove(symbol);
160 symbol->erase();
161}
162
163// TODO: Consider if this should be renamed to something like insertOrUpdate
164/// Insert a new symbol into the table and associated operation if not already
165/// there and rename it as necessary to avoid collisions. Return the name of
166/// the symbol after insertion as attribute.
167StringAttr SymbolTable::insert(Operation *symbol, Block::iterator insertPt) {
168 // The symbol cannot be the child of another op and must be the child of the
169 // symbolTableOp after this.
170 //
171 // TODO: consider if SymbolTable's constructor should behave the same.
172 if (!symbol->getParentOp()) {
173 auto &body = symbolTableOp->getRegion(0).front();
174 if (insertPt == Block::iterator()) {
175 insertPt = Block::iterator(body.end());
176 } else {
177 assert((insertPt == body.end() ||
178 insertPt->getParentOp() == symbolTableOp) &&
179 "expected insertPt to be in the associated module operation");
180 }
181 // Insert before the terminator, if any.
182 if (insertPt == Block::iterator(body.end()) && !body.empty() &&
183 std::prev(body.end())->hasTrait<OpTrait::IsTerminator>())
184 insertPt = std::prev(body.end());
185
186 body.getOperations().insert(insertPt, symbol);
187 }
188 assert(symbol->getParentOp() == symbolTableOp &&
189 "symbol is already inserted in another op");
190
191 // Add this symbol to the symbol table, uniquing the name if a conflict is
192 // detected.
193 StringAttr name = getSymbolName(symbol);
194 if (symbolTable.insert({name, symbol}).second)
195 return name;
196 // If the symbol was already in the table, also return.
197 if (symbolTable.lookup(name) == symbol)
198 return name;
199
200 MLIRContext *context = symbol->getContext();
202 name.getValue(),
203 [&](StringRef candidate) {
204 return !symbolTable
205 .insert({StringAttr::get(context, candidate), symbol})
206 .second;
207 },
208 uniquingCounter);
209 setSymbolName(symbol, nameBuffer);
210 return getSymbolName(symbol);
211}
212
213LogicalResult SymbolTable::rename(StringAttr from, StringAttr to) {
214 Operation *op = lookup(from);
215 return rename(op, to);
216}
217
218LogicalResult SymbolTable::rename(Operation *op, StringAttr to) {
219 StringAttr from = getNameIfSymbol(op);
220 (void)from;
221
222 assert(from && "expected valid 'name' attribute");
223 assert(op->getParentOp() == symbolTableOp &&
224 "expected this operation to be inside of the operation with this "
225 "SymbolTable");
226 assert(lookup(from) == op && "current name does not resolve to op");
227 assert(lookup(to) == nullptr && "new name already exists");
228
229 if (failed(SymbolTable::replaceAllSymbolUses(op, to, getOp())))
230 return failure();
231
232 // Remove op with old name, change name, add with new name. The order is
233 // important here due to how `remove` and `insert` rely on the op name.
234 remove(op);
235 setSymbolName(op, to);
236 insert(op);
237
238 assert(lookup(to) == op && "new name does not resolve to renamed op");
239 assert(lookup(from) == nullptr && "old name still exists");
240
241 return success();
242}
243
244LogicalResult SymbolTable::rename(StringAttr from, StringRef to) {
245 auto toAttr = StringAttr::get(getOp()->getContext(), to);
246 return rename(from, toAttr);
247}
248
249LogicalResult SymbolTable::rename(Operation *op, StringRef to) {
250 auto toAttr = StringAttr::get(getOp()->getContext(), to);
251 return rename(op, toAttr);
252}
253
254FailureOr<StringAttr>
257
258 // Determine new name that is unique in all symbol tables.
259 StringAttr newName;
260 {
261 MLIRContext *context = oldName.getContext();
262 SmallString<64> prefix = oldName.getValue();
263 int uniqueId = 0;
264 prefix.push_back('_');
265 while (true) {
266 newName = StringAttr::get(context, prefix + Twine(uniqueId++));
267 auto lookupNewName = [&](SymbolTable *st) { return st->lookup(newName); };
268 if (!lookupNewName(this) && llvm::none_of(others, lookupNewName)) {
269 break;
270 }
271 }
272 }
273
274 // Apply renaming.
275 if (failed(rename(oldName, newName)))
276 return failure();
277 return newName;
278}
279
280FailureOr<StringAttr>
282 StringAttr from = getNameIfSymbol(op);
283 assert(from && "expected valid 'name' attribute");
284 return renameToUnique(from, others);
285}
286
287/// Returns the name of the given symbol operation.
289 auto symbolOp = cast<SymbolOpInterface>(symbol);
290 StringAttr name = symbolOp.getNameAttr();
291 assert(name && "expected valid symbol name");
292 return name;
293}
294
295/// Sets the name of the given symbol operation.
296void SymbolTable::setSymbolName(Operation *symbol, StringAttr name) {
297 auto symbolOp = cast<SymbolOpInterface>(symbol);
298 symbolOp.setSymbolName(name);
299}
300
301/// Returns the visibility of the given symbol operation.
303 auto symbolOp = dyn_cast<SymbolOpInterface>(symbol);
304 assert(symbolOp && "expected valid symbol operation");
305 return symbolOp.getVisibility();
306}
307/// Sets the visibility of the given symbol operation.
309 auto symbolOp = dyn_cast<SymbolOpInterface>(symbol);
310 assert(symbolOp && "expected valid symbol operation");
311 symbolOp.setVisibility(vis);
312}
313
314/// Returns the nearest symbol table from a given operation `from`. Returns
315/// nullptr if no valid parent symbol table could be found.
317 assert(from && "expected valid operation");
319 return nullptr;
320
321 while (!from->hasTrait<OpTrait::SymbolTable>()) {
322 from = from->getParentOp();
323
324 // Check that this is a valid op and isn't an unknown symbol table.
325 if (!from || isPotentiallyUnknownSymbolTable(from))
326 return nullptr;
327 }
328 return from;
329}
330
331/// Walks all symbol table operations nested within, and including, `op`. For
332/// each symbol table operation, the provided callback is invoked with the op
333/// and a boolean signifying if the symbols within that symbol table can be
334/// treated as if all uses are visible. `allSymUsesVisible` identifies whether
335/// all of the symbol uses of symbols within `op` are visible.
337 Operation *op, bool allSymUsesVisible,
338 function_ref<void(Operation *, bool)> callback) {
339 bool isSymbolTable = op->hasTrait<OpTrait::SymbolTable>();
340 if (isSymbolTable) {
341 SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op);
342 allSymUsesVisible |= !symbol || symbol.isPrivate();
343 } else {
344 // Otherwise if 'op' is not a symbol table, any nested symbols are
345 // guaranteed to be hidden.
346 allSymUsesVisible = true;
347 }
348
349 for (Region &region : op->getRegions())
350 for (Block &block : region)
351 for (Operation &nestedOp : block)
352 walkSymbolTables(&nestedOp, allSymUsesVisible, callback);
353
354 // If 'op' had the symbol table trait, visit it after any nested symbol
355 // tables.
356 if (isSymbolTable)
357 callback(op, allSymUsesVisible);
358}
359
360/// Returns the operation registered with the given symbol name with the
361/// regions of 'symbolTableOp'. 'symbolTableOp' is required to be an operation
362/// with the 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol
363/// was found.
365 StringAttr symbol) {
366 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
367 Region &region = symbolTableOp->getRegion(0);
368 if (region.empty())
369 return nullptr;
370
371 // Look for a symbol with the given name.
372 for (auto &op : region.front())
373 if (getNameIfSymbol(&op) == symbol)
374 return &op;
375 return nullptr;
376}
378 SymbolRefAttr symbol) {
379 SmallVector<Operation *, 4> resolvedSymbols;
380 if (failed(lookupSymbolIn(symbolTableOp, symbol, resolvedSymbols)))
381 return nullptr;
382 return resolvedSymbols.back();
383}
384
385/// Internal implementation of `lookupSymbolIn` that allows for specialized
386/// implementations of the lookup function.
387static LogicalResult lookupSymbolInImpl(
388 Operation *symbolTableOp, SymbolRefAttr symbol,
390 function_ref<Operation *(Operation *, StringAttr)> lookupSymbolFn) {
391 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
392
393 // Lookup the root reference for this symbol.
394 auto *symbolOp = lookupSymbolFn(symbolTableOp, symbol.getRootReference());
395 if (!symbolOp)
396 return failure();
397 symbols.push_back(symbolOp);
398
399 // Lookup each of the nested references.
400 for (FlatSymbolRefAttr ref : symbol.getNestedReferences()) {
401 // Check that we have a valid symbol table to lookup ref.
402 if (!symbolOp->hasTrait<OpTrait::SymbolTable>())
403 return failure();
404 symbolOp = lookupSymbolFn(symbolOp, ref.getAttr());
405 if (!symbolOp)
406 return failure();
407 // If the nested symbol is private, lookup failed.
408 auto nestedSymbol = dyn_cast<SymbolOpInterface>(symbolOp);
409 if (nestedSymbol && nestedSymbol.isPrivate())
410 return failure();
411 symbols.push_back(symbolOp);
412 }
413 return success();
414}
415
416LogicalResult
417SymbolTable::lookupSymbolIn(Operation *symbolTableOp, SymbolRefAttr symbol,
419 auto lookupFn = [](Operation *symbolTableOp, StringAttr symbol) {
420 return lookupSymbolIn(symbolTableOp, symbol);
421 };
422 return lookupSymbolInImpl(symbolTableOp, symbol, symbols, lookupFn);
423}
424
425/// Returns the operation registered with the given symbol name within the
426/// closes parent operation with the 'OpTrait::SymbolTable' trait. Returns
427/// nullptr if no valid symbol was found.
429 StringAttr symbol) {
430 Operation *symbolTableOp = getNearestSymbolTable(from);
431 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
432}
434 SymbolRefAttr symbol) {
435 Operation *symbolTableOp = getNearestSymbolTable(from);
436 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
437}
438
440 SymbolTable::Visibility visibility) {
441 switch (visibility) {
443 return os << "public";
445 return os << "private";
447 return os << "nested";
448 }
449 llvm_unreachable("Unexpected visibility");
450}
451
452//===----------------------------------------------------------------------===//
453// SymbolTable Trait Types
454//===----------------------------------------------------------------------===//
455
457 if (op->getNumRegions() != 1)
458 return op->emitOpError()
459 << "Operations with a 'SymbolTable' must have exactly one region";
460 if (!op->getRegion(0).hasOneBlock())
461 return op->emitOpError()
462 << "Operations with a 'SymbolTable' must have exactly one block";
463
464 // Check that all symbols are uniquely named within child regions.
465 DenseMap<Attribute, Location> nameToOrigLoc;
466 for (auto &block : op->getRegion(0)) {
467 for (auto &op : block) {
468 // Check for a symbol name attribute.
469 StringAttr nameAttr = getNameIfSymbol(&op);
470 if (!nameAttr)
471 continue;
472
473 // Try to insert this symbol into the table.
474 auto it = nameToOrigLoc.try_emplace(nameAttr, op.getLoc());
475 if (!it.second)
476 return op.emitError()
477 .append("redefinition of symbol named '", nameAttr.getValue(), "'")
478 .attachNote(it.first->second)
479 .append("see existing symbol definition here");
480 }
481 }
482
483 // Verify any nested symbol user operations.
484 SymbolTableCollection symbolTable;
485 auto verifySymbolUserFn = [&](Operation *op) -> std::optional<WalkResult> {
486 if (SymbolUserOpInterface user = dyn_cast<SymbolUserOpInterface>(op))
487 if (failed(user.verifySymbolUses(symbolTable)))
488 return WalkResult::interrupt();
489 for (auto &attr : op->getDiscardableAttrDictionary().getValue()) {
490 if (auto user = dyn_cast<SymbolUserAttrInterface>(attr.getValue())) {
491 if (failed(user.verifySymbolUses(op, symbolTable)))
492 return WalkResult::interrupt();
493 }
494 }
495 return WalkResult::advance();
496 };
497
498 std::optional<WalkResult> result =
499 walkSymbolTable(op->getRegions(), verifySymbolUserFn);
500 return success(result && !result->wasInterrupted());
501}
502
503LogicalResult detail::verifySymbol(Operation *op) {
504 // Verify the name attribute.
505 if (!cast<SymbolOpInterface>(op).getNameAttr())
506 return op->emitOpError("requires a symbol name");
507
508 // Verify the visibility attribute.
509 StringRef visAttrName =
510 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
511 if (Attribute vis = op->getInherentAttr(visAttrName).value_or(Attribute{})) {
512 StringAttr visStrAttr = llvm::dyn_cast<StringAttr>(vis);
513 if (!visStrAttr)
514 return op->emitOpError()
515 << "requires visibility attribute '" << visAttrName
516 << "' to be a string attribute, but got " << vis;
517
518 if (!llvm::is_contained(ArrayRef<StringRef>{"public", "private", "nested"},
519 visStrAttr.getValue()))
520 return op->emitOpError()
521 << "visibility expected to be one of [\"public\", \"private\", "
522 "\"nested\"], but got "
523 << visStrAttr;
524 }
525 return success();
526}
527
528//===----------------------------------------------------------------------===//
529// Symbol Use Lists
530//===----------------------------------------------------------------------===//
531
532/// Walk all of the symbol references within the given operation, invoking the
533/// provided callback for each found use. The callbacks takes the use of the
534/// symbol.
535static WalkResult
538 bool interrupted = false;
539 auto walk = [&](Attribute attr) {
540 if (interrupted)
541 return;
542 interrupted = attr.walk<WalkOrder::PreOrder>([&](SymbolRefAttr symbolRef) {
543 if (callback({op, symbolRef}).wasInterrupted())
544 return WalkResult::interrupt();
545
546 // Don't walk nested references.
547 return WalkResult::skip();
548 })
549 .wasInterrupted();
550 };
551 walk(op->getRawDictionaryAttrs());
553 op, [&](StringRef, Attribute &attr) { walk(attr); });
554 return interrupted ? WalkResult::interrupt() : WalkResult::advance();
555}
556
557/// Walk all of the uses, for any symbol, that are nested within the given
558/// regions, invoking the provided callback for each. This does not traverse
559/// into any nested symbol tables.
560static std::optional<WalkResult>
563 return walkSymbolTable(regions,
564 [&](Operation *op) -> std::optional<WalkResult> {
565 // Check that this isn't a potentially unknown symbol
566 // table.
568 return std::nullopt;
569
570 return walkSymbolRefs(op, callback);
571 });
572}
573/// Walk all of the uses, for any symbol, that are nested within the given
574/// operation 'from', invoking the provided callback for each. This does not
575/// traverse into any nested symbol tables.
576static std::optional<WalkResult>
579 // If this operation has regions, and it, as well as its dialect, isn't
580 // registered then conservatively fail. The operation may define a
581 // symbol table, so we can't opaquely know if we should traverse to find
582 // nested uses.
584 return std::nullopt;
585
586 // Walk the uses on this operation.
587 if (walkSymbolRefs(from, callback).wasInterrupted())
588 return WalkResult::interrupt();
589
590 // Only recurse if this operation is not a symbol table. A symbol table
591 // defines a new scope, so we can't walk the attributes from within the symbol
592 // table op.
593 if (!from->hasTrait<OpTrait::SymbolTable>())
594 return walkSymbolUses(from->getRegions(), callback);
595 return WalkResult::advance();
596}
597
598namespace {
599/// This class represents a single symbol scope. A symbol scope represents the
600/// set of operations nested within a symbol table that may reference symbols
601/// within that table. A symbol scope does not contain the symbol table
602/// operation itself, just its contained operations. A scope ends at leaf
603/// operations or another symbol table operation.
604struct SymbolScope {
605 /// Walk the symbol uses within this scope, invoking the given callback.
606 /// This variant is used when the callback type matches that expected by
607 /// 'walkSymbolUses'.
608 template <typename CallbackT,
609 std::enable_if_t<!std::is_same<
610 typename llvm::function_traits<CallbackT>::result_t,
611 void>::value> * = nullptr>
612 std::optional<WalkResult> walk(CallbackT cback) {
613 if (Region *region = llvm::dyn_cast_if_present<Region *>(limit))
614 return walkSymbolUses(*region, cback);
615 return walkSymbolUses(cast<Operation *>(limit), cback);
616 }
617 /// This variant is used when the callback type matches a stripped down type:
618 /// void(SymbolTable::SymbolUse use)
619 template <typename CallbackT,
620 std::enable_if_t<std::is_same<
621 typename llvm::function_traits<CallbackT>::result_t,
622 void>::value> * = nullptr>
623 std::optional<WalkResult> walk(CallbackT cback) {
624 return walk([=](SymbolTable::SymbolUse use) {
625 return cback(use), WalkResult::advance();
626 });
627 }
628
629 /// Walk all of the operations nested under the current scope without
630 /// traversing into any nested symbol tables.
631 template <typename CallbackT>
632 std::optional<WalkResult> walkSymbolTable(CallbackT &&cback) {
633 if (Region *region = llvm::dyn_cast_if_present<Region *>(limit))
634 return ::walkSymbolTable(*region, cback);
635 return ::walkSymbolTable(cast<Operation *>(limit), cback);
636 }
637
638 /// The representation of the symbol within this scope.
639 SymbolRefAttr symbol;
640
641 /// The IR unit representing this scope.
642 llvm::PointerUnion<Operation *, Region *> limit;
643};
644} // namespace
645
646/// Collect all of the symbol scopes from 'symbol' to (inclusive) 'limit'.
648 Operation *limit) {
649 StringAttr symName = SymbolTable::getSymbolName(symbol);
650 assert(!symbol->hasTrait<OpTrait::SymbolTable>() || symbol != limit);
651
652 // Compute the ancestors of 'limit'.
655 limitAncestors;
656 Operation *limitAncestor = limit;
657 do {
658 // Check to see if 'symbol' is an ancestor of 'limit'.
659 if (limitAncestor == symbol) {
660 // Check that the nearest symbol table is 'symbol's parent. SymbolRefAttr
661 // doesn't support parent references.
663 symbol->getParentOp())
664 return {{SymbolRefAttr::get(symName), limit}};
665 return {};
666 }
667
668 limitAncestors.insert(limitAncestor);
669 } while ((limitAncestor = limitAncestor->getParentOp()));
670
671 // Try to find the first ancestor of 'symbol' that is an ancestor of 'limit'.
672 Operation *commonAncestor = symbol->getParentOp();
673 do {
674 if (limitAncestors.count(commonAncestor))
675 break;
676 } while ((commonAncestor = commonAncestor->getParentOp()));
677 assert(commonAncestor && "'limit' and 'symbol' have no common ancestor");
678
679 // Compute the set of valid nested references for 'symbol' as far up to the
680 // common ancestor as possible.
682 bool collectedAllReferences = succeeded(
683 collectValidReferencesFor(symbol, symName, commonAncestor, references));
684
685 // Handle the case where the common ancestor is 'limit'.
686 if (commonAncestor == limit) {
688
689 // Walk each of the ancestors of 'symbol', calling the compute function for
690 // each one.
691 Operation *limitIt = symbol->getParentOp();
692 for (size_t i = 0, e = references.size(); i != e;
693 ++i, limitIt = limitIt->getParentOp()) {
694 assert(limitIt->hasTrait<OpTrait::SymbolTable>());
695 scopes.push_back({references[i], &limitIt->getRegion(0)});
696 }
697 return scopes;
698 }
699
700 // Otherwise, we just need the symbol reference for 'symbol' that will be
701 // used within 'limit'. This is the last reference in the list we computed
702 // above if we were able to collect all references.
703 if (!collectedAllReferences)
704 return {};
705 return {{references.back(), limit}};
706}
708 Region *limit) {
709 auto scopes = collectSymbolScopes(symbol, limit->getParentOp());
710
711 // If we collected some scopes to walk, make sure to constrain the one for
712 // limit to the specific region requested.
713 if (!scopes.empty())
714 scopes.back().limit = limit;
715 return scopes;
716}
718 Region *limit) {
719 return {{SymbolRefAttr::get(symbol), limit}};
720}
721
723 Operation *limit) {
725 auto symbolRef = SymbolRefAttr::get(symbol);
726 for (auto &region : limit->getRegions())
727 scopes.push_back({symbolRef, &region});
728 return scopes;
729}
730
731/// Returns true if the given reference 'SubRef' is a sub reference of the
732/// reference 'ref', i.e. 'ref' is a further qualified reference.
733static bool isReferencePrefixOf(SymbolRefAttr subRef, SymbolRefAttr ref) {
734 if (ref == subRef)
735 return true;
736
737 // If the references are not pointer equal, check to see if `subRef` is a
738 // prefix of `ref`.
739 if (llvm::isa<FlatSymbolRefAttr>(ref) ||
740 ref.getRootReference() != subRef.getRootReference())
741 return false;
742
743 auto refLeafs = ref.getNestedReferences();
744 auto subRefLeafs = subRef.getNestedReferences();
745 return subRefLeafs.size() < refLeafs.size() &&
746 subRefLeafs == refLeafs.take_front(subRefLeafs.size());
747}
748
749//===----------------------------------------------------------------------===//
750// SymbolTable::getSymbolUses
751//===----------------------------------------------------------------------===//
752
753/// The implementation of SymbolTable::getSymbolUses below.
754template <typename FromT>
755static std::optional<SymbolTable::UseRange> getSymbolUsesImpl(FromT from) {
756 std::vector<SymbolTable::SymbolUse> uses;
757 auto walkFn = [&](SymbolTable::SymbolUse symbolUse) {
758 uses.push_back(symbolUse);
759 return WalkResult::advance();
760 };
761 auto result = walkSymbolUses(from, walkFn);
762 return result ? std::optional<SymbolTable::UseRange>(std::move(uses))
763 : std::nullopt;
764}
765
766/// Get an iterator range for all of the uses, for any symbol, that are nested
767/// within the given operation 'from'. This does not traverse into any nested
768/// symbol tables, and will also only return uses on 'from' if it does not
769/// also define a symbol table. This is because we treat the region as the
770/// boundary of the symbol table, and not the op itself. This function returns
771/// std::nullopt if there are any unknown operations that may potentially be
772/// symbol tables.
773auto SymbolTable::getSymbolUses(Operation *from) -> std::optional<UseRange> {
774 return getSymbolUsesImpl(from);
775}
776auto SymbolTable::getSymbolUses(Region *from) -> std::optional<UseRange> {
778}
779
780//===----------------------------------------------------------------------===//
781// SymbolTable::getSymbolUses
782//===----------------------------------------------------------------------===//
783
784/// The implementation of SymbolTable::getSymbolUses below.
785template <typename SymbolT, typename IRUnitT>
786static std::optional<SymbolTable::UseRange> getSymbolUsesImpl(SymbolT symbol,
787 IRUnitT *limit) {
788 std::vector<SymbolTable::SymbolUse> uses;
789 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
790 if (!scope.walk([&](SymbolTable::SymbolUse symbolUse) {
791 if (isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef()))
792 uses.push_back(symbolUse);
793 }))
794 return std::nullopt;
795 }
796 return SymbolTable::UseRange(std::move(uses));
797}
798
799/// Get all of the uses of the given symbol that are nested within the given
800/// operation 'from'. This does not traverse into any nested symbol tables.
801/// This function returns std::nullopt if there are any unknown operations that
802/// may potentially be symbol tables.
803auto SymbolTable::getSymbolUses(StringAttr symbol, Operation *from)
804 -> std::optional<UseRange> {
805 return getSymbolUsesImpl(symbol, from);
806}
808 -> std::optional<UseRange> {
809 return getSymbolUsesImpl(symbol, from);
810}
811auto SymbolTable::getSymbolUses(StringAttr symbol, Region *from)
812 -> std::optional<UseRange> {
813 return getSymbolUsesImpl(symbol, from);
814}
816 -> std::optional<UseRange> {
817 return getSymbolUsesImpl(symbol, from);
818}
819
820//===----------------------------------------------------------------------===//
821// SymbolTable::symbolKnownUseEmpty
822//===----------------------------------------------------------------------===//
823
824/// The implementation of SymbolTable::symbolKnownUseEmpty below.
825template <typename SymbolT, typename IRUnitT>
826static bool symbolKnownUseEmptyImpl(SymbolT symbol, IRUnitT *limit) {
827 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
828 // Walk all of the symbol uses looking for a reference to 'symbol'.
829 if (scope.walk([&](SymbolTable::SymbolUse symbolUse) {
830 return isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef())
831 ? WalkResult::interrupt()
832 : WalkResult::advance();
833 }) != WalkResult::advance())
834 return false;
835 }
836 return true;
837}
838
839/// Return if the given symbol is known to have no uses that are nested within
840/// the given operation 'from'. This does not traverse into any nested symbol
841/// tables. This function will also return false if there are any unknown
842/// operations that may potentially be symbol tables.
843bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Operation *from) {
844 return symbolKnownUseEmptyImpl(symbol, from);
845}
847 return symbolKnownUseEmptyImpl(symbol, from);
848}
849bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Region *from) {
850 return symbolKnownUseEmptyImpl(symbol, from);
851}
853 return symbolKnownUseEmptyImpl(symbol, from);
854}
855
856//===----------------------------------------------------------------------===//
857// SymbolTable::replaceAllSymbolUses
858//===----------------------------------------------------------------------===//
859
860/// Generates a new symbol reference attribute with a new leaf reference.
861static SymbolRefAttr generateNewRefAttr(SymbolRefAttr oldAttr,
862 FlatSymbolRefAttr newLeafAttr) {
863 if (llvm::isa<FlatSymbolRefAttr>(oldAttr))
864 return newLeafAttr;
865 auto nestedRefs = llvm::to_vector<2>(oldAttr.getNestedReferences());
866 nestedRefs.back() = newLeafAttr;
867 return SymbolRefAttr::get(oldAttr.getRootReference(), nestedRefs);
868}
869
870/// The implementation of SymbolTable::replaceAllSymbolUses below.
871template <typename SymbolT, typename IRUnitT>
872static LogicalResult
873replaceAllSymbolUsesImpl(SymbolT symbol, StringAttr newSymbol, IRUnitT *limit) {
874 // Generate a new attribute to replace the given attribute.
875 FlatSymbolRefAttr newLeafAttr = FlatSymbolRefAttr::get(newSymbol);
876 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
877 SymbolRefAttr oldAttr = scope.symbol;
878 SymbolRefAttr newAttr = generateNewRefAttr(scope.symbol, newLeafAttr);
879 AttrTypeReplacer replacer;
880 replacer.addReplacement(
881 [&](SymbolRefAttr attr) -> std::pair<Attribute, WalkResult> {
882 // Regardless of the match, don't walk nested SymbolRefAttrs, we don't
883 // want to accidentally replace an inner reference.
884 if (attr == oldAttr)
885 return {newAttr, WalkResult::skip()};
886 // Handle prefix matches.
887 if (isReferencePrefixOf(oldAttr, attr)) {
888 auto oldNestedRefs = oldAttr.getNestedReferences();
889 auto nestedRefs = attr.getNestedReferences();
890 if (oldNestedRefs.empty())
891 return {SymbolRefAttr::get(newSymbol, nestedRefs),
893
894 auto newNestedRefs = llvm::to_vector<4>(nestedRefs);
895 newNestedRefs[oldNestedRefs.size() - 1] = newLeafAttr;
896 return {SymbolRefAttr::get(attr.getRootReference(), newNestedRefs),
898 }
899 return {attr, WalkResult::skip()};
900 });
901
902 auto walkFn = [&](Operation *op) -> std::optional<WalkResult> {
903 replacer.replaceElementsIn(op);
904 return WalkResult::advance();
905 };
906 if (!scope.walkSymbolTable(walkFn))
907 return failure();
908 }
909 return success();
910}
911
912/// Attempt to replace all uses of the given symbol 'oldSymbol' with the
913/// provided symbol 'newSymbol' that are nested within the given operation
914/// 'from'. This does not traverse into any nested symbol tables. If there are
915/// any unknown operations that may potentially be symbol tables, no uses are
916/// replaced and failure is returned.
917LogicalResult SymbolTable::replaceAllSymbolUses(StringAttr oldSymbol,
918 StringAttr newSymbol,
919 Operation *from) {
920 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
921}
923 StringAttr newSymbol,
924 Operation *from) {
925 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
926}
927LogicalResult SymbolTable::replaceAllSymbolUses(StringAttr oldSymbol,
928 StringAttr newSymbol,
929 Region *from) {
930 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
931}
933 StringAttr newSymbol,
934 Region *from) {
935 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
936}
937
938//===----------------------------------------------------------------------===//
939// SymbolTableCollection
940//===----------------------------------------------------------------------===//
941
943 StringAttr symbol) {
944 return getSymbolTable(symbolTableOp).lookup(symbol);
945}
947 SymbolRefAttr name) {
949 if (failed(lookupSymbolIn(symbolTableOp, name, symbols)))
950 return nullptr;
951 return symbols.back();
952}
953/// A variant of 'lookupSymbolIn' that returns all of the symbols referenced by
954/// a given SymbolRefAttr. Returns failure if any of the nested references could
955/// not be resolved.
956LogicalResult
958 SymbolRefAttr name,
960 auto lookupFn = [this](Operation *symbolTableOp, StringAttr symbol) {
961 return lookupSymbolIn(symbolTableOp, symbol);
962 };
963 return lookupSymbolInImpl(symbolTableOp, name, symbols, lookupFn);
964}
965
966/// Returns the operation registered with the given symbol name within the
967/// closest parent operation of, or including, 'from' with the
968/// 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol was
969/// found.
971 StringAttr symbol) {
972 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
973 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
974}
975Operation *
977 SymbolRefAttr symbol) {
978 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
979 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
980}
981
982/// Lookup, or create, a symbol table for an operation.
984 auto it = symbolTables.try_emplace(op, nullptr);
985 if (it.second)
986 it.first->second = std::make_unique<SymbolTable>(op);
987 return *it.first->second;
988}
989
991 symbolTables.erase(op);
992}
993
994//===----------------------------------------------------------------------===//
995// LockedSymbolTableCollection
996//===----------------------------------------------------------------------===//
997
999 StringAttr symbol) {
1000 return getSymbolTable(symbolTableOp).lookup(symbol);
1001}
1002
1003Operation *
1005 FlatSymbolRefAttr symbol) {
1006 return lookupSymbolIn(symbolTableOp, symbol.getAttr());
1007}
1008
1010 SymbolRefAttr name) {
1012 if (failed(lookupSymbolIn(symbolTableOp, name, symbols)))
1013 return nullptr;
1014 return symbols.back();
1015}
1016
1018 Operation *symbolTableOp, SymbolRefAttr name,
1020 auto lookupFn = [this](Operation *symbolTableOp, StringAttr symbol) {
1021 return lookupSymbolIn(symbolTableOp, symbol);
1022 };
1023 return lookupSymbolInImpl(symbolTableOp, name, symbols, lookupFn);
1024}
1025
1027LockedSymbolTableCollection::getSymbolTable(Operation *symbolTableOp) {
1028 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
1029 // Try to find an existing symbol table.
1030 {
1031 llvm::sys::SmartScopedReader<true> lock(mutex);
1032 auto it = collection.symbolTables.find(symbolTableOp);
1033 if (it != collection.symbolTables.end())
1034 return *it->second;
1035 }
1036 // Create a symbol table for the operation. Perform construction outside of
1037 // the critical section.
1038 auto symbolTable = std::make_unique<SymbolTable>(symbolTableOp);
1039 // Insert the constructed symbol table.
1040 llvm::sys::SmartScopedWriter<true> lock(mutex);
1041 return *collection.symbolTables
1042 .insert({symbolTableOp, std::move(symbolTable)})
1043 .first->second;
1044}
1045
1046//===----------------------------------------------------------------------===//
1047// SymbolUserMap
1048//===----------------------------------------------------------------------===//
1049
1051 Operation *symbolTableOp)
1052 : symbolTable(symbolTable) {
1053 // Walk each of the symbol tables looking for discardable callgraph nodes.
1055 auto walkFn = [&](Operation *symbolTableOp, bool allUsesVisible) {
1056 for (Operation &nestedOp : symbolTableOp->getRegion(0).getOps()) {
1057 auto symbolUses = SymbolTable::getSymbolUses(&nestedOp);
1058 assert(symbolUses && "expected uses to be valid");
1059
1060 for (const SymbolTable::SymbolUse &use : *symbolUses) {
1061 symbols.clear();
1062 (void)symbolTable.lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
1063 symbols);
1064 for (Operation *symbolOp : symbols)
1065 symbolToUsers[symbolOp].insert(use.getUser());
1066 }
1067 }
1068 };
1069 // We just set `allSymUsesVisible` to false here because it isn't necessary
1070 // for building the user map.
1071 SymbolTable::walkSymbolTables(symbolTableOp, /*allSymUsesVisible=*/false,
1072 walkFn);
1073}
1074
1076 StringAttr newSymbolName) {
1077 auto it = symbolToUsers.find(symbol);
1078 if (it == symbolToUsers.end())
1079 return;
1080
1081 // Replace the uses within the users of `symbol`.
1082 for (Operation *user : it->second)
1083 (void)SymbolTable::replaceAllSymbolUses(symbol, newSymbolName, user);
1084
1085 // Move the current users of `symbol` to the new symbol if it is in the
1086 // symbol table.
1087 Operation *newSymbol =
1088 symbolTable.lookupSymbolIn(symbol->getParentOp(), newSymbolName);
1089 if (newSymbol != symbol) {
1090 // Transfer over the users to the new symbol. The reference to the old one
1091 // is fetched again as the iterator is invalidated during the insertion.
1092 auto newIt = symbolToUsers.try_emplace(newSymbol);
1093 auto oldIt = symbolToUsers.find(symbol);
1094 assert(oldIt != symbolToUsers.end() && "missing old users list");
1095 if (newIt.second)
1096 newIt.first->second = std::move(oldIt->second);
1097 else
1098 newIt.first->second.set_union(oldIt->second);
1099 symbolToUsers.erase(oldIt);
1100 }
1101}
1102
1103//===----------------------------------------------------------------------===//
1104// Visibility parsing implementation.
1105//===----------------------------------------------------------------------===//
1106
1108 NamedAttrList &attrs) {
1109 StringRef visibility;
1110 if (parser.parseOptionalKeyword(&visibility, {"public", "private", "nested"}))
1111 return failure();
1112
1113 StringAttr visibilityAttr = parser.getBuilder().getStringAttr(visibility);
1114 attrs.push_back(parser.getBuilder().getNamedAttr(
1115 SymbolOpInterface::getDefaultVisibilityAttrName(), visibilityAttr));
1116 return success();
1117}
1118
1119//===----------------------------------------------------------------------===//
1120// Symbol Interfaces
1121//===----------------------------------------------------------------------===//
1122
1123/// Include the generated symbol interfaces.
1124#include "mlir/IR/SymbolInterfaces.cpp.inc"
1125#include "mlir/IR/SymbolInterfacesAttrInterface.cpp.inc"
return success()
b getContext())
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 std::optional< SymbolTable::UseRange > getSymbolUsesImpl(FromT from)
The implementation of SymbolTable::getSymbolUses below.
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 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.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:164
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition Builders.cpp:102
Diagnostic & append(Arg1 &&arg1, Arg2 &&arg2, Args &&...args)
Append arguments to the diagnostic.
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.
Diagnostic & attachNote(std::optional< Location > noteLoc=std::nullopt)
Attaches a note to this 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.
Definition MLIRContext.h:63
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.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:738
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:726
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
Definition Operation.h:561
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
Definition Operation.h:553
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition Operation.h:288
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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.
Definition Region.h:26
Block & front()
Definition Region.h:65
iterator_range< OpIterator > getOps()
Definition Region.h:185
bool empty()
Definition Region.h:60
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:213
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
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...
Definition SymbolTable.h:24
static SmallString< N > generateSymbolName(StringRef name, UniqueChecker uniqueChecker, unsigned &uniquingCounter)
Generate a unique symbol name.
static Visibility getSymbolVisibility(Operation *symbol)
Returns the visibility of the given symbol operation, which is required to implement SymbolOpInterfac...
static void setSymbolVisibility(Operation *symbol, Visibility vis)
Sets the visibility of the given symbol operation, which is required to implement SymbolOpInterface.
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:84
@ Nested
The symbol is visible to the current IR, which may include operations in symbol tables above the one ...
Definition SymbolTable.h:97
@ Public
The symbol is public and may be referenced anywhere internal or external to the visible references in...
Definition SymbolTable.h:87
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
Definition SymbolTable.h:91
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.
Operation * getOp() const
Returns the associated operation.
Definition SymbolTable.h:76
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:
Definition WalkResult.h:29
static WalkResult skip()
Definition WalkResult.h:48
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
void addReplacement(ReplaceFn< Attribute > fn)
AttrTypeReplacerBase.
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.
Definition Visitors.h:102
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.
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147