Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | //===- IndirectionUtils.h - Utilities for adding indirections ---*- C++ -*-===// |
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 | // Contains utilities for adding indirections and breaking up modules. |
||
10 | // |
||
11 | //===----------------------------------------------------------------------===// |
||
12 | |||
13 | #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H |
||
14 | #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H |
||
15 | |||
16 | #include "llvm/ADT/StringMap.h" |
||
17 | #include "llvm/ADT/StringRef.h" |
||
18 | #include "llvm/ExecutionEngine/JITSymbol.h" |
||
19 | #include "llvm/ExecutionEngine/Orc/Core.h" |
||
20 | #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" |
||
21 | #include "llvm/Support/Error.h" |
||
22 | #include "llvm/Support/Memory.h" |
||
23 | #include "llvm/Support/Process.h" |
||
24 | #include "llvm/Transforms/Utils/ValueMapper.h" |
||
25 | #include <algorithm> |
||
26 | #include <cassert> |
||
27 | #include <cstdint> |
||
28 | #include <functional> |
||
29 | #include <future> |
||
30 | #include <map> |
||
31 | #include <memory> |
||
32 | #include <system_error> |
||
33 | #include <utility> |
||
34 | #include <vector> |
||
35 | |||
36 | namespace llvm { |
||
37 | |||
38 | class Constant; |
||
39 | class Function; |
||
40 | class FunctionType; |
||
41 | class GlobalAlias; |
||
42 | class GlobalVariable; |
||
43 | class Module; |
||
44 | class PointerType; |
||
45 | class Triple; |
||
46 | class Twine; |
||
47 | class Value; |
||
48 | class MCDisassembler; |
||
49 | class MCInstrAnalysis; |
||
50 | |||
51 | namespace jitlink { |
||
52 | class LinkGraph; |
||
53 | class Symbol; |
||
54 | } // namespace jitlink |
||
55 | |||
56 | namespace orc { |
||
57 | |||
58 | /// Base class for pools of compiler re-entry trampolines. |
||
59 | /// These trampolines are callable addresses that save all register state |
||
60 | /// before calling a supplied function to return the trampoline landing |
||
61 | /// address, then restore all state before jumping to that address. They |
||
62 | /// are used by various ORC APIs to support lazy compilation |
||
63 | class TrampolinePool { |
||
64 | public: |
||
65 | using NotifyLandingResolvedFunction = |
||
66 | unique_function<void(JITTargetAddress) const>; |
||
67 | |||
68 | using ResolveLandingFunction = unique_function<void( |
||
69 | JITTargetAddress TrampolineAddr, |
||
70 | NotifyLandingResolvedFunction OnLandingResolved) const>; |
||
71 | |||
72 | virtual ~TrampolinePool(); |
||
73 | |||
74 | /// Get an available trampoline address. |
||
75 | /// Returns an error if no trampoline can be created. |
||
76 | Expected<JITTargetAddress> getTrampoline() { |
||
77 | std::lock_guard<std::mutex> Lock(TPMutex); |
||
78 | if (AvailableTrampolines.empty()) { |
||
79 | if (auto Err = grow()) |
||
80 | return std::move(Err); |
||
81 | } |
||
82 | assert(!AvailableTrampolines.empty() && "Failed to grow trampoline pool"); |
||
83 | auto TrampolineAddr = AvailableTrampolines.back(); |
||
84 | AvailableTrampolines.pop_back(); |
||
85 | return TrampolineAddr; |
||
86 | } |
||
87 | |||
88 | /// Returns the given trampoline to the pool for re-use. |
||
89 | void releaseTrampoline(JITTargetAddress TrampolineAddr) { |
||
90 | std::lock_guard<std::mutex> Lock(TPMutex); |
||
91 | AvailableTrampolines.push_back(TrampolineAddr); |
||
92 | } |
||
93 | |||
94 | protected: |
||
95 | virtual Error grow() = 0; |
||
96 | |||
97 | std::mutex TPMutex; |
||
98 | std::vector<JITTargetAddress> AvailableTrampolines; |
||
99 | }; |
||
100 | |||
101 | /// A trampoline pool for trampolines within the current process. |
||
102 | template <typename ORCABI> class LocalTrampolinePool : public TrampolinePool { |
||
103 | public: |
||
104 | /// Creates a LocalTrampolinePool with the given RunCallback function. |
||
105 | /// Returns an error if this function is unable to correctly allocate, write |
||
106 | /// and protect the resolver code block. |
||
107 | static Expected<std::unique_ptr<LocalTrampolinePool>> |
||
108 | Create(ResolveLandingFunction ResolveLanding) { |
||
109 | Error Err = Error::success(); |
||
110 | |||
111 | auto LTP = std::unique_ptr<LocalTrampolinePool>( |
||
112 | new LocalTrampolinePool(std::move(ResolveLanding), Err)); |
||
113 | |||
114 | if (Err) |
||
115 | return std::move(Err); |
||
116 | return std::move(LTP); |
||
117 | } |
||
118 | |||
119 | private: |
||
120 | static JITTargetAddress reenter(void *TrampolinePoolPtr, void *TrampolineId) { |
||
121 | LocalTrampolinePool<ORCABI> *TrampolinePool = |
||
122 | static_cast<LocalTrampolinePool *>(TrampolinePoolPtr); |
||
123 | |||
124 | std::promise<JITTargetAddress> LandingAddressP; |
||
125 | auto LandingAddressF = LandingAddressP.get_future(); |
||
126 | |||
127 | TrampolinePool->ResolveLanding(pointerToJITTargetAddress(TrampolineId), |
||
128 | [&](JITTargetAddress LandingAddress) { |
||
129 | LandingAddressP.set_value(LandingAddress); |
||
130 | }); |
||
131 | return LandingAddressF.get(); |
||
132 | } |
||
133 | |||
134 | LocalTrampolinePool(ResolveLandingFunction ResolveLanding, Error &Err) |
||
135 | : ResolveLanding(std::move(ResolveLanding)) { |
||
136 | |||
137 | ErrorAsOutParameter _(&Err); |
||
138 | |||
139 | /// Try to set up the resolver block. |
||
140 | std::error_code EC; |
||
141 | ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( |
||
142 | ORCABI::ResolverCodeSize, nullptr, |
||
143 | sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); |
||
144 | if (EC) { |
||
145 | Err = errorCodeToError(EC); |
||
146 | return; |
||
147 | } |
||
148 | |||
149 | ORCABI::writeResolverCode(static_cast<char *>(ResolverBlock.base()), |
||
150 | pointerToJITTargetAddress(ResolverBlock.base()), |
||
151 | pointerToJITTargetAddress(&reenter), |
||
152 | pointerToJITTargetAddress(this)); |
||
153 | |||
154 | EC = sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(), |
||
155 | sys::Memory::MF_READ | |
||
156 | sys::Memory::MF_EXEC); |
||
157 | if (EC) { |
||
158 | Err = errorCodeToError(EC); |
||
159 | return; |
||
160 | } |
||
161 | } |
||
162 | |||
163 | Error grow() override { |
||
164 | assert(AvailableTrampolines.empty() && "Growing prematurely?"); |
||
165 | |||
166 | std::error_code EC; |
||
167 | auto TrampolineBlock = |
||
168 | sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( |
||
169 | sys::Process::getPageSizeEstimate(), nullptr, |
||
170 | sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); |
||
171 | if (EC) |
||
172 | return errorCodeToError(EC); |
||
173 | |||
174 | unsigned NumTrampolines = |
||
175 | (sys::Process::getPageSizeEstimate() - ORCABI::PointerSize) / |
||
176 | ORCABI::TrampolineSize; |
||
177 | |||
178 | char *TrampolineMem = static_cast<char *>(TrampolineBlock.base()); |
||
179 | ORCABI::writeTrampolines( |
||
180 | TrampolineMem, pointerToJITTargetAddress(TrampolineMem), |
||
181 | pointerToJITTargetAddress(ResolverBlock.base()), NumTrampolines); |
||
182 | |||
183 | for (unsigned I = 0; I < NumTrampolines; ++I) |
||
184 | AvailableTrampolines.push_back(pointerToJITTargetAddress( |
||
185 | TrampolineMem + (I * ORCABI::TrampolineSize))); |
||
186 | |||
187 | if (auto EC = sys::Memory::protectMappedMemory( |
||
188 | TrampolineBlock.getMemoryBlock(), |
||
189 | sys::Memory::MF_READ | sys::Memory::MF_EXEC)) |
||
190 | return errorCodeToError(EC); |
||
191 | |||
192 | TrampolineBlocks.push_back(std::move(TrampolineBlock)); |
||
193 | return Error::success(); |
||
194 | } |
||
195 | |||
196 | ResolveLandingFunction ResolveLanding; |
||
197 | |||
198 | sys::OwningMemoryBlock ResolverBlock; |
||
199 | std::vector<sys::OwningMemoryBlock> TrampolineBlocks; |
||
200 | }; |
||
201 | |||
202 | /// Target-independent base class for compile callback management. |
||
203 | class JITCompileCallbackManager { |
||
204 | public: |
||
205 | using CompileFunction = std::function<JITTargetAddress()>; |
||
206 | |||
207 | virtual ~JITCompileCallbackManager() = default; |
||
208 | |||
209 | /// Reserve a compile callback. |
||
210 | Expected<JITTargetAddress> getCompileCallback(CompileFunction Compile); |
||
211 | |||
212 | /// Execute the callback for the given trampoline id. Called by the JIT |
||
213 | /// to compile functions on demand. |
||
214 | JITTargetAddress executeCompileCallback(JITTargetAddress TrampolineAddr); |
||
215 | |||
216 | protected: |
||
217 | /// Construct a JITCompileCallbackManager. |
||
218 | JITCompileCallbackManager(std::unique_ptr<TrampolinePool> TP, |
||
219 | ExecutionSession &ES, |
||
220 | JITTargetAddress ErrorHandlerAddress) |
||
221 | : TP(std::move(TP)), ES(ES), |
||
222 | CallbacksJD(ES.createBareJITDylib("<Callbacks>")), |
||
223 | ErrorHandlerAddress(ErrorHandlerAddress) {} |
||
224 | |||
225 | void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) { |
||
226 | this->TP = std::move(TP); |
||
227 | } |
||
228 | |||
229 | private: |
||
230 | std::mutex CCMgrMutex; |
||
231 | std::unique_ptr<TrampolinePool> TP; |
||
232 | ExecutionSession &ES; |
||
233 | JITDylib &CallbacksJD; |
||
234 | JITTargetAddress ErrorHandlerAddress; |
||
235 | std::map<JITTargetAddress, SymbolStringPtr> AddrToSymbol; |
||
236 | size_t NextCallbackId = 0; |
||
237 | }; |
||
238 | |||
239 | /// Manage compile callbacks for in-process JITs. |
||
240 | template <typename ORCABI> |
||
241 | class LocalJITCompileCallbackManager : public JITCompileCallbackManager { |
||
242 | public: |
||
243 | /// Create a new LocalJITCompileCallbackManager. |
||
244 | static Expected<std::unique_ptr<LocalJITCompileCallbackManager>> |
||
245 | Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddress) { |
||
246 | Error Err = Error::success(); |
||
247 | auto CCMgr = std::unique_ptr<LocalJITCompileCallbackManager>( |
||
248 | new LocalJITCompileCallbackManager(ES, ErrorHandlerAddress, Err)); |
||
249 | if (Err) |
||
250 | return std::move(Err); |
||
251 | return std::move(CCMgr); |
||
252 | } |
||
253 | |||
254 | private: |
||
255 | /// Construct a InProcessJITCompileCallbackManager. |
||
256 | /// @param ErrorHandlerAddress The address of an error handler in the target |
||
257 | /// process to be used if a compile callback fails. |
||
258 | LocalJITCompileCallbackManager(ExecutionSession &ES, |
||
259 | JITTargetAddress ErrorHandlerAddress, |
||
260 | Error &Err) |
||
261 | : JITCompileCallbackManager(nullptr, ES, ErrorHandlerAddress) { |
||
262 | using NotifyLandingResolvedFunction = |
||
263 | TrampolinePool::NotifyLandingResolvedFunction; |
||
264 | |||
265 | ErrorAsOutParameter _(&Err); |
||
266 | auto TP = LocalTrampolinePool<ORCABI>::Create( |
||
267 | [this](JITTargetAddress TrampolineAddr, |
||
268 | NotifyLandingResolvedFunction NotifyLandingResolved) { |
||
269 | NotifyLandingResolved(executeCompileCallback(TrampolineAddr)); |
||
270 | }); |
||
271 | |||
272 | if (!TP) { |
||
273 | Err = TP.takeError(); |
||
274 | return; |
||
275 | } |
||
276 | |||
277 | setTrampolinePool(std::move(*TP)); |
||
278 | } |
||
279 | }; |
||
280 | |||
281 | /// Base class for managing collections of named indirect stubs. |
||
282 | class IndirectStubsManager { |
||
283 | public: |
||
284 | /// Map type for initializing the manager. See init. |
||
285 | using StubInitsMap = StringMap<std::pair<JITTargetAddress, JITSymbolFlags>>; |
||
286 | |||
287 | virtual ~IndirectStubsManager() = default; |
||
288 | |||
289 | /// Create a single stub with the given name, target address and flags. |
||
290 | virtual Error createStub(StringRef StubName, JITTargetAddress StubAddr, |
||
291 | JITSymbolFlags StubFlags) = 0; |
||
292 | |||
293 | /// Create StubInits.size() stubs with the given names, target |
||
294 | /// addresses, and flags. |
||
295 | virtual Error createStubs(const StubInitsMap &StubInits) = 0; |
||
296 | |||
297 | /// Find the stub with the given name. If ExportedStubsOnly is true, |
||
298 | /// this will only return a result if the stub's flags indicate that it |
||
299 | /// is exported. |
||
300 | virtual JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) = 0; |
||
301 | |||
302 | /// Find the implementation-pointer for the stub. |
||
303 | virtual JITEvaluatedSymbol findPointer(StringRef Name) = 0; |
||
304 | |||
305 | /// Change the value of the implementation pointer for the stub. |
||
306 | virtual Error updatePointer(StringRef Name, JITTargetAddress NewAddr) = 0; |
||
307 | |||
308 | private: |
||
309 | virtual void anchor(); |
||
310 | }; |
||
311 | |||
312 | template <typename ORCABI> class LocalIndirectStubsInfo { |
||
313 | public: |
||
314 | LocalIndirectStubsInfo(unsigned NumStubs, sys::OwningMemoryBlock StubsMem) |
||
315 | : NumStubs(NumStubs), StubsMem(std::move(StubsMem)) {} |
||
316 | |||
317 | static Expected<LocalIndirectStubsInfo> create(unsigned MinStubs, |
||
318 | unsigned PageSize) { |
||
319 | auto ISAS = getIndirectStubsBlockSizes<ORCABI>(MinStubs, PageSize); |
||
320 | |||
321 | assert((ISAS.StubBytes % PageSize == 0) && |
||
322 | "StubBytes is not a page size multiple"); |
||
323 | uint64_t PointerAlloc = alignTo(ISAS.PointerBytes, PageSize); |
||
324 | |||
325 | // Allocate memory for stubs and pointers in one call. |
||
326 | std::error_code EC; |
||
327 | auto StubsAndPtrsMem = |
||
328 | sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( |
||
329 | ISAS.StubBytes + PointerAlloc, nullptr, |
||
330 | sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); |
||
331 | if (EC) |
||
332 | return errorCodeToError(EC); |
||
333 | |||
334 | sys::MemoryBlock StubsBlock(StubsAndPtrsMem.base(), ISAS.StubBytes); |
||
335 | auto StubsBlockMem = static_cast<char *>(StubsAndPtrsMem.base()); |
||
336 | auto PtrBlockAddress = |
||
337 | pointerToJITTargetAddress(StubsBlockMem) + ISAS.StubBytes; |
||
338 | |||
339 | ORCABI::writeIndirectStubsBlock(StubsBlockMem, |
||
340 | pointerToJITTargetAddress(StubsBlockMem), |
||
341 | PtrBlockAddress, ISAS.NumStubs); |
||
342 | |||
343 | if (auto EC = sys::Memory::protectMappedMemory( |
||
344 | StubsBlock, sys::Memory::MF_READ | sys::Memory::MF_EXEC)) |
||
345 | return errorCodeToError(EC); |
||
346 | |||
347 | return LocalIndirectStubsInfo(ISAS.NumStubs, std::move(StubsAndPtrsMem)); |
||
348 | } |
||
349 | |||
350 | unsigned getNumStubs() const { return NumStubs; } |
||
351 | |||
352 | void *getStub(unsigned Idx) const { |
||
353 | return static_cast<char *>(StubsMem.base()) + Idx * ORCABI::StubSize; |
||
354 | } |
||
355 | |||
356 | void **getPtr(unsigned Idx) const { |
||
357 | char *PtrsBase = |
||
358 | static_cast<char *>(StubsMem.base()) + NumStubs * ORCABI::StubSize; |
||
359 | return reinterpret_cast<void **>(PtrsBase) + Idx; |
||
360 | } |
||
361 | |||
362 | private: |
||
363 | unsigned NumStubs = 0; |
||
364 | sys::OwningMemoryBlock StubsMem; |
||
365 | }; |
||
366 | |||
367 | /// IndirectStubsManager implementation for the host architecture, e.g. |
||
368 | /// OrcX86_64. (See OrcArchitectureSupport.h). |
||
369 | template <typename TargetT> |
||
370 | class LocalIndirectStubsManager : public IndirectStubsManager { |
||
371 | public: |
||
372 | Error createStub(StringRef StubName, JITTargetAddress StubAddr, |
||
373 | JITSymbolFlags StubFlags) override { |
||
374 | std::lock_guard<std::mutex> Lock(StubsMutex); |
||
375 | if (auto Err = reserveStubs(1)) |
||
376 | return Err; |
||
377 | |||
378 | createStubInternal(StubName, StubAddr, StubFlags); |
||
379 | |||
380 | return Error::success(); |
||
381 | } |
||
382 | |||
383 | Error createStubs(const StubInitsMap &StubInits) override { |
||
384 | std::lock_guard<std::mutex> Lock(StubsMutex); |
||
385 | if (auto Err = reserveStubs(StubInits.size())) |
||
386 | return Err; |
||
387 | |||
388 | for (const auto &Entry : StubInits) |
||
389 | createStubInternal(Entry.first(), Entry.second.first, |
||
390 | Entry.second.second); |
||
391 | |||
392 | return Error::success(); |
||
393 | } |
||
394 | |||
395 | JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { |
||
396 | std::lock_guard<std::mutex> Lock(StubsMutex); |
||
397 | auto I = StubIndexes.find(Name); |
||
398 | if (I == StubIndexes.end()) |
||
399 | return nullptr; |
||
400 | auto Key = I->second.first; |
||
401 | void *StubAddr = IndirectStubsInfos[Key.first].getStub(Key.second); |
||
402 | assert(StubAddr && "Missing stub address"); |
||
403 | auto StubTargetAddr = |
||
404 | static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(StubAddr)); |
||
405 | auto StubSymbol = JITEvaluatedSymbol(StubTargetAddr, I->second.second); |
||
406 | if (ExportedStubsOnly && !StubSymbol.getFlags().isExported()) |
||
407 | return nullptr; |
||
408 | return StubSymbol; |
||
409 | } |
||
410 | |||
411 | JITEvaluatedSymbol findPointer(StringRef Name) override { |
||
412 | std::lock_guard<std::mutex> Lock(StubsMutex); |
||
413 | auto I = StubIndexes.find(Name); |
||
414 | if (I == StubIndexes.end()) |
||
415 | return nullptr; |
||
416 | auto Key = I->second.first; |
||
417 | void *PtrAddr = IndirectStubsInfos[Key.first].getPtr(Key.second); |
||
418 | assert(PtrAddr && "Missing pointer address"); |
||
419 | auto PtrTargetAddr = |
||
420 | static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(PtrAddr)); |
||
421 | return JITEvaluatedSymbol(PtrTargetAddr, I->second.second); |
||
422 | } |
||
423 | |||
424 | Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override { |
||
425 | using AtomicIntPtr = std::atomic<uintptr_t>; |
||
426 | |||
427 | std::lock_guard<std::mutex> Lock(StubsMutex); |
||
428 | auto I = StubIndexes.find(Name); |
||
429 | assert(I != StubIndexes.end() && "No stub pointer for symbol"); |
||
430 | auto Key = I->second.first; |
||
431 | AtomicIntPtr *AtomicStubPtr = reinterpret_cast<AtomicIntPtr *>( |
||
432 | IndirectStubsInfos[Key.first].getPtr(Key.second)); |
||
433 | *AtomicStubPtr = static_cast<uintptr_t>(NewAddr); |
||
434 | return Error::success(); |
||
435 | } |
||
436 | |||
437 | private: |
||
438 | Error reserveStubs(unsigned NumStubs) { |
||
439 | if (NumStubs <= FreeStubs.size()) |
||
440 | return Error::success(); |
||
441 | |||
442 | unsigned NewStubsRequired = NumStubs - FreeStubs.size(); |
||
443 | unsigned NewBlockId = IndirectStubsInfos.size(); |
||
444 | auto ISI = |
||
445 | LocalIndirectStubsInfo<TargetT>::create(NewStubsRequired, PageSize); |
||
446 | if (!ISI) |
||
447 | return ISI.takeError(); |
||
448 | for (unsigned I = 0; I < ISI->getNumStubs(); ++I) |
||
449 | FreeStubs.push_back(std::make_pair(NewBlockId, I)); |
||
450 | IndirectStubsInfos.push_back(std::move(*ISI)); |
||
451 | return Error::success(); |
||
452 | } |
||
453 | |||
454 | void createStubInternal(StringRef StubName, JITTargetAddress InitAddr, |
||
455 | JITSymbolFlags StubFlags) { |
||
456 | auto Key = FreeStubs.back(); |
||
457 | FreeStubs.pop_back(); |
||
458 | *IndirectStubsInfos[Key.first].getPtr(Key.second) = |
||
459 | jitTargetAddressToPointer<void *>(InitAddr); |
||
460 | StubIndexes[StubName] = std::make_pair(Key, StubFlags); |
||
461 | } |
||
462 | |||
463 | unsigned PageSize = sys::Process::getPageSizeEstimate(); |
||
464 | std::mutex StubsMutex; |
||
465 | std::vector<LocalIndirectStubsInfo<TargetT>> IndirectStubsInfos; |
||
466 | using StubKey = std::pair<uint16_t, uint16_t>; |
||
467 | std::vector<StubKey> FreeStubs; |
||
468 | StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes; |
||
469 | }; |
||
470 | |||
471 | /// Create a local compile callback manager. |
||
472 | /// |
||
473 | /// The given target triple will determine the ABI, and the given |
||
474 | /// ErrorHandlerAddress will be used by the resulting compile callback |
||
475 | /// manager if a compile callback fails. |
||
476 | Expected<std::unique_ptr<JITCompileCallbackManager>> |
||
477 | createLocalCompileCallbackManager(const Triple &T, ExecutionSession &ES, |
||
478 | JITTargetAddress ErrorHandlerAddress); |
||
479 | |||
480 | /// Create a local indriect stubs manager builder. |
||
481 | /// |
||
482 | /// The given target triple will determine the ABI. |
||
483 | std::function<std::unique_ptr<IndirectStubsManager>()> |
||
484 | createLocalIndirectStubsManagerBuilder(const Triple &T); |
||
485 | |||
486 | /// Build a function pointer of FunctionType with the given constant |
||
487 | /// address. |
||
488 | /// |
||
489 | /// Usage example: Turn a trampoline address into a function pointer constant |
||
490 | /// for use in a stub. |
||
491 | Constant *createIRTypedAddress(FunctionType &FT, JITTargetAddress Addr); |
||
492 | |||
493 | /// Create a function pointer with the given type, name, and initializer |
||
494 | /// in the given Module. |
||
495 | GlobalVariable *createImplPointer(PointerType &PT, Module &M, const Twine &Name, |
||
496 | Constant *Initializer); |
||
497 | |||
498 | /// Turn a function declaration into a stub function that makes an |
||
499 | /// indirect call using the given function pointer. |
||
500 | void makeStub(Function &F, Value &ImplPointer); |
||
501 | |||
502 | /// Promotes private symbols to global hidden, and renames to prevent clashes |
||
503 | /// with other promoted symbols. The same SymbolPromoter instance should be |
||
504 | /// used for all symbols to be added to a single JITDylib. |
||
505 | class SymbolLinkagePromoter { |
||
506 | public: |
||
507 | /// Promote symbols in the given module. Returns the set of global values |
||
508 | /// that have been renamed/promoted. |
||
509 | std::vector<GlobalValue *> operator()(Module &M); |
||
510 | |||
511 | private: |
||
512 | unsigned NextId = 0; |
||
513 | }; |
||
514 | |||
515 | /// Clone a function declaration into a new module. |
||
516 | /// |
||
517 | /// This function can be used as the first step towards creating a callback |
||
518 | /// stub (see makeStub), or moving a function body (see moveFunctionBody). |
||
519 | /// |
||
520 | /// If the VMap argument is non-null, a mapping will be added between F and |
||
521 | /// the new declaration, and between each of F's arguments and the new |
||
522 | /// declaration's arguments. This map can then be passed in to moveFunction to |
||
523 | /// move the function body if required. Note: When moving functions between |
||
524 | /// modules with these utilities, all decls should be cloned (and added to a |
||
525 | /// single VMap) before any bodies are moved. This will ensure that references |
||
526 | /// between functions all refer to the versions in the new module. |
||
527 | Function *cloneFunctionDecl(Module &Dst, const Function &F, |
||
528 | ValueToValueMapTy *VMap = nullptr); |
||
529 | |||
530 | /// Move the body of function 'F' to a cloned function declaration in a |
||
531 | /// different module (See related cloneFunctionDecl). |
||
532 | /// |
||
533 | /// If the target function declaration is not supplied via the NewF parameter |
||
534 | /// then it will be looked up via the VMap. |
||
535 | /// |
||
536 | /// This will delete the body of function 'F' from its original parent module, |
||
537 | /// but leave its declaration. |
||
538 | void moveFunctionBody(Function &OrigF, ValueToValueMapTy &VMap, |
||
539 | ValueMaterializer *Materializer = nullptr, |
||
540 | Function *NewF = nullptr); |
||
541 | |||
542 | /// Clone a global variable declaration into a new module. |
||
543 | GlobalVariable *cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV, |
||
544 | ValueToValueMapTy *VMap = nullptr); |
||
545 | |||
546 | /// Move global variable GV from its parent module to cloned global |
||
547 | /// declaration in a different module. |
||
548 | /// |
||
549 | /// If the target global declaration is not supplied via the NewGV parameter |
||
550 | /// then it will be looked up via the VMap. |
||
551 | /// |
||
552 | /// This will delete the initializer of GV from its original parent module, |
||
553 | /// but leave its declaration. |
||
554 | void moveGlobalVariableInitializer(GlobalVariable &OrigGV, |
||
555 | ValueToValueMapTy &VMap, |
||
556 | ValueMaterializer *Materializer = nullptr, |
||
557 | GlobalVariable *NewGV = nullptr); |
||
558 | |||
559 | /// Clone a global alias declaration into a new module. |
||
560 | GlobalAlias *cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA, |
||
561 | ValueToValueMapTy &VMap); |
||
562 | |||
563 | /// Clone module flags metadata into the destination module. |
||
564 | void cloneModuleFlagsMetadata(Module &Dst, const Module &Src, |
||
565 | ValueToValueMapTy &VMap); |
||
566 | |||
567 | /// Introduce relocations to \p Sym in its own definition if there are any |
||
568 | /// pointers formed via PC-relative address that do not already have a |
||
569 | /// relocation. |
||
570 | /// |
||
571 | /// This is useful when introducing indirection via a stub function at link time |
||
572 | /// without compiler support. If a function pointer is formed without a |
||
573 | /// relocation, e.g. in the definition of \c foo |
||
574 | /// |
||
575 | /// \code |
||
576 | /// _foo: |
||
577 | /// leaq -7(%rip), rax # form pointer to _foo without relocation |
||
578 | /// _bar: |
||
579 | /// leaq (%rip), %rax # uses X86_64_RELOC_SIGNED to '_foo' |
||
580 | /// \endcode |
||
581 | /// |
||
582 | /// the pointer to \c _foo computed by \c _foo and \c _bar may differ if we |
||
583 | /// introduce a stub for _foo. If the pointer is used as a key, this may be |
||
584 | /// observable to the program. This pass will attempt to introduce the missing |
||
585 | /// "self-relocation" on the leaq instruction. |
||
586 | /// |
||
587 | /// This is based on disassembly and should be considered "best effort". It may |
||
588 | /// silently fail to add relocations. |
||
589 | Error addFunctionPointerRelocationsToCurrentSymbol(jitlink::Symbol &Sym, |
||
590 | jitlink::LinkGraph &G, |
||
591 | MCDisassembler &Disassembler, |
||
592 | MCInstrAnalysis &MIA); |
||
593 | |||
594 | } // end namespace orc |
||
595 | |||
596 | } // end namespace llvm |
||
597 | |||
598 | #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H |