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))
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})")

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) {
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) {

View file

@ -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;
};

View file

@ -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> &params) {
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".

View file

@ -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);

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(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),

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
+ 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);
}

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());
}
}