Home | History | Annotate | Download | only in Orc
      1 //===-- IndirectionUtils.h - Utilities for adding indirections --*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // Contains utilities for adding indirections and breaking up modules.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
     15 #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
     16 
     17 #include "JITSymbol.h"
     18 #include "LambdaResolver.h"
     19 #include "llvm/ExecutionEngine/RuntimeDyld.h"
     20 #include "llvm/IR/IRBuilder.h"
     21 #include "llvm/IR/Mangler.h"
     22 #include "llvm/IR/Module.h"
     23 #include "llvm/Support/Process.h"
     24 #include "llvm/Transforms/Utils/ValueMapper.h"
     25 
     26 namespace llvm {
     27 namespace orc {
     28 
     29 /// @brief Target-independent base class for compile callback management.
     30 class JITCompileCallbackManager {
     31 public:
     32   typedef std::function<TargetAddress()> CompileFtor;
     33 
     34   /// @brief Handle to a newly created compile callback. Can be used to get an
     35   ///        IR constant representing the address of the trampoline, and to set
     36   ///        the compile action for the callback.
     37   class CompileCallbackInfo {
     38   public:
     39     CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile)
     40         : Addr(Addr), Compile(Compile) {}
     41 
     42     TargetAddress getAddress() const { return Addr; }
     43     void setCompileAction(CompileFtor Compile) {
     44       this->Compile = std::move(Compile);
     45     }
     46 
     47   private:
     48     TargetAddress Addr;
     49     CompileFtor &Compile;
     50   };
     51 
     52   /// @brief Construct a JITCompileCallbackManager.
     53   /// @param ErrorHandlerAddress The address of an error handler in the target
     54   ///                            process to be used if a compile callback fails.
     55   JITCompileCallbackManager(TargetAddress ErrorHandlerAddress)
     56       : ErrorHandlerAddress(ErrorHandlerAddress) {}
     57 
     58   virtual ~JITCompileCallbackManager() {}
     59 
     60   /// @brief Execute the callback for the given trampoline id. Called by the JIT
     61   ///        to compile functions on demand.
     62   TargetAddress executeCompileCallback(TargetAddress TrampolineAddr) {
     63     auto I = ActiveTrampolines.find(TrampolineAddr);
     64     // FIXME: Also raise an error in the Orc error-handler when we finally have
     65     //        one.
     66     if (I == ActiveTrampolines.end())
     67       return ErrorHandlerAddress;
     68 
     69     // Found a callback handler. Yank this trampoline out of the active list and
     70     // put it back in the available trampolines list, then try to run the
     71     // handler's compile and update actions.
     72     // Moving the trampoline ID back to the available list first means there's
     73     // at
     74     // least one available trampoline if the compile action triggers a request
     75     // for
     76     // a new one.
     77     auto Compile = std::move(I->second);
     78     ActiveTrampolines.erase(I);
     79     AvailableTrampolines.push_back(TrampolineAddr);
     80 
     81     if (auto Addr = Compile())
     82       return Addr;
     83 
     84     return ErrorHandlerAddress;
     85   }
     86 
     87   /// @brief Reserve a compile callback.
     88   CompileCallbackInfo getCompileCallback() {
     89     TargetAddress TrampolineAddr = getAvailableTrampolineAddr();
     90     auto &Compile = this->ActiveTrampolines[TrampolineAddr];
     91     return CompileCallbackInfo(TrampolineAddr, Compile);
     92   }
     93 
     94   /// @brief Get a CompileCallbackInfo for an existing callback.
     95   CompileCallbackInfo getCompileCallbackInfo(TargetAddress TrampolineAddr) {
     96     auto I = ActiveTrampolines.find(TrampolineAddr);
     97     assert(I != ActiveTrampolines.end() && "Not an active trampoline.");
     98     return CompileCallbackInfo(I->first, I->second);
     99   }
    100 
    101   /// @brief Release a compile callback.
    102   ///
    103   ///   Note: Callbacks are auto-released after they execute. This method should
    104   /// only be called to manually release a callback that is not going to
    105   /// execute.
    106   void releaseCompileCallback(TargetAddress TrampolineAddr) {
    107     auto I = ActiveTrampolines.find(TrampolineAddr);
    108     assert(I != ActiveTrampolines.end() && "Not an active trampoline.");
    109     ActiveTrampolines.erase(I);
    110     AvailableTrampolines.push_back(TrampolineAddr);
    111   }
    112 
    113 protected:
    114   TargetAddress ErrorHandlerAddress;
    115 
    116   typedef std::map<TargetAddress, CompileFtor> TrampolineMapT;
    117   TrampolineMapT ActiveTrampolines;
    118   std::vector<TargetAddress> AvailableTrampolines;
    119 
    120 private:
    121   TargetAddress getAvailableTrampolineAddr() {
    122     if (this->AvailableTrampolines.empty())
    123       grow();
    124     assert(!this->AvailableTrampolines.empty() &&
    125            "Failed to grow available trampolines.");
    126     TargetAddress TrampolineAddr = this->AvailableTrampolines.back();
    127     this->AvailableTrampolines.pop_back();
    128     return TrampolineAddr;
    129   }
    130 
    131   // Create new trampolines - to be implemented in subclasses.
    132   virtual void grow() = 0;
    133 
    134   virtual void anchor();
    135 };
    136 
    137 /// @brief Manage compile callbacks for in-process JITs.
    138 template <typename TargetT>
    139 class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
    140 public:
    141   /// @brief Construct a InProcessJITCompileCallbackManager.
    142   /// @param ErrorHandlerAddress The address of an error handler in the target
    143   ///                            process to be used if a compile callback fails.
    144   LocalJITCompileCallbackManager(TargetAddress ErrorHandlerAddress)
    145       : JITCompileCallbackManager(ErrorHandlerAddress) {
    146 
    147     /// Set up the resolver block.
    148     std::error_code EC;
    149     ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
    150         TargetT::ResolverCodeSize, nullptr,
    151         sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
    152     assert(!EC && "Failed to allocate resolver block");
    153 
    154     TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
    155                                &reenter, this);
    156 
    157     EC = sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(),
    158                                           sys::Memory::MF_READ |
    159                                               sys::Memory::MF_EXEC);
    160     assert(!EC && "Failed to mprotect resolver block");
    161   }
    162 
    163 private:
    164   static TargetAddress reenter(void *CCMgr, void *TrampolineId) {
    165     JITCompileCallbackManager *Mgr =
    166         static_cast<JITCompileCallbackManager *>(CCMgr);
    167     return Mgr->executeCompileCallback(
    168         static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(TrampolineId)));
    169   }
    170 
    171   void grow() override {
    172     assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
    173 
    174     std::error_code EC;
    175     auto TrampolineBlock =
    176         sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
    177             sys::Process::getPageSize(), nullptr,
    178             sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
    179     assert(!EC && "Failed to allocate trampoline block");
    180 
    181     unsigned NumTrampolines =
    182         (sys::Process::getPageSize() - TargetT::PointerSize) /
    183         TargetT::TrampolineSize;
    184 
    185     uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
    186     TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
    187                               NumTrampolines);
    188 
    189     for (unsigned I = 0; I < NumTrampolines; ++I)
    190       this->AvailableTrampolines.push_back(
    191           static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(
    192               TrampolineMem + (I * TargetT::TrampolineSize))));
    193 
    194     EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
    195                                           sys::Memory::MF_READ |
    196                                               sys::Memory::MF_EXEC);
    197     assert(!EC && "Failed to mprotect trampoline block");
    198 
    199     TrampolineBlocks.push_back(std::move(TrampolineBlock));
    200   }
    201 
    202   sys::OwningMemoryBlock ResolverBlock;
    203   std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
    204 };
    205 
    206 /// @brief Base class for managing collections of named indirect stubs.
    207 class IndirectStubsManager {
    208 public:
    209   /// @brief Map type for initializing the manager. See init.
    210   typedef StringMap<std::pair<TargetAddress, JITSymbolFlags>> StubInitsMap;
    211 
    212   virtual ~IndirectStubsManager() {}
    213 
    214   /// @brief Create a single stub with the given name, target address and flags.
    215   virtual Error createStub(StringRef StubName, TargetAddress StubAddr,
    216                            JITSymbolFlags StubFlags) = 0;
    217 
    218   /// @brief Create StubInits.size() stubs with the given names, target
    219   ///        addresses, and flags.
    220   virtual Error createStubs(const StubInitsMap &StubInits) = 0;
    221 
    222   /// @brief Find the stub with the given name. If ExportedStubsOnly is true,
    223   ///        this will only return a result if the stub's flags indicate that it
    224   ///        is exported.
    225   virtual JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) = 0;
    226 
    227   /// @brief Find the implementation-pointer for the stub.
    228   virtual JITSymbol findPointer(StringRef Name) = 0;
    229 
    230   /// @brief Change the value of the implementation pointer for the stub.
    231   virtual Error updatePointer(StringRef Name, TargetAddress NewAddr) = 0;
    232 
    233 private:
    234   virtual void anchor();
    235 };
    236 
    237 /// @brief IndirectStubsManager implementation for the host architecture, e.g.
    238 ///        OrcX86_64. (See OrcArchitectureSupport.h).
    239 template <typename TargetT>
    240 class LocalIndirectStubsManager : public IndirectStubsManager {
    241 public:
    242   Error createStub(StringRef StubName, TargetAddress StubAddr,
    243                    JITSymbolFlags StubFlags) override {
    244     if (auto Err = reserveStubs(1))
    245       return Err;
    246 
    247     createStubInternal(StubName, StubAddr, StubFlags);
    248 
    249     return Error::success();
    250   }
    251 
    252   Error createStubs(const StubInitsMap &StubInits) override {
    253     if (auto Err = reserveStubs(StubInits.size()))
    254       return Err;
    255 
    256     for (auto &Entry : StubInits)
    257       createStubInternal(Entry.first(), Entry.second.first,
    258                          Entry.second.second);
    259 
    260     return Error::success();
    261   }
    262 
    263   JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) override {
    264     auto I = StubIndexes.find(Name);
    265     if (I == StubIndexes.end())
    266       return nullptr;
    267     auto Key = I->second.first;
    268     void *StubAddr = IndirectStubsInfos[Key.first].getStub(Key.second);
    269     assert(StubAddr && "Missing stub address");
    270     auto StubTargetAddr =
    271         static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(StubAddr));
    272     auto StubSymbol = JITSymbol(StubTargetAddr, I->second.second);
    273     if (ExportedStubsOnly && !StubSymbol.isExported())
    274       return nullptr;
    275     return StubSymbol;
    276   }
    277 
    278   JITSymbol findPointer(StringRef Name) override {
    279     auto I = StubIndexes.find(Name);
    280     if (I == StubIndexes.end())
    281       return nullptr;
    282     auto Key = I->second.first;
    283     void *PtrAddr = IndirectStubsInfos[Key.first].getPtr(Key.second);
    284     assert(PtrAddr && "Missing pointer address");
    285     auto PtrTargetAddr =
    286         static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(PtrAddr));
    287     return JITSymbol(PtrTargetAddr, I->second.second);
    288   }
    289 
    290   Error updatePointer(StringRef Name, TargetAddress NewAddr) override {
    291     auto I = StubIndexes.find(Name);
    292     assert(I != StubIndexes.end() && "No stub pointer for symbol");
    293     auto Key = I->second.first;
    294     *IndirectStubsInfos[Key.first].getPtr(Key.second) =
    295         reinterpret_cast<void *>(static_cast<uintptr_t>(NewAddr));
    296     return Error::success();
    297   }
    298 
    299 private:
    300   Error reserveStubs(unsigned NumStubs) {
    301     if (NumStubs <= FreeStubs.size())
    302       return Error::success();
    303 
    304     unsigned NewStubsRequired = NumStubs - FreeStubs.size();
    305     unsigned NewBlockId = IndirectStubsInfos.size();
    306     typename TargetT::IndirectStubsInfo ISI;
    307     if (auto Err =
    308             TargetT::emitIndirectStubsBlock(ISI, NewStubsRequired, nullptr))
    309       return Err;
    310     for (unsigned I = 0; I < ISI.getNumStubs(); ++I)
    311       FreeStubs.push_back(std::make_pair(NewBlockId, I));
    312     IndirectStubsInfos.push_back(std::move(ISI));
    313     return Error::success();
    314   }
    315 
    316   void createStubInternal(StringRef StubName, TargetAddress InitAddr,
    317                           JITSymbolFlags StubFlags) {
    318     auto Key = FreeStubs.back();
    319     FreeStubs.pop_back();
    320     *IndirectStubsInfos[Key.first].getPtr(Key.second) =
    321         reinterpret_cast<void *>(static_cast<uintptr_t>(InitAddr));
    322     StubIndexes[StubName] = std::make_pair(Key, StubFlags);
    323   }
    324 
    325   std::vector<typename TargetT::IndirectStubsInfo> IndirectStubsInfos;
    326   typedef std::pair<uint16_t, uint16_t> StubKey;
    327   std::vector<StubKey> FreeStubs;
    328   StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes;
    329 };
    330 
    331 /// @brief Create a local compile callback manager.
    332 ///
    333 /// The given target triple will determine the ABI, and the given
    334 /// ErrorHandlerAddress will be used by the resulting compile callback
    335 /// manager if a compile callback fails.
    336 std::unique_ptr<JITCompileCallbackManager>
    337 createLocalCompileCallbackManager(const Triple &T,
    338                                   TargetAddress ErrorHandlerAddress);
    339 
    340 /// @brief Create a local indriect stubs manager builder.
    341 ///
    342 /// The given target triple will determine the ABI.
    343 std::function<std::unique_ptr<IndirectStubsManager>()>
    344 createLocalIndirectStubsManagerBuilder(const Triple &T);
    345 
    346 /// @brief Build a function pointer of FunctionType with the given constant
    347 ///        address.
    348 ///
    349 ///   Usage example: Turn a trampoline address into a function pointer constant
    350 /// for use in a stub.
    351 Constant *createIRTypedAddress(FunctionType &FT, TargetAddress Addr);
    352 
    353 /// @brief Create a function pointer with the given type, name, and initializer
    354 ///        in the given Module.
    355 GlobalVariable *createImplPointer(PointerType &PT, Module &M, const Twine &Name,
    356                                   Constant *Initializer);
    357 
    358 /// @brief Turn a function declaration into a stub function that makes an
    359 ///        indirect call using the given function pointer.
    360 void makeStub(Function &F, Value &ImplPointer);
    361 
    362 /// @brief Raise linkage types and rename as necessary to ensure that all
    363 ///        symbols are accessible for other modules.
    364 ///
    365 ///   This should be called before partitioning a module to ensure that the
    366 /// partitions retain access to each other's symbols.
    367 void makeAllSymbolsExternallyAccessible(Module &M);
    368 
    369 /// @brief Clone a function declaration into a new module.
    370 ///
    371 ///   This function can be used as the first step towards creating a callback
    372 /// stub (see makeStub), or moving a function body (see moveFunctionBody).
    373 ///
    374 ///   If the VMap argument is non-null, a mapping will be added between F and
    375 /// the new declaration, and between each of F's arguments and the new
    376 /// declaration's arguments. This map can then be passed in to moveFunction to
    377 /// move the function body if required. Note: When moving functions between
    378 /// modules with these utilities, all decls should be cloned (and added to a
    379 /// single VMap) before any bodies are moved. This will ensure that references
    380 /// between functions all refer to the versions in the new module.
    381 Function *cloneFunctionDecl(Module &Dst, const Function &F,
    382                             ValueToValueMapTy *VMap = nullptr);
    383 
    384 /// @brief Move the body of function 'F' to a cloned function declaration in a
    385 ///        different module (See related cloneFunctionDecl).
    386 ///
    387 ///   If the target function declaration is not supplied via the NewF parameter
    388 /// then it will be looked up via the VMap.
    389 ///
    390 ///   This will delete the body of function 'F' from its original parent module,
    391 /// but leave its declaration.
    392 void moveFunctionBody(Function &OrigF, ValueToValueMapTy &VMap,
    393                       ValueMaterializer *Materializer = nullptr,
    394                       Function *NewF = nullptr);
    395 
    396 /// @brief Clone a global variable declaration into a new module.
    397 GlobalVariable *cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV,
    398                                         ValueToValueMapTy *VMap = nullptr);
    399 
    400 /// @brief Move global variable GV from its parent module to cloned global
    401 ///        declaration in a different module.
    402 ///
    403 ///   If the target global declaration is not supplied via the NewGV parameter
    404 /// then it will be looked up via the VMap.
    405 ///
    406 ///   This will delete the initializer of GV from its original parent module,
    407 /// but leave its declaration.
    408 void moveGlobalVariableInitializer(GlobalVariable &OrigGV,
    409                                    ValueToValueMapTy &VMap,
    410                                    ValueMaterializer *Materializer = nullptr,
    411                                    GlobalVariable *NewGV = nullptr);
    412 
    413 /// @brief Clone
    414 GlobalAlias *cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA,
    415                                   ValueToValueMapTy &VMap);
    416 
    417 } // End namespace orc.
    418 } // End namespace llvm.
    419 
    420 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
    421