LLVM 24.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 S->linkPhase2(std::move(S), std::move(AR));
58 });
59}
60
61void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self,
62 AllocResult AR) {
63
64 LLVM_DEBUG(dbgs() << "Starting link phase 2\n");
65
66 if (AR)
67 Alloc = std::move(*AR);
68 else
69 return Ctx->notifyFailed(AR.takeError());
70
72 dbgs() << "Link graph before post-allocation passes:\n";
73 G->dump(dbgs());
74 });
75
76 // Run post-allocation passes.
77 if (auto Err = runPasses(Passes.PostAllocationPasses))
78 return abandonAllocAndBailOut(std::move(Self), std::move(Err));
79
80 // Notify client that the defined symbols have been assigned addresses.
81 LLVM_DEBUG(dbgs() << "Resolving symbols defined in " << G->getName() << "\n");
82
83 if (auto Err = Ctx->notifyResolved(*G))
84 return abandonAllocAndBailOut(std::move(Self), std::move(Err));
85
86 auto ExternalSymbols = getExternalSymbolNames();
87
88 // If there are no external symbols then proceed immediately with phase 3.
89 if (ExternalSymbols.empty()) {
91 dbgs() << "No external symbols for " << G->getName()
92 << ". Proceeding immediately with link phase 3.\n";
93 });
94 Self->linkPhase3(std::move(Self), AsyncLookupResult());
95 return;
96 }
97
98 // Otherwise look up the externals.
100 dbgs() << "Issuing lookup for external symbols for " << G->getName()
101 << " (may trigger materialization/linking of other graphs)...\n";
102 });
103
104 Ctx->lookup(std::move(ExternalSymbols),
106 [S = std::move(Self)](
107 Expected<AsyncLookupResult> LookupResult) mutable {
108 S->linkPhase3(std::move(S), std::move(LookupResult));
109 }));
110}
111
112void JITLinkerBase::linkPhase3(std::unique_ptr<JITLinkerBase> Self,
114
115 LLVM_DEBUG(dbgs() << "Starting link phase 3\n");
116
117 // If the lookup failed, bail out.
118 if (!LR)
119 return abandonAllocAndBailOut(std::move(Self), LR.takeError());
120
121 // Assign addresses to external addressables.
122 applyLookupResult(*LR);
123
124 LLVM_DEBUG({
125 dbgs() << "Link graph before pre-fixup passes:\n";
126 G->dump(dbgs());
127 });
128
129 if (auto Err = runPasses(Passes.PreFixupPasses))
130 return abandonAllocAndBailOut(std::move(Self), std::move(Err));
131
132 LLVM_DEBUG({
133 dbgs() << "Link graph before copy-and-fixup:\n";
134 G->dump(dbgs());
135 });
136
137 // Fix up block content.
138 if (auto Err = fixUpBlocks(*G))
139 return abandonAllocAndBailOut(std::move(Self), std::move(Err));
140
141 LLVM_DEBUG({
142 dbgs() << "Link graph after copy-and-fixup:\n";
143 G->dump(dbgs());
144 });
145
146 if (auto Err = runPasses(Passes.PostFixupPasses))
147 return abandonAllocAndBailOut(std::move(Self), std::move(Err));
148
149 // Skip straight to phase 4 if the graph has no allocation.
150 if (!Alloc) {
152 return;
153 }
154
155 Alloc->finalize([S = std::move(Self)](FinalizeResult FR) mutable {
156 S->linkPhase4(std::move(S), std::move(FR));
157 });
158}
159
160void JITLinkerBase::linkPhase4(std::unique_ptr<JITLinkerBase> Self,
161 FinalizeResult FR) {
162
163 LLVM_DEBUG(dbgs() << "Starting link phase 4\n");
164
165 if (!FR)
166 return Ctx->notifyFailed(FR.takeError());
167
168 Ctx->notifyFinalized(std::move(*FR));
169
170 LLVM_DEBUG({ dbgs() << "Link complete\n"; });
171}
172
173Error JITLinkerBase::runPasses(LinkGraphPassList &Passes) {
174 for (auto &P : Passes)
175 if (auto Err = P(*G))
176 return Err;
177 return Error::success();
178}
179
180JITLinkContext::LookupMap JITLinkerBase::getExternalSymbolNames() const {
181 // Identify unresolved external symbols.
182 JITLinkContext::LookupMap UnresolvedExternals;
183 for (auto *Sym : G->external_symbols()) {
184 assert(!Sym->getAddress() &&
185 "External has already been assigned an address");
186 assert(Sym->hasName() && "Externals must be named");
187 SymbolLookupFlags LookupFlags =
188 Sym->isWeaklyReferenced() ? SymbolLookupFlags::WeaklyReferencedSymbol
190 UnresolvedExternals[Sym->getName()] = LookupFlags;
191 }
192 return UnresolvedExternals;
193}
194
195void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) {
196 for (auto *Sym : G->external_symbols()) {
197 assert(Sym->getOffset() == 0 &&
198 "External symbol is not at the start of its addressable block");
199 assert(!Sym->getAddress() && "Symbol already resolved");
200 assert(!Sym->isDefined() && "Symbol being resolved is already defined");
201 auto ResultI = Result.find(Sym->getName());
202 if (ResultI != Result.end()) {
203 Sym->getAddressable().setAddress(ResultI->second.getAddress());
204 Sym->setLinkage(ResultI->second.getFlags().isWeak() ? Linkage::Weak
206 Sym->setScope(ResultI->second.getFlags().isExported() ? Scope::Default
207 : Scope::Hidden);
208 } else
209 assert(Sym->isWeaklyReferenced() &&
210 "Failed to resolve non-weak reference");
211 }
212
213 LLVM_DEBUG({
214 dbgs() << "Externals after applying lookup result:\n";
215 for (auto *Sym : G->external_symbols()) {
216 dbgs() << " " << Sym->getName() << ": "
217 << formatv("{0:x16}", Sym->getAddress().getValue());
218 switch (Sym->getLinkage()) {
219 case Linkage::Strong:
220 break;
221 case Linkage::Weak:
222 dbgs() << " (weak)";
223 break;
224 }
225 switch (Sym->getScope()) {
226 case Scope::Local:
228 llvm_unreachable("External symbol should not have local or "
229 "side-effects-only linkage");
230 case Scope::Hidden:
231 break;
232 case Scope::Default:
233 dbgs() << " (exported)";
234 break;
235 }
236 dbgs() << "\n";
237 }
238 });
239}
240
241void JITLinkerBase::abandonAllocAndBailOut(std::unique_ptr<JITLinkerBase> Self,
242 Error Err) {
243 assert(Err && "Should not be bailing out on success value");
244 assert(Alloc && "can not call abandonAllocAndBailOut before allocation");
245 Alloc->abandon([S = std::move(Self), E1 = std::move(Err)](Error E2) mutable {
246 S->Ctx->notifyFailed(joinErrors(std::move(E1), std::move(E2)));
247 });
248}
249
251 std::vector<Symbol *> Worklist;
253
254 // Build the initial worklist from all symbols initially live.
255 for (auto *Sym : G.defined_symbols())
256 if (Sym->isLive())
257 Worklist.push_back(Sym);
258
259 // Propagate live flags to all symbols reachable from the initial live set.
260 while (!Worklist.empty()) {
261 auto *Sym = Worklist.back();
262 Worklist.pop_back();
263
264 auto &B = Sym->getBlock();
265
266 // Skip addressables that we've visited before.
267 if (VisitedBlocks.count(&B))
268 continue;
269
270 VisitedBlocks.insert(&B);
271
272 for (auto &E : Sym->getBlock().edges()) {
273 // If the edge target is a defined symbol that is being newly marked live
274 // then add it to the worklist.
275 if (E.getTarget().isDefined() && !E.getTarget().isLive())
276 Worklist.push_back(&E.getTarget());
277
278 // Mark the target live.
279 E.getTarget().setLive(true);
280 }
281 }
282
283 // Collect all defined symbols to remove, then remove them.
284 {
285 LLVM_DEBUG(dbgs() << "Dead-stripping defined symbols:\n");
286 std::vector<Symbol *> SymbolsToRemove;
287 for (auto *Sym : G.defined_symbols())
288 if (!Sym->isLive())
289 SymbolsToRemove.push_back(Sym);
290 for (auto *Sym : SymbolsToRemove) {
291 LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");
292 G.removeDefinedSymbol(*Sym);
293 }
294 }
295
296 // Delete any unused blocks.
297 {
298 LLVM_DEBUG(dbgs() << "Dead-stripping blocks:\n");
299 std::vector<Block *> BlocksToRemove;
300 for (auto *B : G.blocks())
301 if (!VisitedBlocks.count(B))
302 BlocksToRemove.push_back(B);
303 for (auto *B : BlocksToRemove) {
304 LLVM_DEBUG(dbgs() << " " << *B << "...\n");
305 G.removeBlock(*B);
306 }
307 }
308
309 // Collect all external symbols to remove, then remove them.
310 {
311 LLVM_DEBUG(dbgs() << "Removing unused external symbols:\n");
312 std::vector<Symbol *> SymbolsToRemove;
313 for (auto *Sym : G.external_symbols())
314 if (!Sym->isLive())
315 SymbolsToRemove.push_back(Sym);
316 for (auto *Sym : SymbolsToRemove) {
317 LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");
318 G.removeExternalSymbol(*Sym);
319 }
320 }
321}
322
323} // end namespace jitlink
324} // 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:55
#define P(N)
Function const char * Passes
#define LLVM_DEBUG(...)
Definition Debug.h:119
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
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:1739
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:209