1927b7074SRiver Riddle //===- SymbolTable.cpp - MLIR Symbol Table Class --------------------------===//
2927b7074SRiver Riddle //
3927b7074SRiver Riddle // Copyright 2019 The MLIR Authors.
4927b7074SRiver Riddle //
5927b7074SRiver Riddle // Licensed under the Apache License, Version 2.0 (the "License");
6927b7074SRiver Riddle // you may not use this file except in compliance with the License.
7927b7074SRiver Riddle // You may obtain a copy of the License at
8927b7074SRiver Riddle //
9927b7074SRiver Riddle //   http://www.apache.org/licenses/LICENSE-2.0
10927b7074SRiver Riddle //
11927b7074SRiver Riddle // Unless required by applicable law or agreed to in writing, software
12927b7074SRiver Riddle // distributed under the License is distributed on an "AS IS" BASIS,
13927b7074SRiver Riddle // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14927b7074SRiver Riddle // See the License for the specific language governing permissions and
15927b7074SRiver Riddle // limitations under the License.
16927b7074SRiver Riddle // =============================================================================
17927b7074SRiver Riddle 
18927b7074SRiver Riddle #include "mlir/IR/SymbolTable.h"
19ee8e8b55SRiver Riddle #include "llvm/ADT/SmallString.h"
20927b7074SRiver Riddle 
21927b7074SRiver Riddle using namespace mlir;
22927b7074SRiver Riddle 
23*b3a6ae83SRiver Riddle /// Return true if the given operation is unknown and may potentially define a
24*b3a6ae83SRiver Riddle /// symbol table.
25*b3a6ae83SRiver Riddle static bool isPotentiallyUnknownSymbolTable(Operation *op) {
26*b3a6ae83SRiver Riddle   return !op->getDialect() && op->getNumRegions() == 1;
27*b3a6ae83SRiver Riddle }
28*b3a6ae83SRiver Riddle 
29*b3a6ae83SRiver Riddle //===----------------------------------------------------------------------===//
30*b3a6ae83SRiver Riddle // SymbolTable
31*b3a6ae83SRiver Riddle //===----------------------------------------------------------------------===//
32*b3a6ae83SRiver Riddle 
33ee8e8b55SRiver Riddle /// Build a symbol table with the symbols within the given operation.
34ee8e8b55SRiver Riddle SymbolTable::SymbolTable(Operation *op) : context(op->getContext()) {
35ee8e8b55SRiver Riddle   assert(op->hasTrait<OpTrait::SymbolTable>() &&
36ee8e8b55SRiver Riddle          "expected operation to have SymbolTable trait");
37ee8e8b55SRiver Riddle   assert(op->getNumRegions() == 1 &&
38ee8e8b55SRiver Riddle          "expected operation to have a single region");
39ee8e8b55SRiver Riddle 
40ee8e8b55SRiver Riddle   for (auto &block : op->getRegion(0)) {
41ee8e8b55SRiver Riddle     for (auto &op : block) {
42ee8e8b55SRiver Riddle       auto nameAttr = op.getAttrOfType<StringAttr>(getSymbolAttrName());
43ee8e8b55SRiver Riddle       if (!nameAttr)
44ee8e8b55SRiver Riddle         continue;
45ee8e8b55SRiver Riddle 
46ee8e8b55SRiver Riddle       auto inserted = symbolTable.insert({nameAttr.getValue(), &op});
478c47e2edSRiver Riddle       (void)inserted;
488c47e2edSRiver Riddle       assert(inserted.second &&
49ee8e8b55SRiver Riddle              "expected region to contain uniquely named symbol operations");
50ee8e8b55SRiver Riddle     }
518c47e2edSRiver Riddle   }
528c47e2edSRiver Riddle }
538c47e2edSRiver Riddle 
54927b7074SRiver Riddle /// Look up a symbol with the specified name, returning null if no such name
55927b7074SRiver Riddle /// exists. Names never include the @ on them.
56ee8e8b55SRiver Riddle Operation *SymbolTable::lookup(StringRef name) const {
57e7d594bbSRiver Riddle   return symbolTable.lookup(name);
58927b7074SRiver Riddle }
59927b7074SRiver Riddle 
60927b7074SRiver Riddle /// Erase the given symbol from the table.
61ee8e8b55SRiver Riddle void SymbolTable::erase(Operation *symbol) {
62ee8e8b55SRiver Riddle   auto nameAttr = symbol->getAttrOfType<StringAttr>(getSymbolAttrName());
63ee8e8b55SRiver Riddle   assert(nameAttr && "expected valid 'name' attribute");
64ee8e8b55SRiver Riddle 
65ee8e8b55SRiver Riddle   auto it = symbolTable.find(nameAttr.getValue());
66927b7074SRiver Riddle   if (it != symbolTable.end() && it->second == symbol)
67927b7074SRiver Riddle     symbolTable.erase(it);
68927b7074SRiver Riddle }
69927b7074SRiver Riddle 
70927b7074SRiver Riddle /// Insert a new symbol into the table, and rename it as necessary to avoid
71927b7074SRiver Riddle /// collisions.
72ee8e8b55SRiver Riddle void SymbolTable::insert(Operation *symbol) {
73ee8e8b55SRiver Riddle   auto nameAttr = symbol->getAttrOfType<StringAttr>(getSymbolAttrName());
74ee8e8b55SRiver Riddle   assert(nameAttr && "expected valid 'name' attribute");
75ee8e8b55SRiver Riddle 
76927b7074SRiver Riddle   // Add this symbol to the symbol table, uniquing the name if a conflict is
77927b7074SRiver Riddle   // detected.
78ee8e8b55SRiver Riddle   if (symbolTable.insert({nameAttr.getValue(), symbol}).second)
79927b7074SRiver Riddle     return;
80927b7074SRiver Riddle 
81ee8e8b55SRiver Riddle   // If a conflict was detected, then the symbol will not have been added to
82927b7074SRiver Riddle   // the symbol table. Try suffixes until we get to a unique name that works.
83ee8e8b55SRiver Riddle   SmallString<128> nameBuffer(nameAttr.getValue());
84927b7074SRiver Riddle   unsigned originalLength = nameBuffer.size();
85927b7074SRiver Riddle 
86ee8e8b55SRiver Riddle   // Iteratively try suffixes until we find one that isn't used.
87927b7074SRiver Riddle   do {
88927b7074SRiver Riddle     nameBuffer.resize(originalLength);
89927b7074SRiver Riddle     nameBuffer += '_';
90927b7074SRiver Riddle     nameBuffer += std::to_string(uniquingCounter++);
91e7d594bbSRiver Riddle   } while (!symbolTable.insert({nameBuffer, symbol}).second);
92ee8e8b55SRiver Riddle   symbol->setAttr(getSymbolAttrName(), StringAttr::get(nameBuffer, context));
93ee8e8b55SRiver Riddle }
94ee8e8b55SRiver Riddle 
958cb405a8SRiver Riddle /// Returns the operation registered with the given symbol name with the
968cb405a8SRiver Riddle /// regions of 'symbolTableOp'. 'symbolTableOp' is required to be an operation
978cb405a8SRiver Riddle /// with the 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol
988cb405a8SRiver Riddle /// was found.
998cb405a8SRiver Riddle Operation *SymbolTable::lookupSymbolIn(Operation *symbolTableOp,
1008cb405a8SRiver Riddle                                        StringRef symbol) {
1018cb405a8SRiver Riddle   assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
1028cb405a8SRiver Riddle 
1038cb405a8SRiver Riddle   // Look for a symbol with the given name.
1048cb405a8SRiver Riddle   for (auto &block : symbolTableOp->getRegion(0)) {
1058cb405a8SRiver Riddle     for (auto &op : block) {
1068cb405a8SRiver Riddle       auto nameAttr = op.template getAttrOfType<StringAttr>(
1078cb405a8SRiver Riddle           mlir::SymbolTable::getSymbolAttrName());
1088cb405a8SRiver Riddle       if (nameAttr && nameAttr.getValue() == symbol)
1098cb405a8SRiver Riddle         return &op;
1108cb405a8SRiver Riddle     }
1118cb405a8SRiver Riddle   }
1128cb405a8SRiver Riddle   return nullptr;
1138cb405a8SRiver Riddle }
1148cb405a8SRiver Riddle 
1158cb405a8SRiver Riddle /// Returns the operation registered with the given symbol name within the
1168cb405a8SRiver Riddle /// closes parent operation with the 'OpTrait::SymbolTable' trait. Returns
1178cb405a8SRiver Riddle /// nullptr if no valid symbol was found.
1188cb405a8SRiver Riddle Operation *SymbolTable::lookupNearestSymbolFrom(Operation *from,
1198cb405a8SRiver Riddle                                                 StringRef symbol) {
120*b3a6ae83SRiver Riddle   assert(from && "expected valid operation");
121*b3a6ae83SRiver Riddle   while (!from->hasTrait<OpTrait::SymbolTable>()) {
1228cb405a8SRiver Riddle     from = from->getParentOp();
123*b3a6ae83SRiver Riddle 
124*b3a6ae83SRiver Riddle     // Check that this is a valid op and isn't an unknown symbol table.
125*b3a6ae83SRiver Riddle     if (!from || isPotentiallyUnknownSymbolTable(from))
126*b3a6ae83SRiver Riddle       return nullptr;
127*b3a6ae83SRiver Riddle   }
128*b3a6ae83SRiver Riddle   return lookupSymbolIn(from, symbol);
1298cb405a8SRiver Riddle }
1308cb405a8SRiver Riddle 
131ee8e8b55SRiver Riddle //===----------------------------------------------------------------------===//
132ee8e8b55SRiver Riddle // SymbolTable Trait Types
133ee8e8b55SRiver Riddle //===----------------------------------------------------------------------===//
134ee8e8b55SRiver Riddle 
135ee8e8b55SRiver Riddle LogicalResult OpTrait::impl::verifySymbolTable(Operation *op) {
136ee8e8b55SRiver Riddle   if (op->getNumRegions() != 1)
137ee8e8b55SRiver Riddle     return op->emitOpError()
138ee8e8b55SRiver Riddle            << "Operations with a 'SymbolTable' must have exactly one region";
139ee8e8b55SRiver Riddle 
140ee8e8b55SRiver Riddle   // Check that all symboles are uniquely named within child regions.
141ee8e8b55SRiver Riddle   llvm::StringMap<Location> nameToOrigLoc;
142ee8e8b55SRiver Riddle   for (auto &block : op->getRegion(0)) {
143ee8e8b55SRiver Riddle     for (auto &op : block) {
144ee8e8b55SRiver Riddle       // Check for a symbol name attribute.
145ee8e8b55SRiver Riddle       auto nameAttr =
146ee8e8b55SRiver Riddle           op.getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName());
147ee8e8b55SRiver Riddle       if (!nameAttr)
148ee8e8b55SRiver Riddle         continue;
149ee8e8b55SRiver Riddle 
150ee8e8b55SRiver Riddle       // Try to insert this symbol into the table.
151ee8e8b55SRiver Riddle       auto it = nameToOrigLoc.try_emplace(nameAttr.getValue(), op.getLoc());
152ee8e8b55SRiver Riddle       if (!it.second)
153ee8e8b55SRiver Riddle         return op.emitError()
154ee8e8b55SRiver Riddle             .append("redefinition of symbol named '", nameAttr.getValue(), "'")
155ee8e8b55SRiver Riddle             .attachNote(it.first->second)
156ee8e8b55SRiver Riddle             .append("see existing symbol definition here");
157ee8e8b55SRiver Riddle     }
158ee8e8b55SRiver Riddle   }
159ee8e8b55SRiver Riddle   return success();
160927b7074SRiver Riddle }
161ac91e673SRiver Riddle 
162ac91e673SRiver Riddle //===----------------------------------------------------------------------===//
163ac91e673SRiver Riddle // SymbolTable Trait Types
164ac91e673SRiver Riddle //===----------------------------------------------------------------------===//
165ac91e673SRiver Riddle 
166ac91e673SRiver Riddle /// A utility result for walking a nested attribute for symbol uses.
167ac91e673SRiver Riddle enum HandlerResult {
168ac91e673SRiver Riddle   /// The walk of the containter can continue.
169ac91e673SRiver Riddle   Continue = 0,
170ac91e673SRiver Riddle   /// The walk should recurse into the given attribute, as it is a container.
171ac91e673SRiver Riddle   RecurseNestedAttribute,
172ac91e673SRiver Riddle   /// The walk should end immediately, as an interrupt has been signaled.
173ac91e673SRiver Riddle   Interrupt
174ac91e673SRiver Riddle };
175ac91e673SRiver Riddle 
176ac91e673SRiver Riddle /// Utility function used to handle a nested attribute during a walk of symbol
177ac91e673SRiver Riddle /// uses. It returns the above HandlerResult signaling the next action for the
178ac91e673SRiver Riddle /// walk.
179ac91e673SRiver Riddle HandlerResult handleAttrDuringSymbolWalk(
180ac91e673SRiver Riddle     Operation *op, Attribute attr,
181ac91e673SRiver Riddle     SmallVectorImpl<std::pair<Attribute, unsigned>> &worklist,
182ac91e673SRiver Riddle     function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
183ac91e673SRiver Riddle   switch (attr.getKind()) {
184ac91e673SRiver Riddle   /// Check for a nested container attribute, these will also need to be
185ac91e673SRiver Riddle   /// walked.
186ac91e673SRiver Riddle   case StandardAttributes::Array:
187ac91e673SRiver Riddle   case StandardAttributes::Dictionary: {
188ac91e673SRiver Riddle     worklist.push_back({attr, /*index*/ 0});
189ac91e673SRiver Riddle     return HandlerResult::RecurseNestedAttribute;
190ac91e673SRiver Riddle   }
191ac91e673SRiver Riddle 
192ac91e673SRiver Riddle   // Invoke the provided callback if we find a symbol use and check for a
193ac91e673SRiver Riddle   // requested interrupt.
194ac91e673SRiver Riddle   case StandardAttributes::SymbolRef: {
195ac91e673SRiver Riddle     SymbolTable::SymbolUse use(op, attr.cast<SymbolRefAttr>());
196ac91e673SRiver Riddle     return callback(use).wasInterrupted() ? HandlerResult::Interrupt
197ac91e673SRiver Riddle                                           : HandlerResult::Continue;
198ac91e673SRiver Riddle   }
199ac91e673SRiver Riddle   default:
200ac91e673SRiver Riddle     return HandlerResult::Continue;
201ac91e673SRiver Riddle   }
202ac91e673SRiver Riddle }
203ac91e673SRiver Riddle 
204ac91e673SRiver Riddle /// Walk all of the symbol references within the given operation, invoking the
205ac91e673SRiver Riddle /// provided callback for each found use.
206ac91e673SRiver Riddle static WalkResult
207ac91e673SRiver Riddle walkSymbolRefs(Operation *op,
208ac91e673SRiver Riddle                function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
209ac91e673SRiver Riddle   // Check to see if the operation has any attributes.
210ac91e673SRiver Riddle   DictionaryAttr attrDict = op->getAttrList().getDictionary();
211ac91e673SRiver Riddle   if (!attrDict)
212ac91e673SRiver Riddle     return WalkResult::advance();
213ac91e673SRiver Riddle 
214ac91e673SRiver Riddle   // A worklist of a container attribute and the current index into the held
215ac91e673SRiver Riddle   // attribute list.
216ac91e673SRiver Riddle   SmallVector<std::pair<Attribute, unsigned>, 1> worklist;
217ac91e673SRiver Riddle   worklist.push_back({attrDict, /*index*/ 0});
218ac91e673SRiver Riddle   while (!worklist.empty()) {
219ac91e673SRiver Riddle     Attribute attr = worklist.back().first;
220ac91e673SRiver Riddle     unsigned &index = worklist.back().second;
221ac91e673SRiver Riddle 
222ac91e673SRiver Riddle     // Iterate over the given attribute, which is guaranteed to be a container.
223ac91e673SRiver Riddle     HandlerResult handlerResult = HandlerResult::Continue;
224ac91e673SRiver Riddle     if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
225ac91e673SRiver Riddle       ArrayRef<Attribute> attrs = arrayAttr.getValue();
226ac91e673SRiver Riddle       unsigned attrSize = attrs.size();
227ac91e673SRiver Riddle       while (index != attrSize)
228ac91e673SRiver Riddle         if ((handlerResult = handleAttrDuringSymbolWalk(op, attrs[index++],
229ac91e673SRiver Riddle                                                         worklist, callback)))
230ac91e673SRiver Riddle           break;
231ac91e673SRiver Riddle     } else {
232ac91e673SRiver Riddle       auto dictAttr = attr.cast<DictionaryAttr>();
233ac91e673SRiver Riddle       ArrayRef<NamedAttribute> attrs = dictAttr.getValue();
234ac91e673SRiver Riddle       unsigned attrSize = attrs.size();
235ac91e673SRiver Riddle       while (index != attrSize)
236ac91e673SRiver Riddle         if ((handlerResult = handleAttrDuringSymbolWalk(
237ac91e673SRiver Riddle                  op, attrs[index++].second, worklist, callback)))
238ac91e673SRiver Riddle           break;
239ac91e673SRiver Riddle     }
240ac91e673SRiver Riddle     if (handlerResult == HandlerResult::Interrupt)
241ac91e673SRiver Riddle       return WalkResult::interrupt();
242ac91e673SRiver Riddle 
243ac91e673SRiver Riddle     // If we didn't encounter a nested attribute, pop the last item from the
244ac91e673SRiver Riddle     // worklist.
245ac91e673SRiver Riddle     if (handlerResult != HandlerResult::RecurseNestedAttribute)
246ac91e673SRiver Riddle       worklist.pop_back();
247ac91e673SRiver Riddle   }
248ac91e673SRiver Riddle   return WalkResult::advance();
249ac91e673SRiver Riddle }
250ac91e673SRiver Riddle 
251ac91e673SRiver Riddle /// Walk all of the uses, for any symbol, that are nested within the given
252ac91e673SRiver Riddle /// operation 'from', invoking the provided callback for each. This does not
253ac91e673SRiver Riddle /// traverse into any nested symbol tables, and will also only return uses on
254ac91e673SRiver Riddle /// 'from' if it does not also define a symbol table.
255*b3a6ae83SRiver Riddle static Optional<WalkResult>
256*b3a6ae83SRiver Riddle walkSymbolUses(Operation *from,
257*b3a6ae83SRiver Riddle                function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
258ac91e673SRiver Riddle   // If from is not a symbol table, check for uses. A symbol table defines a new
259ac91e673SRiver Riddle   // scope, so we can't walk the attributes from the symbol table op.
260ac91e673SRiver Riddle   if (!from->hasTrait<OpTrait::SymbolTable>()) {
261ac91e673SRiver Riddle     if (walkSymbolRefs(from, callback).wasInterrupted())
262ac91e673SRiver Riddle       return WalkResult::interrupt();
263ac91e673SRiver Riddle   }
264ac91e673SRiver Riddle 
265ac91e673SRiver Riddle   SmallVector<Region *, 1> worklist;
266ac91e673SRiver Riddle   worklist.reserve(from->getNumRegions());
267ac91e673SRiver Riddle   for (Region &region : from->getRegions())
268ac91e673SRiver Riddle     worklist.push_back(&region);
269ac91e673SRiver Riddle 
270ac91e673SRiver Riddle   while (!worklist.empty()) {
271ac91e673SRiver Riddle     Region *region = worklist.pop_back_val();
272ac91e673SRiver Riddle     for (Block &block : *region) {
273ac91e673SRiver Riddle       for (Operation &op : block) {
274ac91e673SRiver Riddle         if (walkSymbolRefs(&op, callback).wasInterrupted())
275ac91e673SRiver Riddle           return WalkResult::interrupt();
276ac91e673SRiver Riddle 
277*b3a6ae83SRiver Riddle         // If this operation has regions, and it as well as its dialect arent't
278*b3a6ae83SRiver Riddle         // registered then conservatively fail. The operation may define a
279*b3a6ae83SRiver Riddle         // symbol table, so we can't opaquely know if we should traverse to find
280*b3a6ae83SRiver Riddle         // nested uses.
281*b3a6ae83SRiver Riddle         if (isPotentiallyUnknownSymbolTable(&op))
282*b3a6ae83SRiver Riddle           return llvm::None;
283*b3a6ae83SRiver Riddle 
284ac91e673SRiver Riddle         // If this op defines a new symbol table scope, we can't traverse. Any
285ac91e673SRiver Riddle         // symbol references nested within 'op' are different semantically.
286ac91e673SRiver Riddle         if (!op.hasTrait<OpTrait::SymbolTable>()) {
287ac91e673SRiver Riddle           for (Region &region : op.getRegions())
288ac91e673SRiver Riddle             worklist.push_back(&region);
289ac91e673SRiver Riddle         }
290ac91e673SRiver Riddle       }
291ac91e673SRiver Riddle     }
292ac91e673SRiver Riddle   }
293ac91e673SRiver Riddle   return WalkResult::advance();
294ac91e673SRiver Riddle }
295ac91e673SRiver Riddle 
296*b3a6ae83SRiver Riddle /// Get an iterator range for all of the uses, for any symbol, that are nested
297*b3a6ae83SRiver Riddle /// within the given operation 'from'. This does not traverse into any nested
298*b3a6ae83SRiver Riddle /// symbol tables, and will also only return uses on 'from' if it does not
299*b3a6ae83SRiver Riddle /// also define a symbol table. This function returns None if there are any
300*b3a6ae83SRiver Riddle /// unknown operations that may potentially be symbol tables.
301*b3a6ae83SRiver Riddle auto SymbolTable::getSymbolUses(Operation *from) -> Optional<UseRange> {
302*b3a6ae83SRiver Riddle   std::vector<SymbolUse> uses;
303*b3a6ae83SRiver Riddle   Optional<WalkResult> result = walkSymbolUses(from, [&](SymbolUse symbolUse) {
304*b3a6ae83SRiver Riddle     uses.push_back(symbolUse);
305ac91e673SRiver Riddle     return WalkResult::advance();
306ac91e673SRiver Riddle   });
307*b3a6ae83SRiver Riddle   return result ? Optional<UseRange>(std::move(uses)) : Optional<UseRange>();
308ac91e673SRiver Riddle }
309ac91e673SRiver Riddle 
310*b3a6ae83SRiver Riddle /// Get all of the uses of the given symbol that are nested within the given
311*b3a6ae83SRiver Riddle /// operation 'from', invoking the provided callback for each. This does not
312*b3a6ae83SRiver Riddle /// traverse into any nested symbol tables, and will also only return uses on
313*b3a6ae83SRiver Riddle /// 'from' if it does not also define a symbol table. This function returns
314*b3a6ae83SRiver Riddle /// None if there are any unknown operations that may potentially be symbol
315*b3a6ae83SRiver Riddle /// tables.
316*b3a6ae83SRiver Riddle auto SymbolTable::getSymbolUses(StringRef symbol, Operation *from)
317*b3a6ae83SRiver Riddle     -> Optional<UseRange> {
318*b3a6ae83SRiver Riddle   SymbolRefAttr symbolRefAttr = SymbolRefAttr::get(symbol, from->getContext());
319*b3a6ae83SRiver Riddle 
320*b3a6ae83SRiver Riddle   std::vector<SymbolUse> uses;
321*b3a6ae83SRiver Riddle   Optional<WalkResult> result = walkSymbolUses(from, [&](SymbolUse symbolUse) {
322*b3a6ae83SRiver Riddle     if (symbolRefAttr == symbolUse.getSymbolRef())
323*b3a6ae83SRiver Riddle       uses.push_back(symbolUse);
324*b3a6ae83SRiver Riddle     return WalkResult::advance();
325*b3a6ae83SRiver Riddle   });
326*b3a6ae83SRiver Riddle   return result ? Optional<UseRange>(std::move(uses)) : Optional<UseRange>();
327*b3a6ae83SRiver Riddle }
328*b3a6ae83SRiver Riddle 
329*b3a6ae83SRiver Riddle /// Return if the given symbol is known to have no uses that are nested within
330*b3a6ae83SRiver Riddle /// the given operation 'from'. This does not traverse into any nested symbol
331*b3a6ae83SRiver Riddle /// tables, and will also only count uses on 'from' if it does not also define
332*b3a6ae83SRiver Riddle /// a symbol table. This function will also return false if there are any
333*b3a6ae83SRiver Riddle /// unknown operations that may potentially be symbol tables.
334*b3a6ae83SRiver Riddle bool SymbolTable::symbolKnownUseEmpty(StringRef symbol, Operation *from) {
335ac91e673SRiver Riddle   SymbolRefAttr symbolRefAttr = SymbolRefAttr::get(symbol, from->getContext());
336ac91e673SRiver Riddle 
337ac91e673SRiver Riddle   // Walk all of the symbol uses looking for a reference to 'symbol'.
338*b3a6ae83SRiver Riddle   Optional<WalkResult> walkResult =
339*b3a6ae83SRiver Riddle       walkSymbolUses(from, [&](SymbolUse symbolUse) {
340*b3a6ae83SRiver Riddle         return symbolUse.getSymbolRef() == symbolRefAttr
341*b3a6ae83SRiver Riddle                    ? WalkResult::interrupt()
342ac91e673SRiver Riddle                    : WalkResult::advance();
343ac91e673SRiver Riddle       });
344*b3a6ae83SRiver Riddle   return !walkResult || !walkResult->wasInterrupted();
345ac91e673SRiver Riddle }
346