1 //===-- IndirectionUtils.h - Utilities for adding indirections --*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Contains utilities for adding indirections and breaking up modules.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
15 #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
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"
29 /// @brief Base class for JITLayer independent aspects of
30 /// JITCompileCallbackManager.
31 class JITCompileCallbackManagerBase {
34 typedef std::function<TargetAddress()> CompileFtor;
35 typedef std::function<void(TargetAddress)> UpdateFtor;
37 /// @brief Handle to a newly created compile callback. Can be used to get an
38 /// IR constant representing the address of the trampoline, and to set
39 /// the compile and update actions for the callback.
40 class CompileCallbackInfo {
42 CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile,
44 : Addr(Addr), Compile(Compile), Update(Update) {}
46 TargetAddress getAddress() const { return Addr; }
47 void setCompileAction(CompileFtor Compile) {
48 this->Compile = std::move(Compile);
50 void setUpdateAction(UpdateFtor Update) {
51 this->Update = std::move(Update);
59 /// @brief Construct a JITCompileCallbackManagerBase.
60 /// @param ErrorHandlerAddress The address of an error handler in the target
61 /// process to be used if a compile callback fails.
62 /// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no
63 /// available trampoline when getCompileCallback is
65 JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress,
66 unsigned NumTrampolinesPerBlock)
67 : ErrorHandlerAddress(ErrorHandlerAddress),
68 NumTrampolinesPerBlock(NumTrampolinesPerBlock) {}
70 virtual ~JITCompileCallbackManagerBase() {}
72 /// @brief Execute the callback for the given trampoline id. Called by the JIT
73 /// to compile functions on demand.
74 TargetAddress executeCompileCallback(TargetAddress TrampolineID) {
75 TrampolineMapT::iterator I = ActiveTrampolines.find(TrampolineID);
76 // FIXME: Also raise an error in the Orc error-handler when we finally have
78 if (I == ActiveTrampolines.end())
79 return ErrorHandlerAddress;
81 // Found a callback handler. Yank this trampoline out of the active list and
82 // put it back in the available trampolines list, then try to run the
83 // handler's compile and update actions.
84 // Moving the trampoline ID back to the available list first means there's at
85 // least one available trampoline if the compile action triggers a request for
87 AvailableTrampolines.push_back(I->first);
88 auto CallbackHandler = std::move(I->second);
89 ActiveTrampolines.erase(I);
91 if (auto Addr = CallbackHandler.Compile()) {
92 CallbackHandler.Update(Addr);
95 return ErrorHandlerAddress;
98 /// @brief Get/create a compile callback with the given signature.
99 virtual CompileCallbackInfo getCompileCallback(LLVMContext &Context) = 0;
103 struct CallbackHandler {
108 TargetAddress ErrorHandlerAddress;
109 unsigned NumTrampolinesPerBlock;
111 typedef std::map<TargetAddress, CallbackHandler> TrampolineMapT;
112 TrampolineMapT ActiveTrampolines;
113 std::vector<TargetAddress> AvailableTrampolines;
116 /// @brief Manage compile callbacks.
117 template <typename JITLayerT, typename TargetT>
118 class JITCompileCallbackManager : public JITCompileCallbackManagerBase {
121 /// @brief Construct a JITCompileCallbackManager.
122 /// @param JIT JIT layer to emit callback trampolines, etc. into.
123 /// @param Context LLVMContext to use for trampoline & resolve block modules.
124 /// @param ErrorHandlerAddress The address of an error handler in the target
125 /// process to be used if a compile callback fails.
126 /// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever
127 /// there is no existing callback trampoline.
128 /// (Trampolines are allocated in blocks for
130 JITCompileCallbackManager(JITLayerT &JIT, RuntimeDyld::MemoryManager &MemMgr,
131 LLVMContext &Context,
132 TargetAddress ErrorHandlerAddress,
133 unsigned NumTrampolinesPerBlock)
134 : JITCompileCallbackManagerBase(ErrorHandlerAddress,
135 NumTrampolinesPerBlock),
136 JIT(JIT), MemMgr(MemMgr) {
137 emitResolverBlock(Context);
140 /// @brief Get/create a compile callback with the given signature.
141 CompileCallbackInfo getCompileCallback(LLVMContext &Context) final {
142 TargetAddress TrampolineAddr = getAvailableTrampolineAddr(Context);
143 auto &CallbackHandler =
144 this->ActiveTrampolines[TrampolineAddr];
146 return CompileCallbackInfo(TrampolineAddr, CallbackHandler.Compile,
147 CallbackHandler.Update);
152 std::vector<std::unique_ptr<Module>>
153 SingletonSet(std::unique_ptr<Module> M) {
154 std::vector<std::unique_ptr<Module>> Ms;
155 Ms.push_back(std::move(M));
159 void emitResolverBlock(LLVMContext &Context) {
160 std::unique_ptr<Module> M(new Module("resolver_block_module",
162 TargetT::insertResolverBlock(*M, *this);
164 createLambdaResolver(
165 [](const std::string &Name) -> RuntimeDyld::SymbolInfo {
166 llvm_unreachable("External symbols in resolver block?");
168 [](const std::string &Name) -> RuntimeDyld::SymbolInfo {
169 llvm_unreachable("Dylib symbols in resolver block?");
171 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr,
172 std::move(NonResolver));
173 JIT.emitAndFinalize(H);
174 auto ResolverBlockSymbol =
175 JIT.findSymbolIn(H, TargetT::ResolverBlockName, false);
176 assert(ResolverBlockSymbol && "Failed to insert resolver block");
177 ResolverBlockAddr = ResolverBlockSymbol.getAddress();
180 TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) {
181 if (this->AvailableTrampolines.empty())
183 assert(!this->AvailableTrampolines.empty() &&
184 "Failed to grow available trampolines.");
185 TargetAddress TrampolineAddr = this->AvailableTrampolines.back();
186 this->AvailableTrampolines.pop_back();
187 return TrampolineAddr;
190 void grow(LLVMContext &Context) {
191 assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
192 std::unique_ptr<Module> M(new Module("trampoline_block", Context));
194 TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr,
195 this->NumTrampolinesPerBlock,
196 this->ActiveTrampolines.size());
198 createLambdaResolver(
199 [](const std::string &Name) -> RuntimeDyld::SymbolInfo {
200 llvm_unreachable("External symbols in trampoline block?");
202 [](const std::string &Name) -> RuntimeDyld::SymbolInfo {
203 llvm_unreachable("Dylib symbols in trampoline block?");
205 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr,
206 std::move(NonResolver));
207 JIT.emitAndFinalize(H);
208 for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) {
209 std::string Name = GetLabelName(I);
210 auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false);
211 assert(TrampolineSymbol && "Failed to emit trampoline.");
212 this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress());
217 RuntimeDyld::MemoryManager &MemMgr;
218 TargetAddress ResolverBlockAddr;
221 /// @brief Get an update functor that updates the value of a named function
223 template <typename JITLayerT>
224 JITCompileCallbackManagerBase::UpdateFtor
225 getLocalFPUpdater(JITLayerT &JIT, typename JITLayerT::ModuleSetHandleT H,
227 // FIXME: Move-capture Name once we can use C++14.
228 return [=,&JIT](TargetAddress Addr) {
229 auto FPSym = JIT.findSymbolIn(H, Name, true);
230 assert(FPSym && "Cannot find function pointer to update.");
231 void *FPAddr = reinterpret_cast<void*>(
232 static_cast<uintptr_t>(FPSym.getAddress()));
233 memcpy(FPAddr, &Addr, sizeof(uintptr_t));
237 /// @brief Build a function pointer of FunctionType with the given constant
240 /// Usage example: Turn a trampoline address into a function pointer constant
241 /// for use in a stub.
242 Constant* createIRTypedAddress(FunctionType &FT, TargetAddress Addr);
244 /// @brief Create a function pointer with the given type, name, and initializer
245 /// in the given Module.
246 GlobalVariable* createImplPointer(PointerType &PT, Module &M,
247 const Twine &Name, Constant *Initializer);
249 /// @brief Turn a function declaration into a stub function that makes an
250 /// indirect call using the given function pointer.
251 void makeStub(Function &F, GlobalVariable &ImplPointer);
253 typedef std::map<Module*, DenseSet<const GlobalValue*>> ModulePartitionMap;
255 /// @brief Extract subsections of a Module into the given Module according to
256 /// the given ModulePartitionMap.
257 void partition(Module &M, const ModulePartitionMap &PMap);
259 /// @brief Struct for trivial "complete" partitioning of a module.
260 class FullyPartitionedModule {
262 std::unique_ptr<Module> GlobalVars;
263 std::unique_ptr<Module> Commons;
264 std::vector<std::unique_ptr<Module>> Functions;
266 FullyPartitionedModule() = default;
267 FullyPartitionedModule(FullyPartitionedModule &&S)
268 : GlobalVars(std::move(S.GlobalVars)), Commons(std::move(S.Commons)),
269 Functions(std::move(S.Functions)) {}
272 /// @brief Extract every function in M into a separate module.
273 FullyPartitionedModule fullyPartition(Module &M);
275 } // End namespace orc.
276 } // End namespace llvm.
278 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H