Merge branch 'master' into merge-2.077

Conflicts:
	runtime/druntime
This commit is contained in:
Martin 2017-11-26 18:26:32 +01:00
commit 213de9b2f7
69 changed files with 3581 additions and 61 deletions

View file

@ -382,6 +382,17 @@ set(LDC_WITH_PGO True) # must be a valid Python boolean constant (case sensitiv
message(STATUS "Building LDC with PGO support") message(STATUS "Building LDC with PGO support")
append("-DLDC_WITH_PGO" LDC_CXXFLAGS) append("-DLDC_WITH_PGO" LDC_CXXFLAGS)
#
# Enable Dynamic compilation if supported for this platform and LLVM version.
# LLVM >= 3.9 is required
#
set(LDC_DYNAMIC_COMPILE False) # must be a valid Python boolean constant (case sensitive)
if (NOT (LDC_LLVM_VER LESS 400))
message(STATUS "Building LDC with dynamic compilation support")
add_definitions(-DLDC_DYNAMIC_COMPILE)
set(LDC_DYNAMIC_COMPILE True)
endif()
# #
# Includes, defines. # Includes, defines.
# #

View file

@ -23,6 +23,9 @@ Libraries (lib/ and import/ in binary packages):
Phobos (runtime/phobos), is distributed under the terms of the Boost Phobos (runtime/phobos), is distributed under the terms of the Boost
Software License. See the individual source files for author information. Software License. See the individual source files for author information.
- The ldc-jit-rt library is distributed under the terms of the
Boost Software license (but additionally makes use of LLVM code).
- The profile-rt runtime library (runtime/profile-rt) is a copy of LLVM's - The profile-rt runtime library (runtime/profile-rt) is a copy of LLVM's
compiler-rt/profile library with a small D source addition. It is dual compiler-rt/profile library with a small D source addition. It is dual
licensed under the University of Illinois Open Source License and under the licensed under the University of Illinois Open Source License and under the

View file

@ -445,6 +445,8 @@ immutable Msgtable[] msgtable =
{ "udaWeak", "_weak" }, { "udaWeak", "_weak" },
{ "udaCompute", "compute" }, { "udaCompute", "compute" },
{ "udaKernel", "_kernel" }, { "udaKernel", "_kernel" },
{ "udaDynamicCompile", "_dynamicCompile" },
{ "udaDynamicCompileConst", "_dynamicCompileConst" },
// IN_LLVM: DCompute specific types and functionss // IN_LLVM: DCompute specific types and functionss
{ "dcompute" }, { "dcompute" },

View file

@ -64,6 +64,8 @@ public:
static Identifier *udaLLVMFastMathFlag; static Identifier *udaLLVMFastMathFlag;
static Identifier *udaKernel; static Identifier *udaKernel;
static Identifier *udaCompute; static Identifier *udaCompute;
}; static Identifier *udaDynamicCompile;
static Identifier *udaDynamicCompileConst;
};
#endif /* DMD_ID_H */ #endif /* DMD_ID_H */

76
docs/dynamic_compile.md Normal file
View file

@ -0,0 +1,76 @@
# Dynamic compilation facilities design overview
`@dynamicCompile` attribute can be applied to any function and non thread-local variable (including lambdas and class virtual methods)
## Compiler part:
* In DtoDeclareFunction read function uda's, if there is a `@dynamicCompile` attribute set `IrFunction::dynamicCompile = true`
* If `IrFunction::dynamicCompile` is `true`, call `declareDynamicCompiledFunction` for this function which will create thunk function and save it to `IrFunction::rtCompileFunc`
* `IrFunction::rtCompileFunc` has same signature and attributes as the original function and any attemt to call or take address of the this function will be redirected to rtCompileFunc in DtoCallee
* Call `defineDynamicCompiledFunction` for any function which have `dynamicCompile == true`
* In `defineDynamicCompiledFunction` create global variable which will hold pointer to jitted function and create thunk function body which just calls function pointer from this global var
* (TODO: It is possible to get rid of this global var, can hotpatch code in runtime with compiled code address, does it worth it? How to do this in llvm?)
* For each `@dynamicCompileConst` variable calls `addDynamicCompiledVar`
* In `writeAndFreeLLModule` call function `generateBitcodeForDynamicCompile` for each module
* In `generateBitcodeForDynamicCompile` first recursively search functions calls inside functions body, starting with `@dynamicCompile` functions.
* After that we have two lists of functions:
* Directly marked by user `@dynamicCompile`
* Not marked by user but used by `@dynamicCompile` functions directly or indirectly and with body available
* Both lists are subject to dynamic compilation. For the first list dynamic version will be always used. For the second static version will be used when called from static code and dynamic version when called from dynamic code.
* Search for thread local valiables access in jitted code (required for TLS workaround)
* Also we now have a list of symbols required by dynamic functions (variables and external functions)
* (TODO: Option for limit recursion depth?)
* Clone module via `llvm::CloneModule` copying functions marked for dynamic compilations (directly or indirectly) and `@dynamicCompileConst` variables
* Remove `target-cpu` and `target-features` attributes from all jitted functions except those user set explicitly (these attributes will be set back before jit to host)
* Apply thread local storage workaround to jitted functions if enabled (`replaceDynamicThreadLocals` function, controlled via `-runtime-compile-tls-workaround` command line switch, default on)
* For each thread local variable accessed in jit code generate static accessor function which returns variable address.
* Replace direct access to thread local variables with call to accessor function
* Replace calls to jit thunks in jitted functions with direct calls (`fixRtModule`)
* Create module data structures and bitcode array required for dynamic compilation and add module constructor which registers `RtComileModuleList` into global linked list (`setupModuleBitcodeData`)
```
/// Symbol required by dynamic code
struct RtCompileSymList {
const char *name; // Symbol name
void *sym; // Symbol address
};
/// Dynamic function
struct RtCompileFuncList {
const char *name; // Function name
void *func; // Thunk global var address to store compiled function pointer
};
/// @dynamicCompileConst variable
struct RtCompileVarList {
const char *name;
const void *init;
};
/// Runtime compiled functions info per module
/// organized in linked list
/// starting with `DynamicCompileModulesHeadName` (defined in runtime)
struct RtComileModuleList {
RtComileModuleList *next; // Next module
const void *irData; // Ir data
int32 irDataSize; // Ir data size in bytes
RtCompileFuncList *funcList; // Dynamic functions
int32 funcListSize; // Dynamic functions count
RtCompileSymList *symList; // Symbols
int32 symListSize; // Symbols count
};
```
## Runtime part:
* Defines runtime compiled modules list head
* Defines `rtCompileProcessImplSo` function which do actual compilation
* `rtCompileProcessImplSo` for each module in list:
* Parses ir data into llvm module
* Set `target-cpu` and `target-features` attributes to host for all function which don't have it
* For each `@dynamicCompileConst` variable
* Parse variable value from host process and create initializer
* Set variable const so llvm can optimize it
* Merge all modules into one via `llvm::Linker::linkModules`
* Optimizes resulting module (TODO: optimization setup taken from LDC but not all options available to compiler available in jit)
* Compile module, resolve functions using RtComileModuleList data and update thunk vars

View file

@ -456,6 +456,19 @@ cl::opt<std::string>
cl::value_desc("prefix")); cl::value_desc("prefix"));
#endif #endif
#if defined(LDC_DYNAMIC_COMPILE)
cl::opt<bool> enableDynamicCompile(
"enable-dynamic-compile",
cl::desc("Enable dynamic compilation"),
cl::init(false));
cl::opt<bool> dynamicCompileTlsWorkaround(
"dynamic-compile-tls-workaround",
cl::desc("Enable dynamic compilation TLS workaround"),
cl::init(true),
cl::Hidden);
#endif
static cl::extrahelp footer( static cl::extrahelp footer(
"\n" "\n"
"-d-debug can also be specified without options, in which case it enables " "-d-debug can also be specified without options, in which case it enables "
@ -532,7 +545,7 @@ void hideLLVMOptions() {
"mno-fixup", "mno-ldc1-sdc1", "mno-pairing", "mwarn-missing-parenthesis", "mno-fixup", "mno-ldc1-sdc1", "mno-pairing", "mwarn-missing-parenthesis",
"mwarn-noncontigious-register", "mwarn-sign-mismatch", "nvptx-sched4reg", "mwarn-noncontigious-register", "mwarn-sign-mismatch", "nvptx-sched4reg",
"no-discriminators", "objc-arc-annotation-target-identifier", "no-discriminators", "objc-arc-annotation-target-identifier",
"polly-dump-after", "polly-dump-after-file", "polly-dump-before", "polly-dump-after", "polly-dump-after-file", "polly-dump-before",
"polly-dump-before-file", "polly-dump-before-file",
"pre-RA-sched", "print-after-all", "print-before-all", "pre-RA-sched", "print-after-all", "print-before-all",
"print-machineinstrs", "profile-estimator-loop-weight", "print-machineinstrs", "profile-estimator-loop-weight",

View file

@ -121,5 +121,12 @@ extern cl::opt<std::string> saveOptimizationRecord;
extern cl::list<std::string> dcomputeTargets; extern cl::list<std::string> dcomputeTargets;
extern cl::opt<std::string> dcomputeFilePrefix; extern cl::opt<std::string> dcomputeFilePrefix;
#endif #endif
#if defined(LDC_DYNAMIC_COMPILE)
extern cl::opt<bool> enableDynamicCompile;
extern cl::opt<bool> dynamicCompileTlsWorkaround;
#else
constexpr bool enableDynamicCompile = false;
#endif
} }
#endif #endif

View file

@ -19,6 +19,7 @@
#include "gen/logger.h" #include "gen/logger.h"
#include "gen/modules.h" #include "gen/modules.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/dynamiccompile.h"
#include "llvm/Support/FileSystem.h" #include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/ToolOutputFile.h"
@ -254,6 +255,7 @@ void CodeGenerator::writeAndFreeLLModule(const char *filename) {
ir_->replaceGlobals(); ir_->replaceGlobals();
ir_->DBuilder.Finalize(); ir_->DBuilder.Finalize();
generateBitcodeForDynamicCompile(ir_);
emitLLVMUsedArray(*ir_); emitLLVMUsedArray(*ir_);
emitLinkerOptions(*ir_, ir_->module, ir_->context()); emitLinkerOptions(*ir_, ir_->module, ir_->context());

View file

@ -342,6 +342,11 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
args.push_back("-lldc-profile-rt"); args.push_back("-lldc-profile-rt");
} }
if (opts::enableDynamicCompile) {
args.push_back("-lldc-jit-rt");
args.push_back("-lldc-jit");
}
// user libs // user libs
for (auto libfile : *global.params.libfiles) { for (auto libfile : *global.params.libfiles) {
args.push_back(libfile); args.push_back(libfile);

View file

@ -116,6 +116,11 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker,
args.push_back("ws2_32.lib"); args.push_back("ws2_32.lib");
} }
if (opts::enableDynamicCompile) {
args.push_back("ldc-jit-rt.lib");
args.push_back("ldc-jit.lib");
}
// user libs // user libs
for (auto libfile : *global.params.libfiles) { for (auto libfile : *global.params.libfiles) {
args.push_back(libfile); args.push_back(libfile);

View file

@ -934,6 +934,10 @@ void registerPredefinedVersions() {
// `D_ObjectiveC` is added by the ddmd.objc.Supported ctor // `D_ObjectiveC` is added by the ddmd.objc.Supported ctor
if (opts::enableDynamicCompile) {
VersionCondition::addPredefinedGlobalIdent("LDC_DynamicCompilation");
}
// Define sanitizer versions. // Define sanitizer versions.
if (opts::isSanitizerEnabled(opts::AddressSanitizer)) { if (opts::isSanitizerEnabled(opts::AddressSanitizer)) {
VersionCondition::addPredefinedGlobalIdent("LDC_AddressSanitizer"); VersionCondition::addPredefinedGlobalIdent("LDC_AddressSanitizer");

826
gen/dynamiccompile.cpp Normal file
View file

@ -0,0 +1,826 @@
//===-- dynamiccompile.cpp ------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "gen/dynamiccompile.h"
#if defined(LDC_DYNAMIC_COMPILE)
#include <unordered_map>
#include <unordered_set>
#include "driver/cl_options.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "ir/irfunction.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/TypeBuilder.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
namespace {
const char *DynamicCompileModulesHeadName = "dynamiccompile_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);
if (fun->hasPersonalityFn()) {
if (auto personality =
llvm::dyn_cast<llvm::GlobalValue>(fun->getPersonalityFn())) {
handler(personality);
}
}
for (auto &&bb : *fun) {
for (auto &&instr : bb) {
enumOperands(instr, std::forward<F>(handler));
}
}
}
enum class GlobalValVisibility {
Internal,
External,
Declaration,
};
using GlobalValsMap =
std::unordered_map<llvm::GlobalValue *, GlobalValVisibility>;
void getPredefinedSymbols(IRState *irs, GlobalValsMap &symList) {
assert(nullptr != irs);
const llvm::Triple *triple = global.params.targetTriple;
if (!opts::dynamicCompileTlsWorkaround) {
if (triple->isWindowsMSVCEnvironment() ||
triple->isWindowsGNUEnvironment()) {
symList.insert(std::make_pair(
getPredefinedSymbol(irs->module, "_tls_index",
llvm::Type::getInt32Ty(irs->context())),
GlobalValVisibility::Declaration));
if (triple->isArch32Bit()) {
symList.insert(std::make_pair(
getPredefinedSymbol(irs->module, "_tls_array",
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->dynamicCompiledFunctions.size());
for (auto &&it : irs->dynamicCompiledFunctions) {
ret.insert({it.first, GlobalValVisibility::External});
newFunctions.push_back(it.first);
}
std::unordered_set<llvm::GlobalValue *> runtimeCompiledVars;
for (auto &&var : irs->dynamicCompiledVars) {
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({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;
}
template <typename F>
void iterateFuncInstructions(llvm::Function &func, F &&handler) {
for (auto &&bb : func) {
// We can change bb contents in this loop
// so we reiterate it from start after each change
bool bbChanged = true;
while (bbChanged) {
bbChanged = false;
for (auto &&instr : bb) {
if (handler(instr)) {
bbChanged = true;
break;
}
} // for (auto &&instr : bb)
} // while (bbChanged)
} // for (auto &&bb : fun)
}
void fixRtModule(llvm::Module &newModule,
const decltype(IRState::dynamicCompiledFunctions) &funcs) {
std::unordered_map<std::string, std::string> thunkVar2func;
std::unordered_map<std::string, std::string> thunkFun2func;
std::unordered_set<std::string> externalFuncs;
for (auto &&it : funcs) {
assert(nullptr != it.first);
assert(nullptr != it.second.thunkFunc);
if (nullptr == it.second.thunkVar) {
// thunkVar is not available
// e.g. runtimeCompile function from other module, ignore
continue;
}
assert(!contains(thunkVar2func, it.second.thunkVar->getName()));
thunkVar2func.insert({it.second.thunkVar->getName(), it.first->getName()});
thunkFun2func.insert({it.second.thunkFunc->getName(), it.first->getName()});
externalFuncs.insert(it.first->getName());
}
// Replace call to thunks in jitted code with direct calls to functions
for (auto &&fun : newModule.functions()) {
iterateFuncInstructions(fun, [&](llvm::Instruction &instr) -> bool {
if (auto call = llvm::dyn_cast<llvm::CallInst>(&instr)) {
auto callee = call->getCalledValue();
assert(nullptr != callee);
auto it = thunkFun2func.find(callee->getName());
if (thunkFun2func.end() != it) {
auto realFunc = newModule.getFunction(it->second);
assert(nullptr != realFunc);
call->setCalledFunction(realFunc);
}
}
return false;
});
}
int objectsFixed = 0;
for (auto &&obj : newModule.globals()) {
auto it = thunkVar2func.find(obj.getName());
if (thunkVar2func.end() != it) {
if (obj.hasInitializer()) {
auto func = newModule.getFunction(it->second);
assert(nullptr != func);
obj.setConstant(true);
obj.setInitializer(func);
}
++objectsFixed;
}
}
for (auto &&obj : newModule.functions()) {
if (contains(externalFuncs, obj.getName())) {
obj.setLinkage(llvm::GlobalValue::ExternalLinkage);
obj.setVisibility(llvm::GlobalValue::DefaultVisibility);
++objectsFixed;
} else {
if (llvm::GlobalValue::ExternalLinkage == obj.getLinkage() &&
!obj.isDeclaration()) {
obj.setLinkage(llvm::GlobalValue::InternalLinkage);
};
}
}
assert((thunkVar2func.size() + externalFuncs.size()) ==
static_cast<std::size_t>(objectsFixed));
}
void removeFunctionsTargets(IRState *irs, llvm::Module &module) {
assert(nullptr != irs);
std::unordered_map<std::string, IrFunction *> funcMap;
for (auto &&fun : irs->targetCpuOrFeaturesOverridden) {
assert(nullptr != fun);
funcMap.insert({fun->getLLVMFunc()->getName(), fun});
}
for (auto &&fun : module.functions()) {
// Remove 'target-cpu' and 'target-features' attributes from all
// functions except when they are set explicitly by user.
// They will be set again by jitting lib to jit host values
auto it = funcMap.find(fun.getName());
if (funcMap.end() != it) {
auto irFunc = it->second;
if (!irFunc->targetCpuOverridden) {
fun.removeFnAttr("target-cpu");
}
if (!irFunc->targetFeaturesOverridden) {
fun.removeFnAttr("target-features");
}
} else {
fun.removeFnAttr("target-cpu");
fun.removeFnAttr("target-features");
}
}
}
llvm::Function *createGlobalVarLoadFun(llvm::Module &module,
llvm::GlobalVariable *var,
const llvm::Twine &funcName) {
assert(nullptr != var);
auto &context = module.getContext();
auto varType = var->getType();
auto funcType = llvm::FunctionType::get(varType, false);
auto func = llvm::Function::Create(
funcType, llvm::GlobalValue::WeakODRLinkage, funcName, &module);
auto bb = llvm::BasicBlock::Create(context, "", func);
llvm::IRBuilder<> builder(context);
builder.SetInsertPoint(bb);
builder.CreateRet(var);
return func;
}
void replaceDynamicThreadLocals(llvm::Module &oldModule,
llvm::Module &newModule,
GlobalValsMap &valsMap) {
// Wrap all thread locals access in dynamic code by function calls
// to 'normal' code
std::unordered_map<llvm::GlobalVariable *, llvm::Function *>
threadLocalAccessors;
auto getAccessor = [&](llvm::GlobalVariable *var) {
assert(nullptr != var);
auto it = threadLocalAccessors.find(var);
if (threadLocalAccessors.end() != it) {
return it->second;
}
auto srcVar = oldModule.getGlobalVariable(var->getName());
assert(nullptr != srcVar);
auto srcFunc = createGlobalVarLoadFun(oldModule, srcVar,
"." + var->getName() + "_accessor");
srcFunc->addFnAttr(llvm::Attribute::NoInline);
auto dstFunc = llvm::Function::Create(srcFunc->getFunctionType(),
llvm::GlobalValue::ExternalLinkage,
srcFunc->getName(), &newModule);
threadLocalAccessors.insert({var, dstFunc});
valsMap.insert({srcFunc, GlobalValVisibility::Declaration});
return dstFunc;
};
for (auto &&fun : newModule.functions()) {
iterateFuncInstructions(fun, [&](llvm::Instruction &instr) -> bool {
bool changed = false;
for (unsigned int i = 0; i < instr.getNumOperands(); ++i) {
auto op = instr.getOperand(i);
if (auto globalVar = llvm::dyn_cast<llvm::GlobalVariable>(op)) {
if (globalVar->isThreadLocal()) {
auto accessor = getAccessor(globalVar);
assert(nullptr != accessor);
auto callResult = llvm::CallInst::Create(accessor);
callResult->insertBefore(&instr);
instr.setOperand(i, callResult);
changed = true;
}
}
}
return changed;
});
} // for (auto &&fun : newModule.functions())
for (auto &&it : threadLocalAccessors) {
it.first->eraseFromParent();
}
}
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 RtCompileModuleList
// {
// RtCompileModuleList* 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 /*, "RtCompileModuleList"*/); // 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->dynamicCompiledFunctions) {
assert(nullptr != it.first);
assert(nullptr != it.second.thunkFunc);
if (nullptr == it.second.thunkVar) {
// thunkVar is not available
// e.g. runtimeCompile function from other module, ignore
continue;
}
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->dynamicCompiledVars) {
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(DynamicCompileModulesHeadName); if (nullptr !=
// existingVar) {
// if (type != existingVar->getType()) {
// error(Loc(), "Invalid DynamicCompileModulesHeadName type");
// fatal();
// }
// return existingVar;
// }
return new llvm::GlobalVariable(module, type, false,
llvm::GlobalValue::ExternalLinkage, nullptr,
DynamicCompileModulesHeadName);
}
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 generateBitcodeForDynamicCompile(IRState *irs) {
assert(nullptr != irs);
if (irs->dynamicCompiledFunctions.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;
});
removeFunctionsTargets(irs, *newModule);
if (opts::dynamicCompileTlsWorkaround) {
replaceDynamicThreadLocals(irs->module, *newModule, filter);
}
fixRtModule(*newModule, irs->dynamicCompiledFunctions);
setupModuleBitcodeData(*newModule, irs, filter);
}
void declareDynamicCompiledFunction(IRState *irs, IrFunction *func) {
assert(nullptr != irs);
assert(nullptr != func);
assert(nullptr != func->getLLVMFunc());
if (!opts::enableDynamicCompile) {
return;
}
auto srcFunc = func->getLLVMFunc();
auto thunkFunc = duplicateFunc(irs->module, srcFunc);
func->rtCompileFunc = thunkFunc;
assert(!contains(irs->dynamicCompiledFunctions, srcFunc));
irs->dynamicCompiledFunctions.insert(
std::make_pair(srcFunc, IRState::RtCompiledFuncDesc{nullptr, thunkFunc}));
}
void defineDynamicCompiledFunction(IRState *irs, IrFunction *func) {
assert(nullptr != irs);
assert(nullptr != func);
assert(nullptr != func->getLLVMFunc());
assert(nullptr != func->rtCompileFunc);
if (!opts::enableDynamicCompile) {
return;
}
auto srcFunc = func->getLLVMFunc();
auto it = irs->dynamicCompiledFunctions.find(srcFunc);
assert(irs->dynamicCompiledFunctions.end() != it);
auto thunkVarType = srcFunc->getFunctionType()->getPointerTo();
auto thunkVar = new llvm::GlobalVariable(
irs->module, thunkVarType, false, llvm::GlobalValue::PrivateLinkage,
llvm::ConstantPointerNull::get(thunkVarType),
".rtcompile_thunkvar_" + srcFunc->getName());
auto dstFunc = it->second.thunkFunc;
createThunkFunc(irs->module, srcFunc, dstFunc, thunkVar);
it->second.thunkVar = thunkVar;
}
void addDynamicCompiledVar(IRState *irs, IrGlobal *var) {
assert(nullptr != irs);
assert(nullptr != var);
assert(nullptr != var->value);
assert(nullptr != var->V);
if (!opts::enableDynamicCompile) {
return;
}
if (var->V->isThreadlocal()) {
error(Loc(), "Runtime compiled variable \"%s\" cannot be thread local",
var->V->toChars());
fatal();
}
irs->dynamicCompiledVars.insert(var);
}
#else // defined(LDC_DYNAMIC_COMPILE)
void generateBitcodeForDynamicCompile(IRState *) {
// nothing
}
void declareDynamicCompiledFunction(IRState *, IrFunction *) {
// nothing
}
void defineDynamicCompiledFunction(IRState *, IrFunction *) {
// nothing
}
void addDynamicCompiledVar(IRState *, IrGlobal *) {
// nothing
}
#endif

26
gen/dynamiccompile.h Normal file
View file

@ -0,0 +1,26 @@
//===-- gen/dynamiccompile.h - jit support ----------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Dynamic compilation routines.
//
//===----------------------------------------------------------------------===//
#ifndef LDC_GEN_DYNAMICCOMPILE_H
#define LDC_GEN_DYNAMICCOMPILE_H
struct IRState;
struct IrFunction;
struct IrGlobal;
void generateBitcodeForDynamicCompile(IRState *irs);
void declareDynamicCompiledFunction(IRState *irs, IrFunction *func);
void defineDynamicCompiledFunction(IRState *irs, IrFunction *func);
void addDynamicCompiledVar(IRState *irs, IrGlobal *var);
#endif // LDC_GEN_DYNAMICCOMPILE_H

View file

@ -39,6 +39,7 @@
#include "gen/pgo.h" #include "gen/pgo.h"
#include "gen/pragma.h" #include "gen/pragma.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/dynamiccompile.h"
#include "gen/scope_exit.h" #include "gen/scope_exit.h"
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "gen/uda.h" #include "gen/uda.h"
@ -566,6 +567,15 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
applyTargetMachineAttributes(*func, *gTargetMachine); applyTargetMachineAttributes(*func, *gTargetMachine);
applyFuncDeclUDAs(fdecl, irFunc); applyFuncDeclUDAs(fdecl, irFunc);
if(irFunc->dynamicCompile) {
declareDynamicCompiledFunction(gIR, irFunc);
}
if (irFunc->targetCpuOverridden ||
irFunc->targetFeaturesOverridden) {
gIR->targetCpuOrFeaturesOverridden.push_back(irFunc);
}
// main // main
if (fdecl->isMain()) { if (fdecl->isMain()) {
// Detect multiple main functions, which is disallowed. DMD checks this // Detect multiple main functions, which is disallowed. DMD checks this
@ -934,6 +944,12 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
return; return;
} }
SCOPE_EXIT {
if (irFunc->dynamicCompile) {
defineDynamicCompiledFunction(gIR, irFunc);
}
};
// debug info // debug info
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd); irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);

View file

@ -222,6 +222,17 @@ public:
// setGlobalVarInitializer(). // setGlobalVarInitializer().
void replaceGlobals(); void replaceGlobals();
// List of functions with cpu or features attributes overriden by user
std::vector<IrFunction *> targetCpuOrFeaturesOverridden;
struct RtCompiledFuncDesc {
llvm::GlobalVariable *thunkVar;
llvm::Function *thunkFunc;
};
std::map<llvm::Function *, RtCompiledFuncDesc> dynamicCompiledFunctions;
std::set<IrGlobal *> dynamicCompiledVars;
/// Vector of options passed to the linker as metadata in object file. /// Vector of options passed to the linker as metadata in object file.
#if LDC_LLVM_VER >= 500 #if LDC_LLVM_VER >= 500
llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs; llvm::SmallVector<llvm::MDNode *, 5> LinkerMetadataArgs;
@ -235,7 +246,7 @@ public:
llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> TypeDescriptorMap; llvm::DenseMap<llvm::Constant *, llvm::GlobalVariable *> TypeDescriptorMap;
#endif #endif
//Target for dcompute. If not nullptr, it owns this. // Target for dcompute. If not nullptr, it owns this.
DComputeTarget *dcomputetarget = nullptr; DComputeTarget *dcomputetarget = nullptr;
}; };

View file

@ -44,6 +44,7 @@
#include <stack> #include <stack>
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "gen/dynamiccompile.h"
llvm::cl::opt<llvm::GlobalVariable::ThreadLocalMode> clThreadModel( llvm::cl::opt<llvm::GlobalVariable::ThreadLocalMode> clThreadModel(
"fthread-model", llvm::cl::ZeroOrMore, llvm::cl::desc("Thread model"), "fthread-model", llvm::cl::ZeroOrMore, llvm::cl::desc("Thread model"),
@ -293,6 +294,11 @@ void DtoCAssert(Module *M, Loc &loc, LLValue *msg) {
args.push_back(file); args.push_back(file);
args.push_back(line); args.push_back(line);
args.push_back(msg); args.push_back(msg);
} else if (global.params.targetTriple->getEnvironment() ==
llvm::Triple::Android) {
args.push_back(file);
args.push_back(line);
args.push_back(msg);
} else { } else {
args.push_back(msg); args.push_back(msg);
args.push_back(file); args.push_back(file);
@ -882,7 +888,8 @@ void DtoResolveVariable(VarDeclaration *vd) {
llvm::GlobalVariable *gvar = llvm::GlobalVariable *gvar =
getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst, getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst,
linkage, nullptr, irMangle, vd->isThreadlocal()); linkage, nullptr, irMangle, vd->isThreadlocal());
getIrGlobal(vd)->value = gvar; auto varIr = getIrGlobal(vd);
varIr->value = gvar;
// Set the alignment (it is important not to use type->alignsize because // Set the alignment (it is important not to use type->alignsize because
// VarDeclarations can have an align() attribute independent of the type // VarDeclarations can have an align() attribute independent of the type
@ -898,6 +905,9 @@ void DtoResolveVariable(VarDeclaration *vd) {
*/ */
applyVarDeclUDAs(vd, gvar); applyVarDeclUDAs(vd, gvar);
if (varIr->dynamicCompileConst) {
addDynamicCompiledVar(gIR, varIr);
}
IF_LOG Logger::cout() << *gvar << '\n'; IF_LOG Logger::cout() << *gvar << '\n';
} }

View file

@ -308,5 +308,6 @@ llvm::GlobalVariable *genModuleInfo(Module *m) {
// Create a global symbol with the above initialiser. // Create a global symbol with the above initialiser.
LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol(); LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol();
b.finalize(moduleInfoSym); b.finalize(moduleInfoSym);
setLinkage({LLGlobalValue::ExternalLinkage, supportsCOMDAT()}, moduleInfoSym);
return moduleInfoSym; return moduleInfoSym;
} }

View file

@ -160,6 +160,13 @@ llvm::Function *getRuntimeFunction(const Loc &loc, llvm::Module &target,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// C assert function:
// OSX: void __assert_rtn(const char *func, const char *file, unsigned line,
// const char *msg)
// Android: void __assert(const char *file, int line, const char *msg)
// MSVC: void _assert(const char *msg, const char *file, unsigned line)
// else: void __assert(const char *msg, const char *file, unsigned line)
static const char *getCAssertFunctionName() { static const char *getCAssertFunctionName() {
if (global.params.targetTriple->isOSDarwin()) { if (global.params.targetTriple->isOSDarwin()) {
return "__assert_rtn"; return "__assert_rtn";
@ -169,6 +176,19 @@ static const char *getCAssertFunctionName() {
return "__assert"; return "__assert";
} }
static std::vector<Type *> getCAssertFunctionParamTypes() {
const auto voidPtr = Type::tvoidptr;
const auto uint = Type::tuns32;
if (global.params.targetTriple->isOSDarwin()) {
return {voidPtr, voidPtr, uint, voidPtr};
}
if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) {
return {voidPtr, uint, voidPtr};
}
return {voidPtr, voidPtr, uint};
}
llvm::Function *getCAssertFunction(const Loc &loc, llvm::Module &target) { llvm::Function *getCAssertFunction(const Loc &loc, llvm::Module &target) {
return getRuntimeFunction(loc, target, getCAssertFunctionName()); return getRuntimeFunction(loc, target, getCAssertFunctionName());
} }
@ -348,16 +368,10 @@ static void buildRuntimeModule() {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// C assert function: // C assert function
// OSX: void __assert_rtn(const char *func, const char *file, unsigned line, createFwdDecl(LINKc, Type::tvoid, {getCAssertFunctionName()},
// const char *msg) getCAssertFunctionParamTypes(), {},
// else: void [_]_assert(const char *msg, const char *file, unsigned line) Attr_Cold_NoReturn_NoUnwind);
createFwdDecl(
LINKc, Type::tvoid, {getCAssertFunctionName()},
global.params.targetTriple->isOSDarwin()
? llvm::ArrayRef<Type *>({voidPtrTy, voidPtrTy, uintTy, voidPtrTy})
: llvm::ArrayRef<Type *>({voidPtrTy, voidPtrTy, uintTy}),
{}, Attr_Cold_NoReturn_NoUnwind);
// void _d_assert(string file, uint line) // void _d_assert(string file, uint line)
// void _d_arraybounds(string file, uint line) // void _d_arraybounds(string file, uint line)

View file

@ -7,6 +7,7 @@
#include "declaration.h" #include "declaration.h"
#include "expression.h" #include "expression.h"
#include "ir/irfunction.h" #include "ir/irfunction.h"
#include "ir/irvar.h"
#include "module.h" #include "module.h"
#include "id.h" #include "id.h"
@ -272,7 +273,7 @@ void applyAttrSection(StructLiteralExp *sle, llvm::GlobalObject *globj) {
globj->setSection(getFirstElemString(sle)); globj->setSection(getFirstElemString(sle));
} }
void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func) { void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func, IrFunction *irFunc) {
// TODO: this is a rudimentary implementation for @target. Many more // TODO: this is a rudimentary implementation for @target. Many more
// target-related attributes could be applied to functions (not just for // target-related attributes could be applied to functions (not just for
// @target): clang applies many attributes that LDC does not. // @target): clang applies many attributes that LDC does not.
@ -325,8 +326,11 @@ void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func) {
features.emplace_back(std::move(f)); features.emplace_back(std::move(f));
} }
if (!CPU.empty()) if (!CPU.empty()) {
func->addFnAttr("target-cpu", CPU); func->addFnAttr("target-cpu", CPU);
irFunc->targetCpuOverridden = true;
}
if (!features.empty()) { if (!features.empty()) {
// Sorting the features puts negative features ("-") after positive features // Sorting the features puts negative features ("-") after positive features
// ("+"). This provides the desired behavior of negative features overriding // ("+"). This provides the desired behavior of negative features overriding
@ -334,6 +338,7 @@ void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func) {
sort(features.begin(), features.end()); sort(features.begin(), features.end());
func->addFnAttr("target-features", func->addFnAttr("target-features",
llvm::join(features.begin(), features.end(), ",")); llvm::join(features.begin(), features.end(), ","));
irFunc->targetFeaturesOverridden = true;
} }
} }
@ -359,6 +364,12 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
ident->toChars()); ident->toChars());
} else if (ident == Id::udaWeak) { } else if (ident == Id::udaWeak) {
// @weak is applied elsewhere // @weak is applied elsewhere
} else if (ident == Id::udaDynamicCompile) {
sle->error(
"Special attribute `ldc.attributes.%s` is only valid for functions",
ident->toChars());
} else if (ident == Id::udaDynamicCompileConst) {
getIrGlobal(decl)->dynamicCompileConst = true;
} else { } else {
sle->warning( sle->warning(
"Ignoring unrecognized special attribute `ldc.attributes.%s`", "Ignoring unrecognized special attribute `ldc.attributes.%s`",
@ -393,9 +404,15 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
} else if (ident == Id::udaSection) { } else if (ident == Id::udaSection) {
applyAttrSection(sle, func); applyAttrSection(sle, func);
} else if (ident == Id::udaTarget) { } else if (ident == Id::udaTarget) {
applyAttrTarget(sle, func); applyAttrTarget(sle, func, irFunc);
} else if (ident == Id::udaWeak || ident == Id::udaKernel) { } else if (ident == Id::udaWeak || ident == Id::udaKernel) {
// @weak and @kernel are applied elsewhere // @weak and @kernel are applied elsewhere
} else if (ident == Id::udaDynamicCompile) {
irFunc->dynamicCompile = true;
} else if (ident == Id::udaDynamicCompileConst) {
sle->error(
"Special attribute `ldc.attributes.%s` is only valid for variables",
ident->toChars());
} else { } else {
sle->warning( sle->warning(
"Ignoring unrecognized special attribute `ldc.attributes.%s`", "Ignoring unrecognized special attribute `ldc.attributes.%s`",

View file

@ -74,7 +74,7 @@ llvm::StringRef IrFunction::getLLVMFuncName() const {
llvm::Function *IrFunction::getLLVMCallee() const { llvm::Function *IrFunction::getLLVMCallee() const {
assert(func != nullptr); assert(func != nullptr);
return func; return rtCompileFunc != nullptr ? rtCompileFunc : func;
} }
IrFunction *getIrFunc(FuncDeclaration *decl, bool create) { IrFunction *getIrFunc(FuncDeclaration *decl, bool create) {

View file

@ -81,6 +81,19 @@ struct IrFunction {
/// These are set e.g. by math related UDA's from ldc.attributes. /// These are set e.g. by math related UDA's from ldc.attributes.
llvm::FastMathFlags FMF; llvm::FastMathFlags FMF;
/// target CPU was overriden by attribute
bool targetCpuOverridden = false;
/// target features was overriden by attributes
bool targetFeaturesOverridden = false;
/// This functions was marked for dynamic compilation
bool dynamicCompile = false;
/// Dynamic compilation thunk, all attempts to call or take address of the
/// original function will be redirected to it
llvm::Function *rtCompileFunc = nullptr;
private: private:
llvm::Function *func = nullptr; llvm::Function *func = nullptr;
}; };

View file

@ -99,10 +99,6 @@ IrTypeClass *IrTypeClass::get(ClassDeclaration *cd) {
builder.addTailPadding(cd->structsize); builder.addTailPadding(cd->structsize);
} }
if (global.errors) {
fatal();
}
// set struct body and copy GEP indices // set struct body and copy GEP indices
isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed); isaStruct(t->type)->setBody(builder.defaultTypes(), t->packed);
t->varGEPIndices = builder.varGEPIndices(); t->varGEPIndices = builder.varGEPIndices();

View file

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

View file

@ -8,6 +8,7 @@ default:
switches = [ switches = [
"-I@RUNTIME_DIR@/src", "-I@RUNTIME_DIR@/src",
"-I@PROFILERT_DIR@/d", "-I@PROFILERT_DIR@/d",
"-I@JITRT_DIR@/d",
"-I@PHOBOS2_DIR@",@SHARED_LIBS_RPATH@ "-I@PHOBOS2_DIR@",@SHARED_LIBS_RPATH@
"-defaultlib=phobos2-ldc,druntime-ldc", "-defaultlib=phobos2-ldc,druntime-ldc",
"-debuglib=phobos2-ldc-debug,druntime-ldc-debug"@ADDITIONAL_DEFAULT_LDC_SWITCHES@ "-debuglib=phobos2-ldc-debug,druntime-ldc-debug"@ADDITIONAL_DEFAULT_LDC_SWITCHES@

View file

@ -135,6 +135,7 @@ get_directory_property(PROJECT_PARENT_DIR DIRECTORY ${PROJECT_SOURCE_DIR} PARENT
set(RUNTIME_DIR ${PROJECT_SOURCE_DIR}/druntime CACHE PATH "druntime root directory") set(RUNTIME_DIR ${PROJECT_SOURCE_DIR}/druntime CACHE PATH "druntime root directory")
set(PHOBOS2_DIR ${PROJECT_SOURCE_DIR}/phobos CACHE PATH "Phobos root directory") set(PHOBOS2_DIR ${PROJECT_SOURCE_DIR}/phobos CACHE PATH "Phobos root directory")
set(PROFILERT_DIR ${PROJECT_SOURCE_DIR}/profile-rt CACHE PATH "profile-rt root directory") set(PROFILERT_DIR ${PROJECT_SOURCE_DIR}/profile-rt CACHE PATH "profile-rt root directory")
set(JITRT_DIR ${PROJECT_SOURCE_DIR}/jit-rt CACHE PATH "jit runtime root directory")
# #
# Gather source files. # Gather source files.
@ -640,9 +641,13 @@ macro(build_all_runtime_variants d_flags c_flags ld_flags path_suffix outlist_ta
endif() endif()
endmacro() endmacro()
# Setup the build of profile-rt # Setup the build of profile-rt
include(profile-rt/DefineBuildProfileRT.cmake) include(profile-rt/DefineBuildProfileRT.cmake)
# Setup the build of jit runtime
include(jit-rt/DefineBuildJitRT.cmake)
# #
# Set up build and install targets # Set up build and install targets
# #
@ -658,6 +663,9 @@ if(MULTILIB AND "${TARGET_SYSTEM}" MATCHES "APPLE")
build_all_runtime_variants("" "${RT_CFLAGS}" "${LD_FLAGS}" "${hostsuffix}" libtargets) build_all_runtime_variants("" "${RT_CFLAGS}" "${LD_FLAGS}" "${hostsuffix}" libtargets)
build_all_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" libtargets) build_all_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" libtargets)
# Do not build multilib version of jit runtime
build_jit_runtime ("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${hostsuffix}" libtargets)
# KLUDGE: Cannot use `$<TARGET_LINKER_FILE:target>` in custom command. # KLUDGE: Cannot use `$<TARGET_LINKER_FILE:target>` in custom command.
# Set up the list of generated libs (without 'lib' prefix) to be merged manually. # Set up the list of generated libs (without 'lib' prefix) to be merged manually.
set(libs_to_install ldc-profile-rt.a) set(libs_to_install ldc-profile-rt.a)
@ -697,6 +705,10 @@ else()
set(libs_to_install) set(libs_to_install)
build_all_runtime_variants("" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libs_to_install) build_all_runtime_variants("" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libs_to_install)
set(libs_to_install_no_multilib)
# Do not build multilib version of jit runtime
build_jit_runtime ("${D_FLAGS};${D_FLAGS_RELEASE}" "${RT_CFLAGS}" "${LD_FLAGS}" "${LIB_SUFFIX}" libs_to_install_no_multilib)
# don't add multilib targets to libs_to_install # don't add multilib targets to libs_to_install
if(MULTILIB) if(MULTILIB)
build_all_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" dummy) build_all_runtime_variants("-m${MULTILIB_SUFFIX}" "-m${MULTILIB_SUFFIX} ${RT_CFLAGS}" "-m${MULTILIB_SUFFIX} ${LD_FLAGS}" "${MULTILIB_SUFFIX}" dummy)
@ -710,6 +722,11 @@ else()
DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${MULTILIB_SUFFIX}) DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${MULTILIB_SUFFIX})
endif() endif()
endforeach() endforeach()
foreach(libname ${libs_to_install_no_multilib})
install(TARGETS ${libname}
DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
endforeach()
endif() endif()
set(DRUNTIME_PACKAGES core etc ldc) set(DRUNTIME_PACKAGES core etc ldc)

@ -1 +1 @@
Subproject commit d410fc19b85778e9dbea2680fe5436a56cd84c8f Subproject commit 6884527162c6a00f0e9e709b57f025ff7b255165

View file

@ -0,0 +1,102 @@
if(LDC_DYNAMIC_COMPILE)
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 -std=c++11 -fvisibility=hidden")
if(NOT APPLE)
set(JITRT_EXTRA_LDFLAGS "-Wl,--exclude-libs=ALL")
endif()
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()
function(build_jit_runtime d_flags c_flags ld_flags path_suffix outlist_targets)
# Jit runtime need a differens set of libraries from compiler and we
# can't do find_package(LLVM) because we already have one in top-level cmake
# Also we don't have access to llvm_map_components_to_libnames because we need
# to do find_package(LLVM CONFIG) for it so here is a hackish way to get it
include("${LLVM_LIBRARY_DIRS}/cmake/llvm/LLVMConfig.cmake")
include("${LLVM_LIBRARY_DIRS}/cmake/llvm/LLVM-Config.cmake")
llvm_map_components_to_libnames(JITRT_LLVM_LIBS core support irreader executionengine passes nativecodegen orcjit target
"${LLVM_NATIVE_ARCH}disassembler" "${LLVM_NATIVE_ARCH}asmprinter")
foreach(libname ${JITRT_LLVM_LIBS})
unset(JITRT_TEMP_LIB CACHE)
find_library(JITRT_TEMP_LIB ${libname} PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if(NOT JITRT_TEMP_LIB)
message(STATUS "lib ${libname} not found, skipping jit runtime build")
return()
endif()
unset(JITRT_TEMP_LIB CACHE)
endforeach()
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}"
)
target_link_libraries(ldc-jit-rt-so${target_suffix} ${JITRT_LLVM_LIBS})
set(jitrt_d_o "")
set(jitrt_d_bc "")
compile_jit_rt_D("-enable-dynamic-compile;${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}"
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}")
set(${outlist_targets} ${${outlist_targets}} PARENT_SCOPE)
endfunction()
# Install D interface files
install(DIRECTORY ${JITRT_DIR}/d/ldc DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.d")
else()
function(build_jit_runtime d_flags c_flags ld_flags path_suffix outlist_targets)
endfunction()
endif()

View file

@ -0,0 +1,19 @@
//===-- callback_ostream.cpp ----------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#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) {}

View file

@ -0,0 +1,34 @@
//===-- callback_ostream.h - jit support ------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Simple llvm::raw_ostream implementation which sink all input to provided
// callback. It uses llvm::function_ref so user must ensure callback lifetime.
//
//===----------------------------------------------------------------------===//
#ifndef CALLBACK_OSTREAM_H
#define CALLBACK_OSTREAM_H
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/raw_ostream.h>
class CallbackOstream : public llvm::raw_ostream {
using CallbackT = llvm::function_ref<void(const char *, size_t)>;
CallbackT callback;
uint64_t currentPos = 0;
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,472 @@
//===-- compile.cpp -------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit runtime - shared library part.
// Defines jit runtime entry point and main compilation routines.
//
//===----------------------------------------------------------------------===//
#include <cassert>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include "callback_ostream.h"
#include "context.h"
#include "disassembler.h"
#include "optimizer.h"
#include "utils.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
#include "llvm/ExecutionEngine/RuntimeDyld.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/Cloning.h"
#if LDC_LLVM_VER >= 500
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#else
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#endif
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 RtCompileModuleList {
RtCompileModuleList *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::InitializeNativeTargetDisassembler();
llvm::InitializeNativeTargetAsmPrinter();
}
};
std::string decorate(const std::string &name) {
#if defined(__APPLE__)
return "_" + name;
#elif defined(_WIN32) && defined(_M_IX86)
assert(!name.empty());
if (0x1 == name[0]) {
return name.substr(1);
}
return "_" + name;
#else
return name;
#endif
}
auto getSymbolInProcess(const std::string &name)
-> decltype(llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name)) {
assert(!name.empty());
#if defined(_WIN32)
if ('_' == name[0]) {
return llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name.substr(1));
}
return llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name);
#else
return llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name);
#endif
}
struct ModuleListener {
llvm::TargetMachine &targetmachine;
llvm::raw_ostream *stream = nullptr;
ModuleListener(llvm::TargetMachine &tm) : targetmachine(tm) {}
template <typename T> auto operator()(T &&object) -> T {
if (nullptr != stream) {
disassemble(targetmachine, *object->getBinary(), *stream);
}
return std::move(object);
}
};
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;
using ListenerLayerT =
llvm::orc::ObjectTransformLayer<ObjectLayerT, ModuleListener>;
using CompileLayerT =
llvm::orc::IRCompileLayer<ListenerLayerT, llvm::orc::SimpleCompiler>;
using ModuleHandleT = CompileLayerT::ModuleHandleT;
#else
using ObjectLayerT = llvm::orc::ObjectLinkingLayer<>;
using ListenerLayerT =
llvm::orc::ObjectTransformLayer<ObjectLayerT, ModuleListener>;
using CompileLayerT = llvm::orc::IRCompileLayer<ListenerLayerT>;
using ModuleHandleT = CompileLayerT::ModuleSetHandleT;
#endif
ObjectLayerT objectLayer;
ListenerLayerT listenerlayer;
CompileLayerT compileLayer;
llvm::LLVMContext context;
bool compiled = false;
ModuleHandleT moduleHandle;
struct ListenerCleaner final {
MyJIT &owner;
ListenerCleaner(MyJIT &o, llvm::raw_ostream *stream) : owner(o) {
owner.listenerlayer.getTransform().stream = stream;
}
~ListenerCleaner() { owner.listenerlayer.getTransform().stream = nullptr; }
};
public:
MyJIT()
: targetmachine(llvm::EngineBuilder().selectTarget(
llvm::Triple(llvm::sys::getProcessTriple()), llvm::StringRef(),
llvm::sys::getHostCPUName(), getHostAttrs())),
dataLayout(targetmachine->createDataLayout()),
#if LDC_LLVM_VER >= 500
objectLayer(
[]() { return std::make_shared<llvm::SectionMemoryManager>(); }),
#endif
listenerlayer(objectLayer, ModuleListener(*targetmachine)),
compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)) {
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
}
llvm::TargetMachine &getTargetMachine() { return *targetmachine; }
bool addModule(std::unique_ptr<llvm::Module> module, const SymMap &symMap,
llvm::raw_ostream *asmListener) {
assert(nullptr != module);
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 = getSymbolInProcess(name)) {
return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported);
}
return llvm::JITSymbol(nullptr);
});
ListenerCleaner cleaner(*this, asmListener);
// Add the set to the JIT with the resolver we created above
#if LDC_LLVM_VER >= 500
auto result = compileLayer.addModule(std::move(module), Resolver);
if (!result) {
return true;
}
moduleHandle = result.get();
#else
std::vector<std::unique_ptr<llvm::Module>> modules;
modules.emplace_back(std::move(module));
moduleHandle = compileLayer.addModuleSet(
std::move(modules), llvm::make_unique<llvm::SectionMemoryManager>(),
std::move(Resolver));
#endif
compiled = true;
return false;
}
llvm::JITSymbol findSymbol(const std::string &name) {
return compileLayer.findSymbol(name, false);
}
llvm::LLVMContext &getContext() { return context; }
void reset() {
if (compiled) {
removeModule(moduleHandle);
moduleHandle = {};
compiled = false;
}
}
private:
void removeModule(const ModuleHandleT &handle) {
#if LDC_LLVM_VER >= 500
cantFail(compileLayer.removeModule(handle));
#else
compileLayer.removeModuleSet(handle);
#endif
}
};
MyJIT &getJit() {
static MyJIT jit;
return jit;
}
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);
}
void *resolveSymbol(llvm::JITSymbol &symbol) {
auto addr = symbol.getAddress();
#if LDC_LLVM_VER >= 500
if (!addr) {
consumeError(addr.takeError());
return nullptr;
} else {
return reinterpret_cast<void *>(addr.get());
}
#else
return reinterpret_cast<void *>(addr);
#endif
}
void dumpModule(const Context &context, const llvm::Module &module,
DumpStage stage) {
if (nullptr != context.dumpHandler) {
auto callback = [&](const char *str, size_t len) {
context.dumpHandler(context.dumpHandlerData, stage, str, len);
};
CallbackOstream os(callback);
module.print(os, nullptr, false, true);
}
}
void setFunctionsTarget(llvm::Module &module, llvm::TargetMachine &TM) {
// Set function target cpu to host if it wasn't set explicitly
for (auto &&func : module.functions()) {
if (!func.hasFnAttribute("target-cpu")) {
func.addFnAttr("target-cpu", TM.getTargetCPU());
}
if (!func.hasFnAttribute("target-features")) {
auto featStr = TM.getTargetFeatureString();
if (!featStr.empty()) {
func.addFnAttr("target-features", featStr);
}
}
}
}
struct JitFinaliser final {
MyJIT &jit;
bool finalized = false;
explicit JitFinaliser(MyJIT &j) : jit(j) {}
~JitFinaliser() {
if (!finalized) {
jit.reset();
}
}
void finalze() { finalized = true; }
};
void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head,
const Context &context) {
if (nullptr == modlist_head) {
// No jit modules to compile
return;
}
interruptPoint(context, "Init");
MyJIT &myJit = getJit();
auto current = modlist_head;
std::vector<std::pair<std::string, void **>> functions;
std::unique_ptr<llvm::Module> finalModule;
SymMap symMap;
OptimizerSettings settings;
settings.optLevel = context.optLevel;
settings.sizeLevel = context.sizeLevel;
while (nullptr != current) {
interruptPoint(context, "load IR");
auto buff = llvm::MemoryBuffer::getMemBuffer(
llvm::StringRef(current->irData,
static_cast<std::size_t>(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);
dumpModule(context, module, DumpStage::OriginalModule);
setFunctionsTarget(module, myJit.getTargetMachine());
module.setDataLayout(myJit.getTargetMachine().createDataLayout());
interruptPoint(context, "setRtCompileVars", name.data());
setRtCompileVars(context, module,
toArray(current->varList,
static_cast<std::size_t>(current->varListSize)));
if (nullptr == finalModule) {
finalModule = std::move(*mod);
} else {
if (llvm::Linker::linkModules(*finalModule, std::move(*mod))) {
fatal(context, "Can't merge module");
}
}
for (auto &&fun :
toArray(current->funcList,
static_cast<std::size_t>(current->funcListSize))) {
functions.push_back(std::make_pair(fun.name, fun.func));
}
for (auto &&sym : toArray(current->symList, static_cast<std::size_t>(
current->symListSize))) {
symMap.insert(std::make_pair(decorate(sym.name), sym.sym));
}
}
current = current->next;
}
assert(nullptr != finalModule);
dumpModule(context, *finalModule, DumpStage::MergedModule);
interruptPoint(context, "Optimize final module");
optimizeModule(context, myJit.getTargetMachine(), settings, *finalModule);
interruptPoint(context, "Verify final module");
verifyModule(context, *finalModule);
dumpModule(context, *finalModule, DumpStage::OptimizedModule);
interruptPoint(context, "Codegen final module");
if (nullptr != context.dumpHandler) {
auto callback = [&](const char *str, size_t len) {
context.dumpHandler(context.dumpHandlerData, DumpStage::FinalAsm, str,
len);
};
CallbackOstream os(callback);
if (myJit.addModule(std::move(finalModule), symMap, &os)) {
fatal(context, "Can't codegen module");
}
} else {
if (myJit.addModule(std::move(finalModule), symMap, nullptr)) {
fatal(context, "Can't codegen module");
}
}
JitFinaliser jitFinalizer(myJit);
interruptPoint(context, "Resolve functions");
for (auto &&fun : functions) {
auto decorated = decorate(fun.first);
auto symbol = myJit.findSymbol(decorated);
auto addr = resolveSymbol(symbol);
if (nullptr == addr) {
std::string desc = std::string("Symbol not found in jitted code: \"") +
fun.first + "\" (\"" + decorated + "\")";
fatal(context, desc);
} else {
*fun.second = addr;
}
if (nullptr != context.interruptPointHandler) {
std::stringstream ss;
ss << fun.first << " to " << addr;
auto str = ss.str();
interruptPoint(context, "Resolved", str.c_str());
}
}
jitFinalizer.finalze();
}
} // anon namespace
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#else
__attribute__ ((visibility ("default")))
#endif
void rtCompileProcessImplSo(const void *modlist_head,
const Context *context, size_t contextSize) {
assert(nullptr != context);
assert(sizeof(*context) == contextSize);
rtCompileProcessImplSoInternal(
static_cast<const RtCompileModuleList *>(modlist_head), *context);
}
}

View file

@ -0,0 +1,43 @@
//===-- context.h - jit support ---------------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit compilation context, must be in sync with runtimecompile.d.
//
//===----------------------------------------------------------------------===//
#ifndef CONTEXT_H
#define CONTEXT_H
#include <cstddef> //size_t
enum class DumpStage : int {
OriginalModule = 0,
MergedModule = 1,
OptimizedModule = 2,
FinalAsm = 3
};
typedef void (*InterruptPointHandlerT)(void *, const char *action,
const char *object);
typedef void (*FatalHandlerT)(void *, const char *reason);
typedef void (*DumpHandlerT)(void *, DumpStage stage, const char *str,
std::size_t len);
struct Context final {
unsigned optLevel = 0;
unsigned sizeLevel = 0;
InterruptPointHandlerT interruptPointHandler = nullptr;
void *interruptPointHandlerData = nullptr;
FatalHandlerT fatalHandler = nullptr;
void *fatalHandlerData = nullptr;
DumpHandlerT dumpHandler = nullptr;
void *dumpHandlerData = nullptr;
};
#endif // CONTEXT_H

View file

@ -0,0 +1,319 @@
//===-- disassembler.cpp --------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "disassembler.h"
#include <unordered_map>
#include <llvm/ADT/Triple.h>
#include <llvm/MC/MCAsmBackend.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/MCCodeEmitter.h>
#include <llvm/MC/MCContext.h>
#include <llvm/MC/MCDisassembler/MCDisassembler.h>
#include <llvm/MC/MCDisassembler/MCSymbolizer.h>
#include <llvm/MC/MCInst.h>
#include <llvm/MC/MCInstPrinter.h>
#include <llvm/MC/MCInstrAnalysis.h>
#include <llvm/MC/MCObjectFileInfo.h>
#include <llvm/MC/MCRegisterInfo.h>
#include <llvm/MC/MCStreamer.h>
#include <llvm/MC/MCSubtargetInfo.h>
#include <llvm/Object/ObjectFile.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Target/TargetMachine.h>
#if LDC_LLVM_VER >= 500
namespace {
template <typename T> std::unique_ptr<T> unique(T *ptr) {
return std::unique_ptr<T>(ptr);
}
enum class Stage {
Scan,
Emit,
};
class SymTable final {
llvm::MCContext &context;
Stage stage;
std::unordered_map<uint64_t, llvm::MCSymbol *> labelsPos;
std::unordered_map<uint64_t, llvm::MCSymbol *> labelsTargets;
std::unordered_map<uint64_t, llvm::MCSymbol *> externalSymbols;
public:
SymTable(llvm::MCContext &ctx) : context(ctx) {}
llvm::MCContext &getContext() { return context; }
void setStage(Stage s) { stage = s; }
Stage getStage() const { return stage; }
void reset() {
labelsPos.clear();
labelsTargets.clear();
externalSymbols.clear();
}
void addLabel(uint64_t pos, uint64_t target, llvm::StringRef name = {}) {
if (auto label = getTargetLabel(target)) {
labelsPos.insert({pos, label});
return;
}
auto sym = name.empty() ? context.createTempSymbol("", false)
: context.getOrCreateSymbol(name);
assert(nullptr != sym);
labelsPos.insert({pos, sym});
labelsTargets.insert({target, sym});
}
llvm::MCSymbol *getPosLabel(uint64_t pos) const {
auto it = labelsPos.find(pos);
if (labelsPos.end() != it) {
return it->second;
}
return nullptr;
}
llvm::MCSymbol *getTargetLabel(uint64_t target) const {
auto it = labelsTargets.find(target);
if (labelsTargets.end() != it) {
return it->second;
}
return nullptr;
}
void addExternalSymbolRel(uint64_t pos, llvm::StringRef name) {
auto sym = context.getOrCreateSymbol(name);
assert(nullptr != sym);
externalSymbols.insert({pos, sym});
}
llvm::MCSymbol *getExternalSymbolRel(uint64_t pos) const {
auto it = externalSymbols.find(pos);
if (externalSymbols.end() != it) {
return it->second;
}
return nullptr;
}
};
void printFunction(const llvm::MCDisassembler &disasm,
const llvm::MCInstrAnalysis &mcia,
llvm::ArrayRef<uint8_t> data, SymTable &symTable,
const llvm::MCSubtargetInfo &sti,
llvm::MCStreamer &streamer) {
const Stage stages[] = {Stage::Scan, Stage::Emit};
for (auto stage : stages) {
symTable.setStage(stage);
uint64_t size = 0;
for (uint64_t pos = 0; pos < static_cast<uint64_t>(data.size());
pos += size) {
llvm::MCInst inst;
auto status = disasm.getInstruction(inst, size, data.slice(pos), pos,
llvm::nulls(), llvm::nulls());
switch (status) {
case llvm::MCDisassembler::Fail:
streamer.EmitRawText("failed to disassemble");
return;
case llvm::MCDisassembler::SoftFail:
streamer.EmitRawText("potentially undefined instruction encoding:");
LLVM_FALLTHROUGH;
case llvm::MCDisassembler::Success:
if (Stage::Scan == stage) {
if (mcia.isBranch(inst) || mcia.isCall(inst)) {
uint64_t target = 0;
if (mcia.evaluateBranch(inst, pos, size, target)) {
symTable.addLabel(pos, target);
}
}
} else if (Stage::Emit == stage) {
if (auto label = symTable.getTargetLabel(pos)) {
streamer.EmitLabel(label);
}
streamer.EmitInstruction(inst, sti);
}
break;
}
assert(0 != size);
}
}
}
class Symbolizer final : public llvm::MCSymbolizer {
SymTable &symTable;
const llvm::MCExpr *createExpr(llvm::MCSymbol *sym, int64_t offset = 0) {
assert(nullptr != sym);
auto &ctx = symTable.getContext();
auto expr = llvm::MCSymbolRefExpr::create(sym, ctx);
if (0 == offset) {
return expr;
}
auto off = llvm::MCConstantExpr::create(offset, ctx);
return llvm::MCBinaryExpr::createAdd(expr, off, ctx);
}
public:
Symbolizer(llvm::MCContext &Ctx,
std::unique_ptr<llvm::MCRelocationInfo> RelInfo,
SymTable &symtable)
: MCSymbolizer(Ctx, std::move(RelInfo)), symTable(symtable) {}
virtual bool tryAddingSymbolicOperand(llvm::MCInst &Inst,
llvm::raw_ostream & /*cStream*/,
int64_t Value, uint64_t Address,
bool IsBranch, uint64_t Offset,
uint64_t /*InstSize*/) override {
if (Stage::Emit == symTable.getStage()) {
if (IsBranch) {
if (auto label = symTable.getPosLabel(Address)) {
Inst.addOperand(llvm::MCOperand::createExpr(createExpr(label)));
return true;
}
}
if (auto sym = symTable.getExternalSymbolRel(Address + Offset)) {
Inst.addOperand(llvm::MCOperand::createExpr(createExpr(sym, Value)));
return true;
}
}
return false;
}
virtual void tryAddingPcLoadReferenceComment(llvm::raw_ostream & /*cStream*/,
int64_t /*Value*/,
uint64_t /*Address*/) override {
// Nothing
}
};
void processRelocations(SymTable &symTable,
const llvm::object::ObjectFile &object,
const llvm::object::SectionRef &sec) {
for (const auto &reloc : sec.relocations()) {
const auto symIt = reloc.getSymbol();
if (object.symbol_end() != symIt) {
const auto sym = *symIt;
symTable.addExternalSymbolRel(reloc.getOffset(),
llvm::cantFail(sym.getName()));
}
}
}
}
void disassemble(const llvm::TargetMachine &tm,
const llvm::object::ObjectFile &object,
llvm::raw_ostream &os) {
auto &target = tm.getTarget();
auto mri = tm.getMCRegisterInfo();
auto mai = tm.getMCAsmInfo();
auto sti = tm.getMCSubtargetInfo();
auto mii = tm.getMCInstrInfo();
if (nullptr == mri || nullptr == mai || nullptr == sti || nullptr == mii) {
// TODO: proper error handling
return;
}
llvm::MCObjectFileInfo mofi;
llvm::MCContext ctx(mai, mri, &mofi);
#if LDC_LLVM_VER >= 600
mofi.InitMCObjectFileInfo(tm.getTargetTriple(), tm.isPositionIndependent(),
ctx, tm.getCodeModel() == llvm::CodeModel::Large);
#else
mofi.InitMCObjectFileInfo(tm.getTargetTriple(), tm.isPositionIndependent(),
tm.getCodeModel(), ctx);
#endif
auto disasm = unique(target.createMCDisassembler(*sti, ctx));
if (nullptr == disasm) {
return;
}
SymTable symTable(ctx);
disasm->setSymbolizer(llvm::make_unique<Symbolizer>(
ctx, llvm::make_unique<llvm::MCRelocationInfo>(ctx), symTable));
auto mcia = unique(target.createMCInstrAnalysis(mii));
if (nullptr == mcia) {
return;
}
auto mip = unique(
target.createMCInstPrinter(tm.getTargetTriple(), 0, *mai, *mii, *mri));
if (nullptr == mip) {
return;
}
auto mce = unique(target.createMCCodeEmitter(*mii, *mri, ctx));
if (nullptr == mce) {
return;
}
llvm::MCTargetOptions opts;
auto mab = unique(target.createMCAsmBackend(
*mri, tm.getTargetTriple().getTriple(), tm.getTargetCPU(), opts));
if (nullptr == mab) {
return;
}
// Streamer takes ownership of mip mce mab
auto asmStreamer = unique(target.createAsmStreamer(
ctx, llvm::make_unique<llvm::formatted_raw_ostream>(os), false, true,
mip.release(), mce.release(), mab.release(), false));
if (nullptr == asmStreamer) {
return;
}
asmStreamer->InitSections(false);
for (const auto &symbol : object.symbols()) {
const auto name = llvm::cantFail(symbol.getName());
const auto secIt = llvm::cantFail(symbol.getSection());
if (object.section_end() != secIt) {
const auto sec = *secIt;
llvm::StringRef data;
sec.getContents(data);
llvm::ArrayRef<uint8_t> buff(
reinterpret_cast<const uint8_t *>(data.data()), data.size());
if (llvm::object::SymbolRef::ST_Function ==
llvm::cantFail(symbol.getType())) {
symTable.reset();
symTable.addLabel(0, 0, name); // Function start
processRelocations(symTable, object, sec);
// TODO: something more optimal
for (const auto &globalSec : object.sections()) {
if (globalSec.getRelocatedSection() == secIt) {
processRelocations(symTable, object, globalSec);
}
}
printFunction(*disasm, *mcia, buff, symTable, *sti, *asmStreamer);
asmStreamer->EmitRawText("");
}
}
}
}
#else
void disassemble(const llvm::TargetMachine & /*tm*/,
const llvm::object::ObjectFile & /*object*/,
llvm::raw_ostream &os) {
os << "Asm output not supported";
os.flush();
}
#endif

View file

@ -0,0 +1,28 @@
//===-- disassembler.h - jit support ----------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit disassembler - allow to disassemble in-memory object files
//
//===----------------------------------------------------------------------===//
#ifndef DISASSEMBLER_H
#define DISASSEMBLER_H
namespace llvm {
namespace object {
class ObjectFile;
}
class TargetMachine;
class raw_ostream;
}
void disassemble(const llvm::TargetMachine &tm,
const llvm::object::ObjectFile &object, llvm::raw_ostream &os);
#endif // DISASSEMBLER_H

View file

@ -0,0 +1,154 @@
//===-- optimizer.cpp -----------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "optimizer.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO/Inliner.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "context.h"
#include "utils.h"
#include "valueparser.h"
namespace {
// TODO: share this function with compiler
void addOptimizationPasses(llvm::legacy::PassManagerBase &mpm,
llvm::legacy::FunctionPassManager &fpm,
unsigned optLevel, unsigned sizeLevel) {
llvm::PassManagerBuilder builder;
builder.OptLevel = optLevel;
builder.SizeLevel = sizeLevel;
// TODO: expose this option from jit
if (/*willInline()*/ true) {
#if LDC_LLVM_VER >= 400
auto params = llvm::getInlineParams(optLevel, sizeLevel);
builder.Inliner = llvm::createFunctionInliningPass(params);
#else
builder.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel);
#endif
} else {
#if LDC_LLVM_VER >= 400
builder.Inliner = llvm::createAlwaysInlinerLegacyPass();
#else
builder.Inliner = llvm::createAlwaysInlinerPass();
#endif
}
builder.DisableUnitAtATime = false;
// TODO: Expose this option
builder.DisableUnrollLoops = optLevel == 0;
// TODO: expose this option
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;
}
// TODO: expose this option
builder.SLPVectorize =
/*disableSLPVectorization*/ false ? false : optLevel > 1 && sizeLevel < 2;
// TODO: sanitizers support in jit?
// TODO: lang specific passes support
// TODO: addStripExternalsPass?
// TODO: PGO support in jit?
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.sizeLevel);
}
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) {
if (fun.isDeclaration()) {
interruptPoint(context, "Func decl", fun.getName().data());
} else {
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);
}
}

View file

@ -0,0 +1,41 @@
//===-- optimizer.h - jit support -------------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit runtime - jit optimizer.
//
//===----------------------------------------------------------------------===//
#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 sizeLevel = 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,48 @@
//===-- utils.cpp ---------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "utils.h"
#include <cassert>
#include <cstdio>
#include <cstdlib>
#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());
fflush(stderr);
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,29 @@
//===-- utils.h - jit support -----------------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit runtime - misc routines.
//
//===----------------------------------------------------------------------===//
#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,116 @@
//===-- valueparser.cpp ---------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "valueparser.h"
#include <cassert>
#include <cstdint>
#include <string>
#include "utils.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/Support/raw_ostream.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,31 @@
//===-- valueparser.h - jit support -----------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit runtime - value parser.
// Reads data from host process and generates llvm::Constant suitable
// as initializer.
//
//===----------------------------------------------------------------------===//
#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,35 @@
//===-- compile.cpp -------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the Boost Software License. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Jit runtime - executable part.
// Defines jit modules list head and and access jit shared library entry point.
//
//===----------------------------------------------------------------------===//
#include <cstddef> // size_t
struct Context;
extern "C" {
// Silence missing-variable-declaration clang warning
extern void *dynamiccompile_modules_head;
void *dynamiccompile_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(dynamiccompile_modules_head, context, contextSize);
}
}

View file

@ -0,0 +1,122 @@
/**
* Contains dynamic compilation API.
*
* Copyright: the LDC team
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
*/
module ldc.dynamic_compile;
version(LDC_DynamicCompilation):
/// Dump handler stage
enum DumpStage : int
{
OriginalModule = 0,
MergedModule = 1,
OptimizedModule = 2,
FinalAsm = 3
}
/// Dynamic compiler settings
struct CompilerSettings
{
/// The Optimization Level - Specify the basic optimization level.
/// 0 = -O0, 1 = -O1, 2 = -O2, 3 = -O3
uint optLevel = 0;
/// SizeLevel - How much we're optimizing for size.
/// 0 = none, 1 = -Os, 2 = -Oz
uint sizeLevel = 0;
/// Optional progress handler, dynamic compiler will report compilation stages through it
/// Signature is (in char[] action, in char[] object)
/// Actual format of reports is not specified and must be used for debugging
/// purposes only
void delegate(in char[], in char[]) progressHandler = null;
/// Optional dump handler, dynamic compiler will report module contents through it
/// This function will be called multiple times during compilation and user must concatenate all
/// reported parts manually
/// Actual format of dump is not specified and must be used for debugging
/// purposes only
void delegate(DumpStage, in char[]) dumpHandler = null;
}
/++
+ Compile all dynamic code.
+ This function must be called before any calls to @dynamicCompile functions and
+ after any changes to @dynamicCompileConst variables
+
+ Consecutive calls to this function do nothing
+
+ This function is not thread-safe
+
+ Example:
+ ---
+ import ldc.attributes, ldc.dynamic_compile, std.stdio;
+
+ @dynamicCompile int foo() { return value * 42; }
+
+ void main() {
+ compileDynamicCode();
+ writeln(foo());
+ }
+/
void compileDynamicCode(in CompilerSettings settings = CompilerSettings.init)
{
Context context;
context.optLevel = settings.optLevel;
context.sizeLevel = settings.sizeLevel;
if (settings.progressHandler !is null)
{
context.interruptPointHandler = &progressHandlerWrapper;
context.interruptPointHandlerData = cast(void*)&settings.progressHandler;
}
if (settings.dumpHandler !is null)
{
context.dumpHandler = &dumpHandlerWrapper;
context.dumpHandlerData = cast(void*)&settings.dumpHandler;
}
rtCompileProcessImpl(context, context.sizeof);
}
private:
extern(C)
{
void progressHandlerWrapper(void* context, const char* desc, const char* obj)
{
import std.string;
alias DelType = typeof(CompilerSettings.progressHandler);
auto del = cast(DelType*)context;
(*del)(fromStringz(desc), fromStringz(obj));
}
void dumpHandlerWrapper(void* context, DumpStage stage, const char* buff, size_t len)
{
alias DelType = typeof(CompilerSettings.dumpHandler);
auto del = cast(DelType*)context;
assert(buff !is null);
(*del)(stage, buff[0..len]);
}
// must be synchronized with cpp
struct Context
{
uint optLevel = 0;
uint sizeLevel = 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*, DumpStage, const char*, size_t) dumpHandler = null;
void* dumpHandlerData = null;
}
extern void rtCompileProcessImpl(const ref Context context, size_t contextSize);
}

View file

@ -9,6 +9,7 @@ struct Config {
string ldcExecutable; string ldcExecutable;
string buildDir; string buildDir;
bool resetBuildDir; bool resetBuildDir;
bool resetOnly;
string ldcSourceDir; string ldcSourceDir;
bool ninja; bool ninja;
bool buildTestrunners; bool buildTestrunners;
@ -35,6 +36,12 @@ int main(string[] args) {
findLdcExecutable(); findLdcExecutable();
prepareBuildDir(); prepareBuildDir();
if (config.resetOnly) {
writefln("Runtime libraries build directory successfully reset (%s)", config.buildDir);
return 0;
}
prepareLdcSource(); prepareLdcSource();
runCMake(); runCMake();
build(); build();
@ -230,47 +237,57 @@ void extractZipArchive(string archivePath, string destination) {
void parseCommandLine(string[] args) { void parseCommandLine(string[] args) {
import std.getopt : arraySep, getopt, defaultGetoptPrinter; import std.getopt : arraySep, getopt, defaultGetoptPrinter;
arraySep = ";"; try {
auto helpInformation = getopt( arraySep = ";";
args, auto helpInformation = getopt(
"ldc", "Path to LDC executable (default: '" ~ defaultLdcExecutable ~ "')", &config.ldcExecutable, args,
"buildDir", "Path to build directory (default: './ldc-build-runtime.tmp')", &config.buildDir, "ldc", "Path to LDC executable (default: '" ~ defaultLdcExecutable ~ "')", &config.ldcExecutable,
"reset", "If build directory exists, start with removing everything but the ldc-src subdirectory", &config.resetBuildDir, "buildDir", "Path to build directory (default: './ldc-build-runtime.tmp')", &config.buildDir,
"ldcSrcDir", "Path to LDC source directory (if not specified: downloads & extracts source archive into '<buildDir>/ldc-src')", &config.ldcSourceDir, "reset", "If build directory exists, start with removing everything but the ldc-src subdirectory", &config.resetBuildDir,
"ninja", "Use Ninja as CMake build system", &config.ninja, "resetOnly", "Like --reset, but only resets the build directory. No other actions are taken.", &config.resetOnly,
"testrunners", "Build the testrunner executables too", &config.buildTestrunners, "ldcSrcDir", "Path to LDC source directory (if not specified: downloads & extracts source archive into '<buildDir>/ldc-src')", &config.ldcSourceDir,
"targetPreset","Target configuration preset by LDC devs, e.g. Android-arm", &config.targetPreset, "ninja", "Use Ninja as CMake build system", &config.ninja,
"targetSystem","Target OS/toolchain (separated by ';'), e.g. Windows;MSVC", &config.targetSystem, "testrunners", "Build the testrunner executables too", &config.buildTestrunners,
"dFlags", "LDC flags for the D modules (separated by ';')", &config.dFlags, "targetPreset","Target configuration preset by LDC devs, e.g. Android-arm", &config.targetPreset,
"cFlags", "C/ASM compiler flags for the handful of C/ASM files (separated by ';')", &config.cFlags, "targetSystem","Target OS/toolchain (separated by ';'), e.g. Windows;MSVC", &config.targetSystem,
"linkerFlags", "C linker flags for shared libraries and testrunner executables (separated by ';')", &config.linkerFlags, "dFlags", "LDC flags for the D modules (separated by ';')", &config.dFlags,
"j", "Number of parallel build jobs", &config.numBuildJobs "cFlags", "C/ASM compiler flags for the handful of C/ASM files (separated by ';')", &config.cFlags,
); "linkerFlags", "C linker flags for shared libraries and testrunner executables (separated by ';')", &config.linkerFlags,
"j", "Number of parallel build jobs", &config.numBuildJobs
);
// getopt() removed all consumed args from `args` // getopt() removed all consumed args from `args`
import std.range : drop; import std.range : drop;
foreach (arg; args.drop(1)) { foreach (arg; args.drop(1)) {
import std.algorithm.searching : findSplit; import std.algorithm.searching : findSplit;
const r = arg.findSplit("="); const r = arg.findSplit("=");
if (r[1].length == 0) { if (r[1].length == 0) {
helpInformation.helpWanted = true; helpInformation.helpWanted = true;
break; break;
}
config.cmakeVars[r[0]] = r[2];
} }
config.cmakeVars[r[0]] = r[2];
}
if (helpInformation.helpWanted) { if (helpInformation.helpWanted) {
defaultGetoptPrinter( defaultGetoptPrinter(
"Builds the LDC runtime libraries.\n" ~ "Builds the LDC runtime libraries.\n" ~
"Programs required to be found in your PATH:\n" ~ "Programs required to be found in your PATH:\n" ~
" * CMake\n" ~ " * CMake\n" ~
" * either Make or Ninja (recommended, enable with '--ninja')\n" ~ " * either Make or Ninja (recommended, enable with '--ninja')\n" ~
" * C toolchain (compiler and linker)\n" ~ " * C toolchain (compiler and linker)\n" ~
"--targetPreset currently supports Android-arm or Android-aarch64.\n" ~ "--targetPreset currently supports Android-arm or Android-aarch64.\n" ~
"All arguments are optional.\n" ~ "All arguments are optional.\n" ~
"CMake variables (see runtime/CMakeLists.txt in LDC source) can be specified via arguments like 'VAR=value'.\n", "CMake variables (see runtime/CMakeLists.txt in LDC source) can be specified via arguments like 'VAR=value'.\n",
helpInformation.options helpInformation.options
); );
exit(1);
}
if (config.resetOnly) config.resetBuildDir = true;
}
catch (Exception e) {
writefln("Error processing command line arguments: %s", e.msg);
writeln("Use '--help' for help.");
exit(1); exit(1);
} }
} }

View file

@ -0,0 +1,5 @@
// RUN: %ldc -c -I%S/inputs %s
import gh2422a;
void main() { auto aa = ["": new Empty]; }

View file

@ -0,0 +1 @@
class Empty { }

View file

@ -0,0 +1,28 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import std.exception;
import ldc.attributes;
import ldc.dynamic_compile;
__gshared int[555] arr1 = 42;
__gshared int[555] arr2 = 42;
@dynamicCompile int foo()
{
int[555] a = arr1;
return a[3];
}
@dynamicCompile int bar()
{
arr2 = 0;
return arr2[3];
}
void main(string[] args)
{
compileDynamicCode();
assert(42 == foo());
assert(0 == bar());
}

View file

@ -0,0 +1,33 @@
// REQUIRES: atleast_llvm500
// RUN: %ldc -enable-dynamic-compile -run %s
import std.array;
import std.string;
import ldc.attributes;
import ldc.dynamic_compile;
__gshared int value = 32;
@dynamicCompile int foo()
{
return value;
}
void main(string[] args)
{
auto dump = appender!string();
CompilerSettings settings;
settings.dumpHandler = (DumpStage stage, in char[] str)
{
if (DumpStage.FinalAsm == stage)
{
dump.put(str);
}
};
compileDynamicCode(settings);
// Check function and variables names in asm
assert(-1 != indexOf(dump.data, foo.mangleof));
assert(-1 != indexOf(dump.data, value.mangleof));
}

View file

@ -0,0 +1,51 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile int foo()
{
return 5;
}
int bar()
{
return 7;
}
@dynamicCompile int baz()
{
return foo() + bar();
}
alias fptr = int function();
@dynamicCompile
{
fptr get_foo_ptr()
{
return &foo;
}
fptr get_bar_ptr()
{
return &bar;
}
fptr get_baz_ptr()
{
return &baz;
}
}
void main(string[] args)
{
compileDynamicCode();
assert(5 == foo());
assert(7 == bar());
assert(12 == baz());
assert(5 == get_foo_ptr()());
assert(7 == get_bar_ptr()());
assert(12 == get_baz_ptr()());
}

View file

@ -0,0 +1,33 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
interface IFoo
{
int foo();
}
class Foo : IFoo
{
int val = 0;
@dynamicCompile int foo()
{
return val;
}
}
void main(string[] args)
{
auto f1 = new Foo;
auto f2 = cast(IFoo)f1;
auto fun = &f1.foo;
f1.val = 42;
compileDynamicCode();
assert(42 == f1.foo());
assert(42 == f2.foo());
assert(42 == fun());
}

View file

@ -0,0 +1,54 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile int foo()
{
return 5;
}
@dynamicCompile int bar(int i = 5)
{
if(i > 0)
{
return bar(i - 1) + 1;
}
return 1;
}
@dynamicCompile int baz()
{
int i = 0;
foreach(j;1..4)
{
i += j * j;
}
return i;
}
void main(string[] args)
{
bool[4] dumpHandlerCalled = false;
bool progressHandlerCalled = false;
CompilerSettings settings;
settings.dumpHandler = (DumpStage stage, in char[] str)
{
dumpHandlerCalled[stage] = true;
};
settings.progressHandler = (in char[] desc, in char[] object)
{
progressHandlerCalled = true;
};
compileDynamicCode(settings);
assert(5 == foo());
assert(6 == bar());
assert(14 == baz());
assert(dumpHandlerCalled[DumpStage.OriginalModule]);
assert(dumpHandlerCalled[DumpStage.MergedModule]);
assert(dumpHandlerCalled[DumpStage.OptimizedModule]);
assert(dumpHandlerCalled[DumpStage.FinalAsm]);
assert(progressHandlerCalled);
}

View file

@ -0,0 +1,9 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.dynamic_compile;
void main(string[] args)
{
compileDynamicCode();
}

View file

@ -0,0 +1,33 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompileConst __gshared int foovar = 0;
@dynamicCompile int foo()
{
return foovar;
}
@dynamicCompileConst __gshared int barvar = 5;
@dynamicCompile int bar()
{
return barvar;
}
void main(string[] args)
{
compileDynamicCode();
assert(0 == foo());
assert(5 == bar());
foovar = 42;
barvar = 43;
assert(0 == foo());
assert(5 == bar());
compileDynamicCode();
assert(42 == foo());
assert(43 == bar());
}

View file

@ -0,0 +1,100 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
struct Foo
{
int i;
float f;
void* p;
int[3] a;
int[] da;
}
struct Bar
{
Foo f;
Foo* pf;
Bar* pb;
}
@dynamicCompileConst
{
__gshared byte i8 = 42 + 1;
__gshared short i16 = 42 + 2;
__gshared int i32 = 42 + 3;
__gshared long i64 = 42 + 4;
__gshared ubyte u8 = 42 + 5;
__gshared ushort u16 = 42 + 6;
__gshared uint u32 = 42 + 7;
__gshared ulong u64 = 42 + 8;
__gshared float f32 = 42 + 9;
__gshared double f64 = 42 + 10;
__gshared void* ptr = cast(void*)(42 + 11);
__gshared int[3] arr = [42 + 12,42 + 13,42 + 14];
__gshared int[] darr = [42 + 15,42 + 16,42 + 17,42 + 18];
__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]);
__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));
}
@dynamicCompile
{
byte foo_i8() { return i8; }
short foo_i16() { return i16; }
int foo_i32() { return i32; }
long foo_i64() { return i64; }
ubyte foo_u8() { return u8; }
ushort foo_u16() { return u16; }
uint foo_u32() { return u32; }
ulong foo_u64() { return u64; }
float foo_f32() { return f32; }
double foo_f64() { return f64; }
void* foo_ptr() { return ptr; }
int[3] foo_arr() { return arr; }
int[] foo_darr() { return darr; }
Foo foo_foo() { return foo; }
Bar foo_bar() { return bar; }
}
void test(T,F)(ref T val, F fun)
{
assert(val == fun());
}
void main(string[] args)
{
compileDynamicCode();
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,10 @@
module inputs.module1;
import inputs.module3;
import ldc.attributes;
@dynamicCompile int get()
{
return 3 + inputs.module3.get1() + inputs.module3.get2();
}

View file

@ -0,0 +1,10 @@
module inputs.module2;
import inputs.module3;
import ldc.attributes;
@dynamicCompile int get()
{
return 4 + inputs.module3.get1() + inputs.module3.get2();
}

View file

@ -0,0 +1,13 @@
module inputs.module3;
import ldc.attributes;
@dynamicCompile int get1()
{
return 3;
}
int get2()
{
return 4;
}

View file

@ -0,0 +1,5 @@
module inputs.rtconst_owner;
import ldc.attributes;
@dynamicCompileConst __gshared int value = 1;

View file

@ -0,0 +1,10 @@
module inputs.rtconst_user;
import inputs.rtconst_owner;
import ldc.attributes;
@dynamicCompile int getValue()
{
return 10 + value;
}

View file

@ -0,0 +1,25 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
void main(string[] args)
{
@dynamicCompile int foo()
{
return 42;
}
int val1 = 5;
int val2 = 3;
@dynamicCompile int bar()
{
val2 += 5;
return val1;
}
compileDynamicCode();
assert(42 == foo());
assert(5 == bar());
assert(8 == val2);
}

View file

@ -0,0 +1,32 @@
// RUN: %ldc -enable-dynamic-compile -lib -I%S %S/inputs/module1.d %S/inputs/module2.d %S/inputs/module3.d -of=%t1%lib
// RUN: %ldc -enable-dynamic-compile -O1 -lib -I%S %S/inputs/module1.d -of=%t2%lib
// RUN: %ldc -enable-dynamic-compile -O2 -lib -I%S %S/inputs/module2.d -of=%t3%lib
// RUN: %ldc -enable-dynamic-compile -O3 -lib -I%S %S/inputs/module3.d -of=%t4%lib
// RUN: %ldc -enable-dynamic-compile -I%S %s %t1%lib -run
// RUN: %ldc -enable-dynamic-compile -Os -I%S %s %t2%lib %t3%lib %t4%lib -run
import ldc.attributes;
import ldc.dynamic_compile;
import inputs.module1;
import inputs.module2;
@dynamicCompile int foo()
{
return inputs.module1.get() + inputs.module2.get();
}
int bar()
{
return inputs.module1.get() + inputs.module2.get();
}
void main(string[] args)
{
compileDynamicCode();
assert(10 == inputs.module1.get());
assert(11 == inputs.module2.get());
assert(21 == foo());
assert(21 == bar());
}

View file

@ -0,0 +1,28 @@
// RUN: %ldc -enable-dynamic-compile -I%S %s %S/inputs/module1.d %S/inputs/module2.d %S/inputs/module3.d -run
// RUN: %ldc -enable-dynamic-compile -singleobj -I%S %s %S/inputs/module1.d %S/inputs/module2.d %S/inputs/module3.d -run
import ldc.attributes;
import ldc.dynamic_compile;
import inputs.module1;
import inputs.module2;
@dynamicCompile int foo()
{
return inputs.module1.get() + inputs.module2.get();
}
int bar()
{
return inputs.module1.get() + inputs.module2.get();
}
void main(string[] args)
{
compileDynamicCode();
assert(10 == inputs.module1.get());
assert(11 == inputs.module2.get());
assert(21 == foo());
assert(21 == bar());
}

View file

@ -0,0 +1,18 @@
// RUN: %ldc -enable-dynamic-compile -I%S %s %S/inputs/rtconst_owner.d %S/inputs/rtconst_user.d -run
// RUN: %ldc -enable-dynamic-compile -singleobj -I%S %s %S/inputs/rtconst_owner.d %S/inputs/rtconst_user.d -run
import ldc.dynamic_compile;
import inputs.rtconst_owner;
import inputs.rtconst_user;
void main(string[] args)
{
compileDynamicCode();
assert(11 == getValue());
value = 2;
assert(11 == getValue());
compileDynamicCode();
assert(12 == getValue());
}

View file

@ -0,0 +1,54 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
__gshared int ctorsCalled = 0;
__gshared int dtorsCalled = 0;
__gshared int fooCalled = 0;
struct Foo
{
this(this)
{
++ctorsCalled;
}
~this()
{
++dtorsCalled;
}
void foo()
{
++fooCalled;
}
}
void func1(Foo f)
{
f.foo();
}
@dynamicCompile void func2(Foo f)
{
f.foo();
}
void main(string[] args)
{
compileDynamicCode();
Foo f;
func1(f);
assert(1 == ctorsCalled);
assert(1 == dtorsCalled);
assert(1 == fooCalled);
ctorsCalled = 0;
dtorsCalled = 0;
fooCalled = 0;
func2(f);
assert(1 == ctorsCalled);
assert(1 == dtorsCalled);
assert(1 == fooCalled);
}

View file

@ -0,0 +1,20 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile int foo(int i)
{
if (i > 0)
{
return foo(i - 1) + 1;
}
return 0;
}
void main(string[] args)
{
compileDynamicCode();
assert(15 == foo(15));
}

View file

@ -0,0 +1,55 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import std.stdio;
import ldc.attributes;
import ldc.dynamic_compile;
version(LDC_DynamicCompilation)
{
}
else
{
static assert(false, "LDC_DynamicCompilation is not defined");
}
@dynamicCompile int foo()
{
return 5;
}
@dynamicCompile int bar()
{
return foo() + 7;
}
@dynamicCompile void baz()
{
writeln("baz");
}
void main(string[] args)
{
void run(CompilerSettings settings)
{
compileDynamicCode(settings);
assert(5 == foo());
assert(12 == bar());
baz();
int function() fptr = &bar;
assert(12 == fptr());
}
foreach(i;0..4)
{
CompilerSettings settings;
settings.optLevel = i;
run(settings);
}
foreach(i;0..3)
{
CompilerSettings settings;
settings.sizeLevel = i;
run(settings);
}
}

View file

@ -0,0 +1,76 @@
// llvm generates overlapped simd reads and writes to init these
// structs but fails on win32 for some reason
// XFAIL: Windows_x86
// RUN: %ldc -enable-dynamic-compile -run %s
import ldc.dynamic_compile;
import ldc.attributes;
import std.stdio;
struct Foo1
{
int[4 + 2] arr = [ 0 , 1, 2, 3, 4, 5];
}
struct Foo2
{
int[8 + 4] arr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11];
}
struct Foo3
{
int[16 + 8] arr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
12,13,14,15,16,17,18,19,20,21,22,23];
}
Foo1 foo1()
{
auto f = Foo1();
return f;
}
Foo2 foo2()
{
auto f = Foo2();
return f;
}
Foo3 foo3()
{
auto f = Foo3();
return f;
}
@dynamicCompile Foo1 bar1()
{
auto f = Foo1();
return f;
}
@dynamicCompile Foo2 bar2()
{
auto f = Foo2();
return f;
}
@dynamicCompile Foo3 bar3()
{
auto f = Foo3();
return f;
}
void main(string[] args)
{
compileDynamicCode();
stdout.flush();
auto f1 = foo1();
auto f2 = foo2();
auto f3 = foo3();
auto b1 = bar1();
auto b2 = bar2();
auto b3 = bar3();
assert(f1 == b1);
assert(f2 == b2);
assert(f3 == b3);
}

View file

@ -0,0 +1,49 @@
// RUN: %ldc -enable-dynamic-compile -run %s
import core.thread;
import ldc.attributes;
import ldc.dynamic_compile;
ThreadID threadId; //thread local
@dynamicCompile void set_val()
{
threadId = Thread.getThis().id();
}
@dynamicCompile ThreadID get_val()
{
return threadId;
}
@dynamicCompile ThreadID* get_ptr()
{
auto ptr = &threadId;
return ptr;
}
void bar()
{
set_val();
auto id = Thread.getThis().id();
assert(id == threadId);
assert(id == get_val());
assert(&threadId is get_ptr());
assert(id == *get_ptr());
}
void main(string[] args)
{
compileDynamicCode();
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,48 @@
// exceptions is broken on windows
// win64 issue https://bugs.llvm.org//show_bug.cgi?id=24233
// XFAIL: Windows
// RUN: %ldc -enable-dynamic-compile -run %s
import std.exception;
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile void foo()
{
throw new Exception("foo");
}
@dynamicCompile int bar()
{
try
{
throw new Exception("foo");
}
catch(Exception e)
{
return 42;
}
return 0;
}
@dynamicCompile int baz()
{
try
{
foo();
}
catch(Exception e)
{
return 42;
}
return 0;
}
void main(string[] args)
{
compileDynamicCode();
assert(collectExceptionMsg(foo()) == "foo");
assert(42 == bar());
assert(42 == baz());
}

View file

@ -0,0 +1,18 @@
// tls without workaround broken on all platforms
// just test ldc accept this option
// RUN: %ldc -enable-dynamic-compile -dynamic-compile-tls-workaround=0 -run %s
// RUN: %ldc -enable-dynamic-compile -dynamic-compile-tls-workaround=1 -run %s
import ldc.attributes;
import ldc.dynamic_compile;
@dynamicCompile void foo()
{
}
void main(string[] args)
{
compileDynamicCode();
foo();
}

View file

@ -21,6 +21,7 @@ config.llvm_version = @LDC_LLVM_VER@
config.llvm_targetsstr = "@LLVM_TARGETS_TO_BUILD@" config.llvm_targetsstr = "@LLVM_TARGETS_TO_BUILD@"
config.default_target_bits = @DEFAULT_TARGET_BITS@ config.default_target_bits = @DEFAULT_TARGET_BITS@
config.with_PGO = @LDC_WITH_PGO@ config.with_PGO = @LDC_WITH_PGO@
config.dynamic_compile = @LDC_DYNAMIC_COMPILE@
config.name = 'LDC' config.name = 'LDC'
@ -46,6 +47,10 @@ config.excludes = [
if not config.with_PGO: if not config.with_PGO:
config.excludes.append('PGO') config.excludes.append('PGO')
# Exclude runtime compilation tests when it's disabled
if not config.dynamic_compile:
config.excludes.append('dynamiccompile')
# Explicit forwarding of environment variables # Explicit forwarding of environment variables
env_cc = os.environ.get('CC', '') env_cc = os.environ.get('CC', '')
if env_cc: if env_cc:
@ -120,11 +125,18 @@ config.substitutions.append( ('%runtimedir', config.ldc2_runtime_dir ) )
# Add platform-dependent file extension substitutions # Add platform-dependent file extension substitutions
if (platform.system() == 'Windows'): if (platform.system() == 'Windows'):
# add LDC lib dir to the path so app will be able to find jit.dll
# TODO: Something more robust
path = os.path.pathsep.join( (config.ldc2_lib_dir, config.environment['PATH']) )
config.environment['PATH'] = path
config.substitutions.append( ('%obj', '.obj') ) config.substitutions.append( ('%obj', '.obj') )
config.substitutions.append( ('%exe', '.exe') ) config.substitutions.append( ('%exe', '.exe') )
config.substitutions.append( ('%lib', '.lib') )
else: else:
config.substitutions.append( ('%obj', '.o') ) config.substitutions.append( ('%obj', '.o') )
config.substitutions.append( ('%exe', '') ) config.substitutions.append( ('%exe', '') )
config.substitutions.append( ('%lib', '.a') )
# Add cdb substitution # Add cdb substitution
if (platform.system() == 'Windows') and (config.default_target_bits == 32): if (platform.system() == 'Windows') and (config.default_target_bits == 32):