mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-11 05:16:19 +03:00
@dynamicCompileEmit attribute (#2747)
This commit is contained in:
parent
00c3e89933
commit
cbeb2b45d6
9 changed files with 101 additions and 28 deletions
1
dmd/id.d
1
dmd/id.d
|
@ -465,6 +465,7 @@ immutable Msgtable[] msgtable =
|
|||
{ "udaKernel", "_kernel" },
|
||||
{ "udaDynamicCompile", "_dynamicCompile" },
|
||||
{ "udaDynamicCompileConst", "_dynamicCompileConst" },
|
||||
{ "udaDynamicCompileEmit", "_dynamicCompileEmit" },
|
||||
|
||||
// IN_LLVM: DCompute specific types and functionss
|
||||
{ "dcompute" },
|
||||
|
|
1
dmd/id.h
1
dmd/id.h
|
@ -87,6 +87,7 @@ public:
|
|||
static Identifier *udaCompute;
|
||||
static Identifier *udaDynamicCompile;
|
||||
static Identifier *udaDynamicCompileConst;
|
||||
static Identifier *udaDynamicCompileEmit;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -192,10 +192,11 @@ void fixRtModule(llvm::Module &newModule,
|
|||
std::unordered_set<std::string> externalFuncs;
|
||||
for (auto &&it : funcs) {
|
||||
assert(nullptr != it.first);
|
||||
assert(nullptr != it.second.thunkFunc);
|
||||
if (nullptr == it.second.thunkVar) {
|
||||
// thunkVar is not available
|
||||
// e.g. runtimeCompile function from other module, ignore
|
||||
if (nullptr == it.second.thunkVar ||
|
||||
nullptr == it.second.thunkFunc) {
|
||||
// thunkVar or thunkFunc is not available
|
||||
// e.g. dynamicCompile function from other module or emit-only dynamic
|
||||
// function, ignore
|
||||
continue;
|
||||
}
|
||||
assert(!contains(thunkVar2func, it.second.thunkVar->getName()));
|
||||
|
@ -225,11 +226,12 @@ void fixRtModule(llvm::Module &newModule,
|
|||
// Thunks should be unused now, strip them
|
||||
for (auto &&it : funcs) {
|
||||
assert(nullptr != it.first);
|
||||
assert(nullptr != it.second.thunkFunc);
|
||||
auto func = newModule.getFunction(it.second.thunkFunc->getName());
|
||||
assert(func != nullptr);
|
||||
if (func->use_empty()) {
|
||||
func->eraseFromParent();
|
||||
if (nullptr != it.second.thunkFunc) {
|
||||
auto func = newModule.getFunction(it.second.thunkFunc->getName());
|
||||
assert(func != nullptr);
|
||||
if (func->use_empty()) {
|
||||
func->eraseFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (nullptr != it.second.thunkVar) {
|
||||
|
@ -525,7 +527,6 @@ generateFuncList(IRState *irs, const Types &types) {
|
|||
std::vector<llvm::Constant *> elements;
|
||||
for (auto &&it : irs->dynamicCompiledFunctions) {
|
||||
assert(nullptr != it.first);
|
||||
assert(nullptr != it.second.thunkFunc);
|
||||
if (nullptr == it.second.thunkVar) {
|
||||
// thunkVar is not available
|
||||
// e.g. runtimeCompile function from other module, ignore
|
||||
|
@ -775,9 +776,13 @@ void declareDynamicCompiledFunction(IRState *irs, IrFunction *func) {
|
|||
if (!opts::enableDynamicCompile) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto srcFunc = func->getLLVMFunc();
|
||||
auto thunkFunc = duplicateFunc(irs->module, srcFunc);
|
||||
func->rtCompileFunc = thunkFunc;
|
||||
llvm::Function *thunkFunc = nullptr;
|
||||
if (func->dynamicCompile) {
|
||||
thunkFunc = duplicateFunc(irs->module, srcFunc);
|
||||
func->rtCompileFunc = thunkFunc;
|
||||
}
|
||||
assert(!contains(irs->dynamicCompiledFunctions, srcFunc));
|
||||
irs->dynamicCompiledFunctions.insert(
|
||||
std::make_pair(srcFunc, IRState::RtCompiledFuncDesc{nullptr, thunkFunc}));
|
||||
|
@ -787,21 +792,24 @@ void defineDynamicCompiledFunction(IRState *irs, IrFunction *func) {
|
|||
assert(nullptr != irs);
|
||||
assert(nullptr != func);
|
||||
assert(nullptr != func->getLLVMFunc());
|
||||
assert(nullptr != func->rtCompileFunc);
|
||||
if (!opts::enableDynamicCompile) {
|
||||
return;
|
||||
}
|
||||
auto srcFunc = func->getLLVMFunc();
|
||||
auto it = irs->dynamicCompiledFunctions.find(srcFunc);
|
||||
assert(irs->dynamicCompiledFunctions.end() != it);
|
||||
auto thunkVarType = srcFunc->getFunctionType()->getPointerTo();
|
||||
auto thunkVar = new llvm::GlobalVariable(
|
||||
irs->module, thunkVarType, false, llvm::GlobalValue::PrivateLinkage,
|
||||
llvm::ConstantPointerNull::get(thunkVarType),
|
||||
".rtcompile_thunkvar_" + srcFunc->getName());
|
||||
auto dstFunc = it->second.thunkFunc;
|
||||
createThunkFunc(irs->module, srcFunc, dstFunc, thunkVar);
|
||||
it->second.thunkVar = thunkVar;
|
||||
|
||||
if (func->dynamicCompile) {
|
||||
assert(nullptr != func->rtCompileFunc);
|
||||
auto srcFunc = func->getLLVMFunc();
|
||||
auto it = irs->dynamicCompiledFunctions.find(srcFunc);
|
||||
assert(irs->dynamicCompiledFunctions.end() != it);
|
||||
auto thunkVarType = srcFunc->getFunctionType()->getPointerTo();
|
||||
auto thunkVar = new llvm::GlobalVariable(
|
||||
irs->module, thunkVarType, false, llvm::GlobalValue::PrivateLinkage,
|
||||
llvm::ConstantPointerNull::get(thunkVarType),
|
||||
".rtcompile_thunkvar_" + srcFunc->getName());
|
||||
auto dstFunc = it->second.thunkFunc;
|
||||
createThunkFunc(irs->module, srcFunc, dstFunc, thunkVar);
|
||||
it->second.thunkVar = thunkVar;
|
||||
}
|
||||
}
|
||||
|
||||
void addDynamicCompiledVar(IRState *irs, IrGlobal *var) {
|
||||
|
|
|
@ -584,7 +584,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
|||
applyTargetMachineAttributes(*func, *gTargetMachine);
|
||||
applyFuncDeclUDAs(fdecl, irFunc);
|
||||
|
||||
if(irFunc->dynamicCompile) {
|
||||
if(irFunc->isDynamicCompiled()) {
|
||||
declareDynamicCompiledFunction(gIR, irFunc);
|
||||
}
|
||||
|
||||
|
@ -976,7 +976,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
|||
}
|
||||
|
||||
SCOPE_EXIT {
|
||||
if (irFunc->dynamicCompile) {
|
||||
if (irFunc->isDynamicCompiled()) {
|
||||
defineDynamicCompiledFunction(gIR, irFunc);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -376,7 +376,8 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
|
|||
applyAttrAssumeUsed(*gIR, sle, gvar);
|
||||
} else if (ident == Id::udaWeak) {
|
||||
// @weak is applied elsewhere
|
||||
} else if (ident == Id::udaDynamicCompile) {
|
||||
} else if (ident == Id::udaDynamicCompile ||
|
||||
ident == Id::udaDynamicCompileEmit) {
|
||||
sle->error(
|
||||
"Special attribute `ldc.attributes.%s` is only valid for functions",
|
||||
ident->toChars());
|
||||
|
@ -423,6 +424,8 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
|
|||
// @weak and @kernel are applied elsewhere
|
||||
} else if (ident == Id::udaDynamicCompile) {
|
||||
irFunc->dynamicCompile = true;
|
||||
} else if (ident == Id::udaDynamicCompileEmit) {
|
||||
irFunc->dynamicCompileEmit = true;
|
||||
} else if (ident == Id::udaDynamicCompileConst) {
|
||||
sle->error(
|
||||
"Special attribute `ldc.attributes.%s` is only valid for variables",
|
||||
|
|
|
@ -80,6 +80,10 @@ llvm::Function *IrFunction::getLLVMCallee() const {
|
|||
return rtCompileFunc != nullptr ? rtCompileFunc : func;
|
||||
}
|
||||
|
||||
bool IrFunction::isDynamicCompiled() const {
|
||||
return dynamicCompile || dynamicCompileEmit;
|
||||
}
|
||||
|
||||
IrFunction *getIrFunc(FuncDeclaration *decl, bool create) {
|
||||
if (!isIrFuncCreated(decl) && create) {
|
||||
assert(decl->ir->irFunc == NULL);
|
||||
|
|
|
@ -50,6 +50,8 @@ struct IrFunction {
|
|||
/// some sort of wrapper, e.g., a JIT wrapper).
|
||||
llvm::Function *getLLVMCallee() const;
|
||||
|
||||
bool isDynamicCompiled() const;
|
||||
|
||||
FuncDeclaration *decl = nullptr;
|
||||
TypeFunction *type = nullptr;
|
||||
|
||||
|
@ -90,6 +92,9 @@ struct IrFunction {
|
|||
/// This functions was marked for dynamic compilation
|
||||
bool dynamicCompile = false;
|
||||
|
||||
/// This functions was marked emit-only for dynamic compilation
|
||||
bool dynamicCompileEmit = false;
|
||||
|
||||
/// Dynamic compilation thunk, all attempts to call or take address of the
|
||||
/// original function will be redirected to it
|
||||
llvm::Function *rtCompileFunc = nullptr;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 41e9dece3fc6c2bc966cfa327ae8160f67a4c569
|
||||
Subproject commit cefb4fc03c5cf8d4ee6f7644b126ddaf5b86c7a1
|
51
tests/dynamiccompile/emit.d
Normal file
51
tests/dynamiccompile/emit.d
Normal file
|
@ -0,0 +1,51 @@
|
|||
|
||||
// RUN: %ldc -enable-dynamic-compile -run %s
|
||||
|
||||
import std.stdio;
|
||||
import std.array;
|
||||
import std.string;
|
||||
import ldc.attributes;
|
||||
import ldc.dynamic_compile;
|
||||
|
||||
int foo()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
@dynamicCompileEmit int bar()
|
||||
{
|
||||
return 43;
|
||||
}
|
||||
|
||||
@dynamicCompileEmit @dynamicCompile int baz()
|
||||
{
|
||||
return 44;
|
||||
}
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
assert(43 == bar());
|
||||
|
||||
auto dump = appender!string();
|
||||
CompilerSettings settings;
|
||||
settings.dumpHandler = (DumpStage stage, in char[] str)
|
||||
{
|
||||
if (DumpStage.OriginalModule == stage)
|
||||
{
|
||||
write(str);
|
||||
dump.put(str);
|
||||
}
|
||||
};
|
||||
writeln("===========================================");
|
||||
compileDynamicCode(settings);
|
||||
writeln();
|
||||
writeln("===========================================");
|
||||
stdout.flush();
|
||||
|
||||
assert(44 == baz());
|
||||
|
||||
// Check function name in original IR
|
||||
assert(count(dump.data, foo.mangleof) == 0);
|
||||
assert(count(dump.data, bar.mangleof) > 0);
|
||||
assert(count(dump.data, baz.mangleof) > 0);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue