mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 00:55:49 +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"]
|
||||
path = runtime/druntime
|
||||
url = https://github.com/ldc-developers/druntime.git
|
||||
url = https://github.com/Hardcode84/druntime.git
|
||||
branch = runtime_compile
|
||||
[submodule "phobos"]
|
||||
path = runtime/phobos
|
||||
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")
|
||||
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.
|
||||
#
|
||||
|
|
|
@ -234,6 +234,8 @@ struct Param
|
|||
uint32_t hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)
|
||||
|
||||
bool outputSourceLocations; // if true, output line tables.
|
||||
|
||||
bool enableRuntimeCompile;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -412,6 +412,7 @@ Msgtable[] msgtable =
|
|||
{ "udaWeak", "_weak" },
|
||||
{ "udaCompute", "compute" },
|
||||
{ "udaKernel", "_kernel" },
|
||||
{ "udaRuntimeCompile", "_runtimeCompile" },
|
||||
|
||||
// IN_LLVM: DCompute specific types and functionss
|
||||
{ "dcompute" },
|
||||
|
|
|
@ -521,6 +521,14 @@ cl::list<std::string>
|
|||
cl::value_desc("targets"));
|
||||
#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(
|
||||
"\n"
|
||||
"-d-debug can also be specified without options, in which case it enables "
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "gen/logger.h"
|
||||
#include "gen/modules.h"
|
||||
#include "gen/runtime.h"
|
||||
#include "gen/runtimecompile.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
@ -246,6 +247,7 @@ void CodeGenerator::writeAndFreeLLModule(const char *filename) {
|
|||
ir_->replaceGlobals();
|
||||
|
||||
ir_->DBuilder.Finalize();
|
||||
generateBitcodeForRuntimeCompile(ir_);
|
||||
|
||||
emitLLVMUsedArray(*ir_);
|
||||
emitLinkerOptions(*ir_, ir_->module, ir_->context());
|
||||
|
|
|
@ -340,6 +340,11 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
|
|||
args.push_back("-lldc-profile-rt");
|
||||
}
|
||||
|
||||
if (global.params.enableRuntimeCompile) {
|
||||
args.push_back("-lldc-jit-rt");
|
||||
args.push_back("-lldc-jit");
|
||||
}
|
||||
|
||||
// user libs
|
||||
for (auto libfile : *global.params.libfiles) {
|
||||
args.push_back(libfile);
|
||||
|
|
|
@ -118,6 +118,11 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker,
|
|||
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
|
||||
for (auto libfile : *global.params.libfiles) {
|
||||
args.push_back(libfile);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "gen/pgo.h"
|
||||
#include "gen/pragma.h"
|
||||
#include "gen/runtime.h"
|
||||
#include "gen/runtimecompile.h"
|
||||
#include "gen/scope_exit.h"
|
||||
#include "gen/tollvm.h"
|
||||
#include "gen/uda.h"
|
||||
|
@ -463,6 +464,18 @@ void applyTargetMachineAttributes(llvm::Function &func,
|
|||
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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -522,7 +535,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
|||
const auto link = forceC ? LINKc : f->linkage;
|
||||
|
||||
// mangled name
|
||||
std::string mangledName = getMangledName(fdecl, link);
|
||||
const std::string mangledName = getMangledName(fdecl, link);
|
||||
|
||||
// construct function
|
||||
LLFunctionType *functype = DtoFunctionType(fdecl);
|
||||
|
@ -566,6 +579,10 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
|||
applyTargetMachineAttributes(*func, *gTargetMachine);
|
||||
applyFuncDeclUDAs(fdecl, irFunc);
|
||||
|
||||
if(irFunc->runtimeCompile) {
|
||||
declareRuntimeCompiledFunction(gIR, irFunc);
|
||||
}
|
||||
|
||||
// main
|
||||
if (fdecl->isMain()) {
|
||||
// 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 (fd->naked) {
|
||||
DtoDefineNakedFunction(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
IrFunction *irFunc = getIrFunc(fd);
|
||||
|
||||
// debug info
|
||||
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
|
||||
|
||||
|
|
|
@ -219,6 +219,14 @@ public:
|
|||
// setGlobalVarInitializer().
|
||||
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.
|
||||
#if LDC_LLVM_VER >= 500
|
||||
llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <stack>
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "gen/runtimecompile.h"
|
||||
|
||||
llvm::cl::opt<llvm::GlobalVariable::ThreadLocalMode> clThreadModel(
|
||||
"fthread-model", llvm::cl::ZeroOrMore, llvm::cl::desc("Thread model"),
|
||||
|
@ -856,7 +857,8 @@ void DtoResolveVariable(VarDeclaration *vd) {
|
|||
llvm::GlobalVariable *gvar =
|
||||
getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst,
|
||||
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
|
||||
// VarDeclarations can have an align() attribute independent of the type
|
||||
|
@ -872,6 +874,9 @@ void DtoResolveVariable(VarDeclaration *vd) {
|
|||
*/
|
||||
|
||||
applyVarDeclUDAs(vd, gvar);
|
||||
if (varIr->runtimeCompile) {
|
||||
addRuntimeCompiledVar(gIR, varIr);
|
||||
}
|
||||
|
||||
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 "expression.h"
|
||||
#include "ir/irfunction.h"
|
||||
#include "ir/irvar.h"
|
||||
#include "module.h"
|
||||
#include "id.h"
|
||||
|
||||
|
@ -359,6 +360,8 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
|
|||
ident->toChars());
|
||||
} else if (ident == Id::udaWeak) {
|
||||
// @weak is applied elsewhere
|
||||
} else if (ident == Id::udaRuntimeCompile) {
|
||||
getIrGlobal(decl)->runtimeCompile = true;
|
||||
} else {
|
||||
sle->warning(
|
||||
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
||||
|
@ -395,6 +398,8 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
|
|||
applyAttrTarget(sle, func);
|
||||
} else if (ident == Id::udaWeak || ident == Id::udaKernel) {
|
||||
// @weak and @kernel are applied elsewhere
|
||||
} else if (ident == Id::udaRuntimeCompile) {
|
||||
irFunc->runtimeCompile = true;
|
||||
} else {
|
||||
sle->warning(
|
||||
"Ignoring unrecognized special attribute 'ldc.attributes.%s'", ident->toChars());
|
||||
|
|
|
@ -74,7 +74,7 @@ llvm::StringRef IrFunction::getLLVMFuncName() const {
|
|||
|
||||
llvm::Function *IrFunction::getLLVMCallee() const {
|
||||
assert(func != nullptr);
|
||||
return func;
|
||||
return rtCompileFunc != nullptr ? rtCompileFunc : func;
|
||||
}
|
||||
|
||||
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.
|
||||
llvm::FastMathFlags FMF;
|
||||
|
||||
bool runtimeCompile = false;
|
||||
llvm::Function *rtCompileFunc = nullptr;
|
||||
|
||||
private:
|
||||
llvm::Function *func = nullptr;
|
||||
};
|
||||
|
|
|
@ -27,6 +27,8 @@ struct IrVar {
|
|||
|
||||
VarDeclaration *V = nullptr;
|
||||
llvm::Value *value = nullptr;
|
||||
|
||||
bool runtimeCompile = false;
|
||||
};
|
||||
|
||||
// represents a global variable
|
||||
|
|
|
@ -8,6 +8,7 @@ default:
|
|||
switches = [
|
||||
"-I@RUNTIME_DIR@/src",
|
||||
"-I@PROFILERT_DIR@/d",
|
||||
"-I@JITRT_DIR@/d",
|
||||
"-I@PHOBOS2_DIR@",
|
||||
"-L-L@CMAKE_BINARY_DIR@/lib@LIB_SUFFIX@", @MULTILIB_ADDITIONAL_PATH@@SHARED_LIBS_RPATH@
|
||||
"-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(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(JITRT_DIR ${PROJECT_SOURCE_DIR}/jit-rt CACHE PATH "jit runtime root directory")
|
||||
|
||||
#
|
||||
# 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)
|
||||
set_common_library_properties(ldc-profile-rt${target_suffix})
|
||||
endif()
|
||||
|
||||
build_jit_runtime ("${d_flags}" "${c_flags}" "${ld_flags}" "${path_suffix}" ${outlist_targets})
|
||||
endmacro()
|
||||
|
||||
|
||||
# Setup the build of profile-rt
|
||||
include(profile-rt/DefineBuildProfileRT.cmake)
|
||||
|
||||
# Setup the build of jit runtime
|
||||
include(jit-rt/DefineBuildJitRT.cmake)
|
||||
|
||||
#
|
||||
# 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.default_target_bits = @DEFAULT_TARGET_BITS@
|
||||
config.with_PGO = @LDC_WITH_PGO@
|
||||
config.runtime_compile = @LDC_RUNTIME_COMPILE@
|
||||
|
||||
config.name = 'LDC'
|
||||
|
||||
|
@ -45,6 +46,10 @@ config.excludes = [
|
|||
if not config.with_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
|
||||
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
|
||||
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( ('%exe', '.exe') )
|
||||
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