10b57cec5SDimitry Andric //===--------- JITLinkGeneric.cpp - Generic JIT linker utilities ----------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Generic JITLinker utility class. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "JITLinkGeneric.h" 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamReader.h" 160b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #define DEBUG_TYPE "jitlink" 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric namespace llvm { 210b57cec5SDimitry Andric namespace jitlink { 220b57cec5SDimitry Andric 2381ad6265SDimitry Andric JITLinkerBase::~JITLinkerBase() = default; 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) { 260b57cec5SDimitry Andric 275ffd83dbSDimitry Andric LLVM_DEBUG({ 285ffd83dbSDimitry Andric dbgs() << "Starting link phase 1 for graph " << G->getName() << "\n"; 295ffd83dbSDimitry Andric }); 305ffd83dbSDimitry Andric 310b57cec5SDimitry Andric // Prune and optimize the graph. 328bcb0991SDimitry Andric if (auto Err = runPasses(Passes.PrePrunePasses)) 330b57cec5SDimitry Andric return Ctx->notifyFailed(std::move(Err)); 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric LLVM_DEBUG({ 368bcb0991SDimitry Andric dbgs() << "Link graph \"" << G->getName() << "\" pre-pruning:\n"; 37fe6060f1SDimitry Andric G->dump(dbgs()); 380b57cec5SDimitry Andric }); 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric prune(*G); 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric LLVM_DEBUG({ 438bcb0991SDimitry Andric dbgs() << "Link graph \"" << G->getName() << "\" post-pruning:\n"; 44fe6060f1SDimitry Andric G->dump(dbgs()); 450b57cec5SDimitry Andric }); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric // Run post-pruning passes. 488bcb0991SDimitry Andric if (auto Err = runPasses(Passes.PostPrunePasses)) 490b57cec5SDimitry Andric return Ctx->notifyFailed(std::move(Err)); 500b57cec5SDimitry Andric 51*1db9f3b2SDimitry Andric // Skip straight to phase 2 if the graph is empty with no associated actions. 52*1db9f3b2SDimitry Andric if (G->allocActions().empty() && llvm::all_of(G->sections(), [](Section &S) { 53*1db9f3b2SDimitry Andric return S.getMemLifetime() == orc::MemLifetime::NoAlloc; 54*1db9f3b2SDimitry Andric })) { 55*1db9f3b2SDimitry Andric linkPhase2(std::move(Self), nullptr); 56*1db9f3b2SDimitry Andric return; 57*1db9f3b2SDimitry Andric } 58*1db9f3b2SDimitry Andric 59349cc55cSDimitry Andric Ctx->getMemoryManager().allocate( 60349cc55cSDimitry Andric Ctx->getJITLinkDylib(), *G, 61349cc55cSDimitry Andric [S = std::move(Self)](AllocResult AR) mutable { 62bdd1243dSDimitry Andric // FIXME: Once MSVC implements c++17 order of evaluation rules for calls 63bdd1243dSDimitry Andric // this can be simplified to 64bdd1243dSDimitry Andric // S->linkPhase2(std::move(S), std::move(AR)); 65349cc55cSDimitry Andric auto *TmpSelf = S.get(); 66349cc55cSDimitry Andric TmpSelf->linkPhase2(std::move(S), std::move(AR)); 67349cc55cSDimitry Andric }); 68349cc55cSDimitry Andric } 690b57cec5SDimitry Andric 70349cc55cSDimitry Andric void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self, 71349cc55cSDimitry Andric AllocResult AR) { 72349cc55cSDimitry Andric 73349cc55cSDimitry Andric if (AR) 74349cc55cSDimitry Andric Alloc = std::move(*AR); 75349cc55cSDimitry Andric else 765f757f3fSDimitry Andric return Ctx->notifyFailed(AR.takeError()); 770b57cec5SDimitry Andric 78e8d8bef9SDimitry Andric LLVM_DEBUG({ 79e8d8bef9SDimitry Andric dbgs() << "Link graph \"" << G->getName() 80e8d8bef9SDimitry Andric << "\" before post-allocation passes:\n"; 81fe6060f1SDimitry Andric G->dump(dbgs()); 82e8d8bef9SDimitry Andric }); 83e8d8bef9SDimitry Andric 84e8d8bef9SDimitry Andric // Run post-allocation passes. 85e8d8bef9SDimitry Andric if (auto Err = runPasses(Passes.PostAllocationPasses)) 8606c3fb27SDimitry Andric return abandonAllocAndBailOut(std::move(Self), std::move(Err)); 87e8d8bef9SDimitry Andric 888bcb0991SDimitry Andric // Notify client that the defined symbols have been assigned addresses. 89fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "Resolving symbols defined in " << G->getName() << "\n"); 90e8d8bef9SDimitry Andric 91e8d8bef9SDimitry Andric if (auto Err = Ctx->notifyResolved(*G)) 9206c3fb27SDimitry Andric return abandonAllocAndBailOut(std::move(Self), std::move(Err)); 930b57cec5SDimitry Andric 940b57cec5SDimitry Andric auto ExternalSymbols = getExternalSymbolNames(); 950b57cec5SDimitry Andric 96349cc55cSDimitry Andric // If there are no external symbols then proceed immediately with phase 3. 97fe6060f1SDimitry Andric if (ExternalSymbols.empty()) { 98fe6060f1SDimitry Andric LLVM_DEBUG({ 99fe6060f1SDimitry Andric dbgs() << "No external symbols for " << G->getName() 100349cc55cSDimitry Andric << ". Proceeding immediately with link phase 3.\n"; 101fe6060f1SDimitry Andric }); 102bdd1243dSDimitry Andric // FIXME: Once MSVC implements c++17 order of evaluation rules for calls 103bdd1243dSDimitry Andric // this can be simplified. See below. 104fe6060f1SDimitry Andric auto &TmpSelf = *Self; 105349cc55cSDimitry Andric TmpSelf.linkPhase3(std::move(Self), AsyncLookupResult()); 106fe6060f1SDimitry Andric return; 107fe6060f1SDimitry Andric } 108fe6060f1SDimitry Andric 109fe6060f1SDimitry Andric // Otherwise look up the externals. 1105ffd83dbSDimitry Andric LLVM_DEBUG({ 1115ffd83dbSDimitry Andric dbgs() << "Issuing lookup for external symbols for " << G->getName() 1125ffd83dbSDimitry Andric << " (may trigger materialization/linking of other graphs)...\n"; 1135ffd83dbSDimitry Andric }); 1145ffd83dbSDimitry Andric 1150b57cec5SDimitry Andric // We're about to hand off ownership of ourself to the continuation. Grab a 1160b57cec5SDimitry Andric // pointer to the context so that we can call it to initiate the lookup. 1170b57cec5SDimitry Andric // 118bdd1243dSDimitry Andric // FIXME: Once MSVC implements c++17 order of evaluation rules for calls this 119bdd1243dSDimitry Andric // can be simplified to: 1200b57cec5SDimitry Andric // 1210b57cec5SDimitry Andric // Ctx->lookup(std::move(UnresolvedExternals), 1220b57cec5SDimitry Andric // [Self=std::move(Self)](Expected<AsyncLookupResult> Result) { 123349cc55cSDimitry Andric // Self->linkPhase3(std::move(Self), std::move(Result)); 1240b57cec5SDimitry Andric // }); 125349cc55cSDimitry Andric Ctx->lookup(std::move(ExternalSymbols), 1268bcb0991SDimitry Andric createLookupContinuation( 127349cc55cSDimitry Andric [S = std::move(Self)]( 1288bcb0991SDimitry Andric Expected<AsyncLookupResult> LookupResult) mutable { 1298bcb0991SDimitry Andric auto &TmpSelf = *S; 130349cc55cSDimitry Andric TmpSelf.linkPhase3(std::move(S), std::move(LookupResult)); 1318bcb0991SDimitry Andric })); 1320b57cec5SDimitry Andric } 1330b57cec5SDimitry Andric 134349cc55cSDimitry Andric void JITLinkerBase::linkPhase3(std::unique_ptr<JITLinkerBase> Self, 135349cc55cSDimitry Andric Expected<AsyncLookupResult> LR) { 1365ffd83dbSDimitry Andric 1375ffd83dbSDimitry Andric LLVM_DEBUG({ 138349cc55cSDimitry Andric dbgs() << "Starting link phase 3 for graph " << G->getName() << "\n"; 1395ffd83dbSDimitry Andric }); 1405ffd83dbSDimitry Andric 1410b57cec5SDimitry Andric // If the lookup failed, bail out. 1420b57cec5SDimitry Andric if (!LR) 143349cc55cSDimitry Andric return abandonAllocAndBailOut(std::move(Self), LR.takeError()); 1440b57cec5SDimitry Andric 1458bcb0991SDimitry Andric // Assign addresses to external addressables. 1460b57cec5SDimitry Andric applyLookupResult(*LR); 1470b57cec5SDimitry Andric 1485ffd83dbSDimitry Andric LLVM_DEBUG({ 1495ffd83dbSDimitry Andric dbgs() << "Link graph \"" << G->getName() 150e8d8bef9SDimitry Andric << "\" before pre-fixup passes:\n"; 151fe6060f1SDimitry Andric G->dump(dbgs()); 1525ffd83dbSDimitry Andric }); 1535ffd83dbSDimitry Andric 154e8d8bef9SDimitry Andric if (auto Err = runPasses(Passes.PreFixupPasses)) 155349cc55cSDimitry Andric return abandonAllocAndBailOut(std::move(Self), std::move(Err)); 1565ffd83dbSDimitry Andric 1570b57cec5SDimitry Andric LLVM_DEBUG({ 1588bcb0991SDimitry Andric dbgs() << "Link graph \"" << G->getName() << "\" before copy-and-fixup:\n"; 159fe6060f1SDimitry Andric G->dump(dbgs()); 1600b57cec5SDimitry Andric }); 1610b57cec5SDimitry Andric 1625ffd83dbSDimitry Andric // Fix up block content. 1635ffd83dbSDimitry Andric if (auto Err = fixUpBlocks(*G)) 164349cc55cSDimitry Andric return abandonAllocAndBailOut(std::move(Self), std::move(Err)); 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric LLVM_DEBUG({ 1678bcb0991SDimitry Andric dbgs() << "Link graph \"" << G->getName() << "\" after copy-and-fixup:\n"; 168fe6060f1SDimitry Andric G->dump(dbgs()); 1690b57cec5SDimitry Andric }); 1700b57cec5SDimitry Andric 1718bcb0991SDimitry Andric if (auto Err = runPasses(Passes.PostFixupPasses)) 172349cc55cSDimitry Andric return abandonAllocAndBailOut(std::move(Self), std::move(Err)); 1730b57cec5SDimitry Andric 174*1db9f3b2SDimitry Andric // Skip straight to phase 4 if the graph has no allocation. 175*1db9f3b2SDimitry Andric if (!Alloc) { 176*1db9f3b2SDimitry Andric linkPhase4(std::move(Self), JITLinkMemoryManager::FinalizedAlloc{}); 177*1db9f3b2SDimitry Andric return; 178*1db9f3b2SDimitry Andric } 179*1db9f3b2SDimitry Andric 180349cc55cSDimitry Andric Alloc->finalize([S = std::move(Self)](FinalizeResult FR) mutable { 181bdd1243dSDimitry Andric // FIXME: Once MSVC implements c++17 order of evaluation rules for calls 182bdd1243dSDimitry Andric // this can be simplified to 183bdd1243dSDimitry Andric // S->linkPhase2(std::move(S), std::move(AR)); 184349cc55cSDimitry Andric auto *TmpSelf = S.get(); 185349cc55cSDimitry Andric TmpSelf->linkPhase4(std::move(S), std::move(FR)); 186349cc55cSDimitry Andric }); 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric 189349cc55cSDimitry Andric void JITLinkerBase::linkPhase4(std::unique_ptr<JITLinkerBase> Self, 190349cc55cSDimitry Andric FinalizeResult FR) { 1915ffd83dbSDimitry Andric 1925ffd83dbSDimitry Andric LLVM_DEBUG({ 193349cc55cSDimitry Andric dbgs() << "Starting link phase 4 for graph " << G->getName() << "\n"; 1945ffd83dbSDimitry Andric }); 1955ffd83dbSDimitry Andric 196349cc55cSDimitry Andric if (!FR) 197349cc55cSDimitry Andric return Ctx->notifyFailed(FR.takeError()); 198349cc55cSDimitry Andric 199349cc55cSDimitry Andric Ctx->notifyFinalized(std::move(*FR)); 2005ffd83dbSDimitry Andric 2015ffd83dbSDimitry Andric LLVM_DEBUG({ dbgs() << "Link of graph " << G->getName() << " complete\n"; }); 2020b57cec5SDimitry Andric } 2030b57cec5SDimitry Andric 2048bcb0991SDimitry Andric Error JITLinkerBase::runPasses(LinkGraphPassList &Passes) { 2050b57cec5SDimitry Andric for (auto &P : Passes) 2068bcb0991SDimitry Andric if (auto Err = P(*G)) 2070b57cec5SDimitry Andric return Err; 2080b57cec5SDimitry Andric return Error::success(); 2090b57cec5SDimitry Andric } 2100b57cec5SDimitry Andric 211480093f4SDimitry Andric JITLinkContext::LookupMap JITLinkerBase::getExternalSymbolNames() const { 2128bcb0991SDimitry Andric // Identify unresolved external symbols. 213480093f4SDimitry Andric JITLinkContext::LookupMap UnresolvedExternals; 2148bcb0991SDimitry Andric for (auto *Sym : G->external_symbols()) { 21504eeddc0SDimitry Andric assert(!Sym->getAddress() && 2160b57cec5SDimitry Andric "External has already been assigned an address"); 2178bcb0991SDimitry Andric assert(Sym->getName() != StringRef() && Sym->getName() != "" && 2180b57cec5SDimitry Andric "Externals must be named"); 219480093f4SDimitry Andric SymbolLookupFlags LookupFlags = 220bdd1243dSDimitry Andric Sym->isWeaklyReferenced() ? SymbolLookupFlags::WeaklyReferencedSymbol 221480093f4SDimitry Andric : SymbolLookupFlags::RequiredSymbol; 222480093f4SDimitry Andric UnresolvedExternals[Sym->getName()] = LookupFlags; 2230b57cec5SDimitry Andric } 2240b57cec5SDimitry Andric return UnresolvedExternals; 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) { 2288bcb0991SDimitry Andric for (auto *Sym : G->external_symbols()) { 229480093f4SDimitry Andric assert(Sym->getOffset() == 0 && 230480093f4SDimitry Andric "External symbol is not at the start of its addressable block"); 23104eeddc0SDimitry Andric assert(!Sym->getAddress() && "Symbol already resolved"); 2328bcb0991SDimitry Andric assert(!Sym->isDefined() && "Symbol being resolved is already defined"); 233480093f4SDimitry Andric auto ResultI = Result.find(Sym->getName()); 234bdd1243dSDimitry Andric if (ResultI != Result.end()) { 23506c3fb27SDimitry Andric Sym->getAddressable().setAddress(ResultI->second.getAddress()); 236bdd1243dSDimitry Andric Sym->setLinkage(ResultI->second.getFlags().isWeak() ? Linkage::Weak 237bdd1243dSDimitry Andric : Linkage::Strong); 238bdd1243dSDimitry Andric Sym->setScope(ResultI->second.getFlags().isExported() ? Scope::Default 239bdd1243dSDimitry Andric : Scope::Hidden); 240bdd1243dSDimitry Andric } else 241bdd1243dSDimitry Andric assert(Sym->isWeaklyReferenced() && 242480093f4SDimitry Andric "Failed to resolve non-weak reference"); 2430b57cec5SDimitry Andric } 2440b57cec5SDimitry Andric 2450b57cec5SDimitry Andric LLVM_DEBUG({ 2460b57cec5SDimitry Andric dbgs() << "Externals after applying lookup result:\n"; 247bdd1243dSDimitry Andric for (auto *Sym : G->external_symbols()) { 2488bcb0991SDimitry Andric dbgs() << " " << Sym->getName() << ": " 249bdd1243dSDimitry Andric << formatv("{0:x16}", Sym->getAddress().getValue()); 250bdd1243dSDimitry Andric switch (Sym->getLinkage()) { 251bdd1243dSDimitry Andric case Linkage::Strong: 252bdd1243dSDimitry Andric break; 253bdd1243dSDimitry Andric case Linkage::Weak: 254bdd1243dSDimitry Andric dbgs() << " (weak)"; 255bdd1243dSDimitry Andric break; 256bdd1243dSDimitry Andric } 257bdd1243dSDimitry Andric switch (Sym->getScope()) { 258bdd1243dSDimitry Andric case Scope::Local: 259bdd1243dSDimitry Andric llvm_unreachable("External symbol should not have local linkage"); 260bdd1243dSDimitry Andric case Scope::Hidden: 261bdd1243dSDimitry Andric break; 262bdd1243dSDimitry Andric case Scope::Default: 263bdd1243dSDimitry Andric dbgs() << " (exported)"; 264bdd1243dSDimitry Andric break; 265bdd1243dSDimitry Andric } 266bdd1243dSDimitry Andric dbgs() << "\n"; 267bdd1243dSDimitry Andric } 2680b57cec5SDimitry Andric }); 2690b57cec5SDimitry Andric } 2700b57cec5SDimitry Andric 271349cc55cSDimitry Andric void JITLinkerBase::abandonAllocAndBailOut(std::unique_ptr<JITLinkerBase> Self, 272349cc55cSDimitry Andric Error Err) { 2730b57cec5SDimitry Andric assert(Err && "Should not be bailing out on success value"); 274349cc55cSDimitry Andric assert(Alloc && "can not call abandonAllocAndBailOut before allocation"); 275349cc55cSDimitry Andric Alloc->abandon([S = std::move(Self), E1 = std::move(Err)](Error E2) mutable { 276349cc55cSDimitry Andric S->Ctx->notifyFailed(joinErrors(std::move(E1), std::move(E2))); 277349cc55cSDimitry Andric }); 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2808bcb0991SDimitry Andric void prune(LinkGraph &G) { 2818bcb0991SDimitry Andric std::vector<Symbol *> Worklist; 2828bcb0991SDimitry Andric DenseSet<Block *> VisitedBlocks; 2830b57cec5SDimitry Andric 2848bcb0991SDimitry Andric // Build the initial worklist from all symbols initially live. 2858bcb0991SDimitry Andric for (auto *Sym : G.defined_symbols()) 2868bcb0991SDimitry Andric if (Sym->isLive()) 2878bcb0991SDimitry Andric Worklist.push_back(Sym); 2880b57cec5SDimitry Andric 2898bcb0991SDimitry Andric // Propagate live flags to all symbols reachable from the initial live set. 2900b57cec5SDimitry Andric while (!Worklist.empty()) { 2918bcb0991SDimitry Andric auto *Sym = Worklist.back(); 2920b57cec5SDimitry Andric Worklist.pop_back(); 2930b57cec5SDimitry Andric 2948bcb0991SDimitry Andric auto &B = Sym->getBlock(); 2950b57cec5SDimitry Andric 2968bcb0991SDimitry Andric // Skip addressables that we've visited before. 2978bcb0991SDimitry Andric if (VisitedBlocks.count(&B)) 2980b57cec5SDimitry Andric continue; 2990b57cec5SDimitry Andric 3008bcb0991SDimitry Andric VisitedBlocks.insert(&B); 3010b57cec5SDimitry Andric 3028bcb0991SDimitry Andric for (auto &E : Sym->getBlock().edges()) { 303e8d8bef9SDimitry Andric // If the edge target is a defined symbol that is being newly marked live 304e8d8bef9SDimitry Andric // then add it to the worklist. 305e8d8bef9SDimitry Andric if (E.getTarget().isDefined() && !E.getTarget().isLive()) 3068bcb0991SDimitry Andric Worklist.push_back(&E.getTarget()); 307e8d8bef9SDimitry Andric 308e8d8bef9SDimitry Andric // Mark the target live. 309e8d8bef9SDimitry Andric E.getTarget().setLive(true); 3100b57cec5SDimitry Andric } 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric 313e8d8bef9SDimitry Andric // Collect all defined symbols to remove, then remove them. 3140b57cec5SDimitry Andric { 315e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Dead-stripping defined symbols:\n"); 3168bcb0991SDimitry Andric std::vector<Symbol *> SymbolsToRemove; 3178bcb0991SDimitry Andric for (auto *Sym : G.defined_symbols()) 3188bcb0991SDimitry Andric if (!Sym->isLive()) 3198bcb0991SDimitry Andric SymbolsToRemove.push_back(Sym); 3208bcb0991SDimitry Andric for (auto *Sym : SymbolsToRemove) { 3218bcb0991SDimitry Andric LLVM_DEBUG(dbgs() << " " << *Sym << "...\n"); 3228bcb0991SDimitry Andric G.removeDefinedSymbol(*Sym); 3238bcb0991SDimitry Andric } 3248bcb0991SDimitry Andric } 3258bcb0991SDimitry Andric 3268bcb0991SDimitry Andric // Delete any unused blocks. 3278bcb0991SDimitry Andric { 3288bcb0991SDimitry Andric LLVM_DEBUG(dbgs() << "Dead-stripping blocks:\n"); 3298bcb0991SDimitry Andric std::vector<Block *> BlocksToRemove; 3308bcb0991SDimitry Andric for (auto *B : G.blocks()) 3318bcb0991SDimitry Andric if (!VisitedBlocks.count(B)) 3328bcb0991SDimitry Andric BlocksToRemove.push_back(B); 3338bcb0991SDimitry Andric for (auto *B : BlocksToRemove) { 3348bcb0991SDimitry Andric LLVM_DEBUG(dbgs() << " " << *B << "...\n"); 3358bcb0991SDimitry Andric G.removeBlock(*B); 3368bcb0991SDimitry Andric } 3370b57cec5SDimitry Andric } 338e8d8bef9SDimitry Andric 339e8d8bef9SDimitry Andric // Collect all external symbols to remove, then remove them. 340e8d8bef9SDimitry Andric { 341e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Removing unused external symbols:\n"); 342e8d8bef9SDimitry Andric std::vector<Symbol *> SymbolsToRemove; 343e8d8bef9SDimitry Andric for (auto *Sym : G.external_symbols()) 344e8d8bef9SDimitry Andric if (!Sym->isLive()) 345e8d8bef9SDimitry Andric SymbolsToRemove.push_back(Sym); 346e8d8bef9SDimitry Andric for (auto *Sym : SymbolsToRemove) { 347e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " " << *Sym << "...\n"); 348e8d8bef9SDimitry Andric G.removeExternalSymbol(*Sym); 349e8d8bef9SDimitry Andric } 350e8d8bef9SDimitry Andric } 3510b57cec5SDimitry Andric } 3520b57cec5SDimitry Andric 3530b57cec5SDimitry Andric } // end namespace jitlink 3540b57cec5SDimitry Andric } // end namespace llvm 355