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 "llvm/ADT/StringSwitch.h"
15#include <optional>
16
17using 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.
27static StringAttr getNameIfSymbol(Operation *op) {
28 return op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
29}
30static 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.
38static LogicalResult
39collectValidReferencesFor(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`.
79static std::optional<WalkResult>
81 function_ref<std::optional<WalkResult>(Operation *)> callback) {
82 SmallVector<Region *, 1> worklist(llvm::make_pointer_range(regions));
83 while (!worklist.empty()) {
84 for (Operation &op : worklist.pop_back_val()->getOps()) {
85 std::optional<WalkResult> result = callback(&op);
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`.
103static std::optional<WalkResult>
105 function_ref<std::optional<WalkResult>(Operation *)> callback) {
106 std::optional<WalkResult> result = callback(op);
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(symbolTableOp->getRegion(0).hasOneBlock() &&
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 // Silently skip duplicate symbol names. Duplicate symbols are an
134 // invalid IR condition diagnosed by the SymbolTable trait's
135 // verifyRegionTrait. The constructor may be called before verification
136 // completes (e.g., when IsolatedFromAbove ops look up symbols in an
137 // ancestor symbol table during verification), so an assert here would
138 // crash instead of producing a proper diagnostic.
139 symbolTable.try_emplace(name, &op);
140 }
141}
142
143/// Look up a symbol with the specified name, returning null if no such name
144/// exists. Names never include the @ on them.
145Operation *SymbolTable::lookup(StringRef name) const {
146 return lookup(StringAttr::get(symbolTableOp->getContext(), name));
147}
148Operation *SymbolTable::lookup(StringAttr name) const {
149 return symbolTable.lookup(name);
150}
151
153 StringAttr name = getNameIfSymbol(op);
154 assert(name && "expected valid 'name' attribute");
155 assert(op->getParentOp() == symbolTableOp &&
156 "expected this operation to be inside of the operation with this "
157 "SymbolTable");
158
159 auto it = symbolTable.find(name);
160 if (it != symbolTable.end() && it->second == op)
161 symbolTable.erase(it);
162}
163
165 remove(symbol);
166 symbol->erase();
167}
168
169// TODO: Consider if this should be renamed to something like insertOrUpdate
170/// Insert a new symbol into the table and associated operation if not already
171/// there and rename it as necessary to avoid collisions. Return the name of
172/// the symbol after insertion as attribute.
173StringAttr SymbolTable::insert(Operation *symbol, Block::iterator insertPt) {
174 // The symbol cannot be the child of another op and must be the child of the
175 // symbolTableOp after this.
176 //
177 // TODO: consider if SymbolTable's constructor should behave the same.
178 if (!symbol->getParentOp()) {
179 auto &body = symbolTableOp->getRegion(0).front();
180 if (insertPt == Block::iterator()) {
181 insertPt = Block::iterator(body.end());
182 } else {
183 assert((insertPt == body.end() ||
184 insertPt->getParentOp() == symbolTableOp) &&
185 "expected insertPt to be in the associated module operation");
186 }
187 // Insert before the terminator, if any.
188 if (insertPt == Block::iterator(body.end()) && !body.empty() &&
189 std::prev(body.end())->hasTrait<OpTrait::IsTerminator>())
190 insertPt = std::prev(body.end());
191
192 body.getOperations().insert(insertPt, symbol);
193 }
194 assert(symbol->getParentOp() == symbolTableOp &&
195 "symbol is already inserted in another op");
196
197 // Add this symbol to the symbol table, uniquing the name if a conflict is
198 // detected.
199 StringAttr name = getSymbolName(symbol);
200 if (symbolTable.insert({name, symbol}).second)
201 return name;
202 // If the symbol was already in the table, also return.
203 if (symbolTable.lookup(name) == symbol)
204 return name;
205
206 MLIRContext *context = symbol->getContext();
208 name.getValue(),
209 [&](StringRef candidate) {
210 return !symbolTable
211 .insert({StringAttr::get(context, candidate), symbol})
212 .second;
213 },
214 uniquingCounter);
215 setSymbolName(symbol, nameBuffer);
216 return getSymbolName(symbol);
217}
218
219LogicalResult SymbolTable::rename(StringAttr from, StringAttr to) {
220 Operation *op = lookup(from);
221 return rename(op, to);
222}
223
224LogicalResult SymbolTable::rename(Operation *op, StringAttr to) {
225 StringAttr from = getNameIfSymbol(op);
226 (void)from;
227
228 assert(from && "expected valid 'name' attribute");
229 assert(op->getParentOp() == symbolTableOp &&
230 "expected this operation to be inside of the operation with this "
231 "SymbolTable");
232 assert(lookup(from) == op && "current name does not resolve to op");
233 assert(lookup(to) == nullptr && "new name already exists");
234
235 if (failed(SymbolTable::replaceAllSymbolUses(op, to, getOp())))
236 return failure();
237
238 // Remove op with old name, change name, add with new name. The order is
239 // important here due to how `remove` and `insert` rely on the op name.
240 remove(op);
241 setSymbolName(op, to);
242 insert(op);
243
244 assert(lookup(to) == op && "new name does not resolve to renamed op");
245 assert(lookup(from) == nullptr && "old name still exists");
246
247 return success();
248}
249
250LogicalResult SymbolTable::rename(StringAttr from, StringRef to) {
251 auto toAttr = StringAttr::get(getOp()->getContext(), to);
252 return rename(from, toAttr);
253}
254
255LogicalResult SymbolTable::rename(Operation *op, StringRef to) {
256 auto toAttr = StringAttr::get(getOp()->getContext(), to);
257 return rename(op, toAttr);
258}
259
260FailureOr<StringAttr>
263
264 // Determine new name that is unique in all symbol tables.
265 StringAttr newName;
266 {
267 MLIRContext *context = oldName.getContext();
268 SmallString<64> prefix = oldName.getValue();
269 int uniqueId = 0;
270 prefix.push_back('_');
271 while (true) {
272 newName = StringAttr::get(context, prefix + Twine(uniqueId++));
273 auto lookupNewName = [&](SymbolTable *st) { return st->lookup(newName); };
274 if (!lookupNewName(this) && llvm::none_of(others, lookupNewName)) {
275 break;
276 }
277 }
278 }
279
280 // Apply renaming.
281 if (failed(rename(oldName, newName)))
282 return failure();
283 return newName;
284}
285
286FailureOr<StringAttr>
288 StringAttr from = getNameIfSymbol(op);
289 assert(from && "expected valid 'name' attribute");
290 return renameToUnique(from, others);
291}
292
293/// Returns the name of the given symbol operation.
295 StringAttr name = getNameIfSymbol(symbol);
296 assert(name && "expected valid symbol name");
297 return name;
298}
299
300/// Sets the name of the given symbol operation.
301void SymbolTable::setSymbolName(Operation *symbol, StringAttr name) {
302 symbol->setAttr(getSymbolAttrName(), name);
303}
304
305/// Returns the visibility of the given symbol operation.
307 // If the attribute doesn't exist, assume public.
308 StringAttr vis = symbol->getAttrOfType<StringAttr>(getVisibilityAttrName());
309 if (!vis)
310 return Visibility::Public;
311
312 // Otherwise, switch on the string value.
313 return StringSwitch<Visibility>(vis.getValue())
314 .Case("private", Visibility::Private)
315 .Case("nested", Visibility::Nested)
316 .Case("public", Visibility::Public);
317}
318/// Sets the visibility of the given symbol operation.
320 MLIRContext *ctx = symbol->getContext();
321
322 // If the visibility is public, just drop the attribute as this is the
323 // default.
324 if (vis == Visibility::Public) {
325 symbol->removeAttr(StringAttr::get(ctx, getVisibilityAttrName()));
326 return;
327 }
328
329 // Otherwise, update the attribute.
330 assert((vis == Visibility::Private || vis == Visibility::Nested) &&
331 "unknown symbol visibility kind");
332
333 StringRef visName = vis == Visibility::Private ? "private" : "nested";
334 symbol->setAttr(getVisibilityAttrName(), StringAttr::get(ctx, visName));
335}
336
337/// Returns the nearest symbol table from a given operation `from`. Returns
338/// nullptr if no valid parent symbol table could be found.
340 assert(from && "expected valid operation");
342 return nullptr;
343
344 while (!from->hasTrait<OpTrait::SymbolTable>()) {
345 from = from->getParentOp();
346
347 // Check that this is a valid op and isn't an unknown symbol table.
348 if (!from || isPotentiallyUnknownSymbolTable(from))
349 return nullptr;
350 }
351 return from;
352}
353
354/// Walks all symbol table operations nested within, and including, `op`. For
355/// each symbol table operation, the provided callback is invoked with the op
356/// and a boolean signifying if the symbols within that symbol table can be
357/// treated as if all uses are visible. `allSymUsesVisible` identifies whether
358/// all of the symbol uses of symbols within `op` are visible.
360 Operation *op, bool allSymUsesVisible,
361 function_ref<void(Operation *, bool)> callback) {
362 bool isSymbolTable = op->hasTrait<OpTrait::SymbolTable>();
363 if (isSymbolTable) {
364 SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op);
365 allSymUsesVisible |= !symbol || symbol.isPrivate();
366 } else {
367 // Otherwise if 'op' is not a symbol table, any nested symbols are
368 // guaranteed to be hidden.
369 allSymUsesVisible = true;
370 }
371
372 for (Region &region : op->getRegions())
373 for (Block &block : region)
374 for (Operation &nestedOp : block)
375 walkSymbolTables(&nestedOp, allSymUsesVisible, callback);
376
377 // If 'op' had the symbol table trait, visit it after any nested symbol
378 // tables.
379 if (isSymbolTable)
380 callback(op, allSymUsesVisible);
381}
382
383/// Returns the operation registered with the given symbol name with the
384/// regions of 'symbolTableOp'. 'symbolTableOp' is required to be an operation
385/// with the 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol
386/// was found.
388 StringAttr symbol) {
389 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
390 Region &region = symbolTableOp->getRegion(0);
391 if (region.empty())
392 return nullptr;
393
394 // Look for a symbol with the given name.
395 StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
397 for (auto &op : region.front())
398 if (getNameIfSymbol(&op, symbolNameId) == symbol)
399 return &op;
400 return nullptr;
401}
403 SymbolRefAttr symbol) {
404 SmallVector<Operation *, 4> resolvedSymbols;
405 if (failed(lookupSymbolIn(symbolTableOp, symbol, resolvedSymbols)))
406 return nullptr;
407 return resolvedSymbols.back();
408}
409
410/// Internal implementation of `lookupSymbolIn` that allows for specialized
411/// implementations of the lookup function.
412static LogicalResult lookupSymbolInImpl(
413 Operation *symbolTableOp, SymbolRefAttr symbol,
415 function_ref<Operation *(Operation *, StringAttr)> lookupSymbolFn) {
416 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
417
418 // Lookup the root reference for this symbol.
419 auto *symbolOp = lookupSymbolFn(symbolTableOp, symbol.getRootReference());
420 if (!symbolOp)
421 return failure();
422 symbols.push_back(symbolOp);
423
424 // Lookup each of the nested references.
425 for (FlatSymbolRefAttr ref : symbol.getNestedReferences()) {
426 // Check that we have a valid symbol table to lookup ref.
427 if (!symbolOp->hasTrait<OpTrait::SymbolTable>())
428 return failure();
429 symbolOp = lookupSymbolFn(symbolOp, ref.getAttr());
430 // If the nested symbol is private, lookup failed.
431 if (!symbolOp || SymbolTable::getSymbolVisibility(symbolOp) ==
433 return failure();
434 symbols.push_back(symbolOp);
435 }
436 return success();
437}
438
439LogicalResult
440SymbolTable::lookupSymbolIn(Operation *symbolTableOp, SymbolRefAttr symbol,
442 auto lookupFn = [](Operation *symbolTableOp, StringAttr symbol) {
443 return lookupSymbolIn(symbolTableOp, symbol);
444 };
445 return lookupSymbolInImpl(symbolTableOp, symbol, symbols, lookupFn);
446}
447
448/// Returns the operation registered with the given symbol name within the
449/// closes parent operation with the 'OpTrait::SymbolTable' trait. Returns
450/// nullptr if no valid symbol was found.
452 StringAttr symbol) {
453 Operation *symbolTableOp = getNearestSymbolTable(from);
454 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
455}
457 SymbolRefAttr symbol) {
458 Operation *symbolTableOp = getNearestSymbolTable(from);
459 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
460}
461
463 SymbolTable::Visibility visibility) {
464 switch (visibility) {
466 return os << "public";
468 return os << "private";
470 return os << "nested";
471 }
472 llvm_unreachable("Unexpected visibility");
473}
474
475//===----------------------------------------------------------------------===//
476// SymbolTable Trait Types
477//===----------------------------------------------------------------------===//
478
479/// Verify the symbol uses held by the types owned by `op`: its operand,
480/// result, and block-argument types, and any types nested within its
481/// attributes. `op` is the anchor used for symbol lookups. `verifiedTypes`
482/// records the types already verified within the current symbol table so that
483/// each type, which may be uniqued and shared across many positions or
484/// operations, is verified at most once. Verification fails fast on the first
485/// invalid symbol use.
486static LogicalResult verifyOpTypeSymbolUses(Operation *op,
487 SymbolTableCollection &symbolTable,
488 SetVector<Type> &verifiedTypes) {
489 // Walk `type` and any nested type parameters reachable from it, verifying
490 // each not-yet-seen type and interrupting on the first failure.
491 auto verify = [&](Type type) {
492 return type.walk<WalkOrder::PreOrder>([&](Type nestedType) {
493 if (!verifiedTypes.insert(nestedType))
494 return WalkResult::advance();
495 if (auto user = dyn_cast<SymbolUserTypeInterface>(nestedType))
496 if (failed(user.verifySymbolUses(op, symbolTable)))
497 return WalkResult::interrupt();
498 return WalkResult::advance();
499 });
500 };
501
502 for (Type type : op->getOperandTypes())
503 if (verify(type).wasInterrupted())
504 return failure();
505 for (Type type : op->getResultTypes())
506 if (verify(type).wasInterrupted())
507 return failure();
508 for (Region &region : op->getRegions())
509 for (Block &block : region)
510 for (BlockArgument argument : block.getArguments())
511 if (verify(argument.getType()).wasInterrupted())
512 return failure();
513
514 // Verify types nested within the operation's attributes.
515 WalkResult attrResult =
516 op->getAttrDictionary().walk<WalkOrder::PreOrder>([&](Type type) {
517 if (verify(type).wasInterrupted())
518 return WalkResult::interrupt();
519 return WalkResult::advance();
520 });
521 return failure(attrResult.wasInterrupted());
522}
523
525 if (op->getNumRegions() != 1)
526 return op->emitOpError()
527 << "Operations with a 'SymbolTable' must have exactly one region";
528 if (!op->getRegion(0).hasOneBlock())
529 return op->emitOpError()
530 << "Operations with a 'SymbolTable' must have exactly one block";
531
532 // Check that all symbols are uniquely named within child regions.
533 DenseMap<Attribute, Location> nameToOrigLoc;
534 for (auto &block : op->getRegion(0)) {
535 for (auto &op : block) {
536 // Check for a symbol name attribute.
537 auto nameAttr =
539 if (!nameAttr)
540 continue;
541
542 // Try to insert this symbol into the table.
543 auto it = nameToOrigLoc.try_emplace(nameAttr, op.getLoc());
544 if (!it.second)
545 return op.emitError()
546 .append("redefinition of symbol named '", nameAttr.getValue(), "'")
547 .attachNote(it.first->second)
548 .append("see existing symbol definition here");
549 }
550 }
551
552 // Verify any nested symbol user operations.
553 SymbolTableCollection symbolTable;
554 // walkSymbolTable does not descend into nested symbol tables, so every
555 // operation visited here shares the same nearest symbol table. A uniqued
556 // attribute or type therefore resolves its symbol uses identically
557 // regardless of which operation anchors the lookup, so each is verified at
558 // most once across the whole scope.
559 SetVector<Attribute> verifiedAttrs;
560 SetVector<Type> verifiedTypes;
561 auto verifySymbolUserFn = [&](Operation *op) -> std::optional<WalkResult> {
562 if (SymbolUserOpInterface user = dyn_cast<SymbolUserOpInterface>(op))
563 if (failed(user.verifySymbolUses(symbolTable)))
564 return WalkResult::interrupt();
565 for (auto &attr : op->getDiscardableAttrs()) {
566 if (auto user = dyn_cast<SymbolUserAttrInterface>(attr.getValue())) {
567 if (!verifiedAttrs.insert(attr.getValue()))
568 continue;
569 if (failed(user.verifySymbolUses(op, symbolTable)))
570 return WalkResult::interrupt();
571 }
572 }
573 if (failed(verifyOpTypeSymbolUses(op, symbolTable, verifiedTypes)))
574 return WalkResult::interrupt();
575 return WalkResult::advance();
576 };
577
578 std::optional<WalkResult> result =
579 walkSymbolTable(op->getRegions(), verifySymbolUserFn);
580 return success(result && !result->wasInterrupted());
581}
582
583LogicalResult detail::verifySymbol(Operation *op) {
584 // Verify the name attribute.
586 return op->emitOpError() << "requires string attribute '"
588
589 // Verify the visibility attribute.
591 StringAttr visStrAttr = llvm::dyn_cast<StringAttr>(vis);
592 if (!visStrAttr)
593 return op->emitOpError() << "requires visibility attribute '"
595 << "' to be a string attribute, but got " << vis;
596
597 if (!llvm::is_contained(ArrayRef<StringRef>{"public", "private", "nested"},
598 visStrAttr.getValue()))
599 return op->emitOpError()
600 << "visibility expected to be one of [\"public\", \"private\", "
601 "\"nested\"], but got "
602 << visStrAttr;
603 }
604 return success();
605}
606
607//===----------------------------------------------------------------------===//
608// Symbol Use Lists
609//===----------------------------------------------------------------------===//
610
611/// Walk all of the symbol references within the given operation, invoking the
612/// provided callback for each found use. The callbacks takes the use of the
613/// symbol.
614static WalkResult
617 return op->getAttrDictionary().walk<WalkOrder::PreOrder>(
618 [&](SymbolRefAttr symbolRef) {
619 if (callback({op, symbolRef}).wasInterrupted())
620 return WalkResult::interrupt();
621
622 // Don't walk nested references.
623 return WalkResult::skip();
624 });
625}
626
627/// Walk all of the uses, for any symbol, that are nested within the given
628/// regions, invoking the provided callback for each. This does not traverse
629/// into any nested symbol tables.
630static std::optional<WalkResult>
633 return walkSymbolTable(regions,
634 [&](Operation *op) -> std::optional<WalkResult> {
635 // Check that this isn't a potentially unknown symbol
636 // table.
638 return std::nullopt;
639
640 return walkSymbolRefs(op, callback);
641 });
642}
643/// Walk all of the uses, for any symbol, that are nested within the given
644/// operation 'from', invoking the provided callback for each. This does not
645/// traverse into any nested symbol tables.
646static std::optional<WalkResult>
649 // If this operation has regions, and it, as well as its dialect, isn't
650 // registered then conservatively fail. The operation may define a
651 // symbol table, so we can't opaquely know if we should traverse to find
652 // nested uses.
654 return std::nullopt;
655
656 // Walk the uses on this operation.
657 if (walkSymbolRefs(from, callback).wasInterrupted())
658 return WalkResult::interrupt();
659
660 // Only recurse if this operation is not a symbol table. A symbol table
661 // defines a new scope, so we can't walk the attributes from within the symbol
662 // table op.
663 if (!from->hasTrait<OpTrait::SymbolTable>())
664 return walkSymbolUses(from->getRegions(), callback);
665 return WalkResult::advance();
666}
667
668namespace {
669/// This class represents a single symbol scope. A symbol scope represents the
670/// set of operations nested within a symbol table that may reference symbols
671/// within that table. A symbol scope does not contain the symbol table
672/// operation itself, just its contained operations. A scope ends at leaf
673/// operations or another symbol table operation.
674struct SymbolScope {
675 /// Walk the symbol uses within this scope, invoking the given callback.
676 /// This variant is used when the callback type matches that expected by
677 /// 'walkSymbolUses'.
678 template <typename CallbackT,
679 std::enable_if_t<!std::is_same<
680 typename llvm::function_traits<CallbackT>::result_t,
681 void>::value> * = nullptr>
682 std::optional<WalkResult> walk(CallbackT cback) {
683 if (Region *region = llvm::dyn_cast_if_present<Region *>(limit))
684 return walkSymbolUses(*region, cback);
685 return walkSymbolUses(cast<Operation *>(limit), cback);
686 }
687 /// This variant is used when the callback type matches a stripped down type:
688 /// void(SymbolTable::SymbolUse use)
689 template <typename CallbackT,
690 std::enable_if_t<std::is_same<
691 typename llvm::function_traits<CallbackT>::result_t,
692 void>::value> * = nullptr>
693 std::optional<WalkResult> walk(CallbackT cback) {
694 return walk([=](SymbolTable::SymbolUse use) {
695 return cback(use), WalkResult::advance();
696 });
697 }
698
699 /// Walk all of the operations nested under the current scope without
700 /// traversing into any nested symbol tables.
701 template <typename CallbackT>
702 std::optional<WalkResult> walkSymbolTable(CallbackT &&cback) {
703 if (Region *region = llvm::dyn_cast_if_present<Region *>(limit))
704 return ::walkSymbolTable(*region, cback);
705 return ::walkSymbolTable(cast<Operation *>(limit), cback);
706 }
707
708 /// The representation of the symbol within this scope.
709 SymbolRefAttr symbol;
710
711 /// The IR unit representing this scope.
712 llvm::PointerUnion<Operation *, Region *> limit;
713};
714} // namespace
715
716/// Collect all of the symbol scopes from 'symbol' to (inclusive) 'limit'.
718 Operation *limit) {
719 StringAttr symName = SymbolTable::getSymbolName(symbol);
720 assert(!symbol->hasTrait<OpTrait::SymbolTable>() || symbol != limit);
721
722 // Compute the ancestors of 'limit'.
725 limitAncestors;
726 Operation *limitAncestor = limit;
727 do {
728 // Check to see if 'symbol' is an ancestor of 'limit'.
729 if (limitAncestor == symbol) {
730 // Check that the nearest symbol table is 'symbol's parent. SymbolRefAttr
731 // doesn't support parent references.
733 symbol->getParentOp())
734 return {{SymbolRefAttr::get(symName), limit}};
735 return {};
736 }
737
738 limitAncestors.insert(limitAncestor);
739 } while ((limitAncestor = limitAncestor->getParentOp()));
740
741 // Try to find the first ancestor of 'symbol' that is an ancestor of 'limit'.
742 Operation *commonAncestor = symbol->getParentOp();
743 do {
744 if (limitAncestors.count(commonAncestor))
745 break;
746 } while ((commonAncestor = commonAncestor->getParentOp()));
747 assert(commonAncestor && "'limit' and 'symbol' have no common ancestor");
748
749 // Compute the set of valid nested references for 'symbol' as far up to the
750 // common ancestor as possible.
752 bool collectedAllReferences = succeeded(
753 collectValidReferencesFor(symbol, symName, commonAncestor, references));
754
755 // Handle the case where the common ancestor is 'limit'.
756 if (commonAncestor == limit) {
758
759 // Walk each of the ancestors of 'symbol', calling the compute function for
760 // each one.
761 Operation *limitIt = symbol->getParentOp();
762 for (size_t i = 0, e = references.size(); i != e;
763 ++i, limitIt = limitIt->getParentOp()) {
764 assert(limitIt->hasTrait<OpTrait::SymbolTable>());
765 scopes.push_back({references[i], &limitIt->getRegion(0)});
766 }
767 return scopes;
768 }
769
770 // Otherwise, we just need the symbol reference for 'symbol' that will be
771 // used within 'limit'. This is the last reference in the list we computed
772 // above if we were able to collect all references.
773 if (!collectedAllReferences)
774 return {};
775 return {{references.back(), limit}};
776}
778 Region *limit) {
779 auto scopes = collectSymbolScopes(symbol, limit->getParentOp());
780
781 // If we collected some scopes to walk, make sure to constrain the one for
782 // limit to the specific region requested.
783 if (!scopes.empty())
784 scopes.back().limit = limit;
785 return scopes;
786}
788 Region *limit) {
789 return {{SymbolRefAttr::get(symbol), limit}};
790}
791
793 Operation *limit) {
795 auto symbolRef = SymbolRefAttr::get(symbol);
796 for (auto &region : limit->getRegions())
797 scopes.push_back({symbolRef, &region});
798 return scopes;
799}
800
801/// Returns true if the given reference 'SubRef' is a sub reference of the
802/// reference 'ref', i.e. 'ref' is a further qualified reference.
803static bool isReferencePrefixOf(SymbolRefAttr subRef, SymbolRefAttr ref) {
804 if (ref == subRef)
805 return true;
806
807 // If the references are not pointer equal, check to see if `subRef` is a
808 // prefix of `ref`.
809 if (llvm::isa<FlatSymbolRefAttr>(ref) ||
810 ref.getRootReference() != subRef.getRootReference())
811 return false;
812
813 auto refLeafs = ref.getNestedReferences();
814 auto subRefLeafs = subRef.getNestedReferences();
815 return subRefLeafs.size() < refLeafs.size() &&
816 subRefLeafs == refLeafs.take_front(subRefLeafs.size());
817}
818
819//===----------------------------------------------------------------------===//
820// SymbolTable::getSymbolUses
821//===----------------------------------------------------------------------===//
822
823/// The implementation of SymbolTable::getSymbolUses below.
824template <typename FromT>
825static std::optional<SymbolTable::UseRange> getSymbolUsesImpl(FromT from) {
826 std::vector<SymbolTable::SymbolUse> uses;
827 auto walkFn = [&](SymbolTable::SymbolUse symbolUse) {
828 uses.push_back(symbolUse);
829 return WalkResult::advance();
830 };
831 auto result = walkSymbolUses(from, walkFn);
832 return result ? std::optional<SymbolTable::UseRange>(std::move(uses))
833 : std::nullopt;
834}
835
836/// Get an iterator range for all of the uses, for any symbol, that are nested
837/// within the given operation 'from'. This does not traverse into any nested
838/// symbol tables, and will also only return uses on 'from' if it does not
839/// also define a symbol table. This is because we treat the region as the
840/// boundary of the symbol table, and not the op itself. This function returns
841/// std::nullopt if there are any unknown operations that may potentially be
842/// symbol tables.
843auto SymbolTable::getSymbolUses(Operation *from) -> std::optional<UseRange> {
844 return getSymbolUsesImpl(from);
845}
846auto SymbolTable::getSymbolUses(Region *from) -> std::optional<UseRange> {
848}
849
850//===----------------------------------------------------------------------===//
851// SymbolTable::getSymbolUses
852//===----------------------------------------------------------------------===//
853
854/// The implementation of SymbolTable::getSymbolUses below.
855template <typename SymbolT, typename IRUnitT>
856static std::optional<SymbolTable::UseRange> getSymbolUsesImpl(SymbolT symbol,
857 IRUnitT *limit) {
858 std::vector<SymbolTable::SymbolUse> uses;
859 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
860 if (!scope.walk([&](SymbolTable::SymbolUse symbolUse) {
861 if (isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef()))
862 uses.push_back(symbolUse);
863 }))
864 return std::nullopt;
865 }
866 return SymbolTable::UseRange(std::move(uses));
867}
868
869/// Get all of the uses of the given symbol that are nested within the given
870/// operation 'from'. This does not traverse into any nested symbol tables.
871/// This function returns std::nullopt if there are any unknown operations that
872/// may potentially be symbol tables.
873auto SymbolTable::getSymbolUses(StringAttr symbol, Operation *from)
874 -> std::optional<UseRange> {
875 return getSymbolUsesImpl(symbol, from);
876}
878 -> std::optional<UseRange> {
879 return getSymbolUsesImpl(symbol, from);
880}
881auto SymbolTable::getSymbolUses(StringAttr symbol, Region *from)
882 -> std::optional<UseRange> {
883 return getSymbolUsesImpl(symbol, from);
884}
886 -> std::optional<UseRange> {
887 return getSymbolUsesImpl(symbol, from);
888}
889
890//===----------------------------------------------------------------------===//
891// SymbolTable::symbolKnownUseEmpty
892//===----------------------------------------------------------------------===//
893
894/// The implementation of SymbolTable::symbolKnownUseEmpty below.
895template <typename SymbolT, typename IRUnitT>
896static bool symbolKnownUseEmptyImpl(SymbolT symbol, IRUnitT *limit) {
897 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
898 // Walk all of the symbol uses looking for a reference to 'symbol'.
899 if (scope.walk([&](SymbolTable::SymbolUse symbolUse) {
900 return isReferencePrefixOf(scope.symbol, symbolUse.getSymbolRef())
901 ? WalkResult::interrupt()
902 : WalkResult::advance();
903 }) != WalkResult::advance())
904 return false;
905 }
906 return true;
907}
908
909/// Return if the given symbol is known to have no uses that are nested within
910/// the given operation 'from'. This does not traverse into any nested symbol
911/// tables. This function will also return false if there are any unknown
912/// operations that may potentially be symbol tables.
913bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Operation *from) {
914 return symbolKnownUseEmptyImpl(symbol, from);
915}
917 return symbolKnownUseEmptyImpl(symbol, from);
918}
919bool SymbolTable::symbolKnownUseEmpty(StringAttr symbol, Region *from) {
920 return symbolKnownUseEmptyImpl(symbol, from);
921}
923 return symbolKnownUseEmptyImpl(symbol, from);
924}
925
926//===----------------------------------------------------------------------===//
927// SymbolTable::replaceAllSymbolUses
928//===----------------------------------------------------------------------===//
929
930/// Generates a new symbol reference attribute with a new leaf reference.
931static SymbolRefAttr generateNewRefAttr(SymbolRefAttr oldAttr,
932 FlatSymbolRefAttr newLeafAttr) {
933 if (llvm::isa<FlatSymbolRefAttr>(oldAttr))
934 return newLeafAttr;
935 auto nestedRefs = llvm::to_vector<2>(oldAttr.getNestedReferences());
936 nestedRefs.back() = newLeafAttr;
937 return SymbolRefAttr::get(oldAttr.getRootReference(), nestedRefs);
938}
939
940/// The implementation of SymbolTable::replaceAllSymbolUses below.
941template <typename SymbolT, typename IRUnitT>
942static LogicalResult
943replaceAllSymbolUsesImpl(SymbolT symbol, StringAttr newSymbol, IRUnitT *limit) {
944 // Generate a new attribute to replace the given attribute.
945 FlatSymbolRefAttr newLeafAttr = FlatSymbolRefAttr::get(newSymbol);
946 for (SymbolScope &scope : collectSymbolScopes(symbol, limit)) {
947 SymbolRefAttr oldAttr = scope.symbol;
948 SymbolRefAttr newAttr = generateNewRefAttr(scope.symbol, newLeafAttr);
949 AttrTypeReplacer replacer;
950 replacer.addReplacement(
951 [&](SymbolRefAttr attr) -> std::pair<Attribute, WalkResult> {
952 // Regardless of the match, don't walk nested SymbolRefAttrs, we don't
953 // want to accidentally replace an inner reference.
954 if (attr == oldAttr)
955 return {newAttr, WalkResult::skip()};
956 // Handle prefix matches.
957 if (isReferencePrefixOf(oldAttr, attr)) {
958 auto oldNestedRefs = oldAttr.getNestedReferences();
959 auto nestedRefs = attr.getNestedReferences();
960 if (oldNestedRefs.empty())
961 return {SymbolRefAttr::get(newSymbol, nestedRefs),
963
964 auto newNestedRefs = llvm::to_vector<4>(nestedRefs);
965 newNestedRefs[oldNestedRefs.size() - 1] = newLeafAttr;
966 return {SymbolRefAttr::get(attr.getRootReference(), newNestedRefs),
968 }
969 return {attr, WalkResult::skip()};
970 });
971
972 auto walkFn = [&](Operation *op) -> std::optional<WalkResult> {
973 replacer.replaceElementsIn(op);
974 return WalkResult::advance();
975 };
976 if (!scope.walkSymbolTable(walkFn))
977 return failure();
978 }
979 return success();
980}
981
982/// Attempt to replace all uses of the given symbol 'oldSymbol' with the
983/// provided symbol 'newSymbol' that are nested within the given operation
984/// 'from'. This does not traverse into any nested symbol tables. If there are
985/// any unknown operations that may potentially be symbol tables, no uses are
986/// replaced and failure is returned.
987LogicalResult SymbolTable::replaceAllSymbolUses(StringAttr oldSymbol,
988 StringAttr newSymbol,
989 Operation *from) {
990 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
991}
993 StringAttr newSymbol,
994 Operation *from) {
995 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
996}
997LogicalResult SymbolTable::replaceAllSymbolUses(StringAttr oldSymbol,
998 StringAttr newSymbol,
999 Region *from) {
1000 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
1001}
1003 StringAttr newSymbol,
1004 Region *from) {
1005 return replaceAllSymbolUsesImpl(oldSymbol, newSymbol, from);
1006}
1007
1008//===----------------------------------------------------------------------===//
1009// SymbolTableCollection
1010//===----------------------------------------------------------------------===//
1011
1013 StringAttr symbol) {
1014 return getSymbolTable(symbolTableOp).lookup(symbol);
1015}
1017 SymbolRefAttr name) {
1019 if (failed(lookupSymbolIn(symbolTableOp, name, symbols)))
1020 return nullptr;
1021 return symbols.back();
1022}
1023/// A variant of 'lookupSymbolIn' that returns all of the symbols referenced by
1024/// a given SymbolRefAttr. Returns failure if any of the nested references could
1025/// not be resolved.
1026LogicalResult
1028 SymbolRefAttr name,
1030 auto lookupFn = [this](Operation *symbolTableOp, StringAttr symbol) {
1031 return lookupSymbolIn(symbolTableOp, symbol);
1032 };
1033 return lookupSymbolInImpl(symbolTableOp, name, symbols, lookupFn);
1034}
1035
1036/// Returns the operation registered with the given symbol name within the
1037/// closest parent operation of, or including, 'from' with the
1038/// 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol was
1039/// found.
1041 StringAttr symbol) {
1042 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
1043 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
1044}
1045Operation *
1047 SymbolRefAttr symbol) {
1048 Operation *symbolTableOp = SymbolTable::getNearestSymbolTable(from);
1049 return symbolTableOp ? lookupSymbolIn(symbolTableOp, symbol) : nullptr;
1050}
1051
1052/// Lookup, or create, a symbol table for an operation.
1054 auto it = symbolTables.try_emplace(op, nullptr);
1055 if (it.second)
1056 it.first->second = std::make_unique<SymbolTable>(op);
1057 return *it.first->second;
1058}
1059
1061 symbolTables.erase(op);
1062}
1063
1064//===----------------------------------------------------------------------===//
1065// LockedSymbolTableCollection
1066//===----------------------------------------------------------------------===//
1067
1069 StringAttr symbol) {
1070 return getSymbolTable(symbolTableOp).lookup(symbol);
1071}
1072
1073Operation *
1075 FlatSymbolRefAttr symbol) {
1076 return lookupSymbolIn(symbolTableOp, symbol.getAttr());
1077}
1078
1080 SymbolRefAttr name) {
1082 if (failed(lookupSymbolIn(symbolTableOp, name, symbols)))
1083 return nullptr;
1084 return symbols.back();
1085}
1086
1088 Operation *symbolTableOp, SymbolRefAttr name,
1090 auto lookupFn = [this](Operation *symbolTableOp, StringAttr symbol) {
1091 return lookupSymbolIn(symbolTableOp, symbol);
1092 };
1093 return lookupSymbolInImpl(symbolTableOp, name, symbols, lookupFn);
1094}
1095
1097LockedSymbolTableCollection::getSymbolTable(Operation *symbolTableOp) {
1098 assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
1099 // Try to find an existing symbol table.
1100 {
1101 llvm::sys::SmartScopedReader<true> lock(mutex);
1102 auto it = collection.symbolTables.find(symbolTableOp);
1103 if (it != collection.symbolTables.end())
1104 return *it->second;
1105 }
1106 // Create a symbol table for the operation. Perform construction outside of
1107 // the critical section.
1108 auto symbolTable = std::make_unique<SymbolTable>(symbolTableOp);
1109 // Insert the constructed symbol table.
1110 llvm::sys::SmartScopedWriter<true> lock(mutex);
1111 return *collection.symbolTables
1112 .insert({symbolTableOp, std::move(symbolTable)})
1113 .first->second;
1114}
1115
1116//===----------------------------------------------------------------------===//
1117// SymbolUserMap
1118//===----------------------------------------------------------------------===//
1119
1121 Operation *symbolTableOp)
1122 : symbolTable(symbolTable) {
1123 // Walk each of the symbol tables looking for discardable callgraph nodes.
1125 auto walkFn = [&](Operation *symbolTableOp, bool allUsesVisible) {
1126 for (Operation &nestedOp : symbolTableOp->getRegion(0).getOps()) {
1127 auto symbolUses = SymbolTable::getSymbolUses(&nestedOp);
1128 assert(symbolUses && "expected uses to be valid");
1129
1130 for (const SymbolTable::SymbolUse &use : *symbolUses) {
1131 symbols.clear();
1132 (void)symbolTable.lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
1133 symbols);
1134 for (Operation *symbolOp : symbols)
1135 symbolToUsers[symbolOp].insert(use.getUser());
1136 }
1137 }
1138 };
1139 // We just set `allSymUsesVisible` to false here because it isn't necessary
1140 // for building the user map.
1141 SymbolTable::walkSymbolTables(symbolTableOp, /*allSymUsesVisible=*/false,
1142 walkFn);
1143}
1144
1146 StringAttr newSymbolName) {
1147 auto it = symbolToUsers.find(symbol);
1148 if (it == symbolToUsers.end())
1149 return;
1150
1151 // Replace the uses within the users of `symbol`.
1152 for (Operation *user : it->second)
1153 (void)SymbolTable::replaceAllSymbolUses(symbol, newSymbolName, user);
1154
1155 // Move the current users of `symbol` to the new symbol if it is in the
1156 // symbol table.
1157 Operation *newSymbol =
1158 symbolTable.lookupSymbolIn(symbol->getParentOp(), newSymbolName);
1159 if (newSymbol != symbol) {
1160 // Transfer over the users to the new symbol. The reference to the old one
1161 // is fetched again as the iterator is invalidated during the insertion.
1162 auto newIt = symbolToUsers.try_emplace(newSymbol);
1163 auto oldIt = symbolToUsers.find(symbol);
1164 assert(oldIt != symbolToUsers.end() && "missing old users list");
1165 if (newIt.second)
1166 newIt.first->second = std::move(oldIt->second);
1167 else
1168 newIt.first->second.set_union(oldIt->second);
1169 symbolToUsers.erase(oldIt);
1170 }
1171}
1172
1173//===----------------------------------------------------------------------===//
1174// Visibility parsing implementation.
1175//===----------------------------------------------------------------------===//
1176
1178 NamedAttrList &attrs) {
1179 StringRef visibility;
1180 if (parser.parseOptionalKeyword(&visibility, {"public", "private", "nested"}))
1181 return failure();
1182
1183 StringAttr visibilityAttr = parser.getBuilder().getStringAttr(visibility);
1184 attrs.push_back(parser.getBuilder().getNamedAttr(
1185 SymbolTable::getVisibilityAttrName(), visibilityAttr));
1186 return success();
1187}
1188
1189//===----------------------------------------------------------------------===//
1190// Symbol Interfaces
1191//===----------------------------------------------------------------------===//
1192
1193/// Include the generated symbol interfaces.
1194#include "mlir/IR/SymbolInterfaces.cpp.inc"
1195#include "mlir/IR/SymbolInterfacesAttrInterface.cpp.inc"
1196#include "mlir/IR/SymbolInterfacesTypeInterface.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 verifyOpTypeSymbolUses(Operation *op, SymbolTableCollection &symbolTable, SetVector< Type > &verifiedTypes)
Verify the symbol uses held by the types owned by op: its operand, result, and block-argument types,...
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
This class represents an argument of a Block.
Definition Value.h:306
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.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
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:711
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:774
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:575
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition Operation.h:559
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:699
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
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition Operation.h:607
auto getDiscardableAttrs()
Return a range of all of discardable attributes on this operation.
Definition Operation.h:511
operand_type_range getOperandTypes()
Definition Operation.h:422
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
result_type_range getResultTypes()
Definition Operation.h:453
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition Operation.h:288
Attribute removeAttr(StringAttr name)
Remove the attribute with the specified name if it exists.
Definition Operation.h:625
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.
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition SymbolTable.h:76
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:90
@ 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...
Definition SymbolTable.h:93
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
Definition SymbolTable.h:97
static StringRef getVisibilityAttrName()
Return the name of the attribute used for symbol visibility.
Definition SymbolTable.h:82
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:79
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'.
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 WalkResult.h:29
static WalkResult skip()
Definition WalkResult.h:48
static WalkResult advance()
Definition WalkResult.h:47
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition WalkResult.h:51
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::StringSwitch< T, R > StringSwitch
Definition LLVM.h:136
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
Definition Verifier.cpp:566
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147