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 |