LLVM 22.0.0git
JITLinkGeneric.cpp
Go to the documentation of this file.
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-exception
6//
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
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
31 dbgs() << "Link graph pre-pruning:\n";
32 G->dump(dbgs());
33 });
34
35 prune(*G);
36
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 calls
58 // this can be simplified to
59 // 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 else
73 return Ctx->notifyFailed(AR.takeError());
74
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()) {
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 calls
99 // 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 a
112 // 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 this
115 // 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),
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,
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) {
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 calls
175 // this can be simplified to
176 // 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::WeaklyReferencedSymbol
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::Weak
228 Sym->setScope(ResultI->second.getFlags().isExported() ? Scope::Default
229 : Scope::Hidden);
230 } else
231 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:
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
273 std::vector<Symbol *> Worklist;
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 live
296 // 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 jitlink
346} // end namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
SmallPtrSet< const BasicBlock *, 8 > VisitedBlocks
#define G(x, y, z)
Definition MD5.cpp:56
#define P(N)
Function const char * Passes
#define LLVM_DEBUG(...)
Definition Debug.h:114
Implements a dense probed hash-table based set.
Definition DenseSet.h:279
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1725
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition Error.h:442
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207