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