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 ®ion : from->getRegions()) 252*ac91e673SRiver Riddle worklist.push_back(®ion); 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 ®ion : op.getRegions()) 265*ac91e673SRiver Riddle worklist.push_back(®ion); 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