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