//===-- 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 #include #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 { enum { ApiVersion = LDC_DYNAMIC_COMPILE_API_VERSION }; 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(type), llvm::GlobalValue::ExternalLinkage, name, &module); } else { ret = new llvm::GlobalVariable( module, type, false, llvm::GlobalValue::ExternalLinkage, nullptr, name); } return ret; } template bool contains(const C &cont, const T &val) { return cont.end() != cont.find(val); } template void enumOperands(const llvm::User &usr, F &&handler) { for (auto &&op : usr.operands()) { llvm::Value *val = op.get(); if (auto opusr = llvm::dyn_cast(val)) { if (auto gv = llvm::dyn_cast(opusr)) { handler(gv); } enumOperands(*opusr, std::forward(handler)); } } } template void enumFuncSymbols(llvm::Function *fun, F &&handler) { assert(nullptr != fun); if (fun->hasPersonalityFn()) { if (auto personality = llvm::dyn_cast(fun->getPersonalityFn())) { handler(personality); } } for (auto &&bb : *fun) { for (auto &&instr : bb) { enumOperands(instr, std::forward(handler)); } } } enum class GlobalValVisibility { Internal, External, Declaration, }; using GlobalValsMap = std::unordered_map; 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 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 runtimeCompiledVars; for (auto &&var : irs->dynamicCompiledVars) { assert(nullptr != var); assert(nullptr != var->value); runtimeCompiledVars.insert(llvm::cast(var->value)); } std::vector 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(gv)) { if (!newFun->isIntrinsic()) { it.first->second = GlobalValVisibility::Internal; functionsToAdd.push_back(newFun); } } } } }); } newFunctions.swap(functionsToAdd); functionsToAdd.clear(); } return ret; } template 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 thunkVar2func; std::unordered_map thunkFun2func; std::unordered_set 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 { for (auto &op : instr.operands()) { auto val = op.get(); if (auto callee = llvm::dyn_cast(val)) { auto it = thunkFun2func.find(callee->getName()); if (thunkFun2func.end() != it) { auto realFunc = newModule.getFunction(it->second); assert(nullptr != realFunc); op.set(realFunc); } } } return false; }); } // Thunks should be unused now, strip them for (auto &&it : funcs) { assert(nullptr != it.first); assert(nullptr != it.second.thunkFunc); auto func = newModule.getFunction(it.second.thunkFunc->getName()); assert(func != nullptr); if (func->use_empty()) { func->eraseFromParent(); } if (nullptr != it.second.thunkVar) { auto var = newModule.getGlobalVariable(it.second.thunkVar->getName()); assert(var != nullptr); if (var->use_empty()) { var->eraseFromParent(); } } } } void removeFunctionsTargets(IRState *irs, llvm::Module &module) { assert(nullptr != irs); std::unordered_map 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 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(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 getArrayAndSize(llvm::Module &module, llvm::Type *elemType, llvm::ArrayRef 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 void createStaticArray(llvm::Module &mod, llvm::GlobalVariable *var, llvm::GlobalVariable *varLen, // can be null llvm::ArrayRef arr) { assert(nullptr != var); const auto dataLen = arr.size(); auto gvar = new llvm::GlobalVariable( mod, llvm::ArrayType::get(llvm::TypeBuilder::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 // { // i32 version; // Must be first // 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::IntegerType::get(context, 32), 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 generateFuncList(IRState *irs, const Types &types) { assert(nullptr != irs); std::vector 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 generateSymList(IRState *irs, const Types &types, const GlobalValsMap &globalVals) { assert(nullptr != irs); std::vector elements; for (auto &&it : globalVals) { if (it.second == GlobalValVisibility::Declaration) { auto val = it.first; if (auto fun = llvm::dyn_cast(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 generateVarList(IRState *irs, const Types &types) { assert(nullptr != irs); std::vector elements; for (auto &&val : irs->dynamicCompiledVars) { auto gvar = llvm::cast(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::ConstantInt::get(irs->context(), APInt(32, ApiVersion)), // version llvm::ConstantPointerNull::get(llvm::dyn_cast( elem_type->getElementType(1))), // 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); 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 elemIndex = llvm::ConstantInt::get(irs->context(), APInt(32, 1)); auto modListHeadPtr = declareModListHead(irs->module, types); llvm::Value *gepVals[] = {zero64, elemIndex}; 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( reinterpret_cast(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 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(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