mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-12 22:14:54 +03:00
jit compiler context (#3154)
This commit is contained in:
parent
a40c6c7fd2
commit
53cd715530
9 changed files with 388 additions and 100 deletions
|
@ -446,7 +446,7 @@ if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO")
|
|||
if (NOT (LDC_LLVM_VER LESS 500))
|
||||
set(LDC_DYNAMIC_COMPILE True)
|
||||
add_definitions(-DLDC_DYNAMIC_COMPILE)
|
||||
add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=2)
|
||||
add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=3)
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})")
|
||||
|
|
|
@ -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) {
|
||||
auto getIrFunc = [&](const void *ptr) -> llvm::Function * {
|
||||
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) {
|
||||
auto &layout = jitContext.getDataLayout();
|
||||
for (auto &elem : moduleInfo.getBindHandles()) {
|
||||
|
@ -266,8 +266,11 @@ void applyBind(const Context &context, JITContext &jitContext,
|
|||
}
|
||||
}
|
||||
|
||||
JITContext &getJit() {
|
||||
static JITContext jit;
|
||||
DynamicCompilerContext &getJit(DynamicCompilerContext *context) {
|
||||
if (context != nullptr) {
|
||||
return *context;
|
||||
}
|
||||
static DynamicCompilerContext jit(/*mainContext*/ true);
|
||||
return jit;
|
||||
}
|
||||
|
||||
|
@ -307,9 +310,9 @@ void setFunctionsTarget(llvm::Module &module, const llvm::TargetMachine &TM) {
|
|||
}
|
||||
|
||||
struct JitFinaliser final {
|
||||
JITContext &jit;
|
||||
DynamicCompilerContext &jit;
|
||||
bool finalized = false;
|
||||
explicit JitFinaliser(JITContext &j) : jit(j) {}
|
||||
explicit JitFinaliser(DynamicCompilerContext &j) : jit(j) {}
|
||||
~JitFinaliser() {
|
||||
if (!finalized) {
|
||||
jit.reset();
|
||||
|
@ -326,7 +329,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
|
|||
return;
|
||||
}
|
||||
interruptPoint(context, "Init");
|
||||
JITContext &myJit = getJit();
|
||||
DynamicCompilerContext &myJit = getJit(context.compilerContext);
|
||||
|
||||
JitModuleInfo moduleInfo(context, modlist_head);
|
||||
std::unique_ptr<llvm::Module> finalModule;
|
||||
|
@ -407,6 +410,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
|
|||
}
|
||||
|
||||
JitFinaliser jitFinalizer(myJit);
|
||||
if (myJit.isMainContext()) {
|
||||
interruptPoint(context, "Resolve functions");
|
||||
for (auto &&fun : moduleInfo.functions()) {
|
||||
if (fun.thunkVar == nullptr) {
|
||||
|
@ -430,6 +434,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
|
|||
interruptPoint(context, "Resolved", str.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
interruptPoint(context, "Update bind handles");
|
||||
applyBind(context, myJit, moduleInfo);
|
||||
jitFinalizer.finalze();
|
||||
|
@ -446,23 +451,34 @@ EXTERNAL void JIT_API_ENTRYPOINT(const void *modlist_head,
|
|||
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,
|
||||
size_t paramsSize) {
|
||||
assert(handle != nullptr);
|
||||
assert(originalFunc != nullptr);
|
||||
assert(exampleFunc != nullptr);
|
||||
JITContext &myJit = getJit();
|
||||
DynamicCompilerContext &myJit = getJit(context);
|
||||
myJit.registerBind(handle, originalFunc, exampleFunc,
|
||||
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);
|
||||
JITContext &myJit = getJit();
|
||||
DynamicCompilerContext &myJit = getJit(context);
|
||||
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,
|
||||
void (*errs)(void *, const char *, size_t),
|
||||
void *errsContext) {
|
||||
|
|
|
@ -36,17 +36,19 @@ enum { ApiVersion = LDC_DYNAMIC_COMPILE_API_VERSION };
|
|||
#endif
|
||||
|
||||
#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)
|
||||
#define JIT_API_ENTRYPOINT \
|
||||
MAKE_JIT_API_CALL(rtCompileProcessImplSo, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_REG_BIND_PAYLOAD \
|
||||
MAKE_JIT_API_CALL(registerBindPayloadImplSo, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_UNREG_BIND_PAYLOAD \
|
||||
MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \
|
||||
LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_SET_OPTS \
|
||||
MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define MAKE_JIT_API_CALL(call) \
|
||||
MAKE_JIT_API_CALL_IMPL1(call, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
|
||||
#define JIT_API_ENTRYPOINT MAKE_JIT_API_CALL(rtCompileProcessImplSo)
|
||||
#define JIT_REG_BIND_PAYLOAD MAKE_JIT_API_CALL(registerBindPayloadImplSo)
|
||||
#define JIT_UNREG_BIND_PAYLOAD MAKE_JIT_API_CALL(unregisterBindPayloadImplSo)
|
||||
#define JIT_CREATE_COMPILER_CONTEXT \
|
||||
MAKE_JIT_API_CALL(createDynamicCompilerContextSo)
|
||||
#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,
|
||||
const char *object);
|
||||
|
@ -54,6 +56,8 @@ typedef void (*FatalHandlerT)(void *, const char *reason);
|
|||
typedef void (*DumpHandlerT)(void *, DumpStage stage, const char *str,
|
||||
std::size_t len);
|
||||
|
||||
class DynamicCompilerContext;
|
||||
|
||||
struct Context final {
|
||||
unsigned optLevel = 0;
|
||||
unsigned sizeLevel = 0;
|
||||
|
@ -63,4 +67,5 @@ struct Context final {
|
|||
void *fatalHandlerData = nullptr;
|
||||
DumpHandlerT dumpHandler = nullptr;
|
||||
void *dumpHandlerData = nullptr;
|
||||
DynamicCompilerContext *compilerContext = nullptr;
|
||||
};
|
||||
|
|
|
@ -39,10 +39,23 @@ llvm::SmallVector<std::string, 4> getHostAttrs() {
|
|||
return features;
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::TargetMachine> createTargetMachine() {
|
||||
struct StaticInitHelper {
|
||||
StaticInitHelper() {
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetDisassembler();
|
||||
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 error;
|
||||
|
@ -77,17 +90,17 @@ auto getSymbolInProcess(const std::string &name)
|
|||
|
||||
} // anon namespace
|
||||
|
||||
JITContext::ListenerCleaner::ListenerCleaner(JITContext &o,
|
||||
llvm::raw_ostream *stream)
|
||||
DynamicCompilerContext::ListenerCleaner::ListenerCleaner(
|
||||
DynamicCompilerContext &o, llvm::raw_ostream *stream)
|
||||
: owner(o) {
|
||||
owner.listenerlayer.getTransform().stream = stream;
|
||||
}
|
||||
|
||||
JITContext::ListenerCleaner::~ListenerCleaner() {
|
||||
DynamicCompilerContext::ListenerCleaner::~ListenerCleaner() {
|
||||
owner.listenerlayer.getTransform().stream = nullptr;
|
||||
}
|
||||
|
||||
JITContext::JITContext()
|
||||
DynamicCompilerContext::DynamicCompilerContext(bool isMainContext)
|
||||
: targetmachine(createTargetMachine()),
|
||||
dataLayout(targetmachine->createDataLayout()),
|
||||
#if LDC_LLVM_VER >= 700
|
||||
|
@ -104,13 +117,15 @@ JITContext::JITContext()
|
|||
[]() { return std::make_shared<llvm::SectionMemoryManager>(); }),
|
||||
#endif
|
||||
listenerlayer(objectLayer, ModuleListener(*targetmachine)),
|
||||
compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)) {
|
||||
compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)),
|
||||
mainContext(isMainContext) {
|
||||
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) {
|
||||
assert(nullptr != module);
|
||||
reset();
|
||||
|
@ -141,17 +156,17 @@ llvm::Error JITContext::addModule(std::unique_ptr<llvm::Module> module,
|
|||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void JITContext::reset() {
|
||||
void DynamicCompilerContext::reset() {
|
||||
if (compiled) {
|
||||
removeModule(moduleHandle);
|
||||
moduleHandle = {};
|
||||
|
@ -159,26 +174,28 @@ void JITContext::reset() {
|
|||
}
|
||||
}
|
||||
|
||||
void JITContext::registerBind(void *handle, void *originalFunc,
|
||||
void *exampleFunc,
|
||||
void DynamicCompilerContext::registerBind(
|
||||
void *handle, void *originalFunc, void *exampleFunc,
|
||||
const llvm::ArrayRef<ParamSlice> ¶ms) {
|
||||
assert(bindInstances.count(handle) == 0);
|
||||
BindDesc::ParamsVec vec(params.begin(), params.end());
|
||||
bindInstances.insert({handle, {originalFunc, exampleFunc, std::move(vec)}});
|
||||
}
|
||||
|
||||
void JITContext::unregisterBind(void *handle) {
|
||||
void DynamicCompilerContext::unregisterBind(void *handle) {
|
||||
assert(bindInstances.count(handle) == 1);
|
||||
bindInstances.erase(handle);
|
||||
}
|
||||
|
||||
bool JITContext::hasBindFunction(const void *handle) const {
|
||||
bool DynamicCompilerContext::hasBindFunction(const void *handle) const {
|
||||
assert(handle != nullptr);
|
||||
auto it = bindInstances.find(const_cast<void *>(handle));
|
||||
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));
|
||||
#if LDC_LLVM_VER >= 700
|
||||
execSession.releaseVModule(handle);
|
||||
|
@ -186,7 +203,8 @@ void JITContext::removeModule(const ModuleHandleT &handle) {
|
|||
}
|
||||
|
||||
#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(
|
||||
execSession,
|
||||
[this](const std::string &name) -> llvm::JITSymbol {
|
||||
|
@ -211,7 +229,8 @@ std::shared_ptr<llvm::orc::SymbolResolver> JITContext::createResolver() {
|
|||
});
|
||||
}
|
||||
#else
|
||||
std::shared_ptr<llvm::JITSymbolResolver> JITContext::createResolver() {
|
||||
std::shared_ptr<llvm::JITSymbolResolver>
|
||||
DynamicCompilerContext::createResolver() {
|
||||
// Build our symbol resolver:
|
||||
// Lambda 1: Look back into the JIT itself to find symbols that are part of
|
||||
// the same "logical dylib".
|
||||
|
|
|
@ -42,7 +42,7 @@ class TargetMachine;
|
|||
|
||||
using SymMap = std::map<std::string, void *>;
|
||||
|
||||
class JITContext final {
|
||||
class DynamicCompilerContext final {
|
||||
private:
|
||||
struct ModuleListener {
|
||||
llvm::TargetMachine &targetmachine;
|
||||
|
@ -64,7 +64,6 @@ private:
|
|||
return std::move(object);
|
||||
}
|
||||
};
|
||||
llvm::llvm_shutdown_obj shutdownObj;
|
||||
std::unique_ptr<llvm::TargetMachine> targetmachine;
|
||||
const llvm::DataLayout dataLayout;
|
||||
#if LDC_LLVM_VER >= 800
|
||||
|
@ -72,7 +71,8 @@ private:
|
|||
using ListenerLayerT =
|
||||
llvm::orc::LegacyObjectTransformLayer<ObjectLayerT, ModuleListener>;
|
||||
using CompileLayerT =
|
||||
llvm::orc::LegacyIRCompileLayer<ListenerLayerT, llvm::orc::SimpleCompiler>;
|
||||
llvm::orc::LegacyIRCompileLayer<ListenerLayerT,
|
||||
llvm::orc::SimpleCompiler>;
|
||||
#else
|
||||
using ObjectLayerT = llvm::orc::RTDyldObjectLinkingLayer;
|
||||
using ListenerLayerT =
|
||||
|
@ -103,16 +103,17 @@ private:
|
|||
ParamsVec params;
|
||||
};
|
||||
llvm::MapVector<void *, BindDesc> bindInstances;
|
||||
const bool mainContext = false;
|
||||
|
||||
struct ListenerCleaner final {
|
||||
JITContext &owner;
|
||||
ListenerCleaner(JITContext &o, llvm::raw_ostream *stream);
|
||||
DynamicCompilerContext &owner;
|
||||
ListenerCleaner(DynamicCompilerContext &o, llvm::raw_ostream *stream);
|
||||
~ListenerCleaner();
|
||||
};
|
||||
|
||||
public:
|
||||
JITContext();
|
||||
~JITContext();
|
||||
DynamicCompilerContext(bool isMainContext);
|
||||
~DynamicCompilerContext();
|
||||
|
||||
llvm::TargetMachine &getTargetMachine() { return *targetmachine; }
|
||||
const llvm::DataLayout &getDataLayout() const { return dataLayout; }
|
||||
|
@ -141,6 +142,8 @@ public:
|
|||
return bindInstances;
|
||||
}
|
||||
|
||||
bool isMainContext() const;
|
||||
|
||||
private:
|
||||
void removeModule(const ModuleHandleT &handle);
|
||||
|
||||
|
|
|
@ -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(prefix, version) \
|
||||
#define MAKE_JIT_API_CALL_IMPL1(prefix, version) \
|
||||
MAKE_JIT_API_CALL_IMPL(prefix, version)
|
||||
#define JIT_API_ENTRYPOINT \
|
||||
MAKE_JIT_API_CALL(rtCompileProcessImplSo, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_REG_BIND_PAYLOAD \
|
||||
MAKE_JIT_API_CALL(registerBindPayloadImplSo, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_UNREG_BIND_PAYLOAD \
|
||||
MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \
|
||||
LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define JIT_SET_OPTS \
|
||||
MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
#define MAKE_JIT_API_CALL(call) \
|
||||
MAKE_JIT_API_CALL_IMPL1(call, LDC_DYNAMIC_COMPILE_API_VERSION)
|
||||
|
||||
#define JIT_API_ENTRYPOINT MAKE_JIT_API_CALL(rtCompileProcessImplSo)
|
||||
#define JIT_REG_BIND_PAYLOAD MAKE_JIT_API_CALL(registerBindPayloadImplSo)
|
||||
#define JIT_UNREG_BIND_PAYLOAD MAKE_JIT_API_CALL(unregisterBindPayloadImplSo)
|
||||
#define JIT_CREATE_COMPILER_CONTEXT \
|
||||
MAKE_JIT_API_CALL(createDynamicCompilerContextSo)
|
||||
#define JIT_DESTROY_COMPILER_CONTEXT \
|
||||
MAKE_JIT_API_CALL(destroyDynamicCompilerContextSo)
|
||||
#define JIT_SET_OPTS MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl)
|
||||
|
||||
struct DynamicCompilerContext;
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
@ -52,10 +56,16 @@ EXTERNAL void JIT_API_ENTRYPOINT(const void *modlist_head,
|
|||
const Context *context,
|
||||
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);
|
||||
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
void registerBindPayload(void *handle, void *originalFunc,
|
||||
const ParamSlice *desc, size_t descSize) {
|
||||
JIT_REG_BIND_PAYLOAD(handle, originalFunc, desc, descSize);
|
||||
void registerBindPayload(DynamicCompilerContext *context, void *handle,
|
||||
void *originalFunc, const ParamSlice *desc,
|
||||
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,
|
||||
void (*errs)(void *, const char *, size_t),
|
||||
|
|
|
@ -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
|
||||
+ after any changes to @dynamicCompileConst variables
|
||||
+
|
||||
|
@ -85,6 +87,58 @@ void compileDynamicCode(in CompilerSettings settings = CompilerSettings.init)
|
|||
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
|
||||
+ 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 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;
|
||||
|
||||
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 std.conv : emplace;
|
||||
|
@ -176,7 +283,7 @@ package:
|
|||
pureFree(payload);
|
||||
}
|
||||
|
||||
emplace(payload, func, args);
|
||||
emplace(payload, context, func, args);
|
||||
payload.register();
|
||||
BindPtr!F ret;
|
||||
ret._payload = cast(Payload*)payload;
|
||||
|
@ -278,8 +385,34 @@ bool setDynamicCompilerOptions(string[] args, scope ErrsHandler errs = null)
|
|||
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:
|
||||
auto bindImpl(F, Args...)(F func, Args args)
|
||||
auto bindImpl(F, Args...)(DynamicCompilerContext context, F func, Args args)
|
||||
{
|
||||
import std.format;
|
||||
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)();
|
||||
alias PartialF = ReturnType!F function(UnbindTypes!(Index, FuncParams));
|
||||
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;
|
||||
|
@ -396,6 +529,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
|
|||
|
||||
Base base;
|
||||
OF originalFunc = null;
|
||||
DynamicCompilerContext context = null;
|
||||
struct ArgStore
|
||||
{
|
||||
import std.meta: staticMap;
|
||||
|
@ -406,10 +540,11 @@ struct BindPayload(OF, F, int[] Index, Args...)
|
|||
ArgStore argStore;
|
||||
bool registered = false;
|
||||
|
||||
this(OF orFunc, Args a)
|
||||
this(DynamicCompilerContext ctx, OF orFunc, Args a)
|
||||
{
|
||||
assert(orFunc !is null);
|
||||
originalFunc = orFunc;
|
||||
context = ctx;
|
||||
static if (hasIndirections!(ArgStore))
|
||||
{
|
||||
pureGcAddRange(&argStore, ArgStore.sizeof);
|
||||
|
@ -427,7 +562,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
|
|||
{
|
||||
if (registered)
|
||||
{
|
||||
unregisterBindPayload(&base.func);
|
||||
unregisterBindPayload(context, &base.func);
|
||||
}
|
||||
static if (hasIndirections!(ArgStore))
|
||||
{
|
||||
|
@ -455,7 +590,7 @@ struct BindPayload(OF, F, int[] Index, Args...)
|
|||
alias Ret = ReturnType!F;
|
||||
alias Params = Parameters!F;
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -512,11 +647,13 @@ struct Context
|
|||
void* fatalHandlerData = null;
|
||||
void function(void*, DumpStage, const char*, size_t) dumpHandler = null;
|
||||
void* dumpHandlerData = null;
|
||||
DynamicCompilerContext compilerContext = null;
|
||||
}
|
||||
extern void rtCompileProcessImpl(const ref Context context, size_t contextSize);
|
||||
|
||||
extern void registerBindPayload(void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize);
|
||||
extern void unregisterBindPayload(void* handle);
|
||||
extern void registerBindPayload(DynamicCompilerContext context, void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize);
|
||||
extern void unregisterBindPayload(DynamicCompilerContext context, 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);
|
||||
}
|
||||
|
||||
|
|
48
tests/dynamiccompile/compiler_context.d
Normal file
48
tests/dynamiccompile/compiler_context.d
Normal 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());
|
||||
}
|
40
tests/dynamiccompile/compiler_context_parallel.d
Normal file
40
tests/dynamiccompile/compiler_context_parallel.d
Normal 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());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue