LLVM  16.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 
17 
18 #define DEBUG_TYPE "jitlink"
19 
20 namespace llvm {
21 namespace jitlink {
22 
24 
25 void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) {
26 
27  LLVM_DEBUG({
28  dbgs() << "Starting link phase 1 for graph " << G->getName() << "\n";
29  });
30 
31  // Prune and optimize the graph.
32  if (auto Err = runPasses(Passes.PrePrunePasses))
33  return Ctx->notifyFailed(std::move(Err));
34 
35  LLVM_DEBUG({
36  dbgs() << "Link graph \"" << G->getName() << "\" pre-pruning:\n";
37  G->dump(dbgs());
38  });
39 
40  prune(*G);
41 
42  LLVM_DEBUG({
43  dbgs() << "Link graph \"" << G->getName() << "\" post-pruning:\n";
44  G->dump(dbgs());
45  });
46 
47  // Run post-pruning passes.
48  if (auto Err = runPasses(Passes.PostPrunePasses))
49  return Ctx->notifyFailed(std::move(Err));
50 
51  Ctx->getMemoryManager().allocate(
52  Ctx->getJITLinkDylib(), *G,
53  [S = std::move(Self)](AllocResult AR) mutable {
54  // FIXME: Once MSVC implements c++17 order of evaluation rules for calls
55  // this can be simplified to
56  // S->linkPhase2(std::move(S), std::move(AR));
57  auto *TmpSelf = S.get();
58  TmpSelf->linkPhase2(std::move(S), std::move(AR));
59  });
60 }
61 
62 void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self,
63  AllocResult AR) {
64 
65  if (AR)
66  Alloc = std::move(*AR);
67  else
68  return Ctx->notifyFailed(AR.takeError());
69 
70  LLVM_DEBUG({
71  dbgs() << "Link graph \"" << G->getName()
72  << "\" 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 Ctx->notifyFailed(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 Ctx->notifyFailed(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()) {
90  LLVM_DEBUG({
91  dbgs() << "No external symbols for " << G->getName()
92  << ". Proceeding immediately with link phase 3.\n";
93  });
94  // FIXME: Once MSVC implements c++17 order of evaluation rules for calls
95  // this can be simplified. See below.
96  auto &TmpSelf = *Self;
97  TmpSelf.linkPhase3(std::move(Self), AsyncLookupResult());
98  return;
99  }
100 
101  // Otherwise look up the externals.
102  LLVM_DEBUG({
103  dbgs() << "Issuing lookup for external symbols for " << G->getName()
104  << " (may trigger materialization/linking of other graphs)...\n";
105  });
106 
107  // We're about to hand off ownership of ourself to the continuation. Grab a
108  // pointer to the context so that we can call it to initiate the lookup.
109  //
110  // FIXME: Once MSVC implements c++17 order of evaluation rules for calls this
111  // can be simplified to:
112  //
113  // Ctx->lookup(std::move(UnresolvedExternals),
114  // [Self=std::move(Self)](Expected<AsyncLookupResult> Result) {
115  // Self->linkPhase3(std::move(Self), std::move(Result));
116  // });
117  Ctx->lookup(std::move(ExternalSymbols),
119  [S = std::move(Self)](
121  auto &TmpSelf = *S;
122  TmpSelf.linkPhase3(std::move(S), std::move(LookupResult));
123  }));
124 }
125 
126 void JITLinkerBase::linkPhase3(std::unique_ptr<JITLinkerBase> Self,
128 
129  LLVM_DEBUG({
130  dbgs() << "Starting link phase 3 for graph " << G->getName() << "\n";
131  });
132 
133  // If the lookup failed, bail out.
134  if (!LR)
135  return abandonAllocAndBailOut(std::move(Self), LR.takeError());
136 
137  // Assign addresses to external addressables.
138  applyLookupResult(*LR);
139 
140  LLVM_DEBUG({
141  dbgs() << "Link graph \"" << G->getName()
142  << "\" before pre-fixup passes:\n";
143  G->dump(dbgs());
144  });
145 
146  if (auto Err = runPasses(Passes.PreFixupPasses))
147  return abandonAllocAndBailOut(std::move(Self), std::move(Err));
148 
149  LLVM_DEBUG({
150  dbgs() << "Link graph \"" << G->getName() << "\" before copy-and-fixup:\n";
151  G->dump(dbgs());
152  });
153 
154  // Fix up block content.
155  if (auto Err = fixUpBlocks(*G))
156  return abandonAllocAndBailOut(std::move(Self), std::move(Err));
157 
158  LLVM_DEBUG({
159  dbgs() << "Link graph \"" << G->getName() << "\" after copy-and-fixup:\n";
160  G->dump(dbgs());
161  });
162 
163  if (auto Err = runPasses(Passes.PostFixupPasses))
164  return abandonAllocAndBailOut(std::move(Self), std::move(Err));
165 
166  Alloc->finalize([S = std::move(Self)](FinalizeResult FR) mutable {
167  // FIXME: Once MSVC implements c++17 order of evaluation rules for calls
168  // this can be simplified to
169  // S->linkPhase2(std::move(S), std::move(AR));
170  auto *TmpSelf = S.get();
171  TmpSelf->linkPhase4(std::move(S), std::move(FR));
172  });
173 }
174 
175 void JITLinkerBase::linkPhase4(std::unique_ptr<JITLinkerBase> Self,
176  FinalizeResult FR) {
177 
178  LLVM_DEBUG({
179  dbgs() << "Starting link phase 4 for graph " << G->getName() << "\n";
180  });
181 
182  if (!FR)
183  return Ctx->notifyFailed(FR.takeError());
184 
185  Ctx->notifyFinalized(std::move(*FR));
186 
187  LLVM_DEBUG({ dbgs() << "Link of graph " << G->getName() << " complete\n"; });
188 }
189 
190 Error JITLinkerBase::runPasses(LinkGraphPassList &Passes) {
191  for (auto &P : Passes)
192  if (auto Err = P(*G))
193  return Err;
194  return Error::success();
195 }
196 
197 JITLinkContext::LookupMap JITLinkerBase::getExternalSymbolNames() const {
198  // Identify unresolved external symbols.
199  JITLinkContext::LookupMap UnresolvedExternals;
200  for (auto *Sym : G->external_symbols()) {
201  assert(!Sym->getAddress() &&
202  "External has already been assigned an address");
203  assert(Sym->getName() != StringRef() && Sym->getName() != "" &&
204  "Externals must be named");
205  SymbolLookupFlags LookupFlags =
206  Sym->isWeaklyReferenced() ? SymbolLookupFlags::WeaklyReferencedSymbol
208  UnresolvedExternals[Sym->getName()] = LookupFlags;
209  }
210  return UnresolvedExternals;
211 }
212 
213 void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) {
214  for (auto *Sym : G->external_symbols()) {
215  assert(Sym->getOffset() == 0 &&
216  "External symbol is not at the start of its addressable block");
217  assert(!Sym->getAddress() && "Symbol already resolved");
218  assert(!Sym->isDefined() && "Symbol being resolved is already defined");
219  auto ResultI = Result.find(Sym->getName());
220  if (ResultI != Result.end()) {
221  Sym->getAddressable().setAddress(
222  orc::ExecutorAddr(ResultI->second.getAddress()));
223  Sym->setLinkage(ResultI->second.getFlags().isWeak() ? Linkage::Weak
224  : Linkage::Strong);
225  Sym->setScope(ResultI->second.getFlags().isExported() ? Scope::Default
226  : Scope::Hidden);
227  } else
228  assert(Sym->isWeaklyReferenced() &&
229  "Failed to resolve non-weak reference");
230  }
231 
232  LLVM_DEBUG({
233  dbgs() << "Externals after applying lookup result:\n";
234  for (auto *Sym : G->external_symbols()) {
235  dbgs() << " " << Sym->getName() << ": "
236  << formatv("{0:x16}", Sym->getAddress().getValue());
237  switch (Sym->getLinkage()) {
238  case Linkage::Strong:
239  break;
240  case Linkage::Weak:
241  dbgs() << " (weak)";
242  break;
243  }
244  switch (Sym->getScope()) {
245  case Scope::Local:
246  llvm_unreachable("External symbol should not have local linkage");
247  case Scope::Hidden:
248  break;
249  case Scope::Default:
250  dbgs() << " (exported)";
251  break;
252  }
253  dbgs() << "\n";
254  }
255  });
256 }
257 
258 void JITLinkerBase::abandonAllocAndBailOut(std::unique_ptr<JITLinkerBase> Self,
259  Error Err) {
260  assert(Err && "Should not be bailing out on success value");
261  assert(Alloc && "can not call abandonAllocAndBailOut before allocation");
262  Alloc->abandon([S = std::move(Self), E1 = std::move(Err)](Error E2) mutable {
263  S->Ctx->notifyFailed(joinErrors(std::move(E1), std::move(E2)));
264  });
265 }
266 
267 void prune(LinkGraph &G) {
268  std::vector<Symbol *> Worklist;
269  DenseSet<Block *> VisitedBlocks;
270 
271  // Build the initial worklist from all symbols initially live.
272  for (auto *Sym : G.defined_symbols())
273  if (Sym->isLive())
274  Worklist.push_back(Sym);
275 
276  // Propagate live flags to all symbols reachable from the initial live set.
277  while (!Worklist.empty()) {
278  auto *Sym = Worklist.back();
279  Worklist.pop_back();
280 
281  auto &B = Sym->getBlock();
282 
283  // Skip addressables that we've visited before.
284  if (VisitedBlocks.count(&B))
285  continue;
286 
287  VisitedBlocks.insert(&B);
288 
289  for (auto &E : Sym->getBlock().edges()) {
290  // If the edge target is a defined symbol that is being newly marked live
291  // then add it to the worklist.
292  if (E.getTarget().isDefined() && !E.getTarget().isLive())
293  Worklist.push_back(&E.getTarget());
294 
295  // Mark the target live.
296  E.getTarget().setLive(true);
297  }
298  }
299 
300  // Collect all defined symbols to remove, then remove them.
301  {
302  LLVM_DEBUG(dbgs() << "Dead-stripping defined symbols:\n");
303  std::vector<Symbol *> SymbolsToRemove;
304  for (auto *Sym : G.defined_symbols())
305  if (!Sym->isLive())
306  SymbolsToRemove.push_back(Sym);
307  for (auto *Sym : SymbolsToRemove) {
308  LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");
309  G.removeDefinedSymbol(*Sym);
310  }
311  }
312 
313  // Delete any unused blocks.
314  {
315  LLVM_DEBUG(dbgs() << "Dead-stripping blocks:\n");
316  std::vector<Block *> BlocksToRemove;
317  for (auto *B : G.blocks())
318  if (!VisitedBlocks.count(B))
319  BlocksToRemove.push_back(B);
320  for (auto *B : BlocksToRemove) {
321  LLVM_DEBUG(dbgs() << " " << *B << "...\n");
322  G.removeBlock(*B);
323  }
324  }
325 
326  // Collect all external symbols to remove, then remove them.
327  {
328  LLVM_DEBUG(dbgs() << "Removing unused external symbols:\n");
329  std::vector<Symbol *> SymbolsToRemove;
330  for (auto *Sym : G.external_symbols())
331  if (!Sym->isLive())
332  SymbolsToRemove.push_back(Sym);
333  for (auto *Sym : SymbolsToRemove) {
334  LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");
335  G.removeExternalSymbol(*Sym);
336  }
337  }
338 }
339 
340 } // end namespace jitlink
341 } // end namespace llvm
MemoryBuffer.h
BinaryStreamReader.h
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
P
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
Definition: README-SSE.txt:411
llvm::Error::success
static ErrorSuccess success()
Create a success value.
Definition: Error.h:329
Passes
const char * Passes
Definition: PassBuilderBindings.cpp:46
llvm::Expected
Tagged union holding either a T or a Error.
Definition: APFloat.h:41
llvm::detail::DenseSetImpl::insert
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:206
llvm::detail::DenseSetImpl::count
size_type count(const_arg_type_t< ValueT > V) const
Return 1 if the specified key is in the set, 0 otherwise.
Definition: DenseSet.h:97
LLVM_DEBUG
#define LLVM_DEBUG(X)
Definition: Debug.h:101
llvm::dbgs
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
llvm::formatv
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
Definition: FormatVariadic.h:251
E
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
llvm::ms_demangle::QualifierMangleMode::Result
@ Result
B
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
llvm::orc::tpctypes::LookupResult
std::vector< ExecutorAddr > LookupResult
Definition: TargetProcessControlTypes.h:90
G
const DataFlowGraph & G
Definition: RDFGraph.cpp:200
llvm::DenseSet< Block * >
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
llvm::joinErrors
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:426
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
llvm_unreachable
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Definition: ErrorHandling.h:143
S
add sub stmia L5 ldr r0 bl L_printf $stub Instead of a and a wouldn t it be better to do three moves *Return an aggregate type is even return S
Definition: README.txt:210
llvm::Error
Lightweight error class with error context and mandatory checking.
Definition: Error.h:155
llvm::Expected::takeError
Error takeError()
Take ownership of the stored error.
Definition: Error.h:596
JITLinkGeneric.h