jit compiler context (#3154)

This commit is contained in:
Ivan Butygin 2019-09-08 13:22:48 +03:00 committed by GitHub
parent a40c6c7fd2
commit 53cd715530
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 388 additions and 100 deletions

View file

@ -446,7 +446,7 @@ if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO")
if (NOT (LDC_LLVM_VER LESS 500)) if (NOT (LDC_LLVM_VER LESS 500))
set(LDC_DYNAMIC_COMPILE True) set(LDC_DYNAMIC_COMPILE True)
add_definitions(-DLDC_DYNAMIC_COMPILE) add_definitions(-DLDC_DYNAMIC_COMPILE)
add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=2) add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=3)
endif() endif()
endif() endif()
message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})") message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})")

View file

@ -171,7 +171,7 @@ void *resolveSymbol(llvm::JITSymbol &symbol) {
} }
} }
void generateBind(const Context &context, JITContext &jitContext, void generateBind(const Context &context, DynamicCompilerContext &jitContext,
JitModuleInfo &moduleInfo, llvm::Module &module) { JitModuleInfo &moduleInfo, llvm::Module &module) {
auto getIrFunc = [&](const void *ptr) -> llvm::Function * { auto getIrFunc = [&](const void *ptr) -> llvm::Function * {
assert(ptr != nullptr); assert(ptr != nullptr);
@ -248,7 +248,7 @@ void generateBind(const Context &context, JITContext &jitContext,
} }
} }
void applyBind(const Context &context, JITContext &jitContext, void applyBind(const Context &context, DynamicCompilerContext &jitContext,
const JitModuleInfo &moduleInfo) { const JitModuleInfo &moduleInfo) {
auto &layout = jitContext.getDataLayout(); auto &layout = jitContext.getDataLayout();
for (auto &elem : moduleInfo.getBindHandles()) { for (auto &elem : moduleInfo.getBindHandles()) {
@ -266,8 +266,11 @@ void applyBind(const Context &context, JITContext &jitContext,
} }
} }
JITContext &getJit() { DynamicCompilerContext &getJit(DynamicCompilerContext *context) {
static JITContext jit; if (context != nullptr) {
return *context;
}
static DynamicCompilerContext jit(/*mainContext*/ true);
return jit; return jit;
} }
@ -307,9 +310,9 @@ void setFunctionsTarget(llvm::Module &module, const llvm::TargetMachine &TM) {
} }
struct JitFinaliser final { struct JitFinaliser final {
JITContext &jit; DynamicCompilerContext &jit;
bool finalized = false; bool finalized = false;
explicit JitFinaliser(JITContext &j) : jit(j) {} explicit JitFinaliser(DynamicCompilerContext &j) : jit(j) {}
~JitFinaliser() { ~JitFinaliser() {
if (!finalized) { if (!finalized) {
jit.reset(); jit.reset();
@ -326,7 +329,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
return; return;
} }
interruptPoint(context, "Init"); interruptPoint(context, "Init");
JITContext &myJit = getJit(); DynamicCompilerContext &myJit = getJit(context.compilerContext);
JitModuleInfo moduleInfo(context, modlist_head); JitModuleInfo moduleInfo(context, modlist_head);
std::unique_ptr<llvm::Module> finalModule; std::unique_ptr<llvm::Module> finalModule;
@ -407,6 +410,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
} }
JitFinaliser jitFinalizer(myJit); JitFinaliser jitFinalizer(myJit);
if (myJit.isMainContext()) {
interruptPoint(context, "Resolve functions"); interruptPoint(context, "Resolve functions");
for (auto &&fun : moduleInfo.functions()) { for (auto &&fun : moduleInfo.functions()) {
if (fun.thunkVar == nullptr) { if (fun.thunkVar == nullptr) {
@ -430,6 +434,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
interruptPoint(context, "Resolved", str.c_str()); interruptPoint(context, "Resolved", str.c_str());
} }
} }
}
interruptPoint(context, "Update bind handles"); interruptPoint(context, "Update bind handles");
applyBind(context, myJit, moduleInfo); applyBind(context, myJit, moduleInfo);
jitFinalizer.finalze(); jitFinalizer.finalze();
@ -446,23 +451,34 @@ EXTERNAL void JIT_API_ENTRYPOINT(const void *modlist_head,
static_cast<const RtCompileModuleList *>(modlist_head), *context); static_cast<const RtCompileModuleList *>(modlist_head), *context);
} }
EXTERNAL void JIT_REG_BIND_PAYLOAD(void *handle, void *originalFunc, EXTERNAL void JIT_REG_BIND_PAYLOAD(class DynamicCompilerContext *context,
void *handle, void *originalFunc,
void *exampleFunc, const ParamSlice *params, void *exampleFunc, const ParamSlice *params,
size_t paramsSize) { size_t paramsSize) {
assert(handle != nullptr); assert(handle != nullptr);
assert(originalFunc != nullptr); assert(originalFunc != nullptr);
assert(exampleFunc != nullptr); assert(exampleFunc != nullptr);
JITContext &myJit = getJit(); DynamicCompilerContext &myJit = getJit(context);
myJit.registerBind(handle, originalFunc, exampleFunc, myJit.registerBind(handle, originalFunc, exampleFunc,
toArray(params, paramsSize)); toArray(params, paramsSize));
} }
EXTERNAL void JIT_UNREG_BIND_PAYLOAD(void *handle) { EXTERNAL void JIT_UNREG_BIND_PAYLOAD(class DynamicCompilerContext *context,
void *handle) {
assert(handle != nullptr); assert(handle != nullptr);
JITContext &myJit = getJit(); DynamicCompilerContext &myJit = getJit(context);
myJit.unregisterBind(handle); myJit.unregisterBind(handle);
} }
EXTERNAL DynamicCompilerContext *JIT_CREATE_COMPILER_CONTEXT() {
return new DynamicCompilerContext(false);
}
EXTERNAL void JIT_DESTROY_COMPILER_CONTEXT(DynamicCompilerContext *context) {
assert(context != nullptr);
delete context;
}
EXTERNAL bool JIT_SET_OPTS(const Slice<Slice<const char>> *args, EXTERNAL bool JIT_SET_OPTS(const Slice<Slice<const char>> *args,
void (*errs)(void *, const char *, size_t), void (*errs)(void *, const char *, size_t),
void *errsContext) { void *errsContext) {

View file

@ -36,17 +36,19 @@ enum { ApiVersion = LDC_DYNAMIC_COMPILE_API_VERSION };
#endif #endif
#define MAKE_JIT_API_CALL_IMPL(prefix, version) prefix##version #define MAKE_JIT_API_CALL_IMPL(prefix, version) prefix##version
#define MAKE_JIT_API_CALL(prefix, version) \ #define MAKE_JIT_API_CALL_IMPL1(prefix, version) \
MAKE_JIT_API_CALL_IMPL(prefix, version) MAKE_JIT_API_CALL_IMPL(prefix, version)
#define JIT_API_ENTRYPOINT \ #define MAKE_JIT_API_CALL(call) \
MAKE_JIT_API_CALL(rtCompileProcessImplSo, LDC_DYNAMIC_COMPILE_API_VERSION) MAKE_JIT_API_CALL_IMPL1(call, LDC_DYNAMIC_COMPILE_API_VERSION)
#define JIT_REG_BIND_PAYLOAD \
MAKE_JIT_API_CALL(registerBindPayloadImplSo, LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_API_ENTRYPOINT MAKE_JIT_API_CALL(rtCompileProcessImplSo)
#define JIT_UNREG_BIND_PAYLOAD \ #define JIT_REG_BIND_PAYLOAD MAKE_JIT_API_CALL(registerBindPayloadImplSo)
MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \ #define JIT_UNREG_BIND_PAYLOAD MAKE_JIT_API_CALL(unregisterBindPayloadImplSo)
LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_CREATE_COMPILER_CONTEXT \
#define JIT_SET_OPTS \ MAKE_JIT_API_CALL(createDynamicCompilerContextSo)
MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_DESTROY_COMPILER_CONTEXT \
MAKE_JIT_API_CALL(destroyDynamicCompilerContextSo)
#define JIT_SET_OPTS MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl)
typedef void (*InterruptPointHandlerT)(void *, const char *action, typedef void (*InterruptPointHandlerT)(void *, const char *action,
const char *object); const char *object);
@ -54,6 +56,8 @@ typedef void (*FatalHandlerT)(void *, const char *reason);
typedef void (*DumpHandlerT)(void *, DumpStage stage, const char *str, typedef void (*DumpHandlerT)(void *, DumpStage stage, const char *str,
std::size_t len); std::size_t len);
class DynamicCompilerContext;
struct Context final { struct Context final {
unsigned optLevel = 0; unsigned optLevel = 0;
unsigned sizeLevel = 0; unsigned sizeLevel = 0;
@ -63,4 +67,5 @@ struct Context final {
void *fatalHandlerData = nullptr; void *fatalHandlerData = nullptr;
DumpHandlerT dumpHandler = nullptr; DumpHandlerT dumpHandler = nullptr;
void *dumpHandlerData = nullptr; void *dumpHandlerData = nullptr;
DynamicCompilerContext *compilerContext = nullptr;
}; };

View file

@ -39,10 +39,23 @@ llvm::SmallVector<std::string, 4> getHostAttrs() {
return features; return features;
} }
std::unique_ptr<llvm::TargetMachine> createTargetMachine() { struct StaticInitHelper {
StaticInitHelper() {
llvm::InitializeNativeTarget(); llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetDisassembler(); llvm::InitializeNativeTargetDisassembler();
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
}
};
StaticInitHelper &staticInit() {
// Initialization may not be thread safe
// Wrap it into static dummy object initialization
static StaticInitHelper obj;
return obj;
}
std::unique_ptr<llvm::TargetMachine> createTargetMachine() {
staticInit();
std::string triple(llvm::sys::getProcessTriple()); std::string triple(llvm::sys::getProcessTriple());
std::string error; std::string error;
@ -77,17 +90,17 @@ auto getSymbolInProcess(const std::string &name)
} // anon namespace } // anon namespace
JITContext::ListenerCleaner::ListenerCleaner(JITContext &o, DynamicCompilerContext::ListenerCleaner::ListenerCleaner(
llvm::raw_ostream *stream) DynamicCompilerContext &o, llvm::raw_ostream *stream)
: owner(o) { : owner(o) {
owner.listenerlayer.getTransform().stream = stream; owner.listenerlayer.getTransform().stream = stream;
} }
JITContext::ListenerCleaner::~ListenerCleaner() { DynamicCompilerContext::ListenerCleaner::~ListenerCleaner() {
owner.listenerlayer.getTransform().stream = nullptr; owner.listenerlayer.getTransform().stream = nullptr;
} }
JITContext::JITContext() DynamicCompilerContext::DynamicCompilerContext(bool isMainContext)
: targetmachine(createTargetMachine()), : targetmachine(createTargetMachine()),
dataLayout(targetmachine->createDataLayout()), dataLayout(targetmachine->createDataLayout()),
#if LDC_LLVM_VER >= 700 #if LDC_LLVM_VER >= 700
@ -104,13 +117,15 @@ JITContext::JITContext()
[]() { return std::make_shared<llvm::SectionMemoryManager>(); }), []() { return std::make_shared<llvm::SectionMemoryManager>(); }),
#endif #endif
listenerlayer(objectLayer, ModuleListener(*targetmachine)), listenerlayer(objectLayer, ModuleListener(*targetmachine)),
compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)) { compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)),
mainContext(isMainContext) {
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
} }
JITContext::~JITContext() {} DynamicCompilerContext::~DynamicCompilerContext() {}
llvm::Error JITContext::addModule(std::unique_ptr<llvm::Module> module, llvm::Error
DynamicCompilerContext::addModule(std::unique_ptr<llvm::Module> module,
llvm::raw_ostream *asmListener) { llvm::raw_ostream *asmListener) {
assert(nullptr != module); assert(nullptr != module);
reset(); reset();
@ -141,17 +156,17 @@ llvm::Error JITContext::addModule(std::unique_ptr<llvm::Module> module,
return llvm::Error::success(); return llvm::Error::success();
} }
llvm::JITSymbol JITContext::findSymbol(const std::string &name) { llvm::JITSymbol DynamicCompilerContext::findSymbol(const std::string &name) {
return compileLayer.findSymbol(name, false); return compileLayer.findSymbol(name, false);
} }
void JITContext::clearSymMap() { symMap.clear(); } void DynamicCompilerContext::clearSymMap() { symMap.clear(); }
void JITContext::addSymbol(std::string &&name, void *value) { void DynamicCompilerContext::addSymbol(std::string &&name, void *value) {
symMap.emplace(std::make_pair(std::move(name), value)); symMap.emplace(std::make_pair(std::move(name), value));
} }
void JITContext::reset() { void DynamicCompilerContext::reset() {
if (compiled) { if (compiled) {
removeModule(moduleHandle); removeModule(moduleHandle);
moduleHandle = {}; moduleHandle = {};
@ -159,26 +174,28 @@ void JITContext::reset() {
} }
} }
void JITContext::registerBind(void *handle, void *originalFunc, void DynamicCompilerContext::registerBind(
void *exampleFunc, void *handle, void *originalFunc, void *exampleFunc,
const llvm::ArrayRef<ParamSlice> &params) { const llvm::ArrayRef<ParamSlice> &params) {
assert(bindInstances.count(handle) == 0); assert(bindInstances.count(handle) == 0);
BindDesc::ParamsVec vec(params.begin(), params.end()); BindDesc::ParamsVec vec(params.begin(), params.end());
bindInstances.insert({handle, {originalFunc, exampleFunc, std::move(vec)}}); bindInstances.insert({handle, {originalFunc, exampleFunc, std::move(vec)}});
} }
void JITContext::unregisterBind(void *handle) { void DynamicCompilerContext::unregisterBind(void *handle) {
assert(bindInstances.count(handle) == 1); assert(bindInstances.count(handle) == 1);
bindInstances.erase(handle); bindInstances.erase(handle);
} }
bool JITContext::hasBindFunction(const void *handle) const { bool DynamicCompilerContext::hasBindFunction(const void *handle) const {
assert(handle != nullptr); assert(handle != nullptr);
auto it = bindInstances.find(const_cast<void *>(handle)); auto it = bindInstances.find(const_cast<void *>(handle));
return it != bindInstances.end(); return it != bindInstances.end();
} }
void JITContext::removeModule(const ModuleHandleT &handle) { bool DynamicCompilerContext::isMainContext() const { return mainContext; }
void DynamicCompilerContext::removeModule(const ModuleHandleT &handle) {
cantFail(compileLayer.removeModule(handle)); cantFail(compileLayer.removeModule(handle));
#if LDC_LLVM_VER >= 700 #if LDC_LLVM_VER >= 700
execSession.releaseVModule(handle); execSession.releaseVModule(handle);
@ -186,7 +203,8 @@ void JITContext::removeModule(const ModuleHandleT &handle) {
} }
#if LDC_LLVM_VER >= 700 #if LDC_LLVM_VER >= 700
std::shared_ptr<llvm::orc::SymbolResolver> JITContext::createResolver() { std::shared_ptr<llvm::orc::SymbolResolver>
DynamicCompilerContext::createResolver() {
return llvm::orc::createLegacyLookupResolver( return llvm::orc::createLegacyLookupResolver(
execSession, execSession,
[this](const std::string &name) -> llvm::JITSymbol { [this](const std::string &name) -> llvm::JITSymbol {
@ -211,7 +229,8 @@ std::shared_ptr<llvm::orc::SymbolResolver> JITContext::createResolver() {
}); });
} }
#else #else
std::shared_ptr<llvm::JITSymbolResolver> JITContext::createResolver() { std::shared_ptr<llvm::JITSymbolResolver>
DynamicCompilerContext::createResolver() {
// Build our symbol resolver: // Build our symbol resolver:
// Lambda 1: Look back into the JIT itself to find symbols that are part of // Lambda 1: Look back into the JIT itself to find symbols that are part of
// the same "logical dylib". // the same "logical dylib".

View file

@ -42,7 +42,7 @@ class TargetMachine;
using SymMap = std::map<std::string, void *>; using SymMap = std::map<std::string, void *>;
class JITContext final { class DynamicCompilerContext final {
private: private:
struct ModuleListener { struct ModuleListener {
llvm::TargetMachine &targetmachine; llvm::TargetMachine &targetmachine;
@ -64,7 +64,6 @@ private:
return std::move(object); return std::move(object);
} }
}; };
llvm::llvm_shutdown_obj shutdownObj;
std::unique_ptr<llvm::TargetMachine> targetmachine; std::unique_ptr<llvm::TargetMachine> targetmachine;
const llvm::DataLayout dataLayout; const llvm::DataLayout dataLayout;
#if LDC_LLVM_VER >= 800 #if LDC_LLVM_VER >= 800
@ -72,7 +71,8 @@ private:
using ListenerLayerT = using ListenerLayerT =
llvm::orc::LegacyObjectTransformLayer<ObjectLayerT, ModuleListener>; llvm::orc::LegacyObjectTransformLayer<ObjectLayerT, ModuleListener>;
using CompileLayerT = using CompileLayerT =
llvm::orc::LegacyIRCompileLayer<ListenerLayerT, llvm::orc::SimpleCompiler>; llvm::orc::LegacyIRCompileLayer<ListenerLayerT,
llvm::orc::SimpleCompiler>;
#else #else
using ObjectLayerT = llvm::orc::RTDyldObjectLinkingLayer; using ObjectLayerT = llvm::orc::RTDyldObjectLinkingLayer;
using ListenerLayerT = using ListenerLayerT =
@ -103,16 +103,17 @@ private:
ParamsVec params; ParamsVec params;
}; };
llvm::MapVector<void *, BindDesc> bindInstances; llvm::MapVector<void *, BindDesc> bindInstances;
const bool mainContext = false;
struct ListenerCleaner final { struct ListenerCleaner final {
JITContext &owner; DynamicCompilerContext &owner;
ListenerCleaner(JITContext &o, llvm::raw_ostream *stream); ListenerCleaner(DynamicCompilerContext &o, llvm::raw_ostream *stream);
~ListenerCleaner(); ~ListenerCleaner();
}; };
public: public:
JITContext(); DynamicCompilerContext(bool isMainContext);
~JITContext(); ~DynamicCompilerContext();
llvm::TargetMachine &getTargetMachine() { return *targetmachine; } llvm::TargetMachine &getTargetMachine() { return *targetmachine; }
const llvm::DataLayout &getDataLayout() const { return dataLayout; } const llvm::DataLayout &getDataLayout() const { return dataLayout; }
@ -141,6 +142,8 @@ public:
return bindInstances; return bindInstances;
} }
bool isMainContext() const;
private: private:
void removeModule(const ModuleHandleT &handle); void removeModule(const ModuleHandleT &handle);

View file

@ -29,17 +29,21 @@ template <typename T> struct Slice final {
}; };
#define MAKE_JIT_API_CALL_IMPL(prefix, version) prefix##version #define MAKE_JIT_API_CALL_IMPL(prefix, version) prefix##version
#define MAKE_JIT_API_CALL(prefix, version) \ #define MAKE_JIT_API_CALL_IMPL1(prefix, version) \
MAKE_JIT_API_CALL_IMPL(prefix, version) MAKE_JIT_API_CALL_IMPL(prefix, version)
#define JIT_API_ENTRYPOINT \ #define MAKE_JIT_API_CALL(call) \
MAKE_JIT_API_CALL(rtCompileProcessImplSo, LDC_DYNAMIC_COMPILE_API_VERSION) MAKE_JIT_API_CALL_IMPL1(call, LDC_DYNAMIC_COMPILE_API_VERSION)
#define JIT_REG_BIND_PAYLOAD \
MAKE_JIT_API_CALL(registerBindPayloadImplSo, LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_API_ENTRYPOINT MAKE_JIT_API_CALL(rtCompileProcessImplSo)
#define JIT_UNREG_BIND_PAYLOAD \ #define JIT_REG_BIND_PAYLOAD MAKE_JIT_API_CALL(registerBindPayloadImplSo)
MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \ #define JIT_UNREG_BIND_PAYLOAD MAKE_JIT_API_CALL(unregisterBindPayloadImplSo)
LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_CREATE_COMPILER_CONTEXT \
#define JIT_SET_OPTS \ MAKE_JIT_API_CALL(createDynamicCompilerContextSo)
MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION) #define JIT_DESTROY_COMPILER_CONTEXT \
MAKE_JIT_API_CALL(destroyDynamicCompilerContextSo)
#define JIT_SET_OPTS MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl)
struct DynamicCompilerContext;
extern "C" { extern "C" {
@ -52,10 +56,16 @@ EXTERNAL void JIT_API_ENTRYPOINT(const void *modlist_head,
const Context *context, const Context *context,
std::size_t contextSize); std::size_t contextSize);
EXTERNAL void JIT_REG_BIND_PAYLOAD(void *handle, void *originalFunc, EXTERNAL void JIT_REG_BIND_PAYLOAD(DynamicCompilerContext *context,
void *handle, void *originalFunc,
const ParamSlice *desc, size_t descSize); const ParamSlice *desc, size_t descSize);
EXTERNAL void JIT_UNREG_BIND_PAYLOAD(void *handle); EXTERNAL void JIT_UNREG_BIND_PAYLOAD(DynamicCompilerContext *context,
void *handle);
EXTERNAL DynamicCompilerContext *JIT_CREATE_COMPILER_CONTEXT();
EXTERNAL void JIT_DESTROY_COMPILER_CONTEXT(DynamicCompilerContext *context);
EXTERNAL bool JIT_SET_OPTS(const Slice<Slice<const char>> *args, EXTERNAL bool JIT_SET_OPTS(const Slice<Slice<const char>> *args,
void (*errs)(void *, const char *, size_t), void (*errs)(void *, const char *, size_t),
@ -65,12 +75,22 @@ void rtCompileProcessImpl(const Context *context, std::size_t contextSize) {
JIT_API_ENTRYPOINT(dynamiccompile_modules_head, context, contextSize); JIT_API_ENTRYPOINT(dynamiccompile_modules_head, context, contextSize);
} }
void registerBindPayload(void *handle, void *originalFunc, void registerBindPayload(DynamicCompilerContext *context, void *handle,
const ParamSlice *desc, size_t descSize) { void *originalFunc, const ParamSlice *desc,
JIT_REG_BIND_PAYLOAD(handle, originalFunc, desc, descSize); size_t descSize) {
JIT_REG_BIND_PAYLOAD(context, handle, originalFunc, desc, descSize);
} }
void unregisterBindPayload(void *handle) { JIT_UNREG_BIND_PAYLOAD(handle); } void unregisterBindPayload(DynamicCompilerContext *context, void *handle) {
JIT_UNREG_BIND_PAYLOAD(context, handle);
}
DynamicCompilerContext *createDynamicCompilerContextImpl() {
return JIT_CREATE_COMPILER_CONTEXT();
}
void destroyDynamicCompilerContextImpl(DynamicCompilerContext *context) {
JIT_DESTROY_COMPILER_CONTEXT(context);
}
bool setDynamicCompilerOpts(const Slice<Slice<const char>> *args, bool setDynamicCompilerOpts(const Slice<Slice<const char>> *args,
void (*errs)(void *, const char *, size_t), void (*errs)(void *, const char *, size_t),

View file

@ -46,7 +46,9 @@ struct CompilerSettings
} }
/++ /++
+ Compile all dynamic code. + Compile all dynamic code associated with global context.
+ This includes bind objects created without explicit context and all
+ @dynamicCompile functions.
+ This function must be called before any calls to @dynamicCompile functions and + This function must be called before any calls to @dynamicCompile functions and
+ after any changes to @dynamicCompileConst variables + after any changes to @dynamicCompileConst variables
+ +
@ -85,6 +87,58 @@ void compileDynamicCode(in CompilerSettings settings = CompilerSettings.init)
rtCompileProcessImpl(context, context.sizeof); rtCompileProcessImpl(context, context.sizeof);
} }
/++
+ Compile all dynamic code associated with particular context.
+ This includes bind objects created without this context.
+ This function must be called before any calls to these bind objects.
+ Context must not be null.
+
+ This function is thread-safe as long as each thread has separate
+ instance of context
+
+ Example:
+ ---
+ import ldc.attributes, ldc.dynamic_compile;
+
+ @dynamicCompileEmit int foo(int a, int b, int c)
+ {
+ return a + b + c;
+ }
+
+ auto context = createCompilerContext();
+ scope(exit) destroyCompilerContext(context);
+
+ auto f = ldc.dynamic_compile.bind(context, &foo, 1,2,3);
+
+ CompilerSettings settings;
+ settings.optLevel = 3;
+
+ compileDynamicCode(context, settings);
+
+ assert(6 == f());
+/
void compileDynamicCode(DynamicCompilerContext ctx, in CompilerSettings settings = CompilerSettings.init)
{
assert(ctx !is null);
Context context;
context.optLevel = settings.optLevel;
context.sizeLevel = settings.sizeLevel;
context.compilerContext = ctx;
if (settings.progressHandler !is null)
{
context.interruptPointHandler = &progressHandlerWrapper;
context.interruptPointHandlerData = cast(void*)&settings.progressHandler;
}
if (settings.dumpHandler !is null)
{
context.dumpHandler = &dumpHandlerWrapper;
context.dumpHandlerData = cast(void*)&settings.dumpHandler;
}
rtCompileProcessImpl(context, context.sizeof);
}
/++ /++
+ Returns a reference-counted functional object based on a function or delegate + Returns a reference-counted functional object based on a function or delegate
+ with values bound to some parameters. + with values bound to some parameters.
@ -132,7 +186,60 @@ auto bind(F, Args...)(F func, Args args) if (isFunctionPointer!F || isDelegate!F
{ {
return context.saved_func(wrapperArgs); return context.saved_func(wrapperArgs);
} }
return bindImpl(&wrapper, Context(func), args); return bindImpl(null, &wrapper, Context(func), args);
}
/++
+ Returns a reference-counted functional object based on a function or delegate
+ with values bound to some parameters.
+ Each argument in `args` must be either a value, convertible to function parameter,
+ or `ldc.dynamic_compile.placeholder`.
+ `func` must be a pointer to a function or to a delegate.
+ The JIT runtime will generate an efficient function specialization based on `args`.
+ The passed function (or delegate) must be marked `@dynamicCompile` or
+ `@dynamicCompileEmit` to be efficiently optimized.
+
+ This version takes additional context parameter. Context must not be null.
+
+ `compileDynamicCode()` with same context must be called before making calls
+ to the returned functional object.
+
+ `toDelegate()` can be called on the returned object to get a callable delegate.
+ The returned delegate does not prolong the lifetime of the original object (and
+ thus a copy of the original object must be kept as long as this delegate is alive).
+
+ Example:
+ ---
+ @dynamicCompile int foo(int a, int b)
+ {
+ return a + b;
+ }
+
+ auto f = bind(context, &foo, 40, placeholder);
+ int delegate(int) d = f.toDelegate();
+
+ compileDynamicCode(context);
+
+ assert(f(2) == 42);
+ assert(d(2) == 42);
+/
auto bind(F, Args...)(DynamicCompilerContext ctx, F func, Args args) if (isFunctionPointer!F || isDelegate!F)
{
assert(ctx !is null);
assert(func !is null);
import std.format;
alias FuncParams = Parameters!F;
enum ParametersCount = FuncParams.length;
static assert(ParametersCount == Args.length, format("Invalid bind parameters count: %s, expected %s", Args.length, ParametersCount));
struct Context
{
F saved_func = null;
}
@dynamicCompileEmit static auto wrapper(Context context, FuncParams wrapperArgs)
{
return context.saved_func(wrapperArgs);
}
return bindImpl(ctx, &wrapper, Context(func), args);
} }
/++ /++
@ -161,7 +268,7 @@ package:
Payload* _payload = null; Payload* _payload = null;
static auto make(int[] Index, OF, Args...)(OF func, Args args) static auto make(int[] Index, OF, Args...)(DynamicCompilerContext context, OF func, Args args)
{ {
import core.exception : onOutOfMemoryError; import core.exception : onOutOfMemoryError;
import std.conv : emplace; import std.conv : emplace;
@ -176,7 +283,7 @@ package:
pureFree(payload); pureFree(payload);
} }
emplace(payload, func, args); emplace(payload, context, func, args);
payload.register(); payload.register();
BindPtr!F ret; BindPtr!F ret;
ret._payload = cast(Payload*)payload; ret._payload = cast(Payload*)payload;
@ -278,8 +385,34 @@ bool setDynamicCompilerOptions(string[] args, scope ErrsHandler errs = null)
return setDynamicCompilerOpts(&args, errsFunc, errsFuncContext); return setDynamicCompilerOpts(&args, errsFunc, errsFuncContext);
} }
pragma(LDC_no_typeinfo)
{
extern (C++, class) abstract class DynamicCompilerContext {}
}
/++
+ Create compilation context.
+ Returns newly create context.
+/
DynamicCompilerContext createCompilerContext() nothrow @nogc
{
auto ret = createDynamicCompilerContextImpl();
assert(ret !is null);
return ret;
}
/++
+ Destroy compilation context.
+ Context must not be null.
+/
void destroyCompilerContext(DynamicCompilerContext context) nothrow @nogc
{
assert(context !is null);
destroyDynamicCompilerContextImpl(context);
}
private: private:
auto bindImpl(F, Args...)(F func, Args args) auto bindImpl(F, Args...)(DynamicCompilerContext context, F func, Args args)
{ {
import std.format; import std.format;
static assert(isFunctionPointer!F, "Function pointer expected as first parameter"); static assert(isFunctionPointer!F, "Function pointer expected as first parameter");
@ -290,7 +423,7 @@ auto bindImpl(F, Args...)(F func, Args args)
enum Index = bindParamsInd!(0, 0, Args)(); enum Index = bindParamsInd!(0, 0, Args)();
alias PartialF = ReturnType!F function(UnbindTypes!(Index, FuncParams)); alias PartialF = ReturnType!F function(UnbindTypes!(Index, FuncParams));
alias BindPtrType = BindPtr!PartialF; alias BindPtrType = BindPtr!PartialF;
return BindPtrType.make!Index(func, mapBindParams!(F, 0)(args).expand); return BindPtrType.make!Index(context, func, mapBindParams!(F, 0)(args).expand);
} }
import std.meta; import std.meta;
@ -396,6 +529,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
Base base; Base base;
OF originalFunc = null; OF originalFunc = null;
DynamicCompilerContext context = null;
struct ArgStore struct ArgStore
{ {
import std.meta: staticMap; import std.meta: staticMap;
@ -406,10 +540,11 @@ struct BindPayload(OF, F, int[] Index, Args...)
ArgStore argStore; ArgStore argStore;
bool registered = false; bool registered = false;
this(OF orFunc, Args a) this(DynamicCompilerContext ctx, OF orFunc, Args a)
{ {
assert(orFunc !is null); assert(orFunc !is null);
originalFunc = orFunc; originalFunc = orFunc;
context = ctx;
static if (hasIndirections!(ArgStore)) static if (hasIndirections!(ArgStore))
{ {
pureGcAddRange(&argStore, ArgStore.sizeof); pureGcAddRange(&argStore, ArgStore.sizeof);
@ -427,7 +562,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
{ {
if (registered) if (registered)
{ {
unregisterBindPayload(&base.func); unregisterBindPayload(context, &base.func);
} }
static if (hasIndirections!(ArgStore)) static if (hasIndirections!(ArgStore))
{ {
@ -455,7 +590,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
alias Ret = ReturnType!F; alias Ret = ReturnType!F;
alias Params = Parameters!F; alias Params = Parameters!F;
@dynamicCompileEmit static Ret exampleFunc(Params) { assert(false); } @dynamicCompileEmit static Ret exampleFunc(Params) { assert(false); }
registerBindPayload(&base.func, cast(void*)originalFunc, cast(void*)&exampleFunc, desc.ptr, desc.length); registerBindPayload(context, &base.func, cast(void*)originalFunc, cast(void*)&exampleFunc, desc.ptr, desc.length);
registered = true; registered = true;
} }
@ -512,11 +647,13 @@ struct Context
void* fatalHandlerData = null; void* fatalHandlerData = null;
void function(void*, DumpStage, const char*, size_t) dumpHandler = null; void function(void*, DumpStage, const char*, size_t) dumpHandler = null;
void* dumpHandlerData = null; void* dumpHandlerData = null;
DynamicCompilerContext compilerContext = null;
} }
extern void rtCompileProcessImpl(const ref Context context, size_t contextSize); extern void rtCompileProcessImpl(const ref Context context, size_t contextSize);
extern void registerBindPayload(DynamicCompilerContext context, void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize);
extern void registerBindPayload(void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize); extern void unregisterBindPayload(DynamicCompilerContext context, void* handle);
extern void unregisterBindPayload(void* handle); extern DynamicCompilerContext createDynamicCompilerContextImpl() nothrow @nogc;
extern void destroyDynamicCompilerContextImpl(DynamicCompilerContext context) nothrow @nogc;
extern bool setDynamicCompilerOpts(const(string[])* args, void function(void*, const char*, size_t) errs, void* errsContext); extern bool setDynamicCompilerOpts(const(string[])* args, void function(void*, const char*, size_t) errs, void* errsContext);
} }

View file

@ -0,0 +1,48 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile
{
int foo(int a, int b, int c)
{
return a + b + c;
}
int bar(int a, int b, int c)
{
return a + b + c;
}
}
void main(string[] args)
{
auto context1 = createCompilerContext();
assert(context1 !is null);
scope(exit) destroyCompilerContext(context1);
auto context2 = createCompilerContext();
assert(context2 !is null);
scope(exit) destroyCompilerContext(context2);
auto f = ldc.dynamic_compile.bind(context1, &foo, 1,2,3);
auto b = ldc.dynamic_compile.bind(context2, &bar, 4,5,6);
CompilerSettings settings;
settings.optLevel = 3;
compileDynamicCode(context1, settings);
assert(f.isCallable());
assert(!b.isCallable());
assert(6 == f());
compileDynamicCode(context2, settings);
assert(f.isCallable());
assert(b.isCallable());
assert(6 == f());
assert(15 == b());
}

View file

@ -0,0 +1,40 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import std.parallelism;
import std.range;
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile
{
int foo(int a, int b, int c)
{
return a + b + c;
}
int bar(int a, int b, int c)
{
return a + b + c;
}
}
void main(string[] args)
{
foreach (i; parallel(iota(16)))
{
auto context = createCompilerContext();
assert(context !is null);
scope(exit) destroyCompilerContext(context);
auto f = ldc.dynamic_compile.bind(context, &foo, 1,2,3);
auto b = ldc.dynamic_compile.bind(context, &bar, 4,5,6);
CompilerSettings settings;
settings.optLevel = 3;
compileDynamicCode(context, settings);
assert(6 == f());
assert(15 == b());
}
}