1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-24 03:33:20 +01:00

[Orc] Update the Orc indirection utils and refactor the CompileOnDemand layer.

This patch replaces most of the Orc indirection utils API with a new class:
JITCompileCallbackManager, which creates and manages JIT callbacks.
Exposing this functionality directly allows the user to create callbacks that
are associated with user supplied compilation actions. For example, you can
create a callback to lazyily IR-gen something from an AST. (A kaleidoscope
example demonstrating this will be committed shortly).

This patch also refactors the CompileOnDemand layer to use the
JITCompileCallbackManager API.

llvm-svn: 229461
This commit is contained in:
Lang Hames 2015-02-17 01:18:38 +00:00
parent 0e48ce380c
commit 126ce3b498
7 changed files with 561 additions and 528 deletions

View File

@ -15,6 +15,7 @@
#ifndef LLVM_EXECUTIONENGINE_ORC_CLONESUBMODULE_H
#define LLVM_EXECUTIONENGINE_ORC_CLONESUBMODULE_H
#include "llvm/ADT/DenseSet.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include <functional>
@ -36,9 +37,9 @@ void copyGVInitializer(GlobalVariable &New, const GlobalVariable &Orig,
void copyFunctionBody(Function &New, const Function &Orig,
ValueToValueMapTy &VMap);
std::unique_ptr<Module>
CloneSubModule(const Module &M, HandleGlobalVariableFtor HandleGlobalVariable,
HandleFunctionFtor HandleFunction, bool KeepInlineAsm);
void CloneSubModule(Module &Dst, const Module &Src,
HandleGlobalVariableFtor HandleGlobalVariable,
HandleFunctionFtor HandleFunction, bool KeepInlineAsm);
}
#endif // LLVM_EXECUTIONENGINE_ORC_CLONESUBMODULE_H

View File

@ -33,7 +33,8 @@ namespace llvm {
/// It is expected that this layer will frequently be used on top of a
/// LazyEmittingLayer. The combination of the two ensures that each function is
/// compiled only when it is first called.
template <typename BaseLayerT> class CompileOnDemandLayer {
template <typename BaseLayerT, typename CompileCallbackMgrT>
class CompileOnDemandLayer {
public:
/// @brief Lookup helper that provides compatibility with the classic
/// static-compilation symbol resolution process.
@ -114,13 +115,6 @@ private:
// Logical module handles.
std::vector<typename CODScopedLookup::LMHandle> LMHandles;
// Persistent manglers - one per TU.
std::vector<PersistentMangler> PersistentManglers;
// Symbol resolution callback handlers - one per TU.
std::vector<std::unique_ptr<JITResolveCallbackHandler>>
JITResolveCallbackHandlers;
// List of vectors of module set handles:
// One vector per logical module - each vector holds the handles for the
// exploded modules for that logical module in the base layer.
@ -143,91 +137,37 @@ public:
/// @brief Handle to a set of loaded modules.
typedef typename ModuleSetInfoListT::iterator ModuleSetHandleT;
/// @brief Convenience typedef for callback inserter.
typedef std::function<void(Module&, JITResolveCallbackHandler&)>
InsertCallbackAsmFtor;
// @brief Fallback lookup functor.
typedef std::function<uint64_t(const std::string &)> LookupFtor;
/// @brief Construct a compile-on-demand layer instance.
CompileOnDemandLayer(BaseLayerT &BaseLayer,
InsertCallbackAsmFtor InsertCallbackAsm)
: BaseLayer(BaseLayer), InsertCallbackAsm(InsertCallbackAsm) {}
CompileOnDemandLayer(BaseLayerT &BaseLayer, LLVMContext &Context)
: BaseLayer(BaseLayer),
CompileCallbackMgr(BaseLayer, Context, 0, 64) {}
/// @brief Add a module to the compile-on-demand layer.
template <typename ModuleSetT>
ModuleSetHandleT addModuleSet(ModuleSetT Ms,
std::unique_ptr<RTDyldMemoryManager> MM) {
LookupFtor FallbackLookup = nullptr) {
const char *JITAddrSuffix = "$orc_addr";
const char *JITImplSuffix = "$orc_impl";
// If the user didn't supply a fallback lookup then just use
// getSymbolAddress.
if (!FallbackLookup)
FallbackLookup = [=](const std::string &Name) {
return findSymbol(Name, true).getAddress();
};
// Create a symbol lookup context and ModuleSetInfo for this module set.
// Create a lookup context and ModuleSetInfo for this module set.
// For the purposes of symbol resolution the set Ms will be treated as if
// the modules it contained had been linked together as a dylib.
auto DylibLookup = std::make_shared<CODScopedLookup>(BaseLayer);
ModuleSetHandleT H =
ModuleSetInfos.insert(ModuleSetInfos.end(), ModuleSetInfo(DylibLookup));
ModuleSetInfo &MSI = ModuleSetInfos.back();
// Process each of the modules in this module set. All modules share the
// same lookup context, but each will get its own TU lookup context.
for (auto &M : Ms) {
// Create a TU lookup context for this module.
auto LMH = DylibLookup->createLogicalModule();
MSI.LMHandles.push_back(LMH);
// Create a persistent mangler for this module.
MSI.PersistentManglers.emplace_back(*M->getDataLayout());
// Make all calls to functions defined in this module indirect.
JITIndirections Indirections =
makeCallsDoubleIndirect(*M, [](const Function &) { return true; },
JITImplSuffix, JITAddrSuffix);
// Then carve up the module into a bunch of single-function modules.
std::vector<std::unique_ptr<Module>> ExplodedModules =
explode(*M, Indirections);
// Add a resolve-callback handler for this module to look up symbol
// addresses when requested via a callback.
MSI.JITResolveCallbackHandlers.push_back(
createCallbackHandlerFromJITIndirections(
Indirections, MSI.PersistentManglers.back(),
[=](StringRef S) {
return DylibLookup->findSymbol(LMH, S).getAddress();
}));
// Insert callback asm code into the first module.
InsertCallbackAsm(*ExplodedModules[0],
*MSI.JITResolveCallbackHandlers.back());
// Now we need to take each of the extracted Modules and add them to
// base layer. Each Module will be added individually to make sure they
// can be compiled separately, and each will get its own lookaside
// memory manager with lookup functors that resolve symbols in sibling
// modules first.OA
for (auto &M : ExplodedModules) {
std::vector<std::unique_ptr<Module>> MSet;
MSet.push_back(std::move(M));
BaseLayerModuleSetHandleT H = BaseLayer.addModuleSet(
std::move(MSet),
createLookasideRTDyldMM<SectionMemoryManager>(
[=](const std::string &Name) {
if (auto Symbol = DylibLookup->findSymbol(LMH, Name))
return Symbol.getAddress();
return findSymbol(Name, true).getAddress();
},
[=](const std::string &Name) {
return DylibLookup->findSymbol(LMH, Name).getAddress();
}));
DylibLookup->addToLogicalModule(LMH, H);
MSI.BaseLayerModuleSetHandles.push_back(H);
}
initializeFuncAddrs(*MSI.JITResolveCallbackHandlers.back(), Indirections,
MSI.PersistentManglers.back(), [=](StringRef S) {
return DylibLookup->findSymbol(LMH, S).getAddress();
});
}
// Process each of the modules in this module set.
for (auto &M : Ms)
partitionAndAdd(*M, MSI, FallbackLookup);
return H;
}
@ -262,8 +202,149 @@ public:
}
private:
void partitionAndAdd(Module &M, ModuleSetInfo &MSI,
LookupFtor FallbackLookup) {
const char *AddrSuffix = "$orc_addr";
const char *BodySuffix = "$orc_body";
// We're going to break M up into a bunch of sub-modules, but we want
// internal linkage symbols to still resolve sensibly. CODScopedLookup
// provides the "logical module" concept to make this work, so create a
// new logical module for M.
auto DylibLookup = MSI.Lookup;
auto LogicalModule = DylibLookup->createLogicalModule();
MSI.LMHandles.push_back(LogicalModule);
// Partition M into a "globals and stubs" module, a "common symbols" module,
// and a list of single-function modules.
auto PartitionedModule = fullyPartition(M);
auto StubsModule = std::move(PartitionedModule.GlobalVars);
auto CommonsModule = std::move(PartitionedModule.Commons);
auto FunctionModules = std::move(PartitionedModule.Functions);
// Emit the commons stright away.
auto CommonHandle = addModule(std::move(CommonsModule), MSI, LogicalModule,
FallbackLookup);
BaseLayer.emitAndFinalize(CommonHandle);
// Map of definition names to callback-info data structures. We'll use
// this to build the compile actions for the stubs below.
typedef std::map<std::string,
typename CompileCallbackMgrT::CompileCallbackInfo>
StubInfoMap;
StubInfoMap StubInfos;
// Now we need to take each of the extracted Modules and add them to
// base layer. Each Module will be added individually to make sure they
// can be compiled separately, and each will get its own lookaside
// memory manager that will resolve within this logical module first.
for (auto &SubM : FunctionModules) {
// Keep track of the stubs we create for this module so that we can set
// their compile actions.
std::vector<typename StubInfoMap::iterator> NewStubInfos;
// Search for function definitions and insert stubs into the stubs
// module.
for (auto &F : *SubM) {
if (F.isDeclaration())
continue;
std::string Name = F.getName();
Function *Proto = StubsModule->getFunction(Name);
assert(Proto && "Failed to clone function decl into stubs module.");
auto CallbackInfo =
CompileCallbackMgr.getCompileCallback(*Proto->getFunctionType());
GlobalVariable *FunctionBodyPointer =
createImplPointer(*Proto, Name + AddrSuffix,
CallbackInfo.getAddress());
makeStub(*Proto, *FunctionBodyPointer);
F.setName(Name + BodySuffix);
F.setVisibility(GlobalValue::HiddenVisibility);
auto KV = std::make_pair(std::move(Name), std::move(CallbackInfo));
NewStubInfos.push_back(StubInfos.insert(StubInfos.begin(), KV));
}
auto H = addModule(std::move(SubM), MSI, LogicalModule, FallbackLookup);
// Set the compile actions for this module:
for (auto &KVPair : NewStubInfos) {
std::string BodyName = Mangle(KVPair->first + BodySuffix,
*M.getDataLayout());
auto &CCInfo = KVPair->second;
CCInfo.setCompileAction(
[=](){
return BaseLayer.findSymbolIn(H, BodyName, false).getAddress();
});
}
}
// Ok - we've processed all the partitioned modules. Now add the
// stubs/globals module and set the update actions.
auto StubsH =
addModule(std::move(StubsModule), MSI, LogicalModule, FallbackLookup);
for (auto &KVPair : StubInfos) {
std::string AddrName = Mangle(KVPair.first + AddrSuffix,
*M.getDataLayout());
auto &CCInfo = KVPair.second;
CCInfo.setUpdateAction(
CompileCallbackMgr.getLocalFPUpdater(StubsH, AddrName));
}
}
// Add the given Module to the base layer using a memory manager that will
// perform the appropriate scoped lookup (i.e. will look first with in the
// module from which it was extracted, then into the set to which that module
// belonged, and finally externally).
BaseLayerModuleSetHandleT addModule(
std::unique_ptr<Module> M,
ModuleSetInfo &MSI,
typename CODScopedLookup::LMHandle LogicalModule,
LookupFtor FallbackLookup) {
// Add this module to the JIT with a memory manager that uses the
// DylibLookup to resolve symbols.
std::vector<std::unique_ptr<Module>> MSet;
MSet.push_back(std::move(M));
auto DylibLookup = MSI.Lookup;
auto MM =
createLookasideRTDyldMM<SectionMemoryManager>(
[=](const std::string &Name) {
if (auto Symbol = DylibLookup->findSymbol(LogicalModule, Name))
return Symbol.getAddress();
return FallbackLookup(Name);
},
[=](const std::string &Name) {
return DylibLookup->findSymbol(LogicalModule, Name).getAddress();
});
BaseLayerModuleSetHandleT H =
BaseLayer.addModuleSet(std::move(MSet), std::move(MM));
// Add this module to the logical module lookup.
DylibLookup->addToLogicalModule(LogicalModule, H);
MSI.BaseLayerModuleSetHandles.push_back(H);
return H;
}
static std::string Mangle(StringRef Name, const DataLayout &DL) {
Mangler M(&DL);
std::string MangledName;
{
raw_string_ostream MangledNameStream(MangledName);
M.getNameWithPrefix(MangledNameStream, Name);
}
return MangledName;
}
BaseLayerT &BaseLayer;
InsertCallbackAsmFtor InsertCallbackAsm;
CompileCallbackMgrT CompileCallbackMgr;
ModuleSetInfoListT ModuleSetInfos;
};
}

View File

@ -15,271 +15,224 @@
#define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H
#include "JITSymbol.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include <sstream>
namespace llvm {
/// @brief Persistent name mangling.
///
/// This class provides name mangling that can outlive a Module (and its
/// DataLayout).
class PersistentMangler {
/// @brief Base class for JITLayer independent aspects of
/// JITCompileCallbackManager.
template <typename TargetT>
class JITCompileCallbackManagerBase {
public:
PersistentMangler(DataLayout DL) : DL(std::move(DL)), M(&this->DL) {}
std::string getMangledName(StringRef Name) const {
std::string MangledName;
{
raw_string_ostream MangledNameStream(MangledName);
M.getNameWithPrefix(MangledNameStream, Name);
/// @brief Construct a JITCompileCallbackManagerBase.
/// @param ErrorHandlerAddress The address of an error handler in the target
/// process to be used if a compile callback fails.
/// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no
/// available trampoline when getCompileCallback is
/// called.
JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress,
unsigned NumTrampolinesPerBlock)
: ErrorHandlerAddress(ErrorHandlerAddress),
NumTrampolinesPerBlock(NumTrampolinesPerBlock) {}
/// @brief Execute the callback for the given trampoline id. Called by the JIT
/// to compile functions on demand.
TargetAddress executeCompileCallback(TargetAddress TrampolineID) {
typename TrampolineMapT::iterator I = ActiveTrampolines.find(TrampolineID);
// FIXME: Also raise an error in the Orc error-handler when we finally have
// one.
if (I == ActiveTrampolines.end())
return ErrorHandlerAddress;
// Found a callback handler. Yank this trampoline out of the active list and
// put it back in the available trampolines list, then try to run the
// handler's compile and update actions.
// Moving the trampoline ID back to the available list first means there's at
// least one available trampoline if the compile action triggers a request for
// a new one.
AvailableTrampolines.push_back(I->first);
auto CallbackHandler = std::move(I->second);
ActiveTrampolines.erase(I);
if (auto Addr = CallbackHandler.Compile()) {
CallbackHandler.Update(Addr);
return Addr;
}
return MangledName;
return ErrorHandlerAddress;
}
protected:
typedef std::function<TargetAddress()> CompileFtorT;
typedef std::function<void(TargetAddress)> UpdateFtorT;
struct CallbackHandler {
CompileFtorT Compile;
UpdateFtorT Update;
};
TargetAddress ErrorHandlerAddress;
unsigned NumTrampolinesPerBlock;
typedef std::map<TargetAddress, CallbackHandler> TrampolineMapT;
TrampolineMapT ActiveTrampolines;
std::vector<TargetAddress> AvailableTrampolines;
};
/// @brief Manage compile callbacks.
template <typename JITLayerT, typename TargetT>
class JITCompileCallbackManager :
public JITCompileCallbackManagerBase<TargetT> {
public:
typedef typename JITCompileCallbackManagerBase<TargetT>::CompileFtorT
CompileFtorT;
typedef typename JITCompileCallbackManagerBase<TargetT>::UpdateFtorT
UpdateFtorT;
/// @brief Construct a JITCompileCallbackManager.
/// @param JIT JIT layer to emit callback trampolines, etc. into.
/// @param Context LLVMContext to use for trampoline & resolve block modules.
/// @param ErrorHandlerAddress The address of an error handler in the target
/// process to be used if a compile callback fails.
/// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever
/// there is no existing callback trampoline.
/// (Trampolines are allocated in blocks for
/// efficiency.)
JITCompileCallbackManager(JITLayerT &JIT, LLVMContext &Context,
TargetAddress ErrorHandlerAddress,
unsigned NumTrampolinesPerBlock)
: JITCompileCallbackManagerBase<TargetT>(ErrorHandlerAddress,
NumTrampolinesPerBlock),
JIT(JIT) {
emitResolverBlock(Context);
}
/// @brief Handle to a newly created compile callback. Can be used to get an
/// IR constant representing the address of the trampoline, and to set
/// the compile and update actions for the callback.
class CompileCallbackInfo {
public:
CompileCallbackInfo(Constant *Addr, CompileFtorT &Compile,
UpdateFtorT &Update)
: Addr(Addr), Compile(Compile), Update(Update) {}
Constant* getAddress() const { return Addr; }
void setCompileAction(CompileFtorT Compile) {
this->Compile = std::move(Compile);
}
void setUpdateAction(UpdateFtorT Update) {
this->Update = std::move(Update);
}
private:
Constant *Addr;
CompileFtorT &Compile;
UpdateFtorT &Update;
};
/// @brief Get/create a compile callback with the given signature.
CompileCallbackInfo getCompileCallback(FunctionType &FT) {
TargetAddress TrampolineAddr = getAvailableTrampolineAddr(FT.getContext());
auto &CallbackHandler =
this->ActiveTrampolines[TrampolineAddr + TargetT::CallSize];
Constant *AddrIntVal =
ConstantInt::get(Type::getInt64Ty(FT.getContext()), TrampolineAddr);
Constant *AddrPtrVal =
ConstantExpr::getCast(Instruction::IntToPtr, AddrIntVal,
PointerType::get(&FT, 0));
return CompileCallbackInfo(AddrPtrVal, CallbackHandler.Compile,
CallbackHandler.Update);
}
/// @brief Get a functor for updating the value of a named function pointer.
UpdateFtorT getLocalFPUpdater(typename JITLayerT::ModuleSetHandleT H,
std::string Name) {
// FIXME: Move-capture Name once we can use C++14.
return [=](TargetAddress Addr) {
auto FPSym = JIT.findSymbolIn(H, Name, true);
assert(FPSym && "Cannot find function pointer to update.");
void *FPAddr = reinterpret_cast<void*>(
static_cast<uintptr_t>(FPSym.getAddress()));
memcpy(FPAddr, &Addr, sizeof(uintptr_t));
};
}
private:
DataLayout DL;
Mangler M;
};
/// @brief Handle callbacks from the JIT process requesting the definitions of
/// symbols.
///
/// This utility is intended to be used to support compile-on-demand for
/// functions.
class JITResolveCallbackHandler {
private:
typedef std::vector<std::string> FuncNameList;
public:
typedef FuncNameList::size_type StubIndex;
public:
/// @brief Create a JITResolveCallbackHandler with the given functors for
/// looking up symbols and updating their use-sites.
///
/// @return A JITResolveCallbackHandler instance that will invoke the
/// Lookup and Update functors as needed to resolve missing symbol
/// definitions.
template <typename LookupFtor, typename UpdateFtor>
static std::unique_ptr<JITResolveCallbackHandler> create(LookupFtor Lookup,
UpdateFtor Update);
/// @brief Destroy instance. Does not modify existing emitted symbols.
///
/// Not-yet-emitted symbols will need to be resolved some other way after
/// this class is destroyed.
virtual ~JITResolveCallbackHandler() {}
/// @brief Add a function to be resolved on demand.
void addFuncName(std::string Name) { FuncNames.push_back(std::move(Name)); }
/// @brief Get the name associated with the given index.
const std::string &getFuncName(StubIndex Idx) const { return FuncNames[Idx]; }
/// @brief Returns the number of symbols being managed by this instance.
StubIndex getNumFuncs() const { return FuncNames.size(); }
/// @brief Get the address for the symbol associated with the given index.
///
/// This is expected to be called by code in the JIT process itself, in
/// order to resolve a function.
virtual TargetAddress resolve(StubIndex StubIdx) = 0;
private:
FuncNameList FuncNames;
};
// Implementation class for JITResolveCallbackHandler.
template <typename LookupFtor, typename UpdateFtor>
class JITResolveCallbackHandlerImpl : public JITResolveCallbackHandler {
public:
JITResolveCallbackHandlerImpl(LookupFtor Lookup, UpdateFtor Update)
: Lookup(std::move(Lookup)), Update(std::move(Update)) {}
TargetAddress resolve(StubIndex StubIdx) override {
const std::string &FuncName = getFuncName(StubIdx);
TargetAddress Addr = Lookup(FuncName);
Update(FuncName, Addr);
return Addr;
std::vector<std::unique_ptr<Module>>
SingletonSet(std::unique_ptr<Module> M) {
std::vector<std::unique_ptr<Module>> Ms;
Ms.push_back(std::move(M));
return Ms;
}
private:
LookupFtor Lookup;
UpdateFtor Update;
};
template <typename LookupFtor, typename UpdateFtor>
std::unique_ptr<JITResolveCallbackHandler>
JITResolveCallbackHandler::create(LookupFtor Lookup, UpdateFtor Update) {
typedef JITResolveCallbackHandlerImpl<LookupFtor, UpdateFtor> Impl;
return make_unique<Impl>(std::move(Lookup), std::move(Update));
}
/// @brief Holds a list of the function names that were indirected, plus
/// mappings from each of these names to (a) the name of function
/// providing the implementation for that name (GetImplNames), and
/// (b) the name of the global variable holding the address of the
/// implementation.
///
/// This data structure can be used with a JITCallbackHandler to look up and
/// update function implementations when lazily compiling.
class JITIndirections {
public:
JITIndirections(std::vector<std::string> IndirectedNames,
std::function<std::string(StringRef)> GetImplName,
std::function<std::string(StringRef)> GetAddrName)
: IndirectedNames(std::move(IndirectedNames)),
GetImplName(std::move(GetImplName)),
GetAddrName(std::move(GetAddrName)) {}
std::vector<std::string> IndirectedNames;
std::function<std::string(StringRef Name)> GetImplName;
std::function<std::string(StringRef Name)> GetAddrName;
};
/// @brief Indirect all calls to functions matching the predicate
/// ShouldIndirect through a global variable containing the address
/// of the implementation.
///
/// @return An indirection structure containing the functions that had their
/// call-sites re-written.
///
/// For each function 'F' that meets the ShouldIndirect predicate, and that
/// is called in this Module, add a common-linkage global variable to the
/// module that will hold the address of the implementation of that function.
/// Rewrite all call-sites of 'F' to be indirect calls (via the global).
/// This allows clients, either directly or via a JITCallbackHandler, to
/// change the address of the implementation of 'F' at runtime.
///
/// Important notes:
///
/// Single indirection does not preserve pointer equality for 'F'. If the
/// program was already calling 'F' indirectly through function pointers, or
/// if it was taking the address of 'F' for the purpose of pointer comparisons
/// or arithmetic double indirection should be used instead.
///
/// This method does *not* initialize the function implementation addresses.
/// The client must do this prior to running any call-sites that have been
/// indirected.
JITIndirections makeCallsSingleIndirect(
llvm::Module &M,
const std::function<bool(const Function &)> &ShouldIndirect,
const char *JITImplSuffix, const char *JITAddrSuffix);
/// @brief Replace the body of functions matching the predicate ShouldIndirect
/// with indirect calls to the implementation.
///
/// @return An indirections structure containing the functions that had their
/// implementations re-written.
///
/// For each function 'F' that meets the ShouldIndirect predicate, add a
/// common-linkage global variable to the module that will hold the address of
/// the implementation of that function and rewrite the implementation of 'F'
/// to call through to the implementation indirectly (via the global).
/// This allows clients, either directly or via a JITCallbackHandler, to
/// change the address of the implementation of 'F' at runtime.
///
/// Important notes:
///
/// Double indirection is slower than single indirection, but preserves
/// function pointer relation tests and correct behavior for function pointers
/// (all calls to 'F', direct or indirect) go the address stored in the global
/// variable at the time of the call.
///
/// This method does *not* initialize the function implementation addresses.
/// The client must do this prior to running any call-sites that have been
/// indirected.
JITIndirections makeCallsDoubleIndirect(
llvm::Module &M,
const std::function<bool(const Function &)> &ShouldIndirect,
const char *JITImplSuffix, const char *JITAddrSuffix);
/// @brief Given a set of indirections and a symbol lookup functor, create a
/// JITResolveCallbackHandler instance that will resolve the
/// implementations for the indirected symbols on demand.
template <typename SymbolLookupFtor>
std::unique_ptr<JITResolveCallbackHandler>
createCallbackHandlerFromJITIndirections(const JITIndirections &Indirs,
const PersistentMangler &NM,
SymbolLookupFtor Lookup) {
auto GetImplName = Indirs.GetImplName;
auto GetAddrName = Indirs.GetAddrName;
std::unique_ptr<JITResolveCallbackHandler> J =
JITResolveCallbackHandler::create(
[=](const std::string &S) {
return Lookup(NM.getMangledName(GetImplName(S)));
},
[=](const std::string &S, TargetAddress Addr) {
void *ImplPtr = reinterpret_cast<void *>(
Lookup(NM.getMangledName(GetAddrName(S))));
memcpy(ImplPtr, &Addr, sizeof(TargetAddress));
});
for (const auto &FuncName : Indirs.IndirectedNames)
J->addFuncName(FuncName);
return J;
}
/// @brief Insert callback asm into module M for the symbols managed by
/// JITResolveCallbackHandler J.
void insertX86CallbackAsm(Module &M, JITResolveCallbackHandler &J);
/// @brief Initialize global indirects to point into the callback asm.
template <typename LookupFtor>
void initializeFuncAddrs(JITResolveCallbackHandler &J,
const JITIndirections &Indirs,
const PersistentMangler &NM, LookupFtor Lookup) {
// Forward declare so that we can access this, even though it's an
// implementation detail.
std::string getJITResolveCallbackIndexLabel(unsigned I);
if (J.getNumFuncs() == 0)
return;
// Force a look up one of the global addresses for a function that has been
// indirected. We need to do this to trigger the emission of the module
// holding the callback asm. We can't rely on that emission happening
// automatically when we look up the callback asm symbols, since lazy-emitting
// layers can't see those.
Lookup(NM.getMangledName(Indirs.GetAddrName(J.getFuncName(0))));
// Now update indirects to point to the JIT resolve callback asm.
for (JITResolveCallbackHandler::StubIndex I = 0; I < J.getNumFuncs(); ++I) {
TargetAddress ResolveCallbackIdxAddr =
Lookup(getJITResolveCallbackIndexLabel(I));
void *AddrPtr = reinterpret_cast<void *>(
Lookup(NM.getMangledName(Indirs.GetAddrName(J.getFuncName(I)))));
assert(AddrPtr && "Can't find stub addr global to initialize.");
memcpy(AddrPtr, &ResolveCallbackIdxAddr, sizeof(TargetAddress));
void emitResolverBlock(LLVMContext &Context) {
std::unique_ptr<Module> M(new Module("resolver_block_module",
Context));
TargetT::insertResolverBlock(*M, *this);
auto H = JIT.addModuleSet(SingletonSet(std::move(M)), nullptr);
JIT.emitAndFinalize(H);
auto ResolverBlockSymbol =
JIT.findSymbolIn(H, TargetT::ResolverBlockName, false);
assert(ResolverBlockSymbol && "Failed to insert resolver block");
ResolverBlockAddr = ResolverBlockSymbol.getAddress();
}
}
/// @brief Extract all functions matching the predicate ShouldExtract in to
/// their own modules. (Does not modify the original module.)
///
/// @return A set of modules, the first containing all symbols (including
/// globals and aliases) that did not pass ShouldExtract, and each
/// subsequent module containing one of the functions that did meet
/// ShouldExtract.
///
/// By adding the resulting modules separately (not as a set) to a
/// LazyEmittingLayer instance, compilation can be deferred until symbols are
/// actually needed.
std::vector<std::unique_ptr<llvm::Module>>
explode(const llvm::Module &OrigMod,
const std::function<bool(const Function &)> &ShouldExtract);
TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) {
if (this->AvailableTrampolines.empty())
grow(Context);
assert(!this->AvailableTrampolines.empty() &&
"Failed to grow available trampolines.");
TargetAddress TrampolineAddr = this->AvailableTrampolines.back();
this->AvailableTrampolines.pop_back();
return TrampolineAddr;
}
void grow(LLVMContext &Context) {
assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
std::unique_ptr<Module> M(new Module("trampoline_block", Context));
auto GetLabelName =
TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr,
this->NumTrampolinesPerBlock,
this->ActiveTrampolines.size());
auto H = JIT.addModuleSet(SingletonSet(std::move(M)), nullptr);
JIT.emitAndFinalize(H);
for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) {
std::string Name = GetLabelName(I);
auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false);
assert(TrampolineSymbol && "Failed to emit trampoline.");
this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress());
}
}
JITLayerT &JIT;
TargetAddress ResolverBlockAddr;
};
GlobalVariable* createImplPointer(Function &F, const Twine &Name,
Constant *Initializer);
void makeStub(Function &F, GlobalVariable &ImplPointer);
typedef std::map<Module*, DenseSet<const GlobalValue*>> ModulePartitionMap;
void partition(Module &M, const ModulePartitionMap &PMap);
/// @brief Struct for trivial "complete" partitioning of a module.
struct FullyPartitionedModule {
std::unique_ptr<Module> GlobalVars;
std::unique_ptr<Module> Commons;
std::vector<std::unique_ptr<Module>> Functions;
};
FullyPartitionedModule fullyPartition(Module &M);
/// @brief Given a module that has been indirectified, break each function
/// that has been indirected out into its own module. (Does not modify
/// the original module).
///
/// @returns A set of modules covering the symbols provided by OrigMod.
std::vector<std::unique_ptr<llvm::Module>>
explode(const llvm::Module &OrigMod, const JITIndirections &Indirections);
}
#endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H

View File

@ -18,9 +18,37 @@
namespace llvm {
/// @brief Insert callback asm into module M for the symbols managed by
/// JITResolveCallbackHandler J.
void insertX86CallbackAsm(Module &M, JITResolveCallbackHandler &J);
class OrcX86_64 {
public:
static const char *ResolverBlockName;
/// @brief Insert module-level inline callback asm into module M for the
/// symbols managed by JITResolveCallbackHandler J.
static void insertResolverBlock(
Module &M,
JITCompileCallbackManagerBase<OrcX86_64> &JCBM);
/// @brief Get a label name from the given index.
typedef std::function<std::string(unsigned)> LabelNameFtor;
static const unsigned CallSize = 6;
/// @brief Insert the requested number of trampolines into the given module.
/// @param M Module to insert the call block into.
/// @param NumCalls Number of calls to create in the call block.
/// @param StartIndex Optional argument specifying the index suffix to start
/// with.
/// @return A functor that provides the symbol name for each entry in the call
/// block.
///
static LabelNameFtor insertCompileCallbackTrampolines(
Module &M,
TargetAddress TrampolineAddr,
unsigned NumCalls,
unsigned StartIndex = 0);
};
}
#endif // LLVM_EXECUTIONENGINE_ORC_ORCTARGETSUPPORT_H

View File

@ -27,27 +27,20 @@ void llvm::copyFunctionBody(Function &New, const Function &Orig,
}
}
std::unique_ptr<Module>
llvm::CloneSubModule(const Module &M,
void llvm::CloneSubModule(llvm::Module &Dst, const Module &Src,
HandleGlobalVariableFtor HandleGlobalVariable,
HandleFunctionFtor HandleFunction, bool KeepInlineAsm) {
HandleFunctionFtor HandleFunction, bool CloneInlineAsm) {
ValueToValueMapTy VMap;
// First off, we need to create the new module.
std::unique_ptr<Module> New =
llvm::make_unique<Module>(M.getModuleIdentifier(), M.getContext());
New->setDataLayout(M.getDataLayout());
New->setTargetTriple(M.getTargetTriple());
if (KeepInlineAsm)
New->setModuleInlineAsm(M.getModuleInlineAsm());
if (CloneInlineAsm)
Dst.appendModuleInlineAsm(Src.getModuleInlineAsm());
// Copy global variables (but not initializers, yet).
for (Module::const_global_iterator I = M.global_begin(), E = M.global_end();
for (Module::const_global_iterator I = Src.global_begin(), E = Src.global_end();
I != E; ++I) {
GlobalVariable *GV = new GlobalVariable(
*New, I->getType()->getElementType(), I->isConstant(), I->getLinkage(),
Dst, I->getType()->getElementType(), I->isConstant(), I->getLinkage(),
(Constant *)nullptr, I->getName(), (GlobalVariable *)nullptr,
I->getThreadLocalMode(), I->getType()->getAddressSpace());
GV->copyAttributesFrom(I);
@ -55,21 +48,21 @@ llvm::CloneSubModule(const Module &M,
}
// Loop over the functions in the module, making external functions as before
for (Module::const_iterator I = M.begin(), E = M.end(); I != E; ++I) {
for (Module::const_iterator I = Src.begin(), E = Src.end(); I != E; ++I) {
Function *NF =
Function::Create(cast<FunctionType>(I->getType()->getElementType()),
I->getLinkage(), I->getName(), &*New);
I->getLinkage(), I->getName(), &Dst);
NF->copyAttributesFrom(I);
VMap[I] = NF;
}
// Loop over the aliases in the module
for (Module::const_alias_iterator I = M.alias_begin(), E = M.alias_end();
for (Module::const_alias_iterator I = Src.alias_begin(), E = Src.alias_end();
I != E; ++I) {
auto *PTy = cast<PointerType>(I->getType());
auto *GA =
GlobalAlias::create(PTy->getElementType(), PTy->getAddressSpace(),
I->getLinkage(), I->getName(), &*New);
I->getLinkage(), I->getName(), &Dst);
GA->copyAttributesFrom(I);
VMap[I] = GA;
}
@ -77,7 +70,7 @@ llvm::CloneSubModule(const Module &M,
// Now that all of the things that global variable initializer can refer to
// have been created, loop through and copy the global variable referrers
// over... We also set the attributes on the global now.
for (Module::const_global_iterator I = M.global_begin(), E = M.global_end();
for (Module::const_global_iterator I = Src.global_begin(), E = Src.global_end();
I != E; ++I) {
GlobalVariable &GV = *cast<GlobalVariable>(VMap[I]);
HandleGlobalVariable(GV, *I, VMap);
@ -85,13 +78,13 @@ llvm::CloneSubModule(const Module &M,
// Similarly, copy over function bodies now...
//
for (Module::const_iterator I = M.begin(), E = M.end(); I != E; ++I) {
for (Module::const_iterator I = Src.begin(), E = Src.end(); I != E; ++I) {
Function &F = *cast<Function>(VMap[I]);
HandleFunction(F, *I, VMap);
}
// And aliases
for (Module::const_alias_iterator I = M.alias_begin(), E = M.alias_end();
for (Module::const_alias_iterator I = Src.alias_begin(), E = Src.alias_end();
I != E; ++I) {
GlobalAlias *GA = cast<GlobalAlias>(VMap[I]);
if (const Constant *C = I->getAliasee())
@ -99,14 +92,13 @@ llvm::CloneSubModule(const Module &M,
}
// And named metadata....
for (Module::const_named_metadata_iterator I = M.named_metadata_begin(),
E = M.named_metadata_end();
for (Module::const_named_metadata_iterator I = Src.named_metadata_begin(),
E = Src.named_metadata_end();
I != E; ++I) {
const NamedMDNode &NMD = *I;
NamedMDNode *NewNMD = New->getOrInsertNamedMetadata(NMD.getName());
NamedMDNode *NewNMD = Dst.getOrInsertNamedMetadata(NMD.getName());
for (unsigned i = 0, e = NMD.getNumOperands(); i != e; ++i)
NewNMD->addOperand(MapMetadata(NMD.getOperand(i), VMap));
}
return New;
}

View File

@ -9,149 +9,101 @@ using namespace llvm;
namespace llvm {
JITIndirections makeCallsSingleIndirect(
Module &M, const std::function<bool(const Function &)> &ShouldIndirect,
const char *JITImplSuffix, const char *JITAddrSuffix) {
std::vector<Function *> Worklist;
std::vector<std::string> FuncNames;
for (auto &F : M)
if (ShouldIndirect(F) && (F.user_begin() != F.user_end())) {
Worklist.push_back(&F);
FuncNames.push_back(F.getName());
}
for (auto *F : Worklist) {
GlobalVariable *FImplAddr = new GlobalVariable(
M, F->getType(), false, GlobalValue::ExternalLinkage,
Constant::getNullValue(F->getType()), F->getName() + JITAddrSuffix,
nullptr, GlobalValue::NotThreadLocal, 0, true);
FImplAddr->setVisibility(GlobalValue::HiddenVisibility);
for (auto *U : F->users()) {
assert(isa<Instruction>(U) && "Cannot indirect non-instruction use");
IRBuilder<> Builder(cast<Instruction>(U));
U->replaceUsesOfWith(F, Builder.CreateLoad(FImplAddr));
}
}
return JITIndirections(
FuncNames, [=](StringRef S) -> std::string { return std::string(S); },
[=](StringRef S)
-> std::string { return std::string(S) + JITAddrSuffix; });
GlobalVariable* createImplPointer(Function &F, const Twine &Name,
Constant *Initializer) {
assert(F.getParent() && "Function isn't in a module.");
if (!Initializer)
Initializer = Constant::getNullValue(F.getType());
Module &M = *F.getParent();
return new GlobalVariable(M, F.getType(), false, GlobalValue::ExternalLinkage,
Initializer, Name, nullptr,
GlobalValue::NotThreadLocal, 0, true);
}
JITIndirections makeCallsDoubleIndirect(
Module &M, const std::function<bool(const Function &)> &ShouldIndirect,
const char *JITImplSuffix, const char *JITAddrSuffix) {
std::vector<Function *> Worklist;
std::vector<std::string> FuncNames;
for (auto &F : M)
if (!F.isDeclaration() && !F.hasAvailableExternallyLinkage() &&
ShouldIndirect(F))
Worklist.push_back(&F);
for (auto *F : Worklist) {
std::string OrigName = F->getName();
F->setName(OrigName + JITImplSuffix);
FuncNames.push_back(OrigName);
GlobalVariable *FImplAddr = new GlobalVariable(
M, F->getType(), false, GlobalValue::ExternalLinkage,
Constant::getNullValue(F->getType()), OrigName + JITAddrSuffix, nullptr,
GlobalValue::NotThreadLocal, 0, true);
FImplAddr->setVisibility(GlobalValue::HiddenVisibility);
Function *FRedirect =
Function::Create(F->getFunctionType(), F->getLinkage(), OrigName, &M);
F->replaceAllUsesWith(FRedirect);
BasicBlock *EntryBlock =
BasicBlock::Create(M.getContext(), "entry", FRedirect);
IRBuilder<> Builder(EntryBlock);
LoadInst *FImplLoadedAddr = Builder.CreateLoad(FImplAddr);
std::vector<Value *> CallArgs;
for (Value &Arg : FRedirect->args())
CallArgs.push_back(&Arg);
CallInst *Call = Builder.CreateCall(FImplLoadedAddr, CallArgs);
Call->setTailCall();
Builder.CreateRet(Call);
}
return JITIndirections(
FuncNames, [=](StringRef S)
-> std::string { return std::string(S) + JITImplSuffix; },
[=](StringRef S)
-> std::string { return std::string(S) + JITAddrSuffix; });
void makeStub(Function &F, GlobalVariable &ImplPointer) {
assert(F.isDeclaration() && "Can't turn a definition into a stub.");
assert(F.getParent() && "Function isn't in a module.");
Module &M = *F.getParent();
BasicBlock *EntryBlock = BasicBlock::Create(M.getContext(), "entry", &F);
IRBuilder<> Builder(EntryBlock);
LoadInst *ImplAddr = Builder.CreateLoad(&ImplPointer);
std::vector<Value*> CallArgs;
for (auto &A : F.args())
CallArgs.push_back(&A);
CallInst *Call = Builder.CreateCall(ImplAddr, CallArgs);
Call->setTailCall();
Builder.CreateRet(Call);
}
std::vector<std::unique_ptr<Module>>
explode(const Module &OrigMod,
const std::function<bool(const Function &)> &ShouldExtract) {
void partition(Module &M, const ModulePartitionMap &PMap) {
std::vector<std::unique_ptr<Module>> NewModules;
for (auto &KVPair : PMap) {
// Split all the globals, non-indirected functions, etc. into a single module.
auto ExtractGlobalVars = [&](GlobalVariable &New, const GlobalVariable &Orig,
ValueToValueMapTy &VMap) {
copyGVInitializer(New, Orig, VMap);
if (New.getLinkage() == GlobalValue::PrivateLinkage) {
New.setLinkage(GlobalValue::ExternalLinkage);
New.setVisibility(GlobalValue::HiddenVisibility);
}
};
auto ExtractNonImplFunctions =
[&](Function &New, const Function &Orig, ValueToValueMapTy &VMap) {
if (!ShouldExtract(New))
copyFunctionBody(New, Orig, VMap);
auto ExtractGlobalVars =
[&](GlobalVariable &New, const GlobalVariable &Orig,
ValueToValueMapTy &VMap) {
if (KVPair.second.count(&Orig)) {
copyGVInitializer(New, Orig, VMap);
}
if (New.getLinkage() == GlobalValue::PrivateLinkage) {
New.setLinkage(GlobalValue::ExternalLinkage);
New.setVisibility(GlobalValue::HiddenVisibility);
}
};
NewModules.push_back(CloneSubModule(OrigMod, ExtractGlobalVars,
ExtractNonImplFunctions, true));
auto ExtractFunctions =
[&](Function &New, const Function &Orig, ValueToValueMapTy &VMap) {
if (KVPair.second.count(&Orig))
copyFunctionBody(New, Orig, VMap);
if (New.getLinkage() == GlobalValue::InternalLinkage) {
New.setLinkage(GlobalValue::ExternalLinkage);
New.setVisibility(GlobalValue::HiddenVisibility);
}
};
// Preserve initializers for Common linkage vars, and make private linkage
// globals external: they are now provided by the globals module extracted
// above.
auto DropGlobalVars = [&](GlobalVariable &New, const GlobalVariable &Orig,
ValueToValueMapTy &VMap) {
if (New.getLinkage() == GlobalValue::CommonLinkage)
copyGVInitializer(New, Orig, VMap);
else if (New.getLinkage() == GlobalValue::PrivateLinkage)
New.setLinkage(GlobalValue::ExternalLinkage);
};
CloneSubModule(*KVPair.first, M, ExtractGlobalVars, ExtractFunctions,
false);
}
}
// Split each 'impl' function out in to its own module.
for (const auto &Func : OrigMod) {
if (Func.isDeclaration() || !ShouldExtract(Func))
FullyPartitionedModule fullyPartition(Module &M) {
FullyPartitionedModule MP;
ModulePartitionMap PMap;
for (auto &F : M) {
if (F.isDeclaration())
continue;
auto ExtractNamedFunction =
[&](Function &New, const Function &Orig, ValueToValueMapTy &VMap) {
if (New.getName() == Func.getName())
copyFunctionBody(New, Orig, VMap);
};
NewModules.push_back(
CloneSubModule(OrigMod, DropGlobalVars, ExtractNamedFunction, false));
std::string NewModuleName = (M.getName() + "." + F.getName()).str();
MP.Functions.push_back(
llvm::make_unique<Module>(NewModuleName, M.getContext()));
MP.Functions.back()->setDataLayout(M.getDataLayout());
PMap[MP.Functions.back().get()].insert(&F);
}
return NewModules;
MP.GlobalVars =
llvm::make_unique<Module>((M.getName() + ".globals_and_stubs").str(),
M.getContext());
MP.GlobalVars->setDataLayout(M.getDataLayout());
MP.Commons =
llvm::make_unique<Module>((M.getName() + ".commons").str(), M.getContext());
MP.Commons->setDataLayout(M.getDataLayout());
// Make sure there's at least an empty set for the stubs map or we'll fail
// to clone anything for it (including the decls).
PMap[MP.GlobalVars.get()] = ModulePartitionMap::mapped_type();
for (auto &GV : M.globals())
if (GV.getLinkage() == GlobalValue::CommonLinkage)
PMap[MP.Commons.get()].insert(&GV);
else
PMap[MP.GlobalVars.get()].insert(&GV);
partition(M, PMap);
return MP;
}
std::vector<std::unique_ptr<Module>>
explode(const Module &OrigMod, const JITIndirections &Indirections) {
std::set<std::string> ImplNames;
for (const auto &FuncName : Indirections.IndirectedNames)
ImplNames.insert(Indirections.GetImplName(FuncName));
return explode(
OrigMod, [&](const Function &F) { return ImplNames.count(F.getName()); });
}
}

View File

@ -1,14 +1,11 @@
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
#include "llvm/ExecutionEngine/Orc/OrcTargetSupport.h"
#include <array>
using namespace llvm;
namespace {
const char *JITCallbackFuncName = "call_jit_for_lazy_compile";
const char *JITCallbackIndexLabelPrefix = "jit_resolve_";
std::array<const char *, 12> X86GPRsToSave = {{
"rbp", "rbx", "r12", "r13", "r14", "r15", // Callee saved.
"rdi", "rsi", "rdx", "rcx", "r8", "r9", // Int args.
@ -41,61 +38,90 @@ template <typename OStream> void restoreX86Regs(OStream &OS) {
OS << " popq %" << X86GPRsToSave[X86GPRsToSave.size() - i - 1] << "\n";
}
uint64_t call_jit_for_fn(JITResolveCallbackHandler *J, uint64_t FuncIdx) {
return J->resolve(FuncIdx);
template <typename TargetT>
uint64_t executeCompileCallback(JITCompileCallbackManagerBase<TargetT> *JCBM,
TargetAddress CallbackID) {
return JCBM->executeCompileCallback(CallbackID);
}
}
namespace llvm {
std::string getJITResolveCallbackIndexLabel(unsigned I) {
std::ostringstream LabelStream;
LabelStream << JITCallbackIndexLabelPrefix << I;
return LabelStream.str();
}
const char* OrcX86_64::ResolverBlockName = "orc_resolver_block";
void insertX86CallbackAsm(Module &M, JITResolveCallbackHandler &J) {
void OrcX86_64::insertResolverBlock(
Module &M,
JITCompileCallbackManagerBase<OrcX86_64> &JCBM) {
uint64_t CallbackAddr =
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(call_jit_for_fn));
static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(executeCompileCallback<OrcX86_64>));
std::ostringstream JITCallbackAsm;
std::ostringstream AsmStream;
Triple TT(M.getTargetTriple());
if (TT.getOS() == Triple::Darwin)
JITCallbackAsm << ".section __TEXT,__text,regular,pure_instructions\n"
<< ".align 4, 0x90\n";
AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
<< ".align 4, 0x90\n";
else
JITCallbackAsm << ".text\n"
<< ".align 16, 0x90\n";
AsmStream << ".text\n"
<< ".align 16, 0x90\n";
JITCallbackAsm << "jit_object_addr:\n"
<< " .quad " << &J << "\n" << JITCallbackFuncName << ":\n";
AsmStream << "jit_callback_manager_addr:\n"
<< " .quad " << &JCBM << "\n"
<< ResolverBlockName << ":\n";
uint64_t ReturnAddrOffset = saveX86Regs(JITCallbackAsm);
uint64_t ReturnAddrOffset = saveX86Regs(AsmStream);
// Compute index, load object address, and call JIT.
JITCallbackAsm << " movq " << ReturnAddrOffset << "(%rsp), %rax\n"
<< " leaq (jit_indices_start+5)(%rip), %rbx\n"
<< " subq %rbx, %rax\n"
<< " xorq %rdx, %rdx\n"
<< " movq $5, %rbx\n"
<< " divq %rbx\n"
<< " movq %rax, %rsi\n"
<< " leaq jit_object_addr(%rip), %rdi\n"
<< " movq (%rdi), %rdi\n"
<< " movabsq $" << CallbackAddr << ", %rax\n"
<< " callq *%rax\n"
<< " movq %rax, " << ReturnAddrOffset << "(%rsp)\n";
AsmStream << " leaq jit_callback_manager_addr(%rip), %rdi\n"
<< " movq (%rdi), %rdi\n"
<< " movq " << ReturnAddrOffset << "(%rsp), %rsi\n"
<< " movabsq $" << CallbackAddr << ", %rax\n"
<< " callq *%rax\n"
<< " movq %rax, " << ReturnAddrOffset << "(%rsp)\n";
restoreX86Regs(JITCallbackAsm);
restoreX86Regs(AsmStream);
JITCallbackAsm << " retq\n"
<< "jit_indices_start:\n";
AsmStream << " retq\n";
for (JITResolveCallbackHandler::StubIndex I = 0; I < J.getNumFuncs(); ++I)
JITCallbackAsm << getJITResolveCallbackIndexLabel(I) << ":\n"
<< " callq " << JITCallbackFuncName << "\n";
M.appendModuleInlineAsm(JITCallbackAsm.str());
M.appendModuleInlineAsm(AsmStream.str());
}
OrcX86_64::LabelNameFtor
OrcX86_64::insertCompileCallbackTrampolines(Module &M,
TargetAddress ResolverBlockAddr,
unsigned NumCalls,
unsigned StartIndex) {
const char *ResolverBlockPtrName = "Lorc_resolve_block_addr";
std::ostringstream AsmStream;
Triple TT(M.getTargetTriple());
if (TT.getOS() == Triple::Darwin)
AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
<< ".align 4, 0x90\n";
else
AsmStream << ".text\n"
<< ".align 16, 0x90\n";
AsmStream << ResolverBlockPtrName << ":\n"
<< " .quad " << ResolverBlockAddr << "\n";
auto GetLabelName =
[=](unsigned I) {
std::ostringstream LabelStream;
LabelStream << "orc_jcc_" << (StartIndex + I);
return LabelStream.str();
};
for (unsigned I = 0; I < NumCalls; ++I)
AsmStream << GetLabelName(I) << ":\n"
<< " callq *" << ResolverBlockPtrName << "(%rip)\n";
M.appendModuleInlineAsm(AsmStream.str());
return GetLabelName;
}
}