D dynamic compilation support

This commit is contained in:
Ivan 2016-12-03 21:14:42 +03:00
parent 1c2271d00d
commit 42f283c221
41 changed files with 2058 additions and 9 deletions

3
.gitmodules vendored
View file

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

View file

@ -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.
#

View file

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

View file

@ -412,6 +412,7 @@ Msgtable[] msgtable =
{ "udaWeak", "_weak" },
{ "udaCompute", "compute" },
{ "udaKernel", "_kernel" },
{ "udaRuntimeCompile", "_runtimeCompile" },
// IN_LLVM: DCompute specific types and functionss
{ "dcompute" },

View file

@ -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 "

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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
View 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

View file

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

View file

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

View file

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

View file

@ -27,6 +27,8 @@ struct IrVar {
VarDeclaration *V = nullptr;
llvm::Value *value = nullptr;
bool runtimeCompile = false;
};
// represents a global variable

View file

@ -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",

View file

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

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

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

View 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

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

View 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

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

View 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

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

View 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

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

View 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

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

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

View file

@ -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:

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

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

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

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

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

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

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