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 
23ee8e8b55SRiver Riddle /// Build a symbol table with the symbols within the given operation.
24ee8e8b55SRiver Riddle SymbolTable::SymbolTable(Operation *op) : context(op->getContext()) {
25ee8e8b55SRiver Riddle   assert(op->hasTrait<OpTrait::SymbolTable>() &&
26ee8e8b55SRiver Riddle          "expected operation to have SymbolTable trait");
27ee8e8b55SRiver Riddle   assert(op->getNumRegions() == 1 &&
28ee8e8b55SRiver Riddle          "expected operation to have a single region");
29ee8e8b55SRiver Riddle 
30ee8e8b55SRiver Riddle   for (auto &block : op->getRegion(0)) {
31ee8e8b55SRiver Riddle     for (auto &op : block) {
32ee8e8b55SRiver Riddle       auto nameAttr = op.getAttrOfType<StringAttr>(getSymbolAttrName());
33ee8e8b55SRiver Riddle       if (!nameAttr)
34ee8e8b55SRiver Riddle         continue;
35ee8e8b55SRiver Riddle 
36ee8e8b55SRiver Riddle       auto inserted = symbolTable.insert({nameAttr.getValue(), &op});
378c47e2edSRiver Riddle       (void)inserted;
388c47e2edSRiver Riddle       assert(inserted.second &&
39ee8e8b55SRiver Riddle              "expected region to contain uniquely named symbol operations");
40ee8e8b55SRiver Riddle     }
418c47e2edSRiver Riddle   }
428c47e2edSRiver Riddle }
438c47e2edSRiver Riddle 
44927b7074SRiver Riddle /// Look up a symbol with the specified name, returning null if no such name
45927b7074SRiver Riddle /// exists. Names never include the @ on them.
46ee8e8b55SRiver Riddle Operation *SymbolTable::lookup(StringRef name) const {
47e7d594bbSRiver Riddle   return symbolTable.lookup(name);
48927b7074SRiver Riddle }
49927b7074SRiver Riddle 
50927b7074SRiver Riddle /// Erase the given symbol from the table.
51ee8e8b55SRiver Riddle void SymbolTable::erase(Operation *symbol) {
52ee8e8b55SRiver Riddle   auto nameAttr = symbol->getAttrOfType<StringAttr>(getSymbolAttrName());
53ee8e8b55SRiver Riddle   assert(nameAttr && "expected valid 'name' attribute");
54ee8e8b55SRiver Riddle 
55ee8e8b55SRiver Riddle   auto it = symbolTable.find(nameAttr.getValue());
56927b7074SRiver Riddle   if (it != symbolTable.end() && it->second == symbol)
57927b7074SRiver Riddle     symbolTable.erase(it);
58927b7074SRiver Riddle }
59927b7074SRiver Riddle 
60927b7074SRiver Riddle /// Insert a new symbol into the table, and rename it as necessary to avoid
61927b7074SRiver Riddle /// collisions.
62ee8e8b55SRiver Riddle void SymbolTable::insert(Operation *symbol) {
63ee8e8b55SRiver Riddle   auto nameAttr = symbol->getAttrOfType<StringAttr>(getSymbolAttrName());
64ee8e8b55SRiver Riddle   assert(nameAttr && "expected valid 'name' attribute");
65ee8e8b55SRiver Riddle 
66927b7074SRiver Riddle   // Add this symbol to the symbol table, uniquing the name if a conflict is
67927b7074SRiver Riddle   // detected.
68ee8e8b55SRiver Riddle   if (symbolTable.insert({nameAttr.getValue(), symbol}).second)
69927b7074SRiver Riddle     return;
70927b7074SRiver Riddle 
71ee8e8b55SRiver Riddle   // If a conflict was detected, then the symbol will not have been added to
72927b7074SRiver Riddle   // the symbol table. Try suffixes until we get to a unique name that works.
73ee8e8b55SRiver Riddle   SmallString<128> nameBuffer(nameAttr.getValue());
74927b7074SRiver Riddle   unsigned originalLength = nameBuffer.size();
75927b7074SRiver Riddle 
76ee8e8b55SRiver Riddle   // Iteratively try suffixes until we find one that isn't used.
77927b7074SRiver Riddle   do {
78927b7074SRiver Riddle     nameBuffer.resize(originalLength);
79927b7074SRiver Riddle     nameBuffer += '_';
80927b7074SRiver Riddle     nameBuffer += std::to_string(uniquingCounter++);
81e7d594bbSRiver Riddle   } while (!symbolTable.insert({nameBuffer, symbol}).second);
82ee8e8b55SRiver Riddle   symbol->setAttr(getSymbolAttrName(), StringAttr::get(nameBuffer, context));
83ee8e8b55SRiver Riddle }
84ee8e8b55SRiver Riddle 
858cb405a8SRiver Riddle /// Returns the operation registered with the given symbol name with the
868cb405a8SRiver Riddle /// regions of 'symbolTableOp'. 'symbolTableOp' is required to be an operation
878cb405a8SRiver Riddle /// with the 'OpTrait::SymbolTable' trait. Returns nullptr if no valid symbol
888cb405a8SRiver Riddle /// was found.
898cb405a8SRiver Riddle Operation *SymbolTable::lookupSymbolIn(Operation *symbolTableOp,
908cb405a8SRiver Riddle                                        StringRef symbol) {
918cb405a8SRiver Riddle   assert(symbolTableOp->hasTrait<OpTrait::SymbolTable>());
928cb405a8SRiver Riddle 
938cb405a8SRiver Riddle   // Look for a symbol with the given name.
948cb405a8SRiver Riddle   for (auto &block : symbolTableOp->getRegion(0)) {
958cb405a8SRiver Riddle     for (auto &op : block) {
968cb405a8SRiver Riddle       auto nameAttr = op.template getAttrOfType<StringAttr>(
978cb405a8SRiver Riddle           mlir::SymbolTable::getSymbolAttrName());
988cb405a8SRiver Riddle       if (nameAttr && nameAttr.getValue() == symbol)
998cb405a8SRiver Riddle         return &op;
1008cb405a8SRiver Riddle     }
1018cb405a8SRiver Riddle   }
1028cb405a8SRiver Riddle   return nullptr;
1038cb405a8SRiver Riddle }
1048cb405a8SRiver Riddle 
1058cb405a8SRiver Riddle /// Returns the operation registered with the given symbol name within the
1068cb405a8SRiver Riddle /// closes parent operation with the 'OpTrait::SymbolTable' trait. Returns
1078cb405a8SRiver Riddle /// nullptr if no valid symbol was found.
1088cb405a8SRiver Riddle Operation *SymbolTable::lookupNearestSymbolFrom(Operation *from,
1098cb405a8SRiver Riddle                                                 StringRef symbol) {
1108cb405a8SRiver Riddle   while (from && !from->hasTrait<OpTrait::SymbolTable>())
1118cb405a8SRiver Riddle     from = from->getParentOp();
1128cb405a8SRiver Riddle   return from ? lookupSymbolIn(from, symbol) : nullptr;
1138cb405a8SRiver Riddle }
1148cb405a8SRiver Riddle 
115ee8e8b55SRiver Riddle //===----------------------------------------------------------------------===//
116ee8e8b55SRiver Riddle // SymbolTable Trait Types
117ee8e8b55SRiver Riddle //===----------------------------------------------------------------------===//
118ee8e8b55SRiver Riddle 
119ee8e8b55SRiver Riddle LogicalResult OpTrait::impl::verifySymbolTable(Operation *op) {
120ee8e8b55SRiver Riddle   if (op->getNumRegions() != 1)
121ee8e8b55SRiver Riddle     return op->emitOpError()
122ee8e8b55SRiver Riddle            << "Operations with a 'SymbolTable' must have exactly one region";
123ee8e8b55SRiver Riddle 
124ee8e8b55SRiver Riddle   // Check that all symboles are uniquely named within child regions.
125ee8e8b55SRiver Riddle   llvm::StringMap<Location> nameToOrigLoc;
126ee8e8b55SRiver Riddle   for (auto &block : op->getRegion(0)) {
127ee8e8b55SRiver Riddle     for (auto &op : block) {
128ee8e8b55SRiver Riddle       // Check for a symbol name attribute.
129ee8e8b55SRiver Riddle       auto nameAttr =
130ee8e8b55SRiver Riddle           op.getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName());
131ee8e8b55SRiver Riddle       if (!nameAttr)
132ee8e8b55SRiver Riddle         continue;
133ee8e8b55SRiver Riddle 
134ee8e8b55SRiver Riddle       // Try to insert this symbol into the table.
135ee8e8b55SRiver Riddle       auto it = nameToOrigLoc.try_emplace(nameAttr.getValue(), op.getLoc());
136ee8e8b55SRiver Riddle       if (!it.second)
137ee8e8b55SRiver Riddle         return op.emitError()
138ee8e8b55SRiver Riddle             .append("redefinition of symbol named '", nameAttr.getValue(), "'")
139ee8e8b55SRiver Riddle             .attachNote(it.first->second)
140ee8e8b55SRiver Riddle             .append("see existing symbol definition here");
141ee8e8b55SRiver Riddle     }
142ee8e8b55SRiver Riddle   }
143ee8e8b55SRiver Riddle   return success();
144927b7074SRiver Riddle }
145*ac91e673SRiver Riddle 
146*ac91e673SRiver Riddle //===----------------------------------------------------------------------===//
147*ac91e673SRiver Riddle // SymbolTable Trait Types
148*ac91e673SRiver Riddle //===----------------------------------------------------------------------===//
149*ac91e673SRiver Riddle 
150*ac91e673SRiver Riddle /// A utility result for walking a nested attribute for symbol uses.
151*ac91e673SRiver Riddle enum HandlerResult {
152*ac91e673SRiver Riddle   /// The walk of the containter can continue.
153*ac91e673SRiver Riddle   Continue = 0,
154*ac91e673SRiver Riddle   /// The walk should recurse into the given attribute, as it is a container.
155*ac91e673SRiver Riddle   RecurseNestedAttribute,
156*ac91e673SRiver Riddle   /// The walk should end immediately, as an interrupt has been signaled.
157*ac91e673SRiver Riddle   Interrupt
158*ac91e673SRiver Riddle };
159*ac91e673SRiver Riddle 
160*ac91e673SRiver Riddle /// Utility function used to handle a nested attribute during a walk of symbol
161*ac91e673SRiver Riddle /// uses. It returns the above HandlerResult signaling the next action for the
162*ac91e673SRiver Riddle /// walk.
163*ac91e673SRiver Riddle HandlerResult handleAttrDuringSymbolWalk(
164*ac91e673SRiver Riddle     Operation *op, Attribute attr,
165*ac91e673SRiver Riddle     SmallVectorImpl<std::pair<Attribute, unsigned>> &worklist,
166*ac91e673SRiver Riddle     function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
167*ac91e673SRiver Riddle   switch (attr.getKind()) {
168*ac91e673SRiver Riddle   /// Check for a nested container attribute, these will also need to be
169*ac91e673SRiver Riddle   /// walked.
170*ac91e673SRiver Riddle   case StandardAttributes::Array:
171*ac91e673SRiver Riddle   case StandardAttributes::Dictionary: {
172*ac91e673SRiver Riddle     worklist.push_back({attr, /*index*/ 0});
173*ac91e673SRiver Riddle     return HandlerResult::RecurseNestedAttribute;
174*ac91e673SRiver Riddle   }
175*ac91e673SRiver Riddle 
176*ac91e673SRiver Riddle   // Invoke the provided callback if we find a symbol use and check for a
177*ac91e673SRiver Riddle   // requested interrupt.
178*ac91e673SRiver Riddle   case StandardAttributes::SymbolRef: {
179*ac91e673SRiver Riddle     SymbolTable::SymbolUse use(op, attr.cast<SymbolRefAttr>());
180*ac91e673SRiver Riddle     return callback(use).wasInterrupted() ? HandlerResult::Interrupt
181*ac91e673SRiver Riddle                                           : HandlerResult::Continue;
182*ac91e673SRiver Riddle   }
183*ac91e673SRiver Riddle   default:
184*ac91e673SRiver Riddle     return HandlerResult::Continue;
185*ac91e673SRiver Riddle   }
186*ac91e673SRiver Riddle }
187*ac91e673SRiver Riddle 
188*ac91e673SRiver Riddle /// Walk all of the symbol references within the given operation, invoking the
189*ac91e673SRiver Riddle /// provided callback for each found use.
190*ac91e673SRiver Riddle static WalkResult
191*ac91e673SRiver Riddle walkSymbolRefs(Operation *op,
192*ac91e673SRiver Riddle                function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
193*ac91e673SRiver Riddle   // Check to see if the operation has any attributes.
194*ac91e673SRiver Riddle   DictionaryAttr attrDict = op->getAttrList().getDictionary();
195*ac91e673SRiver Riddle   if (!attrDict)
196*ac91e673SRiver Riddle     return WalkResult::advance();
197*ac91e673SRiver Riddle 
198*ac91e673SRiver Riddle   // A worklist of a container attribute and the current index into the held
199*ac91e673SRiver Riddle   // attribute list.
200*ac91e673SRiver Riddle   SmallVector<std::pair<Attribute, unsigned>, 1> worklist;
201*ac91e673SRiver Riddle   worklist.push_back({attrDict, /*index*/ 0});
202*ac91e673SRiver Riddle   while (!worklist.empty()) {
203*ac91e673SRiver Riddle     Attribute attr = worklist.back().first;
204*ac91e673SRiver Riddle     unsigned &index = worklist.back().second;
205*ac91e673SRiver Riddle 
206*ac91e673SRiver Riddle     // Iterate over the given attribute, which is guaranteed to be a container.
207*ac91e673SRiver Riddle     HandlerResult handlerResult = HandlerResult::Continue;
208*ac91e673SRiver Riddle     if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
209*ac91e673SRiver Riddle       ArrayRef<Attribute> attrs = arrayAttr.getValue();
210*ac91e673SRiver Riddle       unsigned attrSize = attrs.size();
211*ac91e673SRiver Riddle       while (index != attrSize)
212*ac91e673SRiver Riddle         if ((handlerResult = handleAttrDuringSymbolWalk(op, attrs[index++],
213*ac91e673SRiver Riddle                                                         worklist, callback)))
214*ac91e673SRiver Riddle           break;
215*ac91e673SRiver Riddle     } else {
216*ac91e673SRiver Riddle       auto dictAttr = attr.cast<DictionaryAttr>();
217*ac91e673SRiver Riddle       ArrayRef<NamedAttribute> attrs = dictAttr.getValue();
218*ac91e673SRiver Riddle       unsigned attrSize = attrs.size();
219*ac91e673SRiver Riddle       while (index != attrSize)
220*ac91e673SRiver Riddle         if ((handlerResult = handleAttrDuringSymbolWalk(
221*ac91e673SRiver Riddle                  op, attrs[index++].second, worklist, callback)))
222*ac91e673SRiver Riddle           break;
223*ac91e673SRiver Riddle     }
224*ac91e673SRiver Riddle     if (handlerResult == HandlerResult::Interrupt)
225*ac91e673SRiver Riddle       return WalkResult::interrupt();
226*ac91e673SRiver Riddle 
227*ac91e673SRiver Riddle     // If we didn't encounter a nested attribute, pop the last item from the
228*ac91e673SRiver Riddle     // worklist.
229*ac91e673SRiver Riddle     if (handlerResult != HandlerResult::RecurseNestedAttribute)
230*ac91e673SRiver Riddle       worklist.pop_back();
231*ac91e673SRiver Riddle   }
232*ac91e673SRiver Riddle   return WalkResult::advance();
233*ac91e673SRiver Riddle }
234*ac91e673SRiver Riddle 
235*ac91e673SRiver Riddle /// Walk all of the uses, for any symbol, that are nested within the given
236*ac91e673SRiver Riddle /// operation 'from', invoking the provided callback for each. This does not
237*ac91e673SRiver Riddle /// traverse into any nested symbol tables, and will also only return uses on
238*ac91e673SRiver Riddle /// 'from' if it does not also define a symbol table.
239*ac91e673SRiver Riddle WalkResult
240*ac91e673SRiver Riddle SymbolTable::walkSymbolUses(Operation *from,
241*ac91e673SRiver Riddle                             function_ref<WalkResult(SymbolUse)> callback) {
242*ac91e673SRiver Riddle   // If from is not a symbol table, check for uses. A symbol table defines a new
243*ac91e673SRiver Riddle   // scope, so we can't walk the attributes from the symbol table op.
244*ac91e673SRiver Riddle   if (!from->hasTrait<OpTrait::SymbolTable>()) {
245*ac91e673SRiver Riddle     if (walkSymbolRefs(from, callback).wasInterrupted())
246*ac91e673SRiver Riddle       return WalkResult::interrupt();
247*ac91e673SRiver Riddle   }
248*ac91e673SRiver Riddle 
249*ac91e673SRiver Riddle   SmallVector<Region *, 1> worklist;
250*ac91e673SRiver Riddle   worklist.reserve(from->getNumRegions());
251*ac91e673SRiver Riddle   for (Region &region : from->getRegions())
252*ac91e673SRiver Riddle     worklist.push_back(&region);
253*ac91e673SRiver Riddle 
254*ac91e673SRiver Riddle   while (!worklist.empty()) {
255*ac91e673SRiver Riddle     Region *region = worklist.pop_back_val();
256*ac91e673SRiver Riddle     for (Block &block : *region) {
257*ac91e673SRiver Riddle       for (Operation &op : block) {
258*ac91e673SRiver Riddle         if (walkSymbolRefs(&op, callback).wasInterrupted())
259*ac91e673SRiver Riddle           return WalkResult::interrupt();
260*ac91e673SRiver Riddle 
261*ac91e673SRiver Riddle         // If this op defines a new symbol table scope, we can't traverse. Any
262*ac91e673SRiver Riddle         // symbol references nested within 'op' are different semantically.
263*ac91e673SRiver Riddle         if (!op.hasTrait<OpTrait::SymbolTable>()) {
264*ac91e673SRiver Riddle           for (Region &region : op.getRegions())
265*ac91e673SRiver Riddle             worklist.push_back(&region);
266*ac91e673SRiver Riddle         }
267*ac91e673SRiver Riddle       }
268*ac91e673SRiver Riddle     }
269*ac91e673SRiver Riddle   }
270*ac91e673SRiver Riddle   return WalkResult::advance();
271*ac91e673SRiver Riddle }
272*ac91e673SRiver Riddle 
273*ac91e673SRiver Riddle /// Walk all of the uses, for any symbol, that are nested within the given
274*ac91e673SRiver Riddle /// operation 'from', invoking the provided callback for each. This does not
275*ac91e673SRiver Riddle /// traverse into any nested symbol tables, and will also only return uses on
276*ac91e673SRiver Riddle /// 'from' if it does not also define a symbol table.
277*ac91e673SRiver Riddle WalkResult
278*ac91e673SRiver Riddle SymbolTable::walkSymbolUses(StringRef symbol, Operation *from,
279*ac91e673SRiver Riddle                             function_ref<WalkResult(SymbolUse)> callback) {
280*ac91e673SRiver Riddle   SymbolRefAttr symbolRefAttr = SymbolRefAttr::get(symbol, from->getContext());
281*ac91e673SRiver Riddle   return walkSymbolUses(from, [&](SymbolUse symbolUse) {
282*ac91e673SRiver Riddle     if (symbolUse.getSymbolRef() != symbolRefAttr)
283*ac91e673SRiver Riddle       return WalkResult::advance();
284*ac91e673SRiver Riddle     return callback(std::move(symbolUse));
285*ac91e673SRiver Riddle   });
286*ac91e673SRiver Riddle }
287*ac91e673SRiver Riddle 
288*ac91e673SRiver Riddle /// Return if the given symbol has no uses that are nested within the given
289*ac91e673SRiver Riddle /// operation 'from'. This does not traverse into any nested symbol tables,
290*ac91e673SRiver Riddle /// and will also only count uses on 'from' if it does not also define a
291*ac91e673SRiver Riddle /// symbol table.
292*ac91e673SRiver Riddle bool SymbolTable::symbol_use_empty(StringRef symbol, Operation *from) {
293*ac91e673SRiver Riddle   SymbolRefAttr symbolRefAttr = SymbolRefAttr::get(symbol, from->getContext());
294*ac91e673SRiver Riddle 
295*ac91e673SRiver Riddle   // Walk all of the symbol uses looking for a reference to 'symbol'.
296*ac91e673SRiver Riddle   auto walkResult = walkSymbolUses(from, [&](SymbolUse symbolUse) {
297*ac91e673SRiver Riddle     return symbolUse.getSymbolRef() == symbolRefAttr ? WalkResult::interrupt()
298*ac91e673SRiver Riddle                                                      : WalkResult::advance();
299*ac91e673SRiver Riddle   });
300*ac91e673SRiver Riddle   return !walkResult.wasInterrupted();
301*ac91e673SRiver Riddle }
302