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 "llvm/ADT/DenseSet.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 <sstream>
     24 
     25 namespace llvm {
     26 namespace orc {
     27 
     28 /// @brief Base class for JITLayer independent aspects of
     29 ///        JITCompileCallbackManager.
     30 class JITCompileCallbackManagerBase {
     31 public:
     32 
     33   typedef std::function<TargetAddress()> CompileFtor;
     34   typedef std::function<void(TargetAddress)> UpdateFtor;
     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 and update actions for the callback.
     39   class CompileCallbackInfo {
     40   public:
     41     CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile,
     42                         UpdateFtor &Update)
     43       : Addr(Addr), Compile(Compile), Update(Update) {}
     44 
     45     TargetAddress getAddress() const { return Addr; }
     46     void setCompileAction(CompileFtor Compile) {
     47       this->Compile = std::move(Compile);
     48     }
     49     void setUpdateAction(UpdateFtor Update) {
     50       this->Update = std::move(Update);
     51     }
     52   private:
     53     TargetAddress Addr;
     54     CompileFtor &Compile;
     55     UpdateFtor &Update;
     56   };
     57 
     58   /// @brief Construct a JITCompileCallbackManagerBase.
     59   /// @param ErrorHandlerAddress The address of an error handler in the target
     60   ///                            process to be used if a compile callback fails.
     61   /// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no
     62   ///                             available trampoline when getCompileCallback is
     63   ///                             called.
     64   JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress,
     65                                 unsigned NumTrampolinesPerBlock)
     66     : ErrorHandlerAddress(ErrorHandlerAddress),
     67       NumTrampolinesPerBlock(NumTrampolinesPerBlock) {}
     68 
     69   virtual ~JITCompileCallbackManagerBase() {}
     70 
     71   /// @brief Execute the callback for the given trampoline id. Called by the JIT
     72   ///        to compile functions on demand.
     73   TargetAddress executeCompileCallback(TargetAddress TrampolineID) {
     74     TrampolineMapT::iterator I = ActiveTrampolines.find(TrampolineID);
     75     // FIXME: Also raise an error in the Orc error-handler when we finally have
     76     //        one.
     77     if (I == ActiveTrampolines.end())
     78       return ErrorHandlerAddress;
     79 
     80     // Found a callback handler. Yank this trampoline out of the active list and
     81     // put it back in the available trampolines list, then try to run the
     82     // handler's compile and update actions.
     83     // Moving the trampoline ID back to the available list first means there's at
     84     // least one available trampoline if the compile action triggers a request for
     85     // a new one.
     86     AvailableTrampolines.push_back(I->first);
     87     auto CallbackHandler = std::move(I->second);
     88     ActiveTrampolines.erase(I);
     89 
     90     if (auto Addr = CallbackHandler.Compile()) {
     91       CallbackHandler.Update(Addr);
     92       return Addr;
     93     }
     94     return ErrorHandlerAddress;
     95   }
     96 
     97   /// @brief Get/create a compile callback with the given signature.
     98   virtual CompileCallbackInfo getCompileCallback(LLVMContext &Context) = 0;
     99 
    100 protected:
    101 
    102   struct CallbackHandler {
    103     CompileFtor Compile;
    104     UpdateFtor Update;
    105   };
    106 
    107   TargetAddress ErrorHandlerAddress;
    108   unsigned NumTrampolinesPerBlock;
    109 
    110   typedef std::map<TargetAddress, CallbackHandler> TrampolineMapT;
    111   TrampolineMapT ActiveTrampolines;
    112   std::vector<TargetAddress> AvailableTrampolines;
    113 };
    114 
    115 /// @brief Manage compile callbacks.
    116 template <typename JITLayerT, typename TargetT>
    117 class JITCompileCallbackManager : public JITCompileCallbackManagerBase {
    118 public:
    119 
    120   /// @brief Construct a JITCompileCallbackManager.
    121   /// @param JIT JIT layer to emit callback trampolines, etc. into.
    122   /// @param Context LLVMContext to use for trampoline & resolve block modules.
    123   /// @param ErrorHandlerAddress The address of an error handler in the target
    124   ///                            process to be used if a compile callback fails.
    125   /// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever
    126   ///                               there is no existing callback trampoline.
    127   ///                               (Trampolines are allocated in blocks for
    128   ///                               efficiency.)
    129   JITCompileCallbackManager(JITLayerT &JIT, RuntimeDyld::MemoryManager &MemMgr,
    130                             LLVMContext &Context,
    131                             TargetAddress ErrorHandlerAddress,
    132                             unsigned NumTrampolinesPerBlock)
    133     : JITCompileCallbackManagerBase(ErrorHandlerAddress,
    134                                     NumTrampolinesPerBlock),
    135       JIT(JIT), MemMgr(MemMgr) {
    136     emitResolverBlock(Context);
    137   }
    138 
    139   /// @brief Get/create a compile callback with the given signature.
    140   CompileCallbackInfo getCompileCallback(LLVMContext &Context) final {
    141     TargetAddress TrampolineAddr = getAvailableTrampolineAddr(Context);
    142     auto &CallbackHandler =
    143       this->ActiveTrampolines[TrampolineAddr];
    144 
    145     return CompileCallbackInfo(TrampolineAddr, CallbackHandler.Compile,
    146                                CallbackHandler.Update);
    147   }
    148 
    149 private:
    150 
    151   std::vector<std::unique_ptr<Module>>
    152   SingletonSet(std::unique_ptr<Module> M) {
    153     std::vector<std::unique_ptr<Module>> Ms;
    154     Ms.push_back(std::move(M));
    155     return Ms;
    156   }
    157 
    158   void emitResolverBlock(LLVMContext &Context) {
    159     std::unique_ptr<Module> M(new Module("resolver_block_module",
    160                                          Context));
    161     TargetT::insertResolverBlock(*M, *this);
    162     auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr,
    163                               static_cast<RuntimeDyld::SymbolResolver*>(
    164                                   nullptr));
    165     JIT.emitAndFinalize(H);
    166     auto ResolverBlockSymbol =
    167       JIT.findSymbolIn(H, TargetT::ResolverBlockName, false);
    168     assert(ResolverBlockSymbol && "Failed to insert resolver block");
    169     ResolverBlockAddr = ResolverBlockSymbol.getAddress();
    170   }
    171 
    172   TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) {
    173     if (this->AvailableTrampolines.empty())
    174       grow(Context);
    175     assert(!this->AvailableTrampolines.empty() &&
    176            "Failed to grow available trampolines.");
    177     TargetAddress TrampolineAddr = this->AvailableTrampolines.back();
    178     this->AvailableTrampolines.pop_back();
    179     return TrampolineAddr;
    180   }
    181 
    182   void grow(LLVMContext &Context) {
    183     assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
    184     std::unique_ptr<Module> M(new Module("trampoline_block", Context));
    185     auto GetLabelName =
    186       TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr,
    187                                                 this->NumTrampolinesPerBlock,
    188                                                 this->ActiveTrampolines.size());
    189     auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr,
    190                               static_cast<RuntimeDyld::SymbolResolver*>(
    191                                   nullptr));
    192     JIT.emitAndFinalize(H);
    193     for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) {
    194       std::string Name = GetLabelName(I);
    195       auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false);
    196       assert(TrampolineSymbol && "Failed to emit trampoline.");
    197       this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress());
    198     }
    199   }
    200 
    201   JITLayerT &JIT;
    202   RuntimeDyld::MemoryManager &MemMgr;
    203   TargetAddress ResolverBlockAddr;
    204 };
    205 
    206 /// @brief Get an update functor that updates the value of a named function
    207 ///        pointer.
    208 template <typename JITLayerT>
    209 JITCompileCallbackManagerBase::UpdateFtor
    210 getLocalFPUpdater(JITLayerT &JIT, typename JITLayerT::ModuleSetHandleT H,
    211                   std::string Name) {
    212     // FIXME: Move-capture Name once we can use C++14.
    213     return [=,&JIT](TargetAddress Addr) {
    214       auto FPSym = JIT.findSymbolIn(H, Name, true);
    215       assert(FPSym && "Cannot find function pointer to update.");
    216       void *FPAddr = reinterpret_cast<void*>(
    217                        static_cast<uintptr_t>(FPSym.getAddress()));
    218       memcpy(FPAddr, &Addr, sizeof(uintptr_t));
    219     };
    220   }
    221 
    222 /// @brief Build a function pointer of FunctionType with the given constant
    223 ///        address.
    224 ///
    225 ///   Usage example: Turn a trampoline address into a function pointer constant
    226 /// for use in a stub.
    227 Constant* createIRTypedAddress(FunctionType &FT, TargetAddress Addr);
    228 
    229 /// @brief Create a function pointer with the given type, name, and initializer
    230 ///        in the given Module.
    231 GlobalVariable* createImplPointer(PointerType &PT, Module &M,
    232                                   const Twine &Name, Constant *Initializer);
    233 
    234 /// @brief Turn a function declaration into a stub function that makes an
    235 ///        indirect call using the given function pointer.
    236 void makeStub(Function &F, GlobalVariable &ImplPointer);
    237 
    238 typedef std::map<Module*, DenseSet<const GlobalValue*>> ModulePartitionMap;
    239 
    240 /// @brief Extract subsections of a Module into the given Module according to
    241 ///        the given ModulePartitionMap.
    242 void partition(Module &M, const ModulePartitionMap &PMap);
    243 
    244 /// @brief Struct for trivial "complete" partitioning of a module.
    245 class FullyPartitionedModule {
    246 public:
    247   std::unique_ptr<Module> GlobalVars;
    248   std::unique_ptr<Module> Commons;
    249   std::vector<std::unique_ptr<Module>> Functions;
    250 
    251   FullyPartitionedModule() = default;
    252   FullyPartitionedModule(FullyPartitionedModule &&S)
    253       : GlobalVars(std::move(S.GlobalVars)), Commons(std::move(S.Commons)),
    254         Functions(std::move(S.Functions)) {}
    255 };
    256 
    257 /// @brief Extract every function in M into a separate module.
    258 FullyPartitionedModule fullyPartition(Module &M);
    259 
    260 } // End namespace orc.
    261 } // End namespace llvm.
    262 
    263 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
    264