347 lines · cpp
1//===--------- JITLinkGeneric.cpp - Generic JIT linker utilities ----------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// Generic JITLinker utility class.10//11//===----------------------------------------------------------------------===//12 13#include "JITLinkGeneric.h"14 15#define DEBUG_TYPE "jitlink"16 17namespace llvm {18namespace jitlink {19 20JITLinkerBase::~JITLinkerBase() = default;21 22void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) {23 24 LLVM_DEBUG(dbgs() << "Starting link phase 1\n");25 26 // Prune and optimize the graph.27 if (auto Err = runPasses(Passes.PrePrunePasses))28 return Ctx->notifyFailed(std::move(Err));29 30 LLVM_DEBUG({31 dbgs() << "Link graph pre-pruning:\n";32 G->dump(dbgs());33 });34 35 prune(*G);36 37 LLVM_DEBUG({38 dbgs() << "Link graph post-pruning:\n";39 G->dump(dbgs());40 });41 42 // Run post-pruning passes.43 if (auto Err = runPasses(Passes.PostPrunePasses))44 return Ctx->notifyFailed(std::move(Err));45 46 // Skip straight to phase 2 if the graph is empty with no associated actions.47 if (G->allocActions().empty() && llvm::all_of(G->sections(), [](Section &S) {48 return S.getMemLifetime() == orc::MemLifetime::NoAlloc;49 })) {50 linkPhase2(std::move(Self), nullptr);51 return;52 }53 54 Ctx->getMemoryManager().allocate(55 Ctx->getJITLinkDylib(), *G,56 [S = std::move(Self)](AllocResult AR) mutable {57 // FIXME: Once MSVC implements c++17 order of evaluation rules for calls58 // this can be simplified to59 // S->linkPhase2(std::move(S), std::move(AR));60 auto *TmpSelf = S.get();61 TmpSelf->linkPhase2(std::move(S), std::move(AR));62 });63}64 65void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self,66 AllocResult AR) {67 68 LLVM_DEBUG(dbgs() << "Starting link phase 2\n");69 70 if (AR)71 Alloc = std::move(*AR);72 else73 return Ctx->notifyFailed(AR.takeError());74 75 LLVM_DEBUG({76 dbgs() << "Link graph before post-allocation passes:\n";77 G->dump(dbgs());78 });79 80 // Run post-allocation passes.81 if (auto Err = runPasses(Passes.PostAllocationPasses))82 return abandonAllocAndBailOut(std::move(Self), std::move(Err));83 84 // Notify client that the defined symbols have been assigned addresses.85 LLVM_DEBUG(dbgs() << "Resolving symbols defined in " << G->getName() << "\n");86 87 if (auto Err = Ctx->notifyResolved(*G))88 return abandonAllocAndBailOut(std::move(Self), std::move(Err));89 90 auto ExternalSymbols = getExternalSymbolNames();91 92 // If there are no external symbols then proceed immediately with phase 3.93 if (ExternalSymbols.empty()) {94 LLVM_DEBUG({95 dbgs() << "No external symbols for " << G->getName()96 << ". Proceeding immediately with link phase 3.\n";97 });98 // FIXME: Once MSVC implements c++17 order of evaluation rules for calls99 // this can be simplified. See below.100 auto &TmpSelf = *Self;101 TmpSelf.linkPhase3(std::move(Self), AsyncLookupResult());102 return;103 }104 105 // Otherwise look up the externals.106 LLVM_DEBUG({107 dbgs() << "Issuing lookup for external symbols for " << G->getName()108 << " (may trigger materialization/linking of other graphs)...\n";109 });110 111 // We're about to hand off ownership of ourself to the continuation. Grab a112 // pointer to the context so that we can call it to initiate the lookup.113 //114 // FIXME: Once MSVC implements c++17 order of evaluation rules for calls this115 // can be simplified to:116 //117 // Ctx->lookup(std::move(UnresolvedExternals),118 // [Self=std::move(Self)](Expected<AsyncLookupResult> Result) {119 // Self->linkPhase3(std::move(Self), std::move(Result));120 // });121 Ctx->lookup(std::move(ExternalSymbols),122 createLookupContinuation(123 [S = std::move(Self)](124 Expected<AsyncLookupResult> LookupResult) mutable {125 auto &TmpSelf = *S;126 TmpSelf.linkPhase3(std::move(S), std::move(LookupResult));127 }));128}129 130void JITLinkerBase::linkPhase3(std::unique_ptr<JITLinkerBase> Self,131 Expected<AsyncLookupResult> LR) {132 133 LLVM_DEBUG(dbgs() << "Starting link phase 3\n");134 135 // If the lookup failed, bail out.136 if (!LR)137 return abandonAllocAndBailOut(std::move(Self), LR.takeError());138 139 // Assign addresses to external addressables.140 applyLookupResult(*LR);141 142 LLVM_DEBUG({143 dbgs() << "Link graph before pre-fixup passes:\n";144 G->dump(dbgs());145 });146 147 if (auto Err = runPasses(Passes.PreFixupPasses))148 return abandonAllocAndBailOut(std::move(Self), std::move(Err));149 150 LLVM_DEBUG({151 dbgs() << "Link graph before copy-and-fixup:\n";152 G->dump(dbgs());153 });154 155 // Fix up block content.156 if (auto Err = fixUpBlocks(*G))157 return abandonAllocAndBailOut(std::move(Self), std::move(Err));158 159 LLVM_DEBUG({160 dbgs() << "Link graph after copy-and-fixup:\n";161 G->dump(dbgs());162 });163 164 if (auto Err = runPasses(Passes.PostFixupPasses))165 return abandonAllocAndBailOut(std::move(Self), std::move(Err));166 167 // Skip straight to phase 4 if the graph has no allocation.168 if (!Alloc) {169 linkPhase4(std::move(Self), JITLinkMemoryManager::FinalizedAlloc{});170 return;171 }172 173 Alloc->finalize([S = std::move(Self)](FinalizeResult FR) mutable {174 // FIXME: Once MSVC implements c++17 order of evaluation rules for calls175 // this can be simplified to176 // S->linkPhase2(std::move(S), std::move(AR));177 auto *TmpSelf = S.get();178 TmpSelf->linkPhase4(std::move(S), std::move(FR));179 });180}181 182void JITLinkerBase::linkPhase4(std::unique_ptr<JITLinkerBase> Self,183 FinalizeResult FR) {184 185 LLVM_DEBUG(dbgs() << "Starting link phase 4\n");186 187 if (!FR)188 return Ctx->notifyFailed(FR.takeError());189 190 Ctx->notifyFinalized(std::move(*FR));191 192 LLVM_DEBUG({ dbgs() << "Link complete\n"; });193}194 195Error JITLinkerBase::runPasses(LinkGraphPassList &Passes) {196 for (auto &P : Passes)197 if (auto Err = P(*G))198 return Err;199 return Error::success();200}201 202JITLinkContext::LookupMap JITLinkerBase::getExternalSymbolNames() const {203 // Identify unresolved external symbols.204 JITLinkContext::LookupMap UnresolvedExternals;205 for (auto *Sym : G->external_symbols()) {206 assert(!Sym->getAddress() &&207 "External has already been assigned an address");208 assert(Sym->hasName() && "Externals must be named");209 SymbolLookupFlags LookupFlags =210 Sym->isWeaklyReferenced() ? SymbolLookupFlags::WeaklyReferencedSymbol211 : SymbolLookupFlags::RequiredSymbol;212 UnresolvedExternals[Sym->getName()] = LookupFlags;213 }214 return UnresolvedExternals;215}216 217void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) {218 for (auto *Sym : G->external_symbols()) {219 assert(Sym->getOffset() == 0 &&220 "External symbol is not at the start of its addressable block");221 assert(!Sym->getAddress() && "Symbol already resolved");222 assert(!Sym->isDefined() && "Symbol being resolved is already defined");223 auto ResultI = Result.find(Sym->getName());224 if (ResultI != Result.end()) {225 Sym->getAddressable().setAddress(ResultI->second.getAddress());226 Sym->setLinkage(ResultI->second.getFlags().isWeak() ? Linkage::Weak227 : Linkage::Strong);228 Sym->setScope(ResultI->second.getFlags().isExported() ? Scope::Default229 : Scope::Hidden);230 } else231 assert(Sym->isWeaklyReferenced() &&232 "Failed to resolve non-weak reference");233 }234 235 LLVM_DEBUG({236 dbgs() << "Externals after applying lookup result:\n";237 for (auto *Sym : G->external_symbols()) {238 dbgs() << " " << Sym->getName() << ": "239 << formatv("{0:x16}", Sym->getAddress().getValue());240 switch (Sym->getLinkage()) {241 case Linkage::Strong:242 break;243 case Linkage::Weak:244 dbgs() << " (weak)";245 break;246 }247 switch (Sym->getScope()) {248 case Scope::Local:249 case Scope::SideEffectsOnly:250 llvm_unreachable("External symbol should not have local or "251 "side-effects-only linkage");252 case Scope::Hidden:253 break;254 case Scope::Default:255 dbgs() << " (exported)";256 break;257 }258 dbgs() << "\n";259 }260 });261}262 263void JITLinkerBase::abandonAllocAndBailOut(std::unique_ptr<JITLinkerBase> Self,264 Error Err) {265 assert(Err && "Should not be bailing out on success value");266 assert(Alloc && "can not call abandonAllocAndBailOut before allocation");267 Alloc->abandon([S = std::move(Self), E1 = std::move(Err)](Error E2) mutable {268 S->Ctx->notifyFailed(joinErrors(std::move(E1), std::move(E2)));269 });270}271 272void prune(LinkGraph &G) {273 std::vector<Symbol *> Worklist;274 DenseSet<Block *> VisitedBlocks;275 276 // Build the initial worklist from all symbols initially live.277 for (auto *Sym : G.defined_symbols())278 if (Sym->isLive())279 Worklist.push_back(Sym);280 281 // Propagate live flags to all symbols reachable from the initial live set.282 while (!Worklist.empty()) {283 auto *Sym = Worklist.back();284 Worklist.pop_back();285 286 auto &B = Sym->getBlock();287 288 // Skip addressables that we've visited before.289 if (VisitedBlocks.count(&B))290 continue;291 292 VisitedBlocks.insert(&B);293 294 for (auto &E : Sym->getBlock().edges()) {295 // If the edge target is a defined symbol that is being newly marked live296 // then add it to the worklist.297 if (E.getTarget().isDefined() && !E.getTarget().isLive())298 Worklist.push_back(&E.getTarget());299 300 // Mark the target live.301 E.getTarget().setLive(true);302 }303 }304 305 // Collect all defined symbols to remove, then remove them.306 {307 LLVM_DEBUG(dbgs() << "Dead-stripping defined symbols:\n");308 std::vector<Symbol *> SymbolsToRemove;309 for (auto *Sym : G.defined_symbols())310 if (!Sym->isLive())311 SymbolsToRemove.push_back(Sym);312 for (auto *Sym : SymbolsToRemove) {313 LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");314 G.removeDefinedSymbol(*Sym);315 }316 }317 318 // Delete any unused blocks.319 {320 LLVM_DEBUG(dbgs() << "Dead-stripping blocks:\n");321 std::vector<Block *> BlocksToRemove;322 for (auto *B : G.blocks())323 if (!VisitedBlocks.count(B))324 BlocksToRemove.push_back(B);325 for (auto *B : BlocksToRemove) {326 LLVM_DEBUG(dbgs() << " " << *B << "...\n");327 G.removeBlock(*B);328 }329 }330 331 // Collect all external symbols to remove, then remove them.332 {333 LLVM_DEBUG(dbgs() << "Removing unused external symbols:\n");334 std::vector<Symbol *> SymbolsToRemove;335 for (auto *Sym : G.external_symbols())336 if (!Sym->isLive())337 SymbolsToRemove.push_back(Sym);338 for (auto *Sym : SymbolsToRemove) {339 LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");340 G.removeExternalSymbol(*Sym);341 }342 }343}344 345} // end namespace jitlink346} // end namespace llvm347