mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-06 19:06:02 +03:00
D dynamic compilation support
This commit is contained in:
parent
1c2271d00d
commit
42f283c221
41 changed files with 2058 additions and 9 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,6 +1,7 @@
|
||||||
[submodule "druntime"]
|
[submodule "druntime"]
|
||||||
path = runtime/druntime
|
path = runtime/druntime
|
||||||
url = https://github.com/ldc-developers/druntime.git
|
url = https://github.com/Hardcode84/druntime.git
|
||||||
|
branch = runtime_compile
|
||||||
[submodule "phobos"]
|
[submodule "phobos"]
|
||||||
path = runtime/phobos
|
path = runtime/phobos
|
||||||
url = https://github.com/ldc-developers/phobos.git
|
url = https://github.com/ldc-developers/phobos.git
|
||||||
|
|
|
@ -414,6 +414,17 @@ set(LDC_WITH_PGO True) # must be a valid Python boolean constant (case sensitiv
|
||||||
message(STATUS "Building LDC with PGO support")
|
message(STATUS "Building LDC with PGO support")
|
||||||
append("-DLDC_WITH_PGO" LDC_CXXFLAGS)
|
append("-DLDC_WITH_PGO" LDC_CXXFLAGS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Enable Runtime compilation if supported for this platform and LLVM version.
|
||||||
|
# LLVM >= 3.9 is required
|
||||||
|
#
|
||||||
|
set(LDC_RUNTIME_COMPILE False) # must be a valid Python boolean constant (case sensitive)
|
||||||
|
if (NOT (LDC_LLVM_VER LESS 400))
|
||||||
|
message(STATUS "Building LDC with runtime compilation support")
|
||||||
|
add_definitions(-DLDC_RUNTIME_COMPILE)
|
||||||
|
set(LDC_RUNTIME_COMPILE True)
|
||||||
|
endif()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Includes, defines.
|
# Includes, defines.
|
||||||
#
|
#
|
||||||
|
|
|
@ -234,6 +234,8 @@ struct Param
|
||||||
uint32_t hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)
|
uint32_t hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)
|
||||||
|
|
||||||
bool outputSourceLocations; // if true, output line tables.
|
bool outputSourceLocations; // if true, output line tables.
|
||||||
|
|
||||||
|
bool enableRuntimeCompile;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -412,6 +412,7 @@ Msgtable[] msgtable =
|
||||||
{ "udaWeak", "_weak" },
|
{ "udaWeak", "_weak" },
|
||||||
{ "udaCompute", "compute" },
|
{ "udaCompute", "compute" },
|
||||||
{ "udaKernel", "_kernel" },
|
{ "udaKernel", "_kernel" },
|
||||||
|
{ "udaRuntimeCompile", "_runtimeCompile" },
|
||||||
|
|
||||||
// IN_LLVM: DCompute specific types and functionss
|
// IN_LLVM: DCompute specific types and functionss
|
||||||
{ "dcompute" },
|
{ "dcompute" },
|
||||||
|
|
|
@ -521,6 +521,14 @@ cl::list<std::string>
|
||||||
cl::value_desc("targets"));
|
cl::value_desc("targets"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(LDC_RUNTIME_COMPILE)
|
||||||
|
static cl::opt<bool, true> enableRuntimeCompile(
|
||||||
|
"enable-runtime-compile",
|
||||||
|
cl::desc("Enable runtime compilation"),
|
||||||
|
cl::location(global.params.enableRuntimeCompile),
|
||||||
|
cl::init(false));
|
||||||
|
#endif
|
||||||
|
|
||||||
static cl::extrahelp footer(
|
static cl::extrahelp footer(
|
||||||
"\n"
|
"\n"
|
||||||
"-d-debug can also be specified without options, in which case it enables "
|
"-d-debug can also be specified without options, in which case it enables "
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "gen/logger.h"
|
#include "gen/logger.h"
|
||||||
#include "gen/modules.h"
|
#include "gen/modules.h"
|
||||||
#include "gen/runtime.h"
|
#include "gen/runtime.h"
|
||||||
|
#include "gen/runtimecompile.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/ToolOutputFile.h"
|
#include "llvm/Support/ToolOutputFile.h"
|
||||||
|
@ -246,6 +247,7 @@ void CodeGenerator::writeAndFreeLLModule(const char *filename) {
|
||||||
ir_->replaceGlobals();
|
ir_->replaceGlobals();
|
||||||
|
|
||||||
ir_->DBuilder.Finalize();
|
ir_->DBuilder.Finalize();
|
||||||
|
generateBitcodeForRuntimeCompile(ir_);
|
||||||
|
|
||||||
emitLLVMUsedArray(*ir_);
|
emitLLVMUsedArray(*ir_);
|
||||||
emitLinkerOptions(*ir_, ir_->module, ir_->context());
|
emitLinkerOptions(*ir_, ir_->module, ir_->context());
|
||||||
|
|
|
@ -340,6 +340,11 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
|
||||||
args.push_back("-lldc-profile-rt");
|
args.push_back("-lldc-profile-rt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (global.params.enableRuntimeCompile) {
|
||||||
|
args.push_back("-lldc-jit-rt");
|
||||||
|
args.push_back("-lldc-jit");
|
||||||
|
}
|
||||||
|
|
||||||
// user libs
|
// user libs
|
||||||
for (auto libfile : *global.params.libfiles) {
|
for (auto libfile : *global.params.libfiles) {
|
||||||
args.push_back(libfile);
|
args.push_back(libfile);
|
||||||
|
|
|
@ -118,6 +118,11 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker,
|
||||||
args.push_back("ws2_32.lib");
|
args.push_back("ws2_32.lib");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (global.params.enableRuntimeCompile) {
|
||||||
|
args.push_back("ldc-jit-rt.lib");
|
||||||
|
args.push_back("ldc-jit.lib");
|
||||||
|
}
|
||||||
|
|
||||||
// user libs
|
// user libs
|
||||||
for (auto libfile : *global.params.libfiles) {
|
for (auto libfile : *global.params.libfiles) {
|
||||||
args.push_back(libfile);
|
args.push_back(libfile);
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "gen/pgo.h"
|
#include "gen/pgo.h"
|
||||||
#include "gen/pragma.h"
|
#include "gen/pragma.h"
|
||||||
#include "gen/runtime.h"
|
#include "gen/runtime.h"
|
||||||
|
#include "gen/runtimecompile.h"
|
||||||
#include "gen/scope_exit.h"
|
#include "gen/scope_exit.h"
|
||||||
#include "gen/tollvm.h"
|
#include "gen/tollvm.h"
|
||||||
#include "gen/uda.h"
|
#include "gen/uda.h"
|
||||||
|
@ -463,6 +464,18 @@ void applyTargetMachineAttributes(llvm::Function &func,
|
||||||
opts::disableFpElim ? "true" : "false");
|
opts::disableFpElim ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLFunction* getFunction(llvm::Module& module, LLFunctionType *functype, const std::string& name) {
|
||||||
|
assert(nullptr != functype);
|
||||||
|
LLFunction* func = module.getFunction(name);
|
||||||
|
if (!func) {
|
||||||
|
// All function declarations are "external" - any other linkage type
|
||||||
|
// is set when actually defining the function.
|
||||||
|
func = LLFunction::Create(functype, llvm::GlobalValue::ExternalLinkage,
|
||||||
|
name, &module);
|
||||||
|
}
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -522,7 +535,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||||||
const auto link = forceC ? LINKc : f->linkage;
|
const auto link = forceC ? LINKc : f->linkage;
|
||||||
|
|
||||||
// mangled name
|
// mangled name
|
||||||
std::string mangledName = getMangledName(fdecl, link);
|
const std::string mangledName = getMangledName(fdecl, link);
|
||||||
|
|
||||||
// construct function
|
// construct function
|
||||||
LLFunctionType *functype = DtoFunctionType(fdecl);
|
LLFunctionType *functype = DtoFunctionType(fdecl);
|
||||||
|
@ -566,6 +579,10 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||||||
applyTargetMachineAttributes(*func, *gTargetMachine);
|
applyTargetMachineAttributes(*func, *gTargetMachine);
|
||||||
applyFuncDeclUDAs(fdecl, irFunc);
|
applyFuncDeclUDAs(fdecl, irFunc);
|
||||||
|
|
||||||
|
if(irFunc->runtimeCompile) {
|
||||||
|
declareRuntimeCompiledFunction(gIR, irFunc);
|
||||||
|
}
|
||||||
|
|
||||||
// main
|
// main
|
||||||
if (fdecl->isMain()) {
|
if (fdecl->isMain()) {
|
||||||
// Detect multiple main functions, which is disallowed. DMD checks this
|
// Detect multiple main functions, which is disallowed. DMD checks this
|
||||||
|
@ -909,14 +926,20 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IrFunction *irFunc = getIrFunc(fd);
|
||||||
|
|
||||||
|
SCOPE_EXIT {
|
||||||
|
if (irFunc->runtimeCompile) {
|
||||||
|
defineRuntimeCompiledFunction(gIR, irFunc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// if this function is naked, we take over right away! no standard processing!
|
// if this function is naked, we take over right away! no standard processing!
|
||||||
if (fd->naked) {
|
if (fd->naked) {
|
||||||
DtoDefineNakedFunction(fd);
|
DtoDefineNakedFunction(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrFunction *irFunc = getIrFunc(fd);
|
|
||||||
|
|
||||||
// debug info
|
// debug info
|
||||||
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
|
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,14 @@ public:
|
||||||
// setGlobalVarInitializer().
|
// setGlobalVarInitializer().
|
||||||
void replaceGlobals();
|
void replaceGlobals();
|
||||||
|
|
||||||
|
struct RtCompiledFuncDesc {
|
||||||
|
llvm::GlobalVariable* thunkVar;
|
||||||
|
llvm::Function* thunkFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<llvm::Function*,RtCompiledFuncDesc> runtimeCompiledFunctions;
|
||||||
|
std::set<IrGlobal*> runtimeCompiledVars;
|
||||||
|
|
||||||
/// Vector of options passed to the linker as metadata in object file.
|
/// Vector of options passed to the linker as metadata in object file.
|
||||||
#if LDC_LLVM_VER >= 500
|
#if LDC_LLVM_VER >= 500
|
||||||
llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs;
|
llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs;
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
|
#include "gen/runtimecompile.h"
|
||||||
|
|
||||||
llvm::cl::opt<llvm::GlobalVariable::ThreadLocalMode> clThreadModel(
|
llvm::cl::opt<llvm::GlobalVariable::ThreadLocalMode> clThreadModel(
|
||||||
"fthread-model", llvm::cl::ZeroOrMore, llvm::cl::desc("Thread model"),
|
"fthread-model", llvm::cl::ZeroOrMore, llvm::cl::desc("Thread model"),
|
||||||
|
@ -856,7 +857,8 @@ void DtoResolveVariable(VarDeclaration *vd) {
|
||||||
llvm::GlobalVariable *gvar =
|
llvm::GlobalVariable *gvar =
|
||||||
getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst,
|
getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst,
|
||||||
linkage, nullptr, llName, vd->isThreadlocal());
|
linkage, nullptr, llName, vd->isThreadlocal());
|
||||||
getIrGlobal(vd)->value = gvar;
|
IrGlobal *varIr = getIrGlobal(vd);
|
||||||
|
varIr->value = gvar;
|
||||||
|
|
||||||
// Set the alignment (it is important not to use type->alignsize because
|
// Set the alignment (it is important not to use type->alignsize because
|
||||||
// VarDeclarations can have an align() attribute independent of the type
|
// VarDeclarations can have an align() attribute independent of the type
|
||||||
|
@ -872,6 +874,9 @@ void DtoResolveVariable(VarDeclaration *vd) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
applyVarDeclUDAs(vd, gvar);
|
applyVarDeclUDAs(vd, gvar);
|
||||||
|
if (varIr->runtimeCompile) {
|
||||||
|
addRuntimeCompiledVar(gIR, varIr);
|
||||||
|
}
|
||||||
|
|
||||||
IF_LOG Logger::cout() << *gvar << '\n';
|
IF_LOG Logger::cout() << *gvar << '\n';
|
||||||
}
|
}
|
||||||
|
|
698
gen/runtimecompile.cpp
Normal file
698
gen/runtimecompile.cpp
Normal file
|
@ -0,0 +1,698 @@
|
||||||
|
#include "runtimecompile.h"
|
||||||
|
|
||||||
|
#if defined(LDC_RUNTIME_COMPILE)
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
#include "gen/irstate.h"
|
||||||
|
#include "gen/llvm.h"
|
||||||
|
#include "ir/irfunction.h"
|
||||||
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||||
|
#include "llvm/Transforms/Utils/Cloning.h"
|
||||||
|
#include "llvm/IR/TypeBuilder.h"
|
||||||
|
#include "llvm/Bitcode/BitcodeWriter.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const char* RuntimeCompileModulesHeadName = "runtimecompile_modules_head";
|
||||||
|
|
||||||
|
llvm::GlobalValue* getPredefinedSymbol(llvm::Module &module,
|
||||||
|
llvm::StringRef name,
|
||||||
|
llvm::Type *type) {
|
||||||
|
assert(nullptr != type);
|
||||||
|
auto ret = module.getNamedValue(name);
|
||||||
|
if (nullptr != ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (type->isFunctionTy()) {
|
||||||
|
ret = llvm::Function::Create(llvm::cast<llvm::FunctionType>(type),
|
||||||
|
llvm::GlobalValue::ExternalLinkage,
|
||||||
|
name,
|
||||||
|
&module);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = new llvm::GlobalVariable(module,
|
||||||
|
type,
|
||||||
|
false,
|
||||||
|
llvm::GlobalValue::ExternalLinkage,
|
||||||
|
nullptr,
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
bool contains(const C &cont, const T &val) {
|
||||||
|
return cont.end() != cont.find(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void enumOperands(const llvm::User& usr, F&& handler) {
|
||||||
|
for (auto&& op: usr.operands()) {
|
||||||
|
llvm::Value* val = op.get();
|
||||||
|
if (auto opusr = llvm::dyn_cast<llvm::User>(val)) {
|
||||||
|
if (auto gv = llvm::dyn_cast<llvm::GlobalValue>(opusr)) {
|
||||||
|
handler(gv);
|
||||||
|
}
|
||||||
|
enumOperands(*opusr, std::forward<F>(handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void enumFuncSymbols(llvm::Function* fun, F&& handler) {
|
||||||
|
assert(nullptr != fun);
|
||||||
|
for (auto&& bb: *fun) {
|
||||||
|
for (auto&& instr: bb) {
|
||||||
|
enumOperands(instr, std::forward<F>(handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class GlobalValVisibility {
|
||||||
|
Internal,
|
||||||
|
External,
|
||||||
|
Declaration,
|
||||||
|
};
|
||||||
|
|
||||||
|
using GlobalValsMap = std::map<llvm::GlobalValue*,GlobalValVisibility>;
|
||||||
|
|
||||||
|
void getPredefinedSymbols(IRState *irs,
|
||||||
|
GlobalValsMap &symList) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
const llvm::Triple* triple = global.params.targetTriple;
|
||||||
|
if (triple->isWindowsMSVCEnvironment() ||
|
||||||
|
triple->isWindowsGNUEnvironment()) {
|
||||||
|
symList.insert(std::make_pair(getPredefinedSymbol(irs->module,
|
||||||
|
"_tls_index",
|
||||||
|
llvm::Type::getInt32Ty(irs->context())),
|
||||||
|
GlobalValVisibility::Declaration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalValsMap createGlobalValsFilter(IRState *irs) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
GlobalValsMap ret;
|
||||||
|
getPredefinedSymbols(irs, ret);
|
||||||
|
std::vector<llvm::Function*> newFunctions;
|
||||||
|
newFunctions.reserve(irs->runtimeCompiledFunctions.size());
|
||||||
|
|
||||||
|
for (auto&& it: irs->runtimeCompiledFunctions) {
|
||||||
|
ret.insert(std::make_pair(it.first, GlobalValVisibility::External));
|
||||||
|
newFunctions.push_back(it.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<llvm::GlobalValue*> runtimeCompiledVars;
|
||||||
|
for (auto&& var: irs->runtimeCompiledVars) {
|
||||||
|
assert(nullptr != var);
|
||||||
|
assert(nullptr != var->value);
|
||||||
|
runtimeCompiledVars.insert(llvm::cast<llvm::GlobalValue>(var->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<llvm::Function*> functionsToAdd;
|
||||||
|
while (!newFunctions.empty()) {
|
||||||
|
for (auto&& fun: newFunctions) {
|
||||||
|
enumFuncSymbols(fun, [&](llvm::GlobalValue* gv) {
|
||||||
|
if (!contains(runtimeCompiledVars, gv)) {
|
||||||
|
auto it = ret.insert(std::make_pair(gv, GlobalValVisibility::Declaration));
|
||||||
|
if (it.second && !gv->isDeclaration()) {
|
||||||
|
if (auto newFun = llvm::dyn_cast<llvm::Function>(gv)) {
|
||||||
|
if (!newFun->isIntrinsic()) {
|
||||||
|
it.first->second = GlobalValVisibility::Internal;
|
||||||
|
functionsToAdd.push_back(newFun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
newFunctions.swap(functionsToAdd);
|
||||||
|
functionsToAdd.clear();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixupRtThunks(llvm::Module &newModule,
|
||||||
|
const decltype(IRState::runtimeCompiledFunctions)& funcs) {
|
||||||
|
std::map<std::string, std::string> thunk2func;
|
||||||
|
for (auto&& it: funcs) {
|
||||||
|
assert(nullptr != it.first);
|
||||||
|
assert(nullptr != it.second.thunkVar);
|
||||||
|
assert(nullptr != it.second.thunkFunc);
|
||||||
|
assert(!contains(thunk2func, it.second.thunkVar->getName()));
|
||||||
|
thunk2func.insert(std::make_pair(it.second.thunkVar->getName(),
|
||||||
|
it.first->getName()));
|
||||||
|
}
|
||||||
|
int objectsFixed = 0;
|
||||||
|
for (auto&& obj: newModule.globals()) {
|
||||||
|
auto it = thunk2func.find(obj.getName());
|
||||||
|
if (thunk2func.end() != it) {
|
||||||
|
if (obj.hasInitializer()) {
|
||||||
|
auto func = newModule.getFunction(it->second);
|
||||||
|
assert(nullptr != func);
|
||||||
|
obj.setConstant(true);
|
||||||
|
obj.setInitializer(func);
|
||||||
|
}
|
||||||
|
++objectsFixed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(objectsFixed = thunk2func.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideExternalSymbols(llvm::Module &newModule, const GlobalValsMap &filter) {
|
||||||
|
std::set<std::string> externalSymbols;
|
||||||
|
for (auto&& val: filter) {
|
||||||
|
if(GlobalValVisibility::External == val.second) {
|
||||||
|
externalSymbols.emplace(val.first->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& obj: newModule.global_objects()) {
|
||||||
|
if ((llvm::GlobalValue::ExternalLinkage == obj.getLinkage()) &&
|
||||||
|
(!contains(externalSymbols, obj.getName()))) {
|
||||||
|
obj.setLinkage(llvm::GlobalValue::InternalLinkage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *getArrayPtr(llvm::Constant *array) {
|
||||||
|
assert(nullptr != array);
|
||||||
|
llvm::ConstantInt *zero =
|
||||||
|
llvm::ConstantInt::get(llvm::Type::getInt64Ty(array->getContext()), 0, false);
|
||||||
|
llvm::Constant *idxs[] = {zero, zero};
|
||||||
|
return llvm::ConstantExpr::getGetElementPtr(
|
||||||
|
nullptr, array, idxs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *getI8Ptr(llvm::GlobalValue* val) {
|
||||||
|
assert(nullptr != val);
|
||||||
|
return llvm::ConstantExpr::getBitCast(
|
||||||
|
val,
|
||||||
|
llvm::IntegerType::getInt8PtrTy(val->getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<llvm::Constant*,llvm::Constant*> getArrayAndSize(
|
||||||
|
llvm::Module& module,
|
||||||
|
llvm::Type* elemType,
|
||||||
|
llvm::ArrayRef<llvm::Constant*> elements) {
|
||||||
|
assert(nullptr != elemType);
|
||||||
|
auto arrayType = llvm::ArrayType::get(elemType, elements.size());
|
||||||
|
auto arrVar = new llvm::GlobalVariable(
|
||||||
|
module,
|
||||||
|
arrayType,
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::PrivateLinkage,
|
||||||
|
llvm::ConstantArray::get(arrayType, elements),
|
||||||
|
".str");
|
||||||
|
return std::make_pair(
|
||||||
|
getArrayPtr(arrVar),
|
||||||
|
llvm::ConstantInt::get(module.getContext(),APInt(32, elements.size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void createStaticArray(llvm::Module& mod,
|
||||||
|
llvm::GlobalVariable* var,
|
||||||
|
llvm::GlobalVariable* varLen, //can be null
|
||||||
|
llvm::ArrayRef<T> arr) {
|
||||||
|
assert(nullptr != var);
|
||||||
|
const auto dataLen = arr.size();
|
||||||
|
auto gvar = new llvm::GlobalVariable(
|
||||||
|
mod,
|
||||||
|
llvm::ArrayType::get(llvm::TypeBuilder<T, false>::get(mod.getContext()), dataLen),
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::InternalLinkage,
|
||||||
|
llvm::ConstantDataArray::get(mod.getContext(), arr),
|
||||||
|
".str");
|
||||||
|
var->setInitializer(getArrayPtr(gvar));
|
||||||
|
if (nullptr != varLen) {
|
||||||
|
varLen->setInitializer(llvm::ConstantInt::get(mod.getContext(), APInt(32, dataLen)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *createStringInitializer(llvm::Module &mod,
|
||||||
|
llvm::StringRef str) {
|
||||||
|
auto nameVar = new llvm::GlobalVariable(
|
||||||
|
mod,
|
||||||
|
llvm::ArrayType::get(llvm::Type::getInt8Ty(mod.getContext()), str.size() + 1),
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::PrivateLinkage,
|
||||||
|
llvm::ConstantDataArray::getString(mod.getContext(), str, true),
|
||||||
|
".str");
|
||||||
|
return llvm::ConstantExpr::getBitCast(
|
||||||
|
nameVar,
|
||||||
|
llvm::Type::getInt8PtrTy(mod.getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void createStaticString(llvm::Module& mod,
|
||||||
|
llvm::GlobalVariable* var,
|
||||||
|
llvm::GlobalVariable* varLen, //can be null
|
||||||
|
llvm::StringRef str) {
|
||||||
|
assert(nullptr != var);
|
||||||
|
const auto dataLen = str.size() + 1;
|
||||||
|
auto gvar = new llvm::GlobalVariable(
|
||||||
|
mod,
|
||||||
|
llvm::ArrayType::get(llvm::Type::getInt8Ty(mod.getContext()), dataLen),
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::InternalLinkage,
|
||||||
|
llvm::ConstantDataArray::getString(mod.getContext(), str, true),
|
||||||
|
".str");
|
||||||
|
var->setInitializer(getArrayPtr(gvar));
|
||||||
|
if (nullptr != varLen) {
|
||||||
|
varLen->setInitializer(llvm::ConstantInt::get(mod.getContext(), APInt(32, dataLen)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct RtCompileVarList
|
||||||
|
// {
|
||||||
|
// i8* name;
|
||||||
|
// i8* ptr;
|
||||||
|
// }
|
||||||
|
|
||||||
|
llvm::StructType *getVarListElemType(llvm::LLVMContext &context) {
|
||||||
|
llvm::Type* elements[] = {
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
};
|
||||||
|
return llvm::StructType::create(context, elements, /*"RtCompileVarList"*/"", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct RtCompileSymList
|
||||||
|
// {
|
||||||
|
// i8* name;
|
||||||
|
// i8* sym;
|
||||||
|
// };
|
||||||
|
|
||||||
|
llvm::StructType *getSymListElemType(llvm::LLVMContext &context) {
|
||||||
|
llvm::Type* elements[] = {
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
};
|
||||||
|
return llvm::StructType::create(context, elements, /*"RtCompileSymList"*/"", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct RtCompileFuncList
|
||||||
|
// {
|
||||||
|
// i8* name;
|
||||||
|
// i8* func;
|
||||||
|
// };
|
||||||
|
|
||||||
|
llvm::StructType *getFuncListElemType(llvm::LLVMContext &context) {
|
||||||
|
llvm::Type* elements[] = {
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
};
|
||||||
|
return llvm::StructType::create(context, elements, /*"RtCompileFuncList"*/"", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct RtComileModuleList
|
||||||
|
// {
|
||||||
|
// RtComileModuleList* next;
|
||||||
|
// i8* irData;
|
||||||
|
// i32 irDataSize;
|
||||||
|
// RtCompileFuncList* funcList;
|
||||||
|
// i32 funcListSize;
|
||||||
|
// RtCompileSymList* symList;
|
||||||
|
// i32 symListSize;
|
||||||
|
// };
|
||||||
|
|
||||||
|
llvm::StructType *getModuleListElemType(llvm::LLVMContext &context,
|
||||||
|
llvm::StructType *funcListElemType,
|
||||||
|
llvm::StructType *symListElemType,
|
||||||
|
llvm::StructType *varListElemType) {
|
||||||
|
assert(nullptr != funcListElemType);
|
||||||
|
assert(nullptr != symListElemType);
|
||||||
|
assert(nullptr != varListElemType);
|
||||||
|
llvm::StructType* ret = llvm::StructType::create(context/*, "RtComileModuleList"*/); //fwddecl
|
||||||
|
llvm::Type* elements[] = {
|
||||||
|
llvm::PointerType::getUnqual(ret),
|
||||||
|
llvm::IntegerType::getInt8PtrTy(context),
|
||||||
|
llvm::IntegerType::get(context, 32),
|
||||||
|
llvm::PointerType::getUnqual(funcListElemType),
|
||||||
|
llvm::IntegerType::get(context, 32),
|
||||||
|
llvm::PointerType::getUnqual(symListElemType),
|
||||||
|
llvm::IntegerType::get(context, 32),
|
||||||
|
llvm::PointerType::getUnqual(varListElemType),
|
||||||
|
llvm::IntegerType::get(context, 32),};
|
||||||
|
ret->setBody(elements, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Types {
|
||||||
|
llvm::StructType *funcListElemType;
|
||||||
|
llvm::StructType *symListElemType;
|
||||||
|
llvm::StructType *varListElemType;
|
||||||
|
llvm::StructType *modListElemType;
|
||||||
|
|
||||||
|
Types(llvm::LLVMContext &context):
|
||||||
|
funcListElemType(getFuncListElemType(context)),
|
||||||
|
symListElemType(getSymListElemType(context)),
|
||||||
|
varListElemType(getVarListElemType(context)),
|
||||||
|
modListElemType(getModuleListElemType(context,
|
||||||
|
funcListElemType,
|
||||||
|
symListElemType,
|
||||||
|
varListElemType)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::pair<llvm::Constant*, llvm::Constant*> generateFuncList(
|
||||||
|
IRState *irs,
|
||||||
|
const Types &types) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
std::vector<llvm::Constant*> elements;
|
||||||
|
for (auto&& it: irs->runtimeCompiledFunctions) {
|
||||||
|
assert(nullptr != it.first);
|
||||||
|
assert(nullptr != it.second.thunkVar);
|
||||||
|
assert(nullptr != it.second.thunkFunc);
|
||||||
|
auto name = it.first->getName();
|
||||||
|
llvm::Constant* fields[] = {
|
||||||
|
createStringInitializer(irs->module, name),
|
||||||
|
getI8Ptr(it.second.thunkVar),
|
||||||
|
};
|
||||||
|
elements.push_back(llvm::ConstantStruct::get(types.funcListElemType, fields));
|
||||||
|
}
|
||||||
|
return getArrayAndSize(irs->module, types.funcListElemType, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<llvm::Constant*, llvm::Constant*> generateSymList(
|
||||||
|
IRState *irs,
|
||||||
|
const Types &types,
|
||||||
|
const GlobalValsMap& globalVals) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
std::vector<llvm::Constant*> elements;
|
||||||
|
for (auto&& it: globalVals) {
|
||||||
|
if (it.second == GlobalValVisibility::Declaration) {
|
||||||
|
auto val = it.first;
|
||||||
|
if (auto fun = llvm::dyn_cast<llvm::Function>(val)) {
|
||||||
|
if (fun->isIntrinsic()) continue;
|
||||||
|
}
|
||||||
|
auto name = val->getName();
|
||||||
|
llvm::Constant* fields[] = {
|
||||||
|
createStringInitializer(irs->module, name),
|
||||||
|
getI8Ptr(val),
|
||||||
|
};
|
||||||
|
elements.push_back(llvm::ConstantStruct::get(types.symListElemType, fields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getArrayAndSize(irs->module, types.symListElemType, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<llvm::Constant*, llvm::Constant*> generateVarList(
|
||||||
|
IRState *irs,
|
||||||
|
const Types &types) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
std::vector<llvm::Constant*> elements;
|
||||||
|
for (auto&& val: irs->runtimeCompiledVars) {
|
||||||
|
auto gvar = llvm::cast<llvm::GlobalVariable>(val->value);
|
||||||
|
auto name = gvar->getName();
|
||||||
|
llvm::Constant* fields[] = {
|
||||||
|
createStringInitializer(irs->module, name),
|
||||||
|
getI8Ptr(gvar),
|
||||||
|
};
|
||||||
|
elements.push_back(llvm::ConstantStruct::get(types.varListElemType, fields));
|
||||||
|
}
|
||||||
|
return getArrayAndSize(irs->module, types.varListElemType, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::GlobalVariable *generateModuleListElem(IRState *irs,
|
||||||
|
const Types &types,
|
||||||
|
llvm::GlobalVariable *irData,
|
||||||
|
llvm::GlobalVariable *irDataLen,
|
||||||
|
const GlobalValsMap &globalVals) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
auto elem_type = types.modListElemType;
|
||||||
|
auto funcListInit = generateFuncList(irs, types);
|
||||||
|
auto symListInit = generateSymList(irs, types, globalVals);
|
||||||
|
auto varlistInit = generateVarList(irs, types);
|
||||||
|
llvm::Constant* fields[] = {
|
||||||
|
llvm::ConstantPointerNull::get(llvm::dyn_cast<llvm::PointerType>(elem_type->getElementType(0))), // next
|
||||||
|
irData->getInitializer(), // irdata
|
||||||
|
irDataLen->getInitializer(), // irdata len
|
||||||
|
funcListInit.first, // funclist
|
||||||
|
funcListInit.second, // funclist len
|
||||||
|
symListInit.first, // symlist
|
||||||
|
symListInit.second, // symlist len
|
||||||
|
varlistInit.first, //varlist
|
||||||
|
varlistInit.second, //varlist len
|
||||||
|
};
|
||||||
|
|
||||||
|
auto init = llvm::ConstantStruct::get(elem_type, fields);
|
||||||
|
|
||||||
|
return new llvm::GlobalVariable(
|
||||||
|
irs->module,
|
||||||
|
elem_type,
|
||||||
|
false,
|
||||||
|
llvm::GlobalValue::PrivateLinkage,
|
||||||
|
init,
|
||||||
|
".rtcompile_modlist_elem");
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::PointerType *getModListHeadType(llvm::LLVMContext &context,
|
||||||
|
const Types &types) {
|
||||||
|
(void)types;
|
||||||
|
return llvm::IntegerType::getInt8PtrTy(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::GlobalVariable *declareModListHead(llvm::Module &module,
|
||||||
|
const Types &types) {
|
||||||
|
auto type = getModListHeadType(module.getContext(), types);
|
||||||
|
// auto existingVar = module.getGlobalVariable(RuntimeCompileModulesHeadName);
|
||||||
|
// if (nullptr != existingVar) {
|
||||||
|
// if (type != existingVar->getType()) {
|
||||||
|
// error(Loc(), "Invalid RuntimeCompileModulesHeadName type");
|
||||||
|
// fatal();
|
||||||
|
// }
|
||||||
|
// return existingVar;
|
||||||
|
// }
|
||||||
|
return new llvm::GlobalVariable(
|
||||||
|
module,
|
||||||
|
type,
|
||||||
|
false,
|
||||||
|
llvm::GlobalValue::ExternalLinkage,
|
||||||
|
nullptr,
|
||||||
|
RuntimeCompileModulesHeadName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateCtorBody(IRState *irs,
|
||||||
|
const Types &types,
|
||||||
|
llvm::Function *func,
|
||||||
|
llvm::Value *modListElem) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
assert(nullptr != func);
|
||||||
|
assert(nullptr != modListElem);
|
||||||
|
|
||||||
|
auto bb = llvm::BasicBlock::Create(irs->context(), "", func);
|
||||||
|
|
||||||
|
llvm::IRBuilder<> builder(irs->context());
|
||||||
|
builder.SetInsertPoint(bb);
|
||||||
|
|
||||||
|
auto zero64 = llvm::ConstantInt::get(irs->context(), APInt(64, 0));
|
||||||
|
auto zero32 = llvm::ConstantInt::get(irs->context(), APInt(32, 0));
|
||||||
|
auto modListHeadPtr = declareModListHead(irs->module, types);
|
||||||
|
llvm::Value* gepVals[] = {zero64, zero32};
|
||||||
|
auto elemNextPtr = builder.CreateGEP(modListElem, gepVals);
|
||||||
|
auto prevHeadVal = builder.CreateLoad(builder.CreateBitOrPointerCast(modListHeadPtr, types.modListElemType->getPointerTo()->getPointerTo()));
|
||||||
|
auto voidPtr = builder.CreateBitOrPointerCast(modListElem, llvm::IntegerType::getInt8PtrTy(irs->context()));
|
||||||
|
builder.CreateStore(voidPtr, modListHeadPtr);
|
||||||
|
builder.CreateStore(prevHeadVal, elemNextPtr);
|
||||||
|
|
||||||
|
builder.CreateRetVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupModuleCtor(IRState *irs,
|
||||||
|
llvm::GlobalVariable *irData,
|
||||||
|
llvm::GlobalVariable *irDataLen,
|
||||||
|
const GlobalValsMap &globalVals) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
assert(nullptr != irData);
|
||||||
|
assert(nullptr != irDataLen);
|
||||||
|
Types types(irs->context());
|
||||||
|
auto modListElem = generateModuleListElem(irs,
|
||||||
|
types,
|
||||||
|
irData,
|
||||||
|
irDataLen,
|
||||||
|
globalVals);
|
||||||
|
auto runtimeCompiledCtor = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(irs->context()), false),
|
||||||
|
llvm::GlobalValue::InternalLinkage,
|
||||||
|
".rtcompile_ctor",
|
||||||
|
&irs->module);
|
||||||
|
generateCtorBody(irs, types, runtimeCompiledCtor, modListElem);
|
||||||
|
llvm::appendToGlobalCtors(irs->module, runtimeCompiledCtor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupModuleBitcodeData(const llvm::Module &srcModule,
|
||||||
|
IRState *irs,
|
||||||
|
const GlobalValsMap &globalVals) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
|
||||||
|
llvm::SmallString<1024> str;
|
||||||
|
llvm::raw_svector_ostream os(str);
|
||||||
|
llvm::WriteBitcodeToFile(&srcModule, os);
|
||||||
|
|
||||||
|
auto runtimeCompiledIr = new llvm::GlobalVariable(
|
||||||
|
irs->module,
|
||||||
|
llvm::Type::getInt8PtrTy(irs->context()),
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::PrivateLinkage,
|
||||||
|
nullptr,
|
||||||
|
".rtcompile_ir");
|
||||||
|
|
||||||
|
auto runtimeCompiledIrSize = new llvm::GlobalVariable(
|
||||||
|
irs->module,
|
||||||
|
llvm::IntegerType::get(irs->context(), 32),
|
||||||
|
true,
|
||||||
|
llvm::GlobalValue::PrivateLinkage,
|
||||||
|
nullptr,
|
||||||
|
".rtcompile_irsize");
|
||||||
|
|
||||||
|
createStaticArray(
|
||||||
|
irs->module,
|
||||||
|
runtimeCompiledIr,
|
||||||
|
runtimeCompiledIrSize,
|
||||||
|
llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t*>(str.data()), str.size()));
|
||||||
|
|
||||||
|
|
||||||
|
setupModuleCtor(irs, runtimeCompiledIr, runtimeCompiledIrSize, globalVals);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyFuncAttributes(llvm::Function &dstFunc,
|
||||||
|
const llvm::Function &srcFunc) {
|
||||||
|
dstFunc.setCallingConv(srcFunc.getCallingConv());
|
||||||
|
dstFunc.setAttributes(srcFunc.getAttributes());
|
||||||
|
dstFunc.setDLLStorageClass(srcFunc.getDLLStorageClass());
|
||||||
|
dstFunc.setLinkage(srcFunc.getLinkage());
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Function *duplicateFunc(llvm::Module &module,
|
||||||
|
const llvm::Function *src) {
|
||||||
|
assert(nullptr != src);
|
||||||
|
auto ret = llvm::Function::Create(src->getFunctionType(),
|
||||||
|
llvm::GlobalObject::ExternalLinkage,
|
||||||
|
src->getName() + "__rtcomp_thunk__",
|
||||||
|
&module);
|
||||||
|
copyFuncAttributes(*ret, *src);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createThunkFunc(llvm::Module &module,
|
||||||
|
const llvm::Function *src,
|
||||||
|
llvm::Function *dst,
|
||||||
|
llvm::GlobalVariable *thunkVar) {
|
||||||
|
assert(nullptr != src);
|
||||||
|
assert(nullptr != dst);
|
||||||
|
assert(nullptr != thunkVar);
|
||||||
|
|
||||||
|
auto bb = llvm::BasicBlock::Create(module.getContext(), "", dst);
|
||||||
|
llvm::IRBuilder<> builder(module.getContext());
|
||||||
|
builder.SetInsertPoint(bb);
|
||||||
|
auto thunkPtr = builder.CreateLoad(thunkVar);
|
||||||
|
llvm::SmallVector<llvm::Value*, 6> args;
|
||||||
|
for(auto& arg: dst->args()) {
|
||||||
|
args.push_back(&arg);
|
||||||
|
}
|
||||||
|
auto ret = builder.CreateCall(thunkPtr, args);
|
||||||
|
if (dst->getReturnType()->isVoidTy()) {
|
||||||
|
builder.CreateRetVoid();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.CreateRet(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anon namespace
|
||||||
|
|
||||||
|
void generateBitcodeForRuntimeCompile(IRState *irs) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
if (irs->runtimeCompiledFunctions.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto filter = createGlobalValsFilter(irs);
|
||||||
|
|
||||||
|
llvm::ValueToValueMapTy unused;
|
||||||
|
auto newModule = llvm::CloneModule(&irs->module,
|
||||||
|
unused,
|
||||||
|
[&](const llvm::GlobalValue *val)->bool {
|
||||||
|
// We don't dereference here, so const_cast should be safe
|
||||||
|
auto it = filter.find(const_cast<llvm::GlobalValue*>(val));
|
||||||
|
return filter.end() != it && it->second != GlobalValVisibility::Declaration;
|
||||||
|
});
|
||||||
|
fixupRtThunks(*newModule, irs->runtimeCompiledFunctions);
|
||||||
|
//hideExternalSymbols(*newModule, filter);
|
||||||
|
|
||||||
|
setupModuleBitcodeData(*newModule, irs, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void declareRuntimeCompiledFunction(IRState *irs, IrFunction *func) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
assert(nullptr != func);
|
||||||
|
assert(nullptr != func->getLLVMFunc());
|
||||||
|
if (!global.params.enableRuntimeCompile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto srcFunc = func->getLLVMFunc();
|
||||||
|
auto thunkFunc = duplicateFunc(irs->module, srcFunc);
|
||||||
|
func->rtCompileFunc = thunkFunc;
|
||||||
|
assert(!contains(irs->runtimeCompiledFunctions, srcFunc));
|
||||||
|
irs->runtimeCompiledFunctions.insert(std::make_pair(srcFunc,
|
||||||
|
IRState::RtCompiledFuncDesc{nullptr, thunkFunc}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void defineRuntimeCompiledFunction(IRState *irs, IrFunction *func)
|
||||||
|
{
|
||||||
|
assert(nullptr != irs);
|
||||||
|
assert(nullptr != func);
|
||||||
|
assert(nullptr != func->getLLVMFunc());
|
||||||
|
assert(nullptr != func->rtCompileFunc);
|
||||||
|
if (!global.params.enableRuntimeCompile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto srcFunc = func->getLLVMFunc();
|
||||||
|
auto it = irs->runtimeCompiledFunctions.find(srcFunc);
|
||||||
|
assert(irs->runtimeCompiledFunctions.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 addRuntimeCompiledVar(IRState *irs, IrGlobal *var) {
|
||||||
|
assert(nullptr != irs);
|
||||||
|
assert(nullptr != var);
|
||||||
|
assert(nullptr != var->value);
|
||||||
|
assert(nullptr != var->V);
|
||||||
|
if (!global.params.enableRuntimeCompile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var->V->isThreadlocal()) {
|
||||||
|
error(Loc(),
|
||||||
|
"Invalid runtime compiled variable \"%s\" cannot be thread local",
|
||||||
|
var->V->toChars());
|
||||||
|
fatal();
|
||||||
|
}
|
||||||
|
|
||||||
|
irs->runtimeCompiledVars.insert(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else //defined(LDC_RUNTIME_COMPILE)
|
||||||
|
void generateBitcodeForRuntimeCompile(IRState *) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void declareRuntimeCompiledFunction(IRState *, IrFunction *) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void defineRuntimeCompiledFunction(IRState *, IrFunction *) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRuntimeCompiledVar(IRState *, IrGlobal *) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
13
gen/runtimecompile.h
Normal file
13
gen/runtimecompile.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef RUNTIMECOMPILE_H
|
||||||
|
#define RUNTIMECOMPILE_H
|
||||||
|
|
||||||
|
struct IRState;
|
||||||
|
struct IrFunction;
|
||||||
|
struct IrGlobal;
|
||||||
|
|
||||||
|
void generateBitcodeForRuntimeCompile(IRState *irs);
|
||||||
|
void declareRuntimeCompiledFunction(IRState *irs, IrFunction *func);
|
||||||
|
void defineRuntimeCompiledFunction(IRState *irs, IrFunction *func);
|
||||||
|
void addRuntimeCompiledVar(IRState *irs, IrGlobal *var);
|
||||||
|
|
||||||
|
#endif // RUNTIMECOMPILE_H
|
|
@ -7,6 +7,7 @@
|
||||||
#include "declaration.h"
|
#include "declaration.h"
|
||||||
#include "expression.h"
|
#include "expression.h"
|
||||||
#include "ir/irfunction.h"
|
#include "ir/irfunction.h"
|
||||||
|
#include "ir/irvar.h"
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "id.h"
|
#include "id.h"
|
||||||
|
|
||||||
|
@ -359,6 +360,8 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
|
||||||
ident->toChars());
|
ident->toChars());
|
||||||
} else if (ident == Id::udaWeak) {
|
} else if (ident == Id::udaWeak) {
|
||||||
// @weak is applied elsewhere
|
// @weak is applied elsewhere
|
||||||
|
} else if (ident == Id::udaRuntimeCompile) {
|
||||||
|
getIrGlobal(decl)->runtimeCompile = true;
|
||||||
} else {
|
} else {
|
||||||
sle->warning(
|
sle->warning(
|
||||||
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
||||||
|
@ -395,6 +398,8 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
|
||||||
applyAttrTarget(sle, func);
|
applyAttrTarget(sle, func);
|
||||||
} else if (ident == Id::udaWeak || ident == Id::udaKernel) {
|
} else if (ident == Id::udaWeak || ident == Id::udaKernel) {
|
||||||
// @weak and @kernel are applied elsewhere
|
// @weak and @kernel are applied elsewhere
|
||||||
|
} else if (ident == Id::udaRuntimeCompile) {
|
||||||
|
irFunc->runtimeCompile = true;
|
||||||
} else {
|
} else {
|
||||||
sle->warning(
|
sle->warning(
|
||||||
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
||||||
|
|
|
@ -74,7 +74,7 @@ llvm::StringRef IrFunction::getLLVMFuncName() const {
|
||||||
|
|
||||||
llvm::Function *IrFunction::getLLVMCallee() const {
|
llvm::Function *IrFunction::getLLVMCallee() const {
|
||||||
assert(func != nullptr);
|
assert(func != nullptr);
|
||||||
return func;
|
return rtCompileFunc != nullptr ? rtCompileFunc : func;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrFunction *getIrFunc(FuncDeclaration *decl, bool create) {
|
IrFunction *getIrFunc(FuncDeclaration *decl, bool create) {
|
||||||
|
|
|
@ -81,6 +81,9 @@ struct IrFunction {
|
||||||
/// These are set e.g. by math related UDA's from ldc.attributes.
|
/// These are set e.g. by math related UDA's from ldc.attributes.
|
||||||
llvm::FastMathFlags FMF;
|
llvm::FastMathFlags FMF;
|
||||||
|
|
||||||
|
bool runtimeCompile = false;
|
||||||
|
llvm::Function *rtCompileFunc = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
llvm::Function *func = nullptr;
|
llvm::Function *func = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,8 @@ struct IrVar {
|
||||||
|
|
||||||
VarDeclaration *V = nullptr;
|
VarDeclaration *V = nullptr;
|
||||||
llvm::Value *value = nullptr;
|
llvm::Value *value = nullptr;
|
||||||
|
|
||||||
|
bool runtimeCompile = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// represents a global variable
|
// represents a global variable
|
||||||
|
|
|
@ -8,6 +8,7 @@ default:
|
||||||
switches = [
|
switches = [
|
||||||
"-I@RUNTIME_DIR@/src",
|
"-I@RUNTIME_DIR@/src",
|
||||||
"-I@PROFILERT_DIR@/d",
|
"-I@PROFILERT_DIR@/d",
|
||||||
|
"-I@JITRT_DIR@/d",
|
||||||
"-I@PHOBOS2_DIR@",
|
"-I@PHOBOS2_DIR@",
|
||||||
"-L-L@CMAKE_BINARY_DIR@/lib@LIB_SUFFIX@", @MULTILIB_ADDITIONAL_PATH@@SHARED_LIBS_RPATH@
|
"-L-L@CMAKE_BINARY_DIR@/lib@LIB_SUFFIX@", @MULTILIB_ADDITIONAL_PATH@@SHARED_LIBS_RPATH@
|
||||||
"-defaultlib=phobos2-ldc,druntime-ldc",
|
"-defaultlib=phobos2-ldc,druntime-ldc",
|
||||||
|
|
|
@ -116,6 +116,7 @@ get_directory_property(PROJECT_PARENT_DIR DIRECTORY ${PROJECT_SOURCE_DIR} PARENT
|
||||||
set(RUNTIME_DIR ${PROJECT_SOURCE_DIR}/druntime CACHE PATH "druntime root directory")
|
set(RUNTIME_DIR ${PROJECT_SOURCE_DIR}/druntime CACHE PATH "druntime root directory")
|
||||||
set(PHOBOS2_DIR ${PROJECT_SOURCE_DIR}/phobos CACHE PATH "Phobos root directory")
|
set(PHOBOS2_DIR ${PROJECT_SOURCE_DIR}/phobos CACHE PATH "Phobos root directory")
|
||||||
set(PROFILERT_DIR ${PROJECT_SOURCE_DIR}/profile-rt CACHE PATH "profile-rt root directory")
|
set(PROFILERT_DIR ${PROJECT_SOURCE_DIR}/profile-rt CACHE PATH "profile-rt root directory")
|
||||||
|
set(JITRT_DIR ${PROJECT_SOURCE_DIR}/jit-rt CACHE PATH "jit runtime root directory")
|
||||||
|
|
||||||
#
|
#
|
||||||
# Gather source files.
|
# Gather source files.
|
||||||
|
@ -600,11 +601,17 @@ macro(build_all_runtime_variants d_flags c_flags ld_flags path_suffix outlist_ta
|
||||||
get_target_suffix("" "${path_suffix}" target_suffix)
|
get_target_suffix("" "${path_suffix}" target_suffix)
|
||||||
set_common_library_properties(ldc-profile-rt${target_suffix})
|
set_common_library_properties(ldc-profile-rt${target_suffix})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
build_jit_runtime ("${d_flags}" "${c_flags}" "${ld_flags}" "${path_suffix}" ${outlist_targets})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
|
||||||
# Setup the build of profile-rt
|
# Setup the build of profile-rt
|
||||||
include(profile-rt/DefineBuildProfileRT.cmake)
|
include(profile-rt/DefineBuildProfileRT.cmake)
|
||||||
|
|
||||||
|
# Setup the build of jit runtime
|
||||||
|
include(jit-rt/DefineBuildJitRT.cmake)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set up build and install targets
|
# Set up build and install targets
|
||||||
#
|
#
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6d77cf4784f99e8369c146ab1551dee0b87472dd
|
Subproject commit 0600f868c18d0a5065c63f897eaab88149edd370
|
82
runtime/jit-rt/DefineBuildJitRT.cmake
Normal file
82
runtime/jit-rt/DefineBuildJitRT.cmake
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
if(LDC_RUNTIME_COMPILE)
|
||||||
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
|
file(GLOB LDC_JITRT_D ${JITRT_DIR}/d/ldc/*.d)
|
||||||
|
|
||||||
|
# Choose the correct subfolder depending on the LLVM version
|
||||||
|
file(GLOB LDC_JITRT_CXX ${JITRT_DIR}/cpp/*.cpp)
|
||||||
|
file(GLOB LDC_JITRT_SO_CXX ${JITRT_DIR}/cpp-so/*.cpp)
|
||||||
|
|
||||||
|
# Set compiler-dependent flags
|
||||||
|
if(MSVC)
|
||||||
|
# Omit Default Library Name from the library, so it will work with both release and debug builds
|
||||||
|
set(JITRT_EXTRA_FLAGS "/Zl")
|
||||||
|
|
||||||
|
else()
|
||||||
|
set(JITRT_EXTRA_FLAGS "-fPIC -O3")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Sets up the targets for building the D-source jit-rt object files,
|
||||||
|
# appending the names of the (bitcode) files to link into the library to
|
||||||
|
# outlist_o (outlist_bc).
|
||||||
|
macro(compile_jit_rt_D d_flags lib_suffix path_suffix all_at_once outlist_o outlist_bc)
|
||||||
|
get_target_suffix("${lib_suffix}" "${path_suffix}" target_suffix)
|
||||||
|
dc("${LDC_JITRT_D}"
|
||||||
|
"${JITRT_DIR}/d"
|
||||||
|
"${d_flags}"
|
||||||
|
"${PROJECT_BINARY_DIR}/objects${target_suffix}"
|
||||||
|
"${all_at_once}"
|
||||||
|
${outlist_o}
|
||||||
|
${outlist_bc}
|
||||||
|
)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(build_jit_runtime d_flags c_flags ld_flags path_suffix outlist_targets)
|
||||||
|
get_target_suffix("" "${path_suffix}" target_suffix)
|
||||||
|
|
||||||
|
set(output_path ${CMAKE_BINARY_DIR}/lib${path_suffix})
|
||||||
|
|
||||||
|
add_library(ldc-jit-rt-so${target_suffix} SHARED ${LDC_JITRT_SO_CXX})
|
||||||
|
set_target_properties(
|
||||||
|
ldc-jit-rt-so${target_suffix} PROPERTIES
|
||||||
|
OUTPUT_NAME ldc-jit
|
||||||
|
VERSION ${LDC_VERSION}
|
||||||
|
LINKER_LANGUAGE C
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
COMPILE_FLAGS "${c_flags} ${LDC_CXXFLAGS} ${LLVM_CXXFLAGS} ${JITRT_EXTRA_FLAGS}"
|
||||||
|
LINK_FLAGS "${ld_flags} ${JITRT_EXTRA_LDFLAGS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
llvm_map_components_to_libnames(llvm_libs support core irreader executionengine passes target nativecodegen)
|
||||||
|
target_link_libraries(ldc-jit-rt-so${target_suffix} ${llvm_libs})
|
||||||
|
|
||||||
|
set(jitrt_d_o "")
|
||||||
|
set(jitrt_d_bc "")
|
||||||
|
compile_jit_rt_D("${d_flags}" "" "${path_suffix}" "${COMPILE_ALL_D_FILES_AT_ONCE}" jitrt_d_o jitrt_d_bc)
|
||||||
|
|
||||||
|
add_library(ldc-jit-rt${target_suffix} STATIC ${jitrt_d_o} ${LDC_JITRT_CXX})
|
||||||
|
set_target_properties(
|
||||||
|
ldc-jit-rt${target_suffix} PROPERTIES
|
||||||
|
OUTPUT_NAME ldc-jit-rt
|
||||||
|
VERSION ${LDC_VERSION}
|
||||||
|
LINKER_LANGUAGE C
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${output_path}
|
||||||
|
COMPILE_FLAGS "${c_flags} ${JITRT_EXTRA_FLAGS} /Zl"
|
||||||
|
LINK_FLAGS "${ld_flags} ${JITRT_EXTRA_LDFLAGS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(ldc-jit-rt${target_suffix} ldc-jit-rt-so${target_suffix})
|
||||||
|
|
||||||
|
list(APPEND ${outlist_targets} "ldc-jit-rt-so${target_suffix}")
|
||||||
|
list(APPEND ${outlist_targets} "ldc-jit-rt${target_suffix}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Install D interface files
|
||||||
|
install(DIRECTORY ${JITRT_DIR}/d/ldc DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.d")
|
||||||
|
else()
|
||||||
|
macro(build_jit_runtime d_flags c_flags ld_flags path_suffix outlist_targets)
|
||||||
|
endmacro()
|
||||||
|
endif()
|
20
runtime/jit-rt/cpp-so/callback_ostream.cpp
Normal file
20
runtime/jit-rt/cpp-so/callback_ostream.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#include "callback_ostream.h"
|
||||||
|
|
||||||
|
void CallbackOstream::write_impl(const char *Ptr, size_t Size)
|
||||||
|
{
|
||||||
|
callback(Ptr, Size);
|
||||||
|
currentPos += Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t CallbackOstream::current_pos() const
|
||||||
|
{
|
||||||
|
return currentPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackOstream::CallbackOstream(CallbackOstream::CallbackT c):
|
||||||
|
callback(c)
|
||||||
|
{
|
||||||
|
SetUnbuffered();
|
||||||
|
}
|
||||||
|
|
20
runtime/jit-rt/cpp-so/callback_ostream.h
Normal file
20
runtime/jit-rt/cpp-so/callback_ostream.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef CALLBACK_OSTREAM_H
|
||||||
|
#define CALLBACK_OSTREAM_H
|
||||||
|
|
||||||
|
#include <llvm/Support/raw_ostream.h>
|
||||||
|
#include <llvm/ADT/STLExtras.h>
|
||||||
|
|
||||||
|
class CallbackOstream : public llvm::raw_ostream {
|
||||||
|
using CallbackT = llvm::function_ref<void(const char*,size_t)>;
|
||||||
|
CallbackT callback;
|
||||||
|
uint64_t currentPos = 0;
|
||||||
|
|
||||||
|
/// See raw_ostream::write_impl.
|
||||||
|
void write_impl(const char *Ptr, size_t Size) override;
|
||||||
|
|
||||||
|
uint64_t current_pos() const override;
|
||||||
|
public:
|
||||||
|
explicit CallbackOstream(CallbackT c);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CALLBACK_OSTREAM_H
|
305
runtime/jit-rt/cpp-so/compile.cpp
Normal file
305
runtime/jit-rt/cpp-so/compile.cpp
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "optimizer.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "callback_ostream.h"
|
||||||
|
|
||||||
|
#include "llvm/Support/ManagedStatic.h"
|
||||||
|
|
||||||
|
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||||
|
#include "llvm/ExecutionEngine/RuntimeDyld.h"
|
||||||
|
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||||
|
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
|
||||||
|
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 500
|
||||||
|
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
|
||||||
|
#else
|
||||||
|
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "llvm/Support/DynamicLibrary.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/Support/Host.h"
|
||||||
|
#include "llvm/Target/TargetMachine.h"
|
||||||
|
#include "llvm/Bitcode/BitcodeReader.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
|
||||||
|
struct RtCompileFuncList
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
void** func;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RtCompileSymList
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
void* sym;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RtCompileVarList
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
const void* init;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RtComileModuleList
|
||||||
|
{
|
||||||
|
RtComileModuleList* next;
|
||||||
|
const char* irData;
|
||||||
|
int irDataSize;
|
||||||
|
RtCompileFuncList* funcList;
|
||||||
|
int funcListSize;
|
||||||
|
RtCompileSymList* symList;
|
||||||
|
int symListSize;
|
||||||
|
RtCompileVarList* varList;
|
||||||
|
int varListSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
llvm::SmallVector<std::string, 4> getHostAttrs() {
|
||||||
|
llvm::SmallVector<std::string, 4> features;
|
||||||
|
llvm::StringMap<bool> hostFeatures;
|
||||||
|
if (llvm::sys::getHostCPUFeatures(hostFeatures)) {
|
||||||
|
for (auto &&f : hostFeatures) {
|
||||||
|
features.push_back(((f.second ? "+" : "-") + f.first()).str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
using SymMap = std::map<std::string, void*>;
|
||||||
|
|
||||||
|
struct llvm_init_obj {
|
||||||
|
llvm_init_obj() {
|
||||||
|
llvm::InitializeNativeTarget();
|
||||||
|
llvm::InitializeNativeTargetAsmPrinter();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyJIT {
|
||||||
|
private:
|
||||||
|
llvm_init_obj initObj;
|
||||||
|
llvm::llvm_shutdown_obj shutdownObj;
|
||||||
|
std::unique_ptr<llvm::TargetMachine> targetmachine;
|
||||||
|
const llvm::DataLayout dataLayout;
|
||||||
|
#if LDC_LLVM_VER >= 500
|
||||||
|
using ObjectLayerT = llvm::orc::RTDyldObjectLinkingLayer<>;
|
||||||
|
#else
|
||||||
|
using ObjectLayerT = llvm::orc::ObjectLinkingLayer<>;
|
||||||
|
#endif
|
||||||
|
ObjectLayerT objectLayer;
|
||||||
|
using CompileLayerT = llvm::orc::IRCompileLayer<ObjectLayerT>;
|
||||||
|
CompileLayerT compileLayer;
|
||||||
|
llvm::LLVMContext context;
|
||||||
|
typedef CompileLayerT::ModuleSetHandleT ModuleHandleT;
|
||||||
|
bool compiled = false;
|
||||||
|
ModuleHandleT moduleHandle;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MyJIT():
|
||||||
|
targetmachine(llvm::EngineBuilder()
|
||||||
|
.setRelocationModel(llvm::Reloc::Static)
|
||||||
|
.selectTarget(llvm::Triple(llvm::sys::getProcessTriple()),
|
||||||
|
llvm::StringRef(),
|
||||||
|
llvm::sys::getHostCPUName(),
|
||||||
|
getHostAttrs())),
|
||||||
|
dataLayout(targetmachine->createDataLayout()),
|
||||||
|
compileLayer(objectLayer, llvm::orc::SimpleCompiler(*targetmachine))
|
||||||
|
{
|
||||||
|
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::TargetMachine& getTargetMachine() { return *targetmachine; }
|
||||||
|
|
||||||
|
void addModules(std::vector<std::unique_ptr<llvm::Module>> &&modules,
|
||||||
|
const SymMap& symMap) {
|
||||||
|
reset();
|
||||||
|
// Build our symbol resolver:
|
||||||
|
// Lambda 1: Look back into the JIT itself to find symbols that are part of
|
||||||
|
// the same "logical dylib".
|
||||||
|
// Lambda 2: Search for external symbols in the host process.
|
||||||
|
auto Resolver = llvm::orc::createLambdaResolver(
|
||||||
|
[&](const std::string& name) {
|
||||||
|
if (auto Sym = compileLayer.findSymbol(name, false)) {
|
||||||
|
return Sym;
|
||||||
|
}
|
||||||
|
return llvm::JITSymbol(nullptr);
|
||||||
|
},
|
||||||
|
[&](const std::string& name) {
|
||||||
|
auto it = symMap.find(name);
|
||||||
|
if (symMap.end() != it) {
|
||||||
|
return llvm::JITSymbol(reinterpret_cast<llvm::JITTargetAddress>(it->second),
|
||||||
|
llvm::JITSymbolFlags::Exported);
|
||||||
|
}
|
||||||
|
if (auto SymAddr = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name)) {
|
||||||
|
return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported);
|
||||||
|
}
|
||||||
|
return llvm::JITSymbol(nullptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the set to the JIT with the resolver we created above and a newly
|
||||||
|
// created SectionMemoryManager.
|
||||||
|
moduleHandle = compileLayer.addModuleSet(std::move(modules),
|
||||||
|
llvm::make_unique<llvm::SectionMemoryManager>(),
|
||||||
|
std::move(Resolver));
|
||||||
|
compiled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::JITSymbol findSymbol(const std::string &name) {
|
||||||
|
return compileLayer.findSymbol(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::LLVMContext& getContext() { return context; }
|
||||||
|
|
||||||
|
// void removeModule(ModuleHandle H) {
|
||||||
|
// CompileLayer.removeModuleSet(H);
|
||||||
|
// }
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
if (compiled) {
|
||||||
|
compileLayer.removeModuleSet(moduleHandle);
|
||||||
|
compiled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void setRtCompileVars(const Context &context,
|
||||||
|
llvm::Module& module,
|
||||||
|
llvm::ArrayRef<RtCompileVarList> vals) {
|
||||||
|
for (auto&& val: vals) {
|
||||||
|
setRtCompileVar(context, module, val.name, val.init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
llvm::ArrayRef<T> toArray(T* ptr, size_t size) {
|
||||||
|
return llvm::ArrayRef<T>(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JitFinaliser final {
|
||||||
|
MyJIT& jit;
|
||||||
|
bool finalized = false;
|
||||||
|
explicit JitFinaliser(MyJIT& j):
|
||||||
|
jit(j) {}
|
||||||
|
~JitFinaliser() {
|
||||||
|
if (!finalized) {
|
||||||
|
jit.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalze() { finalized = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
MyJIT& getJit()
|
||||||
|
{
|
||||||
|
static MyJIT jit;
|
||||||
|
return jit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtCompileProcessImplSoInternal(const RtComileModuleList* modlist_head, const Context& context) {
|
||||||
|
interruptPoint(context, "Init");
|
||||||
|
MyJIT& myJit = getJit();
|
||||||
|
auto current = modlist_head;
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, void**> > functions;
|
||||||
|
std::vector<std::unique_ptr<llvm::Module>> ms;
|
||||||
|
SymMap symMap;
|
||||||
|
OptimizerSettings settings;
|
||||||
|
settings.optLevel = context.optLevel;
|
||||||
|
settings.sizeLeve = context.sizeLeve;
|
||||||
|
while (nullptr != current) {
|
||||||
|
interruptPoint(context, "load IR");
|
||||||
|
auto buff = llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(current->irData, current->irDataSize), "", false);
|
||||||
|
interruptPoint(context, "parse IR");
|
||||||
|
auto mod = llvm::parseBitcodeFile(*buff, myJit.getContext());
|
||||||
|
if (!mod) {
|
||||||
|
fatal(context, "Unable to parse IR");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
llvm::Module& module = **mod;
|
||||||
|
const auto name = module.getName();
|
||||||
|
interruptPoint(context,"Verify module", name.data());
|
||||||
|
::verifyModule(context, module);
|
||||||
|
module.setDataLayout(myJit.getTargetMachine().createDataLayout());
|
||||||
|
|
||||||
|
interruptPoint(context, "setRtCompileVars", name.data());
|
||||||
|
setRtCompileVars(context,
|
||||||
|
module,
|
||||||
|
toArray(current->varList, current->varListSize));
|
||||||
|
|
||||||
|
interruptPoint(context, "Optimize module", name.data());
|
||||||
|
optimizeModule(context, myJit.getTargetMachine(), settings, module);
|
||||||
|
|
||||||
|
interruptPoint(context, "Verify module", name.data());
|
||||||
|
::verifyModule(context, module);
|
||||||
|
if (nullptr != context.dumpHandler) {
|
||||||
|
auto callback =[&](const char* str, size_t len) {
|
||||||
|
context.dumpHandler(context.dumpHandlerData, str, len);
|
||||||
|
};
|
||||||
|
|
||||||
|
CallbackOstream os(callback);
|
||||||
|
module.print(os, nullptr, false, true);
|
||||||
|
}
|
||||||
|
ms.push_back(std::move(*mod));
|
||||||
|
|
||||||
|
for (auto&& fun: toArray(current->funcList, current->funcListSize)) {
|
||||||
|
functions.push_back(std::make_pair(fun.name, fun.func));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto&& sym: toArray(current->symList, current->symListSize)) {
|
||||||
|
symMap.insert(std::make_pair(sym.name, sym.sym));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
interruptPoint(context, "Add modules");
|
||||||
|
myJit.addModules(std::move(ms), symMap);
|
||||||
|
JitFinaliser jitFinalizer(myJit);
|
||||||
|
interruptPoint(context, "Resolve functions");
|
||||||
|
for (auto&& fun: functions) {
|
||||||
|
auto symbol = myJit.findSymbol(fun.first);
|
||||||
|
const auto addr = symbol.getAddress();
|
||||||
|
if (0 == addr) {
|
||||||
|
std::string desc = std::string("Symbol not found in jitted code: ") + fun.first;
|
||||||
|
fatal(context, desc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*fun.second = reinterpret_cast<void*>(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jitFinalizer.finalze();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anon namespace
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
__declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
void rtCompileProcessImplSo(const void* modlist_head,
|
||||||
|
const Context* context,
|
||||||
|
size_t contextSize) {
|
||||||
|
assert(nullptr != context);
|
||||||
|
assert(sizeof(*context) == contextSize);
|
||||||
|
rtCompileProcessImplSoInternal(
|
||||||
|
static_cast<const RtComileModuleList*>(modlist_head),
|
||||||
|
*context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
runtime/jit-rt/cpp-so/context.h
Normal file
26
runtime/jit-rt/cpp-so/context.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef CONTEXT_H
|
||||||
|
#define CONTEXT_H
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
// must be synchronized with D source
|
||||||
|
typedef void (*InterruptPointHandlerT)(void*, const char* action, const char* object);
|
||||||
|
typedef void (*FatalHandlerT)(void*, const char* reason);
|
||||||
|
typedef void (*DumpHandlerT)(void*, const char* str, std::size_t len);
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
|
||||||
|
struct Context final
|
||||||
|
{
|
||||||
|
unsigned optLevel = 0;
|
||||||
|
unsigned sizeLeve = 0;
|
||||||
|
InterruptPointHandlerT interruptPointHandler = nullptr;
|
||||||
|
void* interruptPointHandlerData = nullptr;
|
||||||
|
FatalHandlerT fatalHandler = nullptr;
|
||||||
|
void* fatalHandlerData = nullptr;
|
||||||
|
DumpHandlerT dumpHandler = nullptr;
|
||||||
|
void* dumpHandlerData = nullptr;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#endif // CONTEXT_H
|
208
runtime/jit-rt/cpp-so/optimizer.cpp
Normal file
208
runtime/jit-rt/cpp-so/optimizer.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
#include "optimizer.h"
|
||||||
|
|
||||||
|
#include "llvm/Target/TargetMachine.h"
|
||||||
|
|
||||||
|
#include "llvm/IR/LegacyPassManager.h"
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/IR/DataLayout.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/IR/Verifier.h"
|
||||||
|
|
||||||
|
#include "llvm/ADT/Triple.h"
|
||||||
|
|
||||||
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||||
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||||
|
|
||||||
|
#include "llvm/Transforms/IPO.h"
|
||||||
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "valueparser.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void addOptimizationPasses(llvm::legacy::PassManagerBase &mpm,
|
||||||
|
llvm::legacy::FunctionPassManager &fpm,
|
||||||
|
unsigned optLevel, unsigned sizeLevel) {
|
||||||
|
// if (!noVerify) {
|
||||||
|
// fpm.add(createVerifierPass());
|
||||||
|
// }
|
||||||
|
|
||||||
|
llvm::PassManagerBuilder builder;
|
||||||
|
builder.OptLevel = optLevel;
|
||||||
|
builder.SizeLevel = sizeLevel;
|
||||||
|
|
||||||
|
if (/*willInline()*/true) {
|
||||||
|
unsigned threshold = 225;
|
||||||
|
if (sizeLevel == 1) { // -Os
|
||||||
|
threshold = 75;
|
||||||
|
} else if (sizeLevel == 2) { // -Oz
|
||||||
|
threshold = 25;
|
||||||
|
}
|
||||||
|
if (optLevel > 2) {
|
||||||
|
threshold = 275;
|
||||||
|
}
|
||||||
|
builder.Inliner = llvm::createFunctionInliningPass(threshold);
|
||||||
|
}
|
||||||
|
// builder.DisableUnitAtATime = !unitAtATime;
|
||||||
|
builder.DisableUnrollLoops = optLevel == 0;
|
||||||
|
|
||||||
|
// builder.DisableUnrollLoops = (disableLoopUnrolling.getNumOccurrences() > 0)
|
||||||
|
// ? disableLoopUnrolling
|
||||||
|
// : optLevel == 0;
|
||||||
|
|
||||||
|
// This is final, unless there is a #pragma vectorize enable
|
||||||
|
if (/*disableLoopVectorization*/false) {
|
||||||
|
builder.LoopVectorize = false;
|
||||||
|
// If option wasn't forced via cmd line (-vectorize-loops, -loop-vectorize)
|
||||||
|
} else if (!builder.LoopVectorize) {
|
||||||
|
builder.LoopVectorize = optLevel > 1 && sizeLevel < 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When #pragma vectorize is on for SLP, do the same as above
|
||||||
|
builder.SLPVectorize =
|
||||||
|
/*disableSLPVectorization*/false ? false : optLevel > 1 && sizeLevel < 2;
|
||||||
|
|
||||||
|
// if (opts::sanitize == opts::AddressSanitizer) {
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_OptimizerLast,
|
||||||
|
// addAddressSanitizerPasses);
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
|
||||||
|
// addAddressSanitizerPasses);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (opts::sanitize == opts::MemorySanitizer) {
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_OptimizerLast,
|
||||||
|
// addMemorySanitizerPass);
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
|
||||||
|
// addMemorySanitizerPass);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (opts::sanitize == opts::ThreadSanitizer) {
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_OptimizerLast,
|
||||||
|
// addThreadSanitizerPass);
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
|
||||||
|
// addThreadSanitizerPass);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!disableLangSpecificPasses) {
|
||||||
|
// if (!disableSimplifyDruntimeCalls) {
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd,
|
||||||
|
// addSimplifyDRuntimeCallsPass);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!disableGCToStack) {
|
||||||
|
// builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd,
|
||||||
|
// addGarbageCollect2StackPass);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// EP_OptimizerLast does not exist in LLVM 3.0, add it manually below.
|
||||||
|
// builder.addExtension(llvm::PassManagerBuilder::EP_OptimizerLast,
|
||||||
|
// addStripExternalsPass);
|
||||||
|
|
||||||
|
// addInstrProfilingPass(mpm);
|
||||||
|
|
||||||
|
builder.populateFunctionPassManager(fpm);
|
||||||
|
builder.populateModulePassManager(mpm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupPasses(llvm::TargetMachine &targetMachine,
|
||||||
|
const OptimizerSettings& settings,
|
||||||
|
llvm::legacy::PassManager &mpm,
|
||||||
|
llvm::legacy::FunctionPassManager &fpm) {
|
||||||
|
mpm.add(new llvm::TargetLibraryInfoWrapperPass(
|
||||||
|
targetMachine.getTargetTriple()));
|
||||||
|
mpm.add(llvm::createTargetTransformInfoWrapperPass(
|
||||||
|
targetMachine.getTargetIRAnalysis()));
|
||||||
|
fpm.add(llvm::createTargetTransformInfoWrapperPass(
|
||||||
|
targetMachine.getTargetIRAnalysis()));
|
||||||
|
|
||||||
|
if (/*stripDebug*/true) {
|
||||||
|
mpm.add(llvm::createStripSymbolsPass(true));
|
||||||
|
}
|
||||||
|
mpm.add(llvm::createStripDeadPrototypesPass());
|
||||||
|
mpm.add(llvm::createStripDeadDebugInfoPass());
|
||||||
|
|
||||||
|
addOptimizationPasses(mpm, fpm, settings.optLevel, settings.sizeLeve);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FuncFinalizer final {
|
||||||
|
llvm::legacy::FunctionPassManager& fpm;
|
||||||
|
explicit FuncFinalizer(llvm::legacy::FunctionPassManager& _fpm):
|
||||||
|
fpm(_fpm) {
|
||||||
|
fpm.doInitialization();
|
||||||
|
}
|
||||||
|
~FuncFinalizer() {
|
||||||
|
fpm.doFinalization();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anon namespace
|
||||||
|
|
||||||
|
void optimizeModule(const Context &context,
|
||||||
|
llvm::TargetMachine &targetMachine,
|
||||||
|
const OptimizerSettings &settings,
|
||||||
|
llvm::Module &module)
|
||||||
|
{
|
||||||
|
llvm::legacy::PassManager mpm;
|
||||||
|
llvm::legacy::FunctionPassManager fpm(&module);
|
||||||
|
const auto name = module.getName();
|
||||||
|
interruptPoint(context, "Setup passes for module", name.data());
|
||||||
|
setupPasses(targetMachine, settings, mpm, fpm);
|
||||||
|
|
||||||
|
// Run per-function passes.
|
||||||
|
{
|
||||||
|
FuncFinalizer finalizer(fpm);
|
||||||
|
for (auto &fun : module) {
|
||||||
|
interruptPoint(context, "Run passes for function", fun.getName().data());
|
||||||
|
fpm.run(fun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run per-module passes.
|
||||||
|
interruptPoint(context, "Run passes for module", name.data());
|
||||||
|
mpm.run(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRtCompileVar(const Context &context,
|
||||||
|
llvm::Module &module,
|
||||||
|
const char *name,
|
||||||
|
const void *init) {
|
||||||
|
assert(nullptr != name);
|
||||||
|
assert(nullptr != init);
|
||||||
|
auto var = module.getGlobalVariable(name);
|
||||||
|
if (nullptr != var) {
|
||||||
|
auto type = var->getType()->getElementType();
|
||||||
|
auto initializer = parseInitializer(context,
|
||||||
|
module.getDataLayout(),
|
||||||
|
type,
|
||||||
|
init);
|
||||||
|
var->setConstant(true);
|
||||||
|
var->setInitializer(initializer);
|
||||||
|
var->setLinkage(llvm::GlobalValue::PrivateLinkage);
|
||||||
|
// auto tempVar = new llvm::GlobalVariable(
|
||||||
|
// module,
|
||||||
|
// type,
|
||||||
|
// true,
|
||||||
|
// llvm::GlobalValue::PrivateLinkage,
|
||||||
|
// initializer,
|
||||||
|
// ".str");
|
||||||
|
// llvm::Constant *idxs[] = {zero};
|
||||||
|
// auto constPtr = llvm::ConstantExpr::getGetElementPtr(nullptr,
|
||||||
|
// tempVar,
|
||||||
|
// idxs,
|
||||||
|
// true);
|
||||||
|
// for (auto&& use: var->uses()) {
|
||||||
|
// use->dump();
|
||||||
|
// use->getType()->dump();
|
||||||
|
// auto i = llvm::cast<llvm::GlobalVariable>(use);
|
||||||
|
// i->replaceAllUsesWith(constPtr);
|
||||||
|
// i->eraseFromParent();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var->replaceAllUsesWith(initializer);
|
||||||
|
// var->eraseFromParent();
|
||||||
|
}
|
||||||
|
}
|
32
runtime/jit-rt/cpp-so/optimizer.h
Normal file
32
runtime/jit-rt/cpp-so/optimizer.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef OPTIMIZER_HPP
|
||||||
|
#define OPTIMIZER_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace legacy {
|
||||||
|
class PassManager;
|
||||||
|
class FunctionPassManager;
|
||||||
|
}
|
||||||
|
class TargetMachine;
|
||||||
|
class Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context;
|
||||||
|
|
||||||
|
struct OptimizerSettings final {
|
||||||
|
unsigned optLevel = 0;
|
||||||
|
unsigned sizeLeve = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void optimizeModule(const Context &context,
|
||||||
|
llvm::TargetMachine &targetMachine,
|
||||||
|
const OptimizerSettings &settings,
|
||||||
|
llvm::Module &module);
|
||||||
|
|
||||||
|
void setRtCompileVar(const Context &context,
|
||||||
|
llvm::Module& module,
|
||||||
|
const char* name,
|
||||||
|
const void* init);
|
||||||
|
|
||||||
|
#endif // OPTIMIZER_HPP
|
44
runtime/jit-rt/cpp-so/utils.cpp
Normal file
44
runtime/jit-rt/cpp-so/utils.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/IR/Verifier.h"
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
|
||||||
|
void fatal(const Context &context, const std::string &reason)
|
||||||
|
{
|
||||||
|
if (nullptr != context.fatalHandler) {
|
||||||
|
context.fatalHandler(context.fatalHandlerData,
|
||||||
|
reason.c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Runtime compiler fatal: %s\n", reason.c_str());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void interruptPoint(const Context &context, const char *desc, const char *object)
|
||||||
|
{
|
||||||
|
assert(nullptr != desc);
|
||||||
|
if (nullptr != context.interruptPointHandler) {
|
||||||
|
context.interruptPointHandler(context.interruptPointHandlerData,
|
||||||
|
desc,
|
||||||
|
object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyModule(const Context &context, llvm::Module &module)
|
||||||
|
{
|
||||||
|
std::string err;
|
||||||
|
llvm::raw_string_ostream errstream(err);
|
||||||
|
if (llvm::verifyModule(module, &errstream)) {
|
||||||
|
std::string desc = std::string("module verification failed:") +
|
||||||
|
errstream.str();
|
||||||
|
fatal(context, desc);
|
||||||
|
}
|
||||||
|
}
|
15
runtime/jit-rt/cpp-so/utils.h
Normal file
15
runtime/jit-rt/cpp-so/utils.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef UTILS_HPP
|
||||||
|
#define UTILS_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct Context;
|
||||||
|
namespace llvm {
|
||||||
|
class Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatal(const Context &context, const std::string &reason);
|
||||||
|
void interruptPoint(const Context &context, const char *desc, const char *object = "");
|
||||||
|
void verifyModule(const Context &context, llvm::Module &module);
|
||||||
|
|
||||||
|
#endif // UTILS_HPP
|
101
runtime/jit-rt/cpp-so/valueparser.cpp
Normal file
101
runtime/jit-rt/cpp-so/valueparser.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include "valueparser.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/IR/DerivedTypes.h"
|
||||||
|
#include "llvm/IR/DataLayout.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
llvm::ConstantInt *getInt(llvm::LLVMContext &context, const void *data) {
|
||||||
|
assert(nullptr != data);
|
||||||
|
const T val = *static_cast<const T*>(data);
|
||||||
|
return llvm::ConstantInt::get(context, llvm::APInt(sizeof(T) * 8, val, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
llvm::ConstantFP *getFloat(llvm::LLVMContext &context, const void *data) {
|
||||||
|
assert(nullptr != data);
|
||||||
|
const T val = *static_cast<const T*>(data);
|
||||||
|
return llvm::ConstantFP::get(context, llvm::APFloat(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *getPtr(llvm::LLVMContext &context,
|
||||||
|
llvm::Type *targetType,
|
||||||
|
const void *data) {
|
||||||
|
assert(nullptr != targetType);
|
||||||
|
assert(nullptr != data);
|
||||||
|
const auto val = *static_cast<const uintptr_t*>(data);
|
||||||
|
return llvm::ConstantExpr::getIntToPtr(
|
||||||
|
llvm::ConstantInt::get(context, llvm::APInt(sizeof(val) * 8, val)),
|
||||||
|
targetType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *parseInitializer(const Context &context,
|
||||||
|
const llvm::DataLayout &dataLayout,
|
||||||
|
llvm::Type *type,
|
||||||
|
const void *data) {
|
||||||
|
assert(nullptr != type);
|
||||||
|
assert(nullptr != data);
|
||||||
|
auto& llcontext = type->getContext();
|
||||||
|
if (type->isIntegerTy()) {
|
||||||
|
const auto width = type->getIntegerBitWidth();
|
||||||
|
switch (width) {
|
||||||
|
case 8: return getInt<uint8_t>(llcontext, data);
|
||||||
|
case 16: return getInt<uint16_t>(llcontext, data);
|
||||||
|
case 32: return getInt<uint32_t>(llcontext, data);
|
||||||
|
case 64: return getInt<uint64_t>(llcontext, data);
|
||||||
|
default: fatal(context,
|
||||||
|
std::string("Invalid int bit width: ") + std::to_string(width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type->isFloatingPointTy()) {
|
||||||
|
const auto width = type->getPrimitiveSizeInBits();
|
||||||
|
switch (width) {
|
||||||
|
case 32: return getFloat<float>(llcontext, data);
|
||||||
|
case 64: return getFloat<double>(llcontext, data);
|
||||||
|
default: fatal(context,
|
||||||
|
std::string("Invalid fp bit width: ") + std::to_string(width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type->isPointerTy()) {
|
||||||
|
return getPtr(llcontext, type, data);
|
||||||
|
}
|
||||||
|
if (type->isStructTy()) {
|
||||||
|
auto stype = llvm::cast<llvm::StructType>(type);
|
||||||
|
auto slayout = dataLayout.getStructLayout(stype);
|
||||||
|
auto numElements = stype->getNumElements();
|
||||||
|
llvm::SmallVector<llvm::Constant*, 16> elements(numElements);
|
||||||
|
for (unsigned i = 0; i < numElements; ++i) {
|
||||||
|
const auto elemType = stype->getElementType(i);
|
||||||
|
const auto elemOffset = slayout->getElementOffset(i);
|
||||||
|
const auto elemPtr = static_cast<const char*>(data) + elemOffset;
|
||||||
|
elements[i] = parseInitializer(context, dataLayout, elemType, elemPtr);
|
||||||
|
}
|
||||||
|
return llvm::ConstantStruct::get(stype, elements);
|
||||||
|
}
|
||||||
|
if (type->isArrayTy()) {
|
||||||
|
auto elemType = type->getArrayElementType();
|
||||||
|
const auto step = dataLayout.getTypeAllocSize(elemType);
|
||||||
|
const auto numElements = type->getArrayNumElements();
|
||||||
|
llvm::SmallVector<llvm::Constant*, 16> elements(numElements);
|
||||||
|
for (uint64_t i = 0; i < numElements; ++i) {
|
||||||
|
const auto elemPtr = static_cast<const char*>(data) + step * i;
|
||||||
|
elements[i] = parseInitializer(context, dataLayout, elemType, elemPtr);
|
||||||
|
}
|
||||||
|
return llvm::ConstantArray::get(llvm::cast<llvm::ArrayType>(type), elements);
|
||||||
|
}
|
||||||
|
std::string tname;
|
||||||
|
llvm::raw_string_ostream os(tname);
|
||||||
|
type->print(os, true);
|
||||||
|
fatal(context, std::string("Unhandled type: ") + os.str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
18
runtime/jit-rt/cpp-so/valueparser.h
Normal file
18
runtime/jit-rt/cpp-so/valueparser.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef VALUEPARSER_H
|
||||||
|
#define VALUEPARSER_H
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
class Constant;
|
||||||
|
class Type;
|
||||||
|
class DataLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context;
|
||||||
|
|
||||||
|
llvm::Constant *parseInitializer(const Context &context,
|
||||||
|
const llvm::DataLayout &dataLayout,
|
||||||
|
llvm::Type *type,
|
||||||
|
const void *data);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // VALUEPARSER_H
|
22
runtime/jit-rt/cpp/compile.cpp
Normal file
22
runtime/jit-rt/cpp/compile.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
struct Context;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
// Silence missing-variable-declaration clang warning
|
||||||
|
extern void* runtimecompile_modules_head;
|
||||||
|
|
||||||
|
void* runtimecompile_modules_head = nullptr;
|
||||||
|
#ifdef _WIN32
|
||||||
|
__declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
extern void rtCompileProcessImplSo(const void *modlist_head,
|
||||||
|
const Context *context,
|
||||||
|
std::size_t contextSize);
|
||||||
|
|
||||||
|
void rtCompileProcessImpl(const Context *context, std::size_t contextSize)
|
||||||
|
{
|
||||||
|
rtCompileProcessImplSo(runtimecompile_modules_head, context, contextSize);
|
||||||
|
}
|
||||||
|
}
|
71
runtime/jit-rt/d/ldc/runtimecompile.d
Normal file
71
runtime/jit-rt/d/ldc/runtimecompile.d
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
module ldc.runtimecompile;
|
||||||
|
|
||||||
|
struct CompilerSettings
|
||||||
|
{
|
||||||
|
uint optLevel = 0;
|
||||||
|
uint sizeLeve = 0;
|
||||||
|
void delegate(const char*,const char*) interruptHandler = null;
|
||||||
|
void delegate(const char*) fatalHandler = null;
|
||||||
|
void delegate(in char[]) dumpHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtCompileProcess(in CompilerSettings settings = CompilerSettings.init)
|
||||||
|
{
|
||||||
|
Context context;
|
||||||
|
context.optLevel = settings.optLevel;
|
||||||
|
context.sizeLeve = settings.sizeLeve;
|
||||||
|
if (settings.interruptHandler !is null)
|
||||||
|
{
|
||||||
|
context.interruptPointHandler = &delegateWrapper!(const char*,const char*);
|
||||||
|
context.interruptPointHandlerData = cast(void*)&settings.interruptHandler;
|
||||||
|
}
|
||||||
|
if (settings.fatalHandler !is null)
|
||||||
|
{
|
||||||
|
context.fatalHandler = &delegateWrapper!(const char*);
|
||||||
|
context.fatalHandlerData = cast(void*)&settings.fatalHandler;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.fatalHandler = &defaultFatalHandler;
|
||||||
|
}
|
||||||
|
if (settings.dumpHandler !is null)
|
||||||
|
{
|
||||||
|
context.dumpHandler = &delegateWrapper!(const char*, size_t);
|
||||||
|
context.dumpHandlerData = cast(void*)&settings.dumpHandler;
|
||||||
|
}
|
||||||
|
rtCompileProcessImpl(context, context.sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
extern(C)
|
||||||
|
{
|
||||||
|
|
||||||
|
void delegateWrapper(T...)(void* context, T params)
|
||||||
|
{
|
||||||
|
alias del_type = void delegate(T);
|
||||||
|
auto del = cast(del_type*)context;
|
||||||
|
(*del)(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void defaultFatalHandler(void*, const char* reason)
|
||||||
|
{
|
||||||
|
import std.conv;
|
||||||
|
throw new Exception(reason.text.idup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be synchronized with cpp
|
||||||
|
align(1) struct Context
|
||||||
|
{
|
||||||
|
align(1):
|
||||||
|
uint optLevel = 0;
|
||||||
|
uint sizeLeve = 0;
|
||||||
|
void function(void*, const char*, const char*) interruptPointHandler = null;
|
||||||
|
void* interruptPointHandlerData = null;
|
||||||
|
void function(void*, const char*) fatalHandler = null;
|
||||||
|
void* fatalHandlerData = null;
|
||||||
|
void function(void*, const char*, size_t) dumpHandler = null;
|
||||||
|
void* dumpHandlerData = null;
|
||||||
|
}
|
||||||
|
extern void rtCompileProcessImpl(const ref Context context, size_t contextSize);
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ config.llvm_version = @LDC_LLVM_VER@
|
||||||
config.llvm_targetsstr = "@LLVM_TARGETS_TO_BUILD@"
|
config.llvm_targetsstr = "@LLVM_TARGETS_TO_BUILD@"
|
||||||
config.default_target_bits = @DEFAULT_TARGET_BITS@
|
config.default_target_bits = @DEFAULT_TARGET_BITS@
|
||||||
config.with_PGO = @LDC_WITH_PGO@
|
config.with_PGO = @LDC_WITH_PGO@
|
||||||
|
config.runtime_compile = @LDC_RUNTIME_COMPILE@
|
||||||
|
|
||||||
config.name = 'LDC'
|
config.name = 'LDC'
|
||||||
|
|
||||||
|
@ -45,6 +46,10 @@ config.excludes = [
|
||||||
if not config.with_PGO:
|
if not config.with_PGO:
|
||||||
config.excludes.append('PGO')
|
config.excludes.append('PGO')
|
||||||
|
|
||||||
|
# Exclude runtime compilation tests when it's disabled
|
||||||
|
if not config.runtime_compile:
|
||||||
|
config.excludes.append('runtimecompile')
|
||||||
|
|
||||||
|
|
||||||
# Define available features so that we can disable tests depending on LLVM version
|
# Define available features so that we can disable tests depending on LLVM version
|
||||||
config.available_features.add("llvm%d" % config.llvm_version)
|
config.available_features.add("llvm%d" % config.llvm_version)
|
||||||
|
@ -113,6 +118,11 @@ config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir,
|
||||||
|
|
||||||
# Add platform-dependent file extension substitutions
|
# Add platform-dependent file extension substitutions
|
||||||
if (platform.system() == 'Windows'):
|
if (platform.system() == 'Windows'):
|
||||||
|
# add LDC lib dir to the path so app will be able to find jit.dll
|
||||||
|
# TODO: Something more robust
|
||||||
|
path = os.path.pathsep.join( (config.ldc2_lib_dir, config.environment['PATH']) )
|
||||||
|
config.environment['PATH'] = path
|
||||||
|
|
||||||
config.substitutions.append( ('%obj', '.obj') )
|
config.substitutions.append( ('%obj', '.obj') )
|
||||||
config.substitutions.append( ('%exe', '.exe') )
|
config.substitutions.append( ('%exe', '.exe') )
|
||||||
else:
|
else:
|
||||||
|
|
28
tests/runtimecompile/array.d
Normal file
28
tests/runtimecompile/array.d
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import std.exception;
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
__gshared int[555] arr1 = 42;
|
||||||
|
__gshared int[555] arr2 = 42;
|
||||||
|
|
||||||
|
@runtimeCompile int foo()
|
||||||
|
{
|
||||||
|
int[555] a = arr1;
|
||||||
|
return a[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile int bar()
|
||||||
|
{
|
||||||
|
arr2 = 0;
|
||||||
|
return arr2[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
assert(42 == foo());
|
||||||
|
assert(0 == bar());
|
||||||
|
}
|
21
tests/runtimecompile/dump_handler.d
Normal file
21
tests/runtimecompile/dump_handler.d
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
@runtimeCompile int foo()
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
bool dumpHandlerCalled = false;
|
||||||
|
CompilerSettings settings;
|
||||||
|
settings.dumpHandler = ((a) { dumpHandlerCalled = true; });
|
||||||
|
rtCompileProcess(settings);
|
||||||
|
assert(5 == foo());
|
||||||
|
assert(dumpHandlerCalled);
|
||||||
|
}
|
22
tests/runtimecompile/globals.d
Normal file
22
tests/runtimecompile/globals.d
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
@runtimeCompile __gshared int foovar = 0;
|
||||||
|
|
||||||
|
@runtimeCompile int foo()
|
||||||
|
{
|
||||||
|
return foovar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
assert(0 == foo());
|
||||||
|
foovar = 42;
|
||||||
|
assert(0 == foo());
|
||||||
|
rtCompileProcess();
|
||||||
|
assert(42 == foo());
|
||||||
|
}
|
94
tests/runtimecompile/globals_types.d
Normal file
94
tests/runtimecompile/globals_types.d
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
struct Foo
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float f;
|
||||||
|
void* p;
|
||||||
|
int[3] a;
|
||||||
|
int[] da;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar
|
||||||
|
{
|
||||||
|
Foo f;
|
||||||
|
Foo* pf;
|
||||||
|
Bar* pb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile __gshared byte i8 = 42 + 1;
|
||||||
|
@runtimeCompile __gshared short i16 = 42 + 2;
|
||||||
|
@runtimeCompile __gshared int i32 = 42 + 3;
|
||||||
|
@runtimeCompile __gshared long i64 = 42 + 4;
|
||||||
|
|
||||||
|
@runtimeCompile __gshared ubyte u8 = 42 + 5;
|
||||||
|
@runtimeCompile __gshared ushort u16 = 42 + 6;
|
||||||
|
@runtimeCompile __gshared uint u32 = 42 + 7;
|
||||||
|
@runtimeCompile __gshared ulong u64 = 42 + 8;
|
||||||
|
|
||||||
|
@runtimeCompile __gshared float f32 = 42 + 9;
|
||||||
|
@runtimeCompile __gshared double f64 = 42 + 10;
|
||||||
|
|
||||||
|
@runtimeCompile __gshared void* ptr = cast(void*)(42 + 11);
|
||||||
|
|
||||||
|
@runtimeCompile __gshared int[3] arr = [42 + 12,42 + 13,42 + 14];
|
||||||
|
@runtimeCompile __gshared int[] darr = [42 + 15,42 + 16,42 + 17,42 + 18];
|
||||||
|
|
||||||
|
@runtimeCompile __gshared Foo foo = Foo(42 + 19,42 + 20,cast(void*)(42 + 21),[42 + 22,42 + 23,42 + 24],[42 + 25,42 + 26,42 + 27,42 + 28]);
|
||||||
|
@runtimeCompile __gshared Bar bar = Bar(Foo(42 + 19,42 + 20,cast(void*)(42 + 21),[42 + 22,42 + 23,42 + 24],[42 + 25,42 + 26,42 + 27,42 + 28]), cast(Foo*)(42 + 29), cast(Bar*)(42 + 30));
|
||||||
|
|
||||||
|
@runtimeCompile byte foo_i8() { return i8; }
|
||||||
|
@runtimeCompile short foo_i16() { return i16; }
|
||||||
|
@runtimeCompile int foo_i32() { return i32; }
|
||||||
|
@runtimeCompile long foo_i64() { return i64; }
|
||||||
|
|
||||||
|
@runtimeCompile ubyte foo_u8() { return u8; }
|
||||||
|
@runtimeCompile ushort foo_u16() { return u16; }
|
||||||
|
@runtimeCompile uint foo_u32() { return u32; }
|
||||||
|
@runtimeCompile ulong foo_u64() { return u64; }
|
||||||
|
|
||||||
|
@runtimeCompile float foo_f32() { return f32; }
|
||||||
|
@runtimeCompile double foo_f64() { return f64; }
|
||||||
|
|
||||||
|
@runtimeCompile void* foo_ptr() { return ptr; }
|
||||||
|
|
||||||
|
@runtimeCompile int[3] foo_arr() { return arr; }
|
||||||
|
@runtimeCompile int[] foo_darr() { return darr; }
|
||||||
|
|
||||||
|
@runtimeCompile Foo foo_foo() { return foo; }
|
||||||
|
@runtimeCompile Bar foo_bar() { return bar; }
|
||||||
|
|
||||||
|
void test(T,F)(ref T val, F fun)
|
||||||
|
{
|
||||||
|
assert(val == fun());
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
|
||||||
|
test(i8, &foo_i8);
|
||||||
|
test(i16, &foo_i16);
|
||||||
|
test(i32, &foo_i32);
|
||||||
|
test(i64, &foo_i64);
|
||||||
|
|
||||||
|
test(u8, &foo_u8);
|
||||||
|
test(u16, &foo_u16);
|
||||||
|
test(u32, &foo_u32);
|
||||||
|
test(u64, &foo_u64);
|
||||||
|
|
||||||
|
test(f32, &foo_f32);
|
||||||
|
test(f64, &foo_f64);
|
||||||
|
|
||||||
|
assert(ptr is foo_ptr());
|
||||||
|
|
||||||
|
test(arr, &foo_arr);
|
||||||
|
test(darr, &foo_darr);
|
||||||
|
|
||||||
|
test(foo, &foo_foo);
|
||||||
|
test(bar, &foo_bar);
|
||||||
|
}
|
31
tests/runtimecompile/simple.d
Normal file
31
tests/runtimecompile/simple.d
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
@runtimeCompile int foo()
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile int bar()
|
||||||
|
{
|
||||||
|
return foo() + 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile void baz()
|
||||||
|
{
|
||||||
|
writeln("baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
assert(5 == foo());
|
||||||
|
assert(12 == bar());
|
||||||
|
baz();
|
||||||
|
int function() fptr = &bar;
|
||||||
|
assert(12 == fptr());
|
||||||
|
}
|
34
tests/runtimecompile/thread_local.d
Normal file
34
tests/runtimecompile/thread_local.d
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import core.thread;
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
ThreadID threadId; //thread local
|
||||||
|
|
||||||
|
@runtimeCompile void foo()
|
||||||
|
{
|
||||||
|
threadId = Thread.getThis().id();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bar()
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
assert(threadId == Thread.getThis().id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
bar();
|
||||||
|
Thread[] threads = [new Thread(&bar),new Thread(&bar),new Thread(&bar)];
|
||||||
|
foreach(t;threads[])
|
||||||
|
{
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
foreach(t;threads[])
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
45
tests/runtimecompile/throw.d
Normal file
45
tests/runtimecompile/throw.d
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
// RUN: %ldc -enable-runtime-compile -run %s
|
||||||
|
|
||||||
|
import std.exception;
|
||||||
|
import ldc.attributes;
|
||||||
|
import ldc.runtimecompile;
|
||||||
|
|
||||||
|
@runtimeCompile void foo()
|
||||||
|
{
|
||||||
|
throw new Exception("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile int bar()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
throw new Exception("foo");
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@runtimeCompile int baz()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args)
|
||||||
|
{
|
||||||
|
rtCompileProcess();
|
||||||
|
assert(collectExceptionMsg(foo()) == "foo");
|
||||||
|
assert(42 == bar());
|
||||||
|
assert(42 == baz());
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue