diff --git a/.circleci/config.yml b/.circleci/config.yml index c9c996b45a7..d657cd69dd4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,7 +32,7 @@ commonSteps: &commonSteps fi # Install lit python3 --version - python3 -m pip install --user lit + python3 -m pip install --user lit psutil python3 -c "import lit.main; lit.main.main();" --version . | head -n 1 # Download & extract host LDC if HOST_LDC_VERSION is set if [[ -v HOST_LDC_VERSION ]]; then diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index b3cb8e08a80..12083df09ec 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -125,7 +125,7 @@ runs: if [[ '${{ runner.os }}-${{ inputs.arch }}' == 'macOS-arm64' ]]; then brew install lit else - python3 -m pip install --user lit + python3 -m pip install --user lit psutil fi python3 -c "import lit.main; lit.main.main();" --version . | head -n 1 diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 56a7ae09834..bcda36dd021 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -59,7 +59,7 @@ jobs: if [[ '${{ matrix.os }}' == 'macos-14' ]]; then brew install lit else - python3 -m pip install --user lit + python3 -m pip install --user lit psutil fi python3 -c "import lit.main; lit.main.main();" --version . | head -n 1 - name: 'Linux: Install gdb, lld, llvm-dev and libclang-common-dev' diff --git a/CHANGELOG.md b/CHANGELOG.md index e504a75d4eb..08deff0d7ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # LDC master #### Big news +- Revived dynamic-compile (JIT) functionality (formerly unsupported since LLVM 12), supporting LLVM 18+ now. (#4774) - ldc2.conf: `%%ldcversion%%` placeholder added, allowing to refer to version-specific directories. #### Platform support diff --git a/CMakeLists.txt b/CMakeLists.txt index 73cc8b8a592..ed85f8a50e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -452,20 +452,33 @@ include(HandleLTOPGOBuildOptions) # # Enable Dynamic compilation if supported for this platform and LLVM version. # -set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default; not supported for LLVM >= 12.") +set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default; not supported for LLVM < 18.") option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON) if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO") - if(LDC_LLVM_VER LESS 1200) - set(LDC_DYNAMIC_COMPILE ON) - else() - # TODO: port from ORCv1 API (dropped with LLVM 12) to ORCv2 (added with LLVM 7) + if(LDC_LLVM_VER LESS 1800) set(LDC_DYNAMIC_COMPILE OFF) + else() + set(LDC_DYNAMIC_COMPILE ON) endif() endif() +# https://llvm.org/docs/JITLink.html for the list of supported platforms +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|x64|amd64|aarch64|arm64|riscv64|loongarch64") + set(_LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK_DEFAULT ON) +else() + set(_LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK_DEFAULT OFF) +endif() +# Disable LLVM JITLink on Windows for now, currently does not work very well on Windows +if(WIN32) + set(_LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK_DEFAULT OFF) +endif() +option(LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK "Use the experimental but faster LLVM JITLink dynamic code linker" "${_LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK_DEFAULT}") message(STATUS "-- Building LDC with dynamic compilation support (LDC_DYNAMIC_COMPILE): ${LDC_DYNAMIC_COMPILE}") if(LDC_DYNAMIC_COMPILE) add_definitions(-DLDC_DYNAMIC_COMPILE) - add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=3) + add_definitions(-DLDC_DYNAMIC_COMPILE_API_VERSION=4) +endif() +if (LDC_DYNAMIC_COMPILE_USE_LLVM_JITLINK) + add_compile_definitions(-DLDC_JITRT_USE_JITLINK) endif() # @@ -545,7 +558,7 @@ else() # Define a 'HOST_D' CMake linker language for the static LDCShared # library, using the host ldmd2 compiler ≥ v1.5 as archiver, which # supports LTO objects and cross-archiving. - set(CMAKE_HOST_D_CREATE_STATIC_LIBRARY "${D_COMPILER} -lib ${D_COMPILER_FLAGS} ${DFLAGS_BASE} -of= ") + set(CMAKE_HOST_D_CREATE_STATIC_LIBRARY "\"${D_COMPILER}\" -lib ${D_COMPILER_FLAGS} ${DFLAGS_BASE} -of= ") set(LDC_LIB_LANGUAGE HOST_D) endif() endif() diff --git a/cmake/Modules/BuildDExecutable.cmake b/cmake/Modules/BuildDExecutable.cmake index 6318433ea75..d5ee8cddf83 100644 --- a/cmake/Modules/BuildDExecutable.cmake +++ b/cmake/Modules/BuildDExecutable.cmake @@ -48,9 +48,10 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin endif() add_custom_command( OUTPUT ${object_file} - COMMAND ${D_COMPILER} -c ${dflags} -of${object_file} ${d_src_files} + COMMAND "${D_COMPILER}" -c ${dflags} -of${object_file} ${d_src_files} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} DEPENDS ${d_src_files} ${extra_compile_deps} + VERBATIM ) set(object_files ${object_file}) else() @@ -62,9 +63,10 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}/${object_file}${CMAKE_CXX_OUTPUT_EXTENSION}) add_custom_command( OUTPUT ${object_file} - COMMAND ${D_COMPILER} -c ${dflags} -of${object_file} ${f} + COMMAND "${D_COMPILER}" -c ${dflags} -of${object_file} ${f} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} DEPENDS ${f} ${extra_compile_deps} + VERBATIM ) list(APPEND object_files ${object_file}) endforeach() @@ -108,9 +110,10 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin add_custom_command( OUTPUT ${output_exe} - COMMAND ${D_COMPILER} ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} + COMMAND "${D_COMPILER}" ${dflags} -of${output_exe} ${objects_args} ${dep_libs} ${translated_linker_args} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} DEPENDS ${target_name}_d_objects ${object_files} ${link_deps} + VERBATIM ) add_custom_target(${target_name} ALL DEPENDS ${output_exe}) endif() diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 5e0b83134e4..0902167b6f1 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -364,7 +364,7 @@ void writeModule(llvm::Module *m, const char *filename) { // run LLVM optimization passes { ::TimeTraceScope timeScope("Optimize", filename); - ldc_optimize_module(m); + ldc_optimize_module(m, gTargetMachine); } if (global.params.dllimport != DLLImport::none) { diff --git a/gen/dynamiccompile.cpp b/gen/dynamiccompile.cpp index b982013c4f3..817e3c009ee 100644 --- a/gen/dynamiccompile.cpp +++ b/gen/dynamiccompile.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "gen/dynamiccompile.h" +#include #if defined(LDC_DYNAMIC_COMPILE) @@ -91,14 +92,15 @@ using GlobalValsMap = void getPredefinedSymbols(IRState *irs, GlobalValsMap &symList) { assert(nullptr != irs); const llvm::Triple *triple = global.params.targetTriple; - if (triple->isWindowsMSVCEnvironment() || - triple->isWindowsGNUEnvironment()) { - // Actual signatures doesn't matter here, we only want it to show in + if (triple->isWindowsMSVCEnvironment() || triple->isWindowsGNUEnvironment()) { + // Actual signatures doesn't matter here, we only want them to show in // symbols list - symList.insert(std::make_pair( - getPredefinedSymbol(irs->module, "__chkstk", - llvm::Type::getInt32Ty(irs->context())), - GlobalValVisibility::Declaration)); + for (auto &syms : {"\1__chkstk", "memset", "memcpy"}) { + symList.insert(std::make_pair( + getPredefinedSymbol(irs->module, syms, + llvm::Type::getInt32Ty(irs->context())), + GlobalValVisibility::Declaration)); + } if (!opts::dynamicCompileTlsWorkaround) { symList.insert(std::make_pair( getPredefinedSymbol(irs->module, "_tls_index", @@ -364,10 +366,8 @@ llvm::Constant *getArrayPtr(llvm::Type *type, llvm::Constant *array) { idxs, true); } -llvm::Constant *getI8Ptr(llvm::GlobalValue *val) { - assert(nullptr != val); - return llvm::ConstantExpr::getBitCast( - val, llvm::IntegerType::getInt8PtrTy(val->getContext())); +static llvm::PointerType *getI8PtrType(llvm::LLVMContext &C) { + return LLPointerType::getUnqual(LLType::getInt8Ty(C)); } std::pair @@ -410,7 +410,7 @@ llvm::Constant *createStringInitializer(llvm::Module &mod, true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::getString(mod.getContext(), str, true), ".str"); return llvm::ConstantExpr::getBitCast( - nameVar, llvm::Type::getInt8PtrTy(mod.getContext())); + nameVar, getI8PtrType(mod.getContext())); } // void createStaticString(llvm::Module& mod, @@ -436,16 +436,15 @@ llvm::Constant *createStringInitializer(llvm::Module &mod, // struct RtCompileVarList // { // i8* name; -// i8* ptr; +// i8* init; // } llvm::StructType *getVarListElemType(llvm::LLVMContext &context) { llvm::Type *elements[] = { - llvm::IntegerType::getInt8PtrTy(context), - llvm::IntegerType::getInt8PtrTy(context), + getI8PtrType(context), + getI8PtrType(context), }; - return llvm::StructType::create(context, elements, /*"RtCompileVarList"*/ "", - true); + return llvm::StructType::create(context, elements, /*"RtCompileVarList"*/ ""); } // struct RtCompileSymList @@ -456,11 +455,10 @@ llvm::StructType *getVarListElemType(llvm::LLVMContext &context) { llvm::StructType *getSymListElemType(llvm::LLVMContext &context) { llvm::Type *elements[] = { - llvm::IntegerType::getInt8PtrTy(context), - llvm::IntegerType::getInt8PtrTy(context), + getI8PtrType(context), + getI8PtrType(context), }; - return llvm::StructType::create(context, elements, /*"RtCompileSymList"*/ "", - true); + return llvm::StructType::create(context, elements, /*"RtCompileSymList"*/ ""); } // struct RtCompileFuncList @@ -472,12 +470,11 @@ llvm::StructType *getSymListElemType(llvm::LLVMContext &context) { llvm::StructType *getFuncListElemType(llvm::LLVMContext &context) { llvm::Type *elements[] = { - llvm::IntegerType::getInt8PtrTy(context), - llvm::IntegerType::getInt8PtrTy(context), - llvm::IntegerType::getInt8PtrTy(context), + getI8PtrType(context), + getI8PtrType(context), + getI8PtrType(context), }; - return llvm::StructType::create(context, elements, /*"RtCompileFuncList"*/ "", - true); + return llvm::StructType::create(context, elements, /*"RtCompileFuncList"*/ ""); } // struct RtCompileModuleList @@ -490,6 +487,8 @@ llvm::StructType *getFuncListElemType(llvm::LLVMContext &context) { // i32 funcListSize; // RtCompileSymList* symList; // i32 symListSize; +// RtCompileVarList* varList; +// i32 varListSize; // }; llvm::StructType *getModuleListElemType(llvm::LLVMContext &context, @@ -504,7 +503,7 @@ llvm::StructType *getModuleListElemType(llvm::LLVMContext &context, llvm::Type *elements[] = { llvm::IntegerType::get(context, 32), llvm::PointerType::getUnqual(ret), - llvm::IntegerType::getInt8PtrTy(context), + getI8PtrType(context), llvm::IntegerType::get(context, 32), llvm::PointerType::getUnqual(funcListElemType), llvm::IntegerType::get(context, 32), @@ -513,7 +512,7 @@ llvm::StructType *getModuleListElemType(llvm::LLVMContext &context, llvm::PointerType::getUnqual(varListElemType), llvm::IntegerType::get(context, 32), }; - ret->setBody(elements, true); + ret->setBody(elements); return ret; } @@ -546,8 +545,8 @@ generateFuncList(IRState *irs, const Types &types, auto name = it.first->getName(); llvm::Constant *fields[] = { createStringInitializer(irs->module, name), - getI8Ptr(it.second.thunkVar), - getI8Ptr(it.second.thunkFunc), + it.second.thunkVar, + it.second.thunkFunc, }; elements.push_back( llvm::ConstantStruct::get(types.funcListElemType, fields)); @@ -563,7 +562,7 @@ generateFuncList(IRState *irs, const Types &types, llvm::Constant *fields[] = { createStringInitializer(irs->module, name), nullp, - getI8Ptr(func), + func, }; elements.push_back( llvm::ConstantStruct::get(types.funcListElemType, fields)); @@ -578,7 +577,7 @@ llvm::Constant *generateSymListElem(llvm::Module &module, const Types &types, llvm::Constant *fields[] = { createStringInitializer(module, name), - getI8Ptr(&val), + &val, }; return llvm::ConstantStruct::get(types.symListElemType, fields); } @@ -618,7 +617,7 @@ generateVarList(IRState *irs, const Types &types) { auto name = gvar->getName(); llvm::Constant *fields[] = { createStringInitializer(irs->module, name), - getI8Ptr(gvar), + gvar, }; elements.push_back( llvm::ConstantStruct::get(types.varListElemType, fields)); @@ -650,16 +649,18 @@ llvm::GlobalVariable *generateModuleListElem(IRState *irs, const Types &types, }; auto init = llvm::ConstantStruct::get(elem_type, fields); + auto *modListElem = new llvm::GlobalVariable( + irs->module, elem_type, false, llvm::GlobalValue::PrivateLinkage, init, + ".rtcompile_modlist_elem"); + modListElem->setAlignment(irs->module.getDataLayout().getABITypeAlign(elem_type->getPointerTo())); - return new llvm::GlobalVariable(irs->module, elem_type, false, - llvm::GlobalValue::PrivateLinkage, init, - ".rtcompile_modlist_elem"); + return modListElem; } llvm::PointerType *getModListHeadType(llvm::LLVMContext &context, const Types &types) { (void)types; - return llvm::IntegerType::getInt8PtrTy(context); + return getI8PtrType(context); } llvm::GlobalVariable *declareModListHead(llvm::Module &module, @@ -685,11 +686,10 @@ void generateCtorBody(IRState *irs, const Types &types, llvm::Function *func, 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 elemNextPtr = builder.CreateGEP(types.modListElemType, modListElem, gepVals); + auto prevHeadVal = builder.CreateLoad(types.modListElemType->getPointerTo()->getPointerTo(), modListHeadPtr); auto voidPtr = builder.CreateBitOrPointerCast( - modListElem, llvm::IntegerType::getInt8PtrTy(irs->context())); + modListElem, getI8PtrType(irs->context())); builder.CreateStore(voidPtr, modListHeadPtr); builder.CreateStore(prevHeadVal, elemNextPtr); @@ -721,7 +721,7 @@ void setupModuleBitcodeData(const llvm::Module &srcModule, IRState *irs, llvm::WriteBitcodeToFile(srcModule, os); auto runtimeCompiledIr = new llvm::GlobalVariable( - irs->module, llvm::Type::getInt8PtrTy(irs->context()), true, + irs->module, getI8PtrType(irs->context()), true, llvm::GlobalValue::PrivateLinkage, nullptr, ".rtcompile_ir"); auto runtimeCompiledIrSize = new llvm::GlobalVariable( @@ -761,7 +761,7 @@ void createThunkFunc(llvm::Module &module, const llvm::Function *src, auto bb = llvm::BasicBlock::Create(module.getContext(), "", dst); llvm::IRBuilder<> builder(module.getContext()); builder.SetInsertPoint(bb); - auto thunkPtr = builder.CreateLoad(thunkVar); + auto thunkPtr = builder.CreateLoad(llvm::PointerType::getUnqual(module.getContext()), thunkVar); llvm::SmallVector args; for (auto &arg : dst->args()) { args.push_back(&arg); diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 1e15789156e..27d4026e2d8 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -5,21 +5,37 @@ // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // +// This module is compiled into both the compiler and the JIT runtime library +// (with predefined IN_JITRT). +// //===----------------------------------------------------------------------===// +#ifdef IN_JITRT +#include "runtime/jit-rt/cpp-so/optimizer.h" +#include "runtime/jit-rt/cpp-so/valueparser.h" +#include "runtime/jit-rt/cpp-so/utils.h" +#endif + #include "gen/optimizer.h" +#ifndef IN_JITRT #include "dmd/errors.h" #include "gen/logger.h" +#endif + #include "gen/passes/GarbageCollect2Stack.h" #include "gen/passes/StripExternals.h" #include "gen/passes/SimplifyDRuntimeCalls.h" #include "gen/passes/Passes.h" + +#ifndef IN_JITRT #include "driver/cl_options.h" #include "driver/cl_options_instrumentation.h" #include "driver/cl_options_sanitizers.h" #include "driver/plugins.h" #include "driver/targetmachine.h" +#endif + #if LDC_LLVM_VER < 1700 #include "llvm/ADT/Triple.h" #else @@ -55,7 +71,6 @@ #include "llvm/Transforms/Scalar/Reassociate.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" -extern llvm::TargetMachine *gTargetMachine; using namespace llvm; static cl::opt optimizeLevel( @@ -96,6 +111,7 @@ static cl::opt disableGCToStack( "disable-gc2stack", cl::ZeroOrMore, cl::desc("Disable promotion of GC allocations to stack memory")); +#ifndef IN_JITRT static cl::opt> enableInlining( "inlining", cl::ZeroOrMore, @@ -105,6 +121,7 @@ static cl::opt> enableCrossModuleInlining( "cross-module-inlining", cl::ZeroOrMore, cl::Hidden, cl::desc("(*) Enable cross-module function inlining (default disabled)")); +#endif static cl::opt stripDebug( "strip-debug", cl::ZeroOrMore, @@ -135,12 +152,20 @@ static unsigned sizeLevel() { return optimizeLevel < 0 ? -optimizeLevel : 0; } // Determines whether or not to run the normal, full inlining pass. bool willInline() { +#ifdef IN_JITRT + return false; +#else return enableInlining == cl::BOU_TRUE || (enableInlining == cl::BOU_UNSET && optLevel() > 1); +#endif } bool willCrossModuleInline() { +#ifdef IN_JITRT + return false; +#else return enableCrossModuleInlining == llvm::cl::BOU_TRUE && willInline(); +#endif } bool isOptimizationEnabled() { return optimizeLevel != 0; } @@ -180,6 +205,7 @@ static OptimizationLevel getOptimizationLevel(){ return OptimizationLevel::O0; } +#ifndef IN_JITRT static void addAddressSanitizerPasses(ModulePassManager &mpm, OptimizationLevel level ) { AddressSanitizerOptions aso; @@ -262,6 +288,7 @@ static void addPGOPasses(ModulePassManager &mpm, } } } +#endif // !IN_JITRT static void addStripExternalsPass(ModulePassManager &mpm, OptimizationLevel level ) { @@ -296,7 +323,7 @@ static void addGarbageCollect2StackPass(ModulePassManager &mpm, } } - +#ifndef IN_JITRT static llvm::Optional getPGOOptions() { // FIXME: Do we have these anywhere? bool debugInfoForProfiling = false; @@ -341,6 +368,7 @@ static llvm::Optional getPGOOptions() { return std::nullopt; #endif } +#endif // !IN_JITRT static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsigned sizeLevelVal) { PipelineTuningOptions pto; @@ -373,7 +401,7 @@ static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsi * PassManagerBuilder. */ //Run optimization passes using the new pass manager -void runOptimizationPasses(llvm::Module *M) { +void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) { // Create a ModulePassManager to hold and optimize the collection of // per-module passes we are about to build. @@ -415,8 +443,12 @@ void runOptimizationPasses(llvm::Module *M) { si.registerCallbacks(pic, &mam); #endif - PassBuilder pb(gTargetMachine, getPipelineTuningOptions(optLevelVal, sizeLevelVal), + PassBuilder pb(TM, getPipelineTuningOptions(optLevelVal, sizeLevelVal), +#ifdef IN_JITRT + {}, &pic); +#else getPGOOptions(), &pic); +#endif // register the target library analysis directly because clang does :) auto tlii = createTLII(*M); @@ -433,6 +465,7 @@ void runOptimizationPasses(llvm::Module *M) { // TODO: port over strip-debuginfos pass for -strip-debug +#ifndef IN_JITRT pb.registerPipelineStartEPCallback(addPGOPasses); if (opts::isSanitizerEnabled(opts::AddressSanitizer)) { @@ -455,6 +488,7 @@ void runOptimizationPasses(llvm::Module *M) { if (opts::isSanitizerEnabled(opts::CoverageSanitizer)) { pb.registerOptimizerLastEPCallback(addSanitizerCoveragePass); } +#endif // !IN_JITRT if (!disableLangSpecificPasses) { if (!disableSimplifyDruntimeCalls) { @@ -474,7 +508,9 @@ void runOptimizationPasses(llvm::Module *M) { pb.registerOptimizerLastEPCallback(addStripExternalsPass); +#ifndef IN_JITRT registerAllPluginsWithPassBuilder(pb); +#endif pb.registerModuleAnalyses(mam); pb.registerCGSCCAnalyses(cgam); @@ -486,6 +522,9 @@ void runOptimizationPasses(llvm::Module *M) { OptimizationLevel level = getOptimizationLevel(); if (optLevelVal == 0) { +#ifdef IN_JITRT + mpm = pb.buildO0DefaultPipeline(level, false); +#else mpm = pb.buildO0DefaultPipeline(level, opts::isUsingLTO()); #if LDC_LLVM_VER >= 1700 } else if (opts::ltoFatObjects && opts::isUsingLTO()) { @@ -498,6 +537,7 @@ void runOptimizationPasses(llvm::Module *M) { mpm = pb.buildThinLTOPreLinkDefaultPipeline(level); } else if (opts::isUsingLTO()) { mpm = pb.buildLTOPreLinkDefaultPipeline(level); +#endif // !IN_JITRT } else { mpm = pb.buildPerModuleDefaultPipeline(level); } @@ -508,7 +548,8 @@ void runOptimizationPasses(llvm::Module *M) { //////////////////////////////////////////////////////////////////////////////// // This function runs optimization passes based on command line arguments. // Returns true if any optimization passes were invoked. -bool ldc_optimize_module(llvm::Module *M) { +bool ldc_optimize_module(llvm::Module *M, llvm::TargetMachine *TM) { +#ifndef IN_JITRT // Dont optimise spirv modules because turning GEPs into extracts triggers // asserts in the IR -> SPIR-V translation pass. SPIRV doesn't have a target // machine, so any optimisation passes that rely on it to provide analysis, @@ -518,8 +559,9 @@ bool ldc_optimize_module(llvm::Module *M) { // TODO: run rudimentary optimisations to improve IR debuggability. if (getComputeTargetType(M) == ComputeBackend::SPIRV) return false; +#endif - runOptimizationPasses(M); + runOptimizationPasses(M, TM); // Verify the resulting module. if (!noVerify) { @@ -530,18 +572,38 @@ bool ldc_optimize_module(llvm::Module *M) { return true; } +#ifdef IN_JITRT +void optimizeModule(const OptimizerSettings &settings, llvm::Module *M, + llvm::TargetMachine *TM) { + if (settings.sizeLevel > 0) { + optimizeLevel = -settings.sizeLevel; + } else { + optimizeLevel = settings.optLevel; + } + + ldc_optimize_module(M, TM); +} +#endif // IN_JITRT // Verifies the module. void verifyModule(llvm::Module *m) { +#ifndef IN_JITRT Logger::println("Verifying module..."); LOG_SCOPE; +#endif std::string ErrorStr; raw_string_ostream OS(ErrorStr); if (llvm::verifyModule(*m, &OS)) { +#ifndef IN_JITRT error(Loc(), "%s", ErrorStr.c_str()); fatal(); +#else + assert(false && "Verification failed!"); +#endif } +#ifndef IN_JITRT Logger::println("Verification passed!"); +#endif } // Output to `hash_os` all optimization settings that influence object code @@ -559,3 +621,21 @@ void outputOptimizationSettings(llvm::raw_ostream &hash_os) { hash_os << disableLoopVectorization; hash_os << disableSLPVectorization; } + +#ifdef IN_JITRT +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->getValueType(); + auto initializer = + parseInitializer(module.getDataLayout(), *type, init, + [&](const std::string &str) { fatal(context, str); }); + var->setConstant(true); + var->setInitializer(initializer); + var->setLinkage(llvm::GlobalValue::PrivateLinkage); + } +} +#endif // IN_JITRT diff --git a/gen/optimizer.h b/gen/optimizer.h index 8877c3d796c..8ba3521a28d 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -32,9 +32,10 @@ class raw_ostream; namespace llvm { class Module; class TargetLibraryInfoImpl; +class TargetMachine; } -bool ldc_optimize_module(llvm::Module *m); +bool ldc_optimize_module(llvm::Module *m, llvm::TargetMachine *tm); // Returns whether the normal, full inlining pass will be run. bool willInline(); diff --git a/runtime/jit-rt/DefineBuildJitRT.cmake b/runtime/jit-rt/DefineBuildJitRT.cmake index 3e916f4e5b4..5342937fd67 100644 --- a/runtime/jit-rt/DefineBuildJitRT.cmake +++ b/runtime/jit-rt/DefineBuildJitRT.cmake @@ -1,10 +1,9 @@ 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_H ${JITRT_DIR}/cpp/*.h) - file(GLOB LDC_JITRT_SO_CXX ${JITRT_DIR}/cpp-so/*.cpp) + file(GLOB LDC_JITRT_SO_CXX ${JITRT_DIR}/cpp-so/*.cpp ${CMAKE_SOURCE_DIR}/gen/optimizer.cpp) file(GLOB LDC_JITRT_SO_H ${JITRT_DIR}/cpp-so/*.h) message(STATUS "Use custom passes in jit: ${LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES}") if(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES) @@ -47,7 +46,7 @@ if(LDC_DYNAMIC_COMPILE) endmacro() function(build_jit_runtime d_flags c_flags ld_flags path_suffix outlist_targets) - set(jitrt_components core support irreader executionengine passes nativecodegen orcjit target ${LLVM_NATIVE_ARCH}disassembler asmprinter) + set(jitrt_components core support irreader executionengine passes nativecodegen orcjit target ${LLVM_NATIVE_ARCH}disassembler asmprinter ${LLVM_NATIVE_ARCH}asmparser) llvm_set_libs(JITRT_LIBS libs "${jitrt_components}") get_target_suffix("" "${path_suffix}" target_suffix) @@ -62,6 +61,7 @@ if(LDC_DYNAMIC_COMPILE) ) set_target_properties(ldc-jit-rt-so${target_suffix} PROPERTIES LINKER_LANGUAGE CXX) + target_compile_definitions(ldc-jit-rt-so${target_suffix} PRIVATE IN_JITRT) if(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES) target_compile_definitions(ldc-jit-rt-so${target_suffix} PRIVATE LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES) endif() diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index ff87e81b2ef..a17522599ab 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -10,7 +10,9 @@ #include "bind.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" @@ -26,10 +28,15 @@ llvm::FunctionType *getDstFuncType(llvm::FunctionType &srcType, assert(!srcType.isVarArg()); llvm::SmallVector newParams; const auto srcParamsCount = srcType.params().size(); - assert(params.size() == srcParamsCount); - for (size_t i = 0; i < srcParamsCount; ++i) { + assert(params.size() <= srcParamsCount); + const size_t implicitParamsCount = srcParamsCount - params.size(); + for (size_t i = 0; i < implicitParamsCount; ++i) { + newParams.push_back(srcType.getParamType(static_cast(i))); + } + for (size_t i = 0; i < params.size(); ++i) { if (params[i].data == nullptr) { - newParams.push_back(srcType.getParamType(static_cast(i))); + newParams.push_back( + srcType.getParamType(static_cast(i + implicitParamsCount))); } } auto retType = srcType.getReturnType(); @@ -42,6 +49,7 @@ llvm::Function *createBindFunc(llvm::Module &module, llvm::Function &srcFunc, const llvm::ArrayRef ¶ms) { auto newFunc = llvm::Function::Create( &funcType, llvm::GlobalValue::ExternalLinkage, "\1.jit_bind", &module); + newFunc->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); newFunc->setCallingConv(srcFunc.getCallingConv()); // auto srcAttributes = srcFunc.getAttributes(); @@ -65,11 +73,23 @@ llvm::Function *createBindFunc(llvm::Module &module, llvm::Function &srcFunc, llvm::Value * allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, - const llvm::DataLayout &layout, const ParamSlice ¶m, + llvm::Type *byvalType, const llvm::DataLayout &layout, + const ParamSlice ¶m, llvm::function_ref errHandler, const BindOverride &override) { - if (param.type == ParamType::Aggregate && srcType.isPointerTy()) { - auto elemType = srcType.getPointerElementType(); +#if _WIN32 || _WIN64 + if (srcType.isPointerTy() && + (layout.getPointerSizeInBits() / 8 != param.size)) { + // special situation on Windows platforms: fake byval + // we construct a phony array type to store the init data + // TODO: there is currently no way for us to detect fake byval init values + // that are exactly one pointer-size wide + byvalType = llvm::ArrayType::get( + llvm::Type::getInt8Ty(builder.getContext()), param.size); + } +#endif + if (byvalType) { + auto elemType = byvalType; auto stackArg = builder.CreateAlloca(elemType); stackArg->setAlignment(layout.getABITypeAlign(elemType)); auto init = @@ -82,7 +102,7 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, auto init = parseInitializer(layout, srcType, param.data, errHandler, override); builder.CreateStore(init, stackArg); - return builder.CreateLoad(stackArg); + return builder.CreateLoad(&srcType, stackArg); } void doBind(llvm::Module &module, llvm::Function &dstFunc, @@ -98,6 +118,15 @@ void doBind(llvm::Module &module, llvm::Function &dstFunc, auto currentArg = dstFunc.arg_begin(); auto funcType = srcFunc.getFunctionType(); auto &layout = module.getDataLayout(); + const size_t implicitArgsCount = funcType->getNumParams() - params.size(); + + // forward implicit arguments + for (size_t i = 0; i < implicitArgsCount; ++i) { + args.push_back(currentArg); + ++currentArg; + } + + // handle formal arguments for (size_t i = 0; i < params.size(); ++i) { llvm::Value *arg = nullptr; const auto ¶m = params[i]; @@ -105,8 +134,10 @@ void doBind(llvm::Module &module, llvm::Function &dstFunc, arg = currentArg; ++currentArg; } else { - auto type = funcType->getParamType(static_cast(i)); - arg = allocParam(builder, *type, layout, param, errHandler, override); + size_t currentOffset = i + implicitArgsCount; + auto type = funcType->getParamType(static_cast(currentOffset)); + auto byvalType = srcFunc.getParamByValType(currentOffset); + arg = allocParam(builder, *type, byvalType, layout, param, errHandler, override); } assert(arg != nullptr); args.push_back(arg); @@ -115,8 +146,7 @@ void doBind(llvm::Module &module, llvm::Function &dstFunc, auto ret = builder.CreateCall(&srcFunc, args); if (!srcFunc.isDeclaration()) { - ret->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::AlwaysInline); + ret->addRetAttr(llvm::Attribute::AlwaysInline); } ret->setCallingConv(srcFunc.getCallingConv()); ret->setAttributes(srcFunc.getAttributes()); diff --git a/runtime/jit-rt/cpp-so/compile.cpp b/runtime/jit-rt/cpp-so/compile.cpp index e06dae9d291..1dd1df2ba1b 100644 --- a/runtime/jit-rt/cpp-so/compile.cpp +++ b/runtime/jit-rt/cpp-so/compile.cpp @@ -13,10 +13,8 @@ //===----------------------------------------------------------------------===// #include -#include #include #include -#include #include #include @@ -34,13 +32,12 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Mangler.h" #include "llvm/Linker/Linker.h" +#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Cloning.h" namespace { -#pragma pack(push, 1) - struct RtCompileFuncList { const char *name; void **func; @@ -70,11 +67,9 @@ struct RtCompileModuleList { int32_t varListSize; }; -#pragma pack(pop) - template -auto toArray(T *ptr, size_t size) - -> llvm::ArrayRef::type> { +auto toArray(T *ptr, + size_t size) -> llvm::ArrayRef::type> { return llvm::ArrayRef::type>(ptr, size); } @@ -92,14 +87,6 @@ void enumModules(const RtCompileModuleList *modlist_head, } } -std::string decorate(llvm::StringRef name, const llvm::DataLayout &datalayout) { - assert(!name.empty()); - llvm::SmallVector ret; - llvm::Mangler::getNameWithPrefix(ret, name, datalayout); - assert(!ret.empty()); - return std::string(ret.data(), ret.size()); -} - struct JitModuleInfo final { private: struct Func final { @@ -160,27 +147,26 @@ struct JitModuleInfo final { } }; -void *resolveSymbol(llvm::JITSymbol &symbol) { - auto addr = symbol.getAddress(); - if (!addr) { - consumeError(addr.takeError()); +void *resolveSymbol(llvm::Expected &symbol) { + if (!symbol) { + consumeError(symbol.takeError()); + return nullptr; + } + return symbol->toPtr(); +} + +static inline llvm::Function * +getIrFunc(const void *ptr, JitModuleInfo &moduleInfo, llvm::Module &module) { + assert(ptr != nullptr); + auto funcDesc = moduleInfo.getFunc(ptr); + if (funcDesc == nullptr) { return nullptr; - } else { - return reinterpret_cast(addr.get()); } + return module.getFunction(funcDesc->name); } void generateBind(const Context &context, DynamicCompilerContext &jitContext, JitModuleInfo &moduleInfo, llvm::Module &module) { - auto getIrFunc = [&](const void *ptr) -> llvm::Function * { - assert(ptr != nullptr); - auto funcDesc = moduleInfo.getFunc(ptr); - if (funcDesc == nullptr) { - return nullptr; - } - return module.getFunction(funcDesc->name); - }; - std::unordered_map bindFuncs; bindFuncs.reserve(jitContext.getBindInstances().size() * 2); @@ -188,16 +174,22 @@ void generateBind(const Context &context, DynamicCompilerContext &jitContext, const llvm::ArrayRef ¶ms) { assert(bindPtr != nullptr); assert(bindFuncs.end() == bindFuncs.find(bindPtr)); - auto funcToInline = getIrFunc(originalFunc); + auto funcToInline = getIrFunc(originalFunc, moduleInfo, module); if (funcToInline == nullptr) { - fatal(context, "Bind: function body not available"); + fatal(context, "Bind: function body not available"); } - auto exampleIrFunc = getIrFunc(exampleFunc); + auto exampleIrFunc = getIrFunc(exampleFunc, moduleInfo, module); assert(exampleIrFunc != nullptr); auto errhandler = [&](const std::string &str) { fatal(context, str); }; auto overrideHandler = [&](llvm::Type &type, const void *data, size_t size) -> llvm::Constant * { - if (type.isPointerTy()) { + // due to ABI rewrites, function pointers can be an integer literal on + // aarch64 we will do a quick probe here to check if it's a function + // pointer + bool maybeIntPtr = + (jitContext.getTargetTriple().isAArch64() && type.isIntegerTy(64)) || + (jitContext.getTargetTriple().isOSWindows() && type.isIntegerTy(32)); + if (type.isPointerTy() || maybeIntPtr) { auto getBindFunc = [&]() { auto handle = *static_cast(data); return handle != nullptr && jitContext.hasBindFunction(handle) @@ -205,34 +197,36 @@ void generateBind(const Context &context, DynamicCompilerContext &jitContext, : nullptr; }; - auto elemType = type.getPointerElementType(); - if (elemType->isFunctionTy()) { - (void)size; - assert(size == sizeof(void *)); - auto val = *reinterpret_cast(data); - if (val != nullptr) { - auto ret = getIrFunc(val); - if (ret != nullptr && ret->getType() != &type) { - return llvm::ConstantExpr::getBitCast(ret, &type); - } - return ret; - } - } else if (auto handle = getBindFunc()) { + auto *maybeFunctionPtr = *reinterpret_cast(data); + llvm::Function *maybeFunction = + maybeFunctionPtr ? getIrFunc(maybeFunctionPtr, moduleInfo, module) + : nullptr; + if (size == sizeof(void *) && maybeFunction) { + return maybeIntPtr + ? llvm::ConstantExpr::getPtrToInt(maybeFunction, &type) + : llvm::ConstantExpr::getBitCast(maybeFunction, &type); + } + if (auto handle = getBindFunc()) { auto it = bindFuncs.find(handle); + if (bindFuncs.end() == it && maybeIntPtr) { + // maybe it's really not a pointer + return nullptr; + } assert(bindFuncs.end() != it); auto bindIrFunc = it->second; auto funcPtrType = bindIrFunc->getType(); auto globalVar1 = new llvm::GlobalVariable( module, funcPtrType, true, llvm::GlobalValue::PrivateLinkage, bindIrFunc, ".jit_bind_handle"); - return llvm::ConstantExpr::getBitCast(globalVar1, &type); + return maybeIntPtr + ? llvm::ConstantExpr::getPtrToInt(globalVar1, &type) + : llvm::ConstantExpr::getBitCast(globalVar1, &type); } } return nullptr; }; - auto func = - bindParamsToFunc(module, *funcToInline, *exampleIrFunc, params, - errhandler, BindOverride(overrideHandler)); + auto func = bindParamsToFunc(module, *funcToInline, *exampleIrFunc, params, + errhandler, BindOverride(overrideHandler)); moduleInfo.addBindHandle(func->getName(), bindPtr); bindFuncs.insert({bindPtr, func}); }; @@ -247,14 +241,13 @@ void generateBind(const Context &context, DynamicCompilerContext &jitContext, void applyBind(const Context &context, DynamicCompilerContext &jitContext, const JitModuleInfo &moduleInfo) { - auto &layout = jitContext.getDataLayout(); for (auto &elem : moduleInfo.getBindHandles()) { - auto decorated = decorate(elem.name, layout); - auto symbol = jitContext.findSymbol(decorated); + auto symbol = jitContext.lookup(elem.name); auto addr = resolveSymbol(symbol); if (nullptr == addr) { std::string desc = std::string("Symbol not found in jitted code: \"") + - elem.name + "\" (\"" + decorated + "\")"; + elem.name + "\" (\"" + jitContext.mangle(elem.name) + + "\")"; fatal(context, desc); } else { auto handle = static_cast(elem.handle); @@ -267,8 +260,9 @@ DynamicCompilerContext &getJit(DynamicCompilerContext *context) { if (context != nullptr) { return *context; } - static DynamicCompilerContext jit(/*mainContext*/ true); - return jit; + static std::unique_ptr jit = + DynamicCompilerContext::Create(/*mainContext*/ true); + return *jit; } void setRtCompileVars(const Context &context, llvm::Module &module, @@ -290,15 +284,15 @@ void dumpModule(const Context &context, const llvm::Module &module, } } -void setFunctionsTarget(llvm::Module &module, const llvm::TargetMachine &TM) { +void setFunctionsTarget(llvm::Module &module, const 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()); + func.addFnAttr("target-cpu", TM->getTargetCPU()); } if (!func.hasFnAttribute("target-features")) { - auto featStr = TM.getTargetFeatureString(); + auto featStr = TM->getTargetFeatureString(); if (!featStr.empty()) { func.addFnAttr("target-features", featStr); } @@ -319,6 +313,31 @@ struct JitFinaliser final { void finalze() { finalized = true; } }; +#ifndef LDC_JITRT_USE_JITLINK +static void insertABIHacks(const Context &context, + DynamicCompilerContext &jitContext, + llvm::Module &module) { + bool isAArch64 = jitContext.getTargetTriple().isAArch64(); + if (isAArch64) { + // insert DW.ref._d_eh_personality stub + auto targetSymbol = module.getFunction("_d_eh_personality"); + if (!targetSymbol) { + return; + } + constexpr const char *thunkName = "_d_eh_personality__thunk"; + auto *pointerType = llvm::PointerType::getUnqual(module.getContext()); + auto *thunkVariable = new llvm::GlobalVariable( + pointerType, true, llvm::GlobalValue::ExternalLinkage, + llvm::ConstantExpr::getBitCast(targetSymbol, pointerType), thunkName); + module.insertGlobalVariable(thunkVariable); + auto targetSymbolAddr = jitContext.lookup(thunkName); + assert(targetSymbolAddr); + jitContext.addSymbol("DW.ref._d_eh_personality", + targetSymbolAddr->toPtr()); + } +} +#endif + void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head, const Context &context) { if (nullptr == modlist_head) { @@ -329,12 +348,12 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head, DynamicCompilerContext &myJit = getJit(context.compilerContext); JitModuleInfo moduleInfo(context, modlist_head); - std::unique_ptr finalModule; + llvm::orc::ThreadSafeModule finalModule; myJit.clearSymMap(); - auto &layout = myJit.getDataLayout(); OptimizerSettings settings; settings.optLevel = context.optLevel; settings.sizeLevel = context.sizeLevel; + auto TM = myJit.getTargetMachine(); enumModules(modlist_head, context, [&](const RtCompileModuleList ¤t) { interruptPoint(context, "load IR"); auto buff = llvm::MemoryBuffer::getMemBuffer( @@ -342,7 +361,7 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head, static_cast(current.irDataSize)), "", false); interruptPoint(context, "parse IR"); - auto mod = llvm::parseBitcodeFile(*buff, myJit.getContext()); + auto mod = llvm::parseBitcodeFile(*buff, *myJit.getContext()); if (!mod) { fatal(context, "Unable to parse IR: " + llvm::toString(mod.takeError())); } else { @@ -352,73 +371,82 @@ void rtCompileProcessImplSoInternal(const RtCompileModuleList *modlist_head, verifyModule(context, module); dumpModule(context, module, DumpStage::OriginalModule); - setFunctionsTarget(module, myJit.getTargetMachine()); + setFunctionsTarget(module, TM); - module.setDataLayout(myJit.getTargetMachine().createDataLayout()); + module.setDataLayout(myJit.getDataLayout()); interruptPoint(context, "setRtCompileVars", name.data()); setRtCompileVars(context, module, toArray(current.varList, static_cast(current.varListSize))); - if (nullptr == finalModule) { - finalModule = std::move(*mod); + if (!finalModule) { + finalModule = llvm::orc::ThreadSafeModule(std::move(*mod), + myJit.getThreadSafeContext()); } else { - if (llvm::Linker::linkModules(*finalModule, std::move(*mod))) { - fatal(context, "Can't merge module"); - } + finalModule.withModuleDo([&](llvm::Module &M) { + if (llvm::Linker::linkModules(M, std::move(*mod))) { + fatal(context, "Can't merge module"); + } + }); } - for (auto &&sym : toArray(current.symList, static_cast( - current.symListSize))) { - myJit.addSymbol(decorate(sym.name, layout), sym.sym); + llvm::orc::SymbolMap symMap{ + static_cast(current.symListSize)}; + for (int i = 0; i < current.symListSize; ++i) { + const auto &sym = current.symList[i]; + symMap[myJit.mangleAndIntern(sym.name)] = { + llvm::orc::ExecutorAddr::fromPtr(sym.sym), + llvm::JITSymbolFlags::Exported}; } + myJit.addSymbols(symMap); } }); - assert(nullptr != finalModule); + assert(!!finalModule); - interruptPoint(context, "Generate bind functions"); - generateBind(context, myJit, moduleInfo, *finalModule); - dumpModule(context, *finalModule, DumpStage::MergedModule); - interruptPoint(context, "Optimize final module"); - optimizeModule(context, myJit.getTargetMachine(), settings, *finalModule); + finalModule.withModuleDo([&](llvm::Module &M) { + interruptPoint(context, "Generate bind functions"); + generateBind(context, myJit, moduleInfo, M); +#ifndef LDC_JITRT_USE_JITLINK + insertABIHacks(context, myJit, M); +#endif + dumpModule(context, M, DumpStage::MergedModule); + interruptPoint(context, "Optimize final module"); + optimizeModule(settings, &M, TM); - interruptPoint(context, "Verify final module"); - verifyModule(context, *finalModule); + interruptPoint(context, "Verify final module"); + verifyModule(context, M); - dumpModule(context, *finalModule, DumpStage::OptimizedModule); + dumpModule(context, M, DumpStage::OptimizedModule); + }); interruptPoint(context, "Codegen final module"); + auto callback = [&](const char *str, size_t len) { + context.dumpHandler(context.dumpHandlerData, DumpStage::FinalAsm, str, len); + }; + CallbackOstream os{callback}; + std::unique_ptr listener{}; if (nullptr != context.dumpHandler) { - auto callback = [&](const char *str, size_t len) { - context.dumpHandler(context.dumpHandlerData, DumpStage::FinalAsm, str, - len); - }; - - CallbackOstream os(callback); - if (auto err = myJit.addModule(std::move(finalModule), &os)) { - fatal(context, "Can't codegen module: " + llvm::toString(std::move(err))); - } - } else { - if (auto err = myJit.addModule(std::move(finalModule), nullptr)) { - fatal(context, "Can't codegen module: " + llvm::toString(std::move(err))); - } + listener = myJit.addScopedListener(&os); + } + if (auto err = myJit.addModule(std::move(finalModule))) { + fatal(context, "Can't codegen module: " + llvm::toString(std::move(err))); } JitFinaliser jitFinalizer(myJit); - if (myJit.isMainContext()) { + /*if (myJit.isMainContext())*/ { interruptPoint(context, "Resolve functions"); for (auto &&fun : moduleInfo.functions()) { if (fun.thunkVar == nullptr) { continue; } - auto decorated = decorate(fun.name, layout); - auto symbol = myJit.findSymbol(decorated); + auto symbol = myJit.lookup(fun.name); auto addr = resolveSymbol(symbol); if (nullptr == addr) { std::string desc = std::string("Symbol not found in jitted code: \"") + - fun.name.data() + "\" (\"" + decorated + "\")"; + fun.name.data() + "\" (\"" + myJit.mangle(fun.name) + + "\")"; fatal(context, desc); } else { *fun.thunkVar = addr; @@ -468,7 +496,7 @@ EXTERNAL void JIT_UNREG_BIND_PAYLOAD(class DynamicCompilerContext *context, } EXTERNAL DynamicCompilerContext *JIT_CREATE_COMPILER_CONTEXT() { - return new DynamicCompilerContext(false); + return DynamicCompilerContext::Create(/*mainContext*/ false).release(); } EXTERNAL void JIT_DESTROY_COMPILER_CONTEXT(DynamicCompilerContext *context) { diff --git a/runtime/jit-rt/cpp-so/context.h b/runtime/jit-rt/cpp-so/context.h index 6750e57e4a7..f4f9309252b 100644 --- a/runtime/jit-rt/cpp-so/context.h +++ b/runtime/jit-rt/cpp-so/context.h @@ -50,11 +50,9 @@ enum { ApiVersion = LDC_DYNAMIC_COMPILE_API_VERSION }; MAKE_JIT_API_CALL(destroyDynamicCompilerContextSo) #define JIT_SET_OPTS MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl) -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); +using InterruptPointHandlerT = void (*)(void *, const char *, const char *); +using FatalHandlerT = void (*)(void *, const char *); +using DumpHandlerT = void (*)(void *, DumpStage, const char *, std::size_t); class DynamicCompilerContext; diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp index ca91520faae..7ebe21171f2 100644 --- a/runtime/jit-rt/cpp-so/disassembler.cpp +++ b/runtime/jit-rt/cpp-so/disassembler.cpp @@ -29,6 +29,7 @@ #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Error.h" @@ -183,11 +184,10 @@ class Symbolizer final : public llvm::MCSymbolizer { SymTable &symtable) : MCSymbolizer(Ctx, std::move(RelInfo)), symTable(symtable) {} - virtual bool tryAddingSymbolicOperand(llvm::MCInst &Inst, - llvm::raw_ostream & /*cStream*/, + bool tryAddingSymbolicOperand(llvm::MCInst &Inst, llvm::raw_ostream &cStream, int64_t Value, uint64_t Address, bool IsBranch, uint64_t Offset, - uint64_t /*InstSize*/) override { + uint64_t OpSize, uint64_t InstSize) override { if (Stage::Emit == symTable.getStage()) { if (IsBranch) { if (auto label = symTable.getPosLabel(Address)) { @@ -249,9 +249,9 @@ void disassemble(const llvm::TargetMachine &tm, } llvm::MCObjectFileInfo mofi; - llvm::MCContext ctx(mai, mri, &mofi); - mofi.InitMCObjectFileInfo(tm.getTargetTriple(), tm.isPositionIndependent(), - ctx, tm.getCodeModel() == llvm::CodeModel::Large); + llvm::MCContext ctx(tm.getTargetTriple(), mai, mri, sti); + mofi.initMCObjectFileInfo(ctx, tm.isPositionIndependent(), + tm.getCodeModel() == llvm::CodeModel::Large); auto disasm = unique(target.createMCDisassembler(*sti, ctx)); if (nullptr == disasm) { @@ -287,7 +287,7 @@ void disassemble(const llvm::TargetMachine &tm, return; } - asmStreamer->InitSections(false); + asmStreamer->switchSection(mofi.getTextSection()); std::unordered_map> sectionsToProcess; for (const auto &symbol : object.symbols()) { diff --git a/runtime/jit-rt/cpp-so/jit_context.cpp b/runtime/jit-rt/cpp-so/jit_context.cpp index 535647b17e2..8e6dc15d15c 100644 --- a/runtime/jit-rt/cpp-so/jit_context.cpp +++ b/runtime/jit-rt/cpp-so/jit_context.cpp @@ -17,21 +17,28 @@ #include #include "llvm/ADT/StringExtras.h" -#include "llvm/ExecutionEngine/RuntimeDyld.h" -#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/TargetRegistry.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/MC/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" namespace { -llvm::SmallVector getHostAttrs() { +static llvm::SmallVector getHostAttrs() { llvm::SmallVector features; llvm::StringMap hostFeatures; - if (llvm::sys::getHostCPUFeatures(hostFeatures)) { +#if LDC_LLVM_VER >= 1901 + hostFeatures = llvm::sys::getHostCPUFeatures(); +#else + if (llvm::sys::getHostCPUFeatures(hostFeatures)) +#endif + { for (auto &&f : hostFeatures) { features.push_back(((f.second ? "+" : "-") + f.first()).str()); } @@ -43,6 +50,7 @@ struct StaticInitHelper { StaticInitHelper() { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetDisassembler(); + llvm::InitializeNativeTargetAsmParser(); llvm::InitializeNativeTargetAsmPrinter(); } }; @@ -54,105 +62,120 @@ StaticInitHelper &staticInit() { return obj; } -std::unique_ptr createTargetMachine() { +static llvm::orc::JITTargetMachineBuilder createTargetMachine() { staticInit(); - std::string triple(llvm::sys::getProcessTriple()); - std::string error; - auto target = llvm::TargetRegistry::lookupTarget(triple, error); - assert(target != nullptr); - std::unique_ptr ret(target->createTargetMachine( - triple, llvm::sys::getHostCPUName(), llvm::join(getHostAttrs(), ","), {}, - llvm::Optional{}, - llvm::Optional{}, llvm::CodeGenOpt::Default, - /*jit*/ true)); - assert(ret != nullptr); - return ret; -} - -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)); + auto autoJTMB = llvm::orc::JITTargetMachineBuilder::detectHost(); + if (autoJTMB) { + return *autoJTMB; } - return llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name); -#else - return llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name); -#endif + std::string triple(llvm::sys::getProcessTriple()); + llvm::orc::JITTargetMachineBuilder JTMB{llvm::Triple{triple}}; + JTMB.setFeatures(llvm::join(getHostAttrs(), ",")); + return JTMB; } } // anon namespace -DynamicCompilerContext::ListenerCleaner::ListenerCleaner( - DynamicCompilerContext &o, llvm::raw_ostream *stream) - : owner(o) { - owner.listenerlayer.getTransform().stream = stream; +DynamicCompilerContext::DynamicCompilerContext( + llvm::orc::LLJITBuilderState state, llvm::Error error, + std::unique_ptr targetmachine, bool isMainContext) + : llvm::orc::LLJIT(state, error), + context(std::make_unique()), + // we need this targetmachine here because LLJIT ctor will free the + // targetmachine field in LLJITBuilderState before we can even use it + targetmachine(std::move(targetmachine)), listenerstream(nullptr), + compiled(false), mainContext(isMainContext) { + assert(!error); + // setup the assembly code listener + // we assume LLJIT's own ObjTransformLayer is empty (at least this is the case + // for LLVM 12~20) + this->ObjTransformLayer->setTransform( + [&](std::unique_ptr object) + -> llvm::Expected> { + if (nullptr != listenerstream) { + auto objFile = + llvm::cantFail(llvm::object::ObjectFile::createObjectFile( + object->getMemBufferRef())); + disassemble(*this->targetmachine, *objFile, *listenerstream); + } + return object; + }); } -DynamicCompilerContext::ListenerCleaner::~ListenerCleaner() { - owner.listenerlayer.getTransform().stream = nullptr; +static llvm::orc::LLJITBuilder buildLLJITforLDC() { + llvm::orc::LLJITBuilder builder{}; + builder.setJITTargetMachineBuilder(createTargetMachine()) + .setLinkProcessSymbolsByDefault(true) + // we override the object linking layer if we are using LLVM JITLink. + // For RuntimeDyld, we use LLJIT's default setup process + // (which includes a lot of platform-related workarounds we need) +#ifdef LDC_JITRT_USE_JITLINK + .setObjectLinkingLayerCreator([&](llvm::orc::ExecutionSession &ES, + const llvm::Triple &TT) { + auto linker = std::make_unique( + ES, cantFail(llvm::jitlink::InProcessMemoryManager::Create())); + // explicitly register EH frame support (for exception handling) + linker->addPlugin( + std::make_unique( + ES, + std::make_unique())); + return linker; + }) +#endif + ; + cantFail(builder.prepareForConstruction()); + return builder; } -DynamicCompilerContext::DynamicCompilerContext(bool isMainContext) - : targetmachine(createTargetMachine()), - dataLayout(targetmachine->createDataLayout()), - stringPool(std::make_shared()), - execSession(stringPool), resolver(createResolver()), - objectLayer(execSession, - [this](llvm::orc::VModuleKey) { - return ObjectLayerT::Resources{ - std::make_shared(), - resolver}; - }), - listenerlayer(objectLayer, ModuleListener(*targetmachine)), - compileLayer(listenerlayer, llvm::orc::SimpleCompiler(*targetmachine)), - mainContext(isMainContext) { - llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); +std::unique_ptr +DynamicCompilerContext::Create(bool isMainContext) { + auto builder = buildLLJITforLDC(); + auto TM = cantFail(builder.JTMB->createTargetMachine()); + // std::make_unique is unusable here because it does not work when the + // target class constructor is private + return std::unique_ptr{ + new DynamicCompilerContext(std::move(builder), llvm::Error::success(), + std::move(TM), isMainContext)}; } -DynamicCompilerContext::~DynamicCompilerContext() {} - llvm::Error -DynamicCompilerContext::addModule(std::unique_ptr module, - llvm::raw_ostream *asmListener) { - assert(nullptr != module); +DynamicCompilerContext::addModule(llvm::orc::ThreadSafeModule module) { + assert(!!module); reset(); - ListenerCleaner cleaner(*this, asmListener); - // Add the set to the JIT with the resolver we created above - auto handle = execSession.allocateVModule(); - auto result = compileLayer.addModule(handle, std::move(module)); - if (result) { - execSession.releaseVModule(handle); - return result; - } - if (auto err = compileLayer.emitAndFinalize(handle)) { - execSession.releaseVModule(handle); - return err; + auto error = this->addIRModule(*this->Main, std::move(module)); + if (error) { + return error; } - moduleHandle = handle; compiled = true; return llvm::Error::success(); } -llvm::JITSymbol DynamicCompilerContext::findSymbol(const std::string &name) { - return compileLayer.findSymbol(name, false); +void DynamicCompilerContext::reset() { + if (compiled) { + // note that we don't remove the JD because that will destroy the lookup + // order and link order we have setup + cantFail(this->Main->clear()); + compiled = false; + } } -void DynamicCompilerContext::clearSymMap() { symMap.clear(); } +DynamicCompilerContext::~DynamicCompilerContext() { reset(); } -void DynamicCompilerContext::addSymbol(std::string &&name, void *value) { - symMap.emplace(std::make_pair(std::move(name), value)); + +void DynamicCompilerContext::addSymbols(llvm::orc::SymbolMap &symbols) { + // we define the static symbol in the process symbols JD to avoid symbol + // conflicts in the Main JD (pre-fabricated LLJIT instance will still check + // ProcessSymbols if the symbol it needs does not exist in the Main JD) + cantFail(ProcessSymbols->define(llvm::orc::absoluteSymbols(symbols))); } -void DynamicCompilerContext::reset() { - if (compiled) { - removeModule(moduleHandle); - moduleHandle = {}; - compiled = false; - } +void DynamicCompilerContext::addSymbol(std::string &&name, void *value) { + llvm::orc::SymbolMap symbols{1}; + symbols[mangleAndIntern(name)] = {llvm::orc::ExecutorAddr::fromPtr(value), + llvm::JITSymbolFlags::Exported}; + cantFail(ProcessSymbols->define(llvm::orc::absoluteSymbols(symbols))); } void DynamicCompilerContext::registerBind( @@ -173,37 +196,3 @@ bool DynamicCompilerContext::hasBindFunction(const void *handle) const { auto it = bindInstances.find(const_cast(handle)); return it != bindInstances.end(); } - -bool DynamicCompilerContext::isMainContext() const { return mainContext; } - -void DynamicCompilerContext::removeModule(const ModuleHandleT &handle) { - cantFail(compileLayer.removeModule(handle)); - execSession.releaseVModule(handle); -} - -std::shared_ptr -DynamicCompilerContext::createResolver() { - return llvm::orc::createLegacyLookupResolver( - execSession, - [this](llvm::StringRef name_) -> llvm::JITSymbol { - const std::string name = name_.str(); - if (auto Sym = compileLayer.findSymbol(name, false)) { - return Sym; - } else if (auto Err = Sym.takeError()) { - return std::move(Err); - } - auto it = symMap.find(name); - if (symMap.end() != it) { - return llvm::JITSymbol( - reinterpret_cast(it->second), - llvm::JITSymbolFlags::Exported); - } - if (auto SymAddr = getSymbolInProcess(name)) { - return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported); - } - return nullptr; - }, - [](llvm::Error Err) { - llvm::cantFail(std::move(Err), "lookupFlags failed"); - }); -} diff --git a/runtime/jit-rt/cpp-so/jit_context.h b/runtime/jit-rt/cpp-so/jit_context.h index cd76f2f4e51..e04fb09ceb1 100644 --- a/runtime/jit-rt/cpp-so/jit_context.h +++ b/runtime/jit-rt/cpp-so/jit_context.h @@ -14,20 +14,32 @@ #pragma once +#include #include #include -#include #include "llvm/ADT/MapVector.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/Legacy.h" -#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" -#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/Support/ManagedStatic.h" + +#ifdef LDC_JITRT_USE_JITLINK +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#else +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#endif + +#if LDC_LLVM_VER < 1700 +#include "llvm/ADT/Optional.h" +#else +#include +namespace llvm { +template using Optional = std::optional; +} +#endif #include "context.h" #include "disassembler.h" @@ -38,45 +50,19 @@ class TargetMachine; } // namespace llvm using SymMap = std::map; +class LDCSymbolDefinitionGenerator; -class DynamicCompilerContext final { +class DynamicCompilerContext final : public llvm::orc::LLJIT { private: - struct ModuleListener { - llvm::TargetMachine &targetmachine; - llvm::raw_ostream *stream = nullptr; - - ModuleListener(llvm::TargetMachine &tm) : targetmachine(tm) {} - - template auto operator()(T &&object) -> T { - if (nullptr != stream) { - auto objFile = - llvm::cantFail(llvm::object::ObjectFile::createObjectFile( - object->getMemBufferRef())); - disassemble(targetmachine, *objFile, *stream); - } - return std::move(object); - } - }; +#ifdef LDC_JITRT_USE_JITLINK + using ObjectLayerT = llvm::orc::ObjectLinkingLayer; +#else + using ObjectLayerT = llvm::orc::RTDyldObjectLinkingLayer; +#endif + using CompileLayerT = llvm::orc::IRCompileLayer; + llvm::orc::ThreadSafeContext context; std::unique_ptr targetmachine; - const llvm::DataLayout dataLayout; - using ObjectLayerT = llvm::orc::LegacyRTDyldObjectLinkingLayer; - using ListenerLayerT = - llvm::orc::LegacyObjectTransformLayer; - using CompileLayerT = - llvm::orc::LegacyIRCompileLayer; - using ModuleHandleT = llvm::orc::VModuleKey; - std::shared_ptr stringPool; - llvm::orc::ExecutionSession execSession; - std::shared_ptr resolver; - ObjectLayerT objectLayer; - ListenerLayerT listenerlayer; - CompileLayerT compileLayer; - llvm::LLVMContext context; - bool compiled = false; - ModuleHandleT moduleHandle; - SymMap symMap; - + llvm::raw_ostream *listenerstream; struct BindDesc final { void *originalFunc; void *exampleFunc; @@ -84,34 +70,29 @@ class DynamicCompilerContext final { ParamsVec params; }; llvm::MapVector bindInstances; - const bool mainContext = false; + bool compiled; + bool mainContext; + + // internal constructor + DynamicCompilerContext(llvm::orc::LLJITBuilderState S, llvm::Error Err, + std::unique_ptr TM, + bool isMainContext); +public: struct ListenerCleaner final { DynamicCompilerContext &owner; - ListenerCleaner(DynamicCompilerContext &o, llvm::raw_ostream *stream); - ~ListenerCleaner(); + ListenerCleaner(DynamicCompilerContext &o, llvm::raw_ostream *stream) + : owner(o) { + owner.listenerstream = stream; + } + ~ListenerCleaner() { owner.listenerstream = nullptr; } }; - -public: - DynamicCompilerContext(bool isMainContext); + static std::unique_ptr Create(bool isMainContext); ~DynamicCompilerContext(); - - llvm::TargetMachine &getTargetMachine() { return *targetmachine; } - const llvm::DataLayout &getDataLayout() const { return dataLayout; } - - llvm::Error addModule(std::unique_ptr module, - llvm::raw_ostream *asmListener); - - llvm::JITSymbol findSymbol(const std::string &name); - - llvm::LLVMContext &getContext() { return context; } - - void clearSymMap(); - - void addSymbol(std::string &&name, void *value); - + llvm::TargetMachine *getTargetMachine() const { return targetmachine.get(); } + const llvm::DataLayout &getDataLayout() const { return DL; } + llvm::Error addModule(llvm::orc::ThreadSafeModule module); void reset(); - void registerBind(void *handle, void *originalFunc, void *exampleFunc, const llvm::ArrayRef ¶ms); @@ -123,10 +104,16 @@ class DynamicCompilerContext final { return bindInstances; } - bool isMainContext() const; + void addSymbol(std::string &&name, void *value); + void addSymbols(llvm::orc::SymbolMap &symbols); + void clearSymMap() { cantFail(ProcessSymbols->clear()); } + llvm::orc::ThreadSafeContext getThreadSafeContext() const { return context; } + llvm::LLVMContext *getContext() { return context.getContext(); } -private: - void removeModule(const ModuleHandleT &handle); + bool isMainContext() const { return mainContext; } - std::shared_ptr createResolver(); + std::unique_ptr + addScopedListener(llvm::raw_ostream *stream) { + return std::make_unique(*this, stream); + } }; diff --git a/runtime/jit-rt/cpp-so/optimizer.cpp b/runtime/jit-rt/cpp-so/optimizer.cpp deleted file mode 100644 index f9495ca446c..00000000000 --- a/runtime/jit-rt/cpp-so/optimizer.cpp +++ /dev/null @@ -1,263 +0,0 @@ -//===-- optimizer.cpp -----------------------------------------------------===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the Boost Software License. See the LICENSE -// file for details. -// Uses some parts from gen/optimizer.cpp which is under the BSD-style LDC -// license. -// -//===----------------------------------------------------------------------===// - -#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" - -#if LDC_LLVM_VER < 1700 -#include "llvm/ADT/Triple.h" -#else -#include "llvm/TargetParser/Triple.h" -#endif - -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Analysis/TargetTransformInfo.h" - -#include "llvm/Support/CommandLine.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" - -#ifdef LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES -#include "Passes.h" -#endif - -namespace { -namespace cl = llvm::cl; -cl::opt - verifyEach("verify-each", cl::ZeroOrMore, cl::Hidden, - cl::desc("Run verifier after D-specific and explicitly " - "specified optimization passes")); - -/// LDC LICENSE START -#ifdef LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES -cl::opt - disableLangSpecificPasses("disable-d-passes", cl::ZeroOrMore, - cl::desc("Disable all D-specific passes")); - -cl::opt disableSimplifyDruntimeCalls( - "disable-simplify-drtcalls", cl::ZeroOrMore, - cl::desc("Disable simplification of druntime calls")); - -cl::opt disableSimplifyLibCalls( - "disable-simplify-libcalls", cl::ZeroOrMore, - cl::desc("Disable simplification of well-known C runtime calls")); - -cl::opt disableGCToStack( - "disable-gc2stack", cl::ZeroOrMore, - cl::desc("Disable promotion of GC allocations to stack memory")); -#endif -/// LDC LICENSE END - -cl::opt stripDebug( - "strip-debug", cl::ZeroOrMore, - cl::desc("Strip symbolic debug information before optimization")); - -cl::opt disableLoopUnrolling( - "disable-loop-unrolling", cl::ZeroOrMore, - cl::desc("Disable loop unrolling in all relevant passes")); -cl::opt - disableLoopVectorization("disable-loop-vectorization", cl::ZeroOrMore, - cl::desc("Disable the loop vectorization pass")); - -cl::opt - disableSLPVectorization("disable-slp-vectorization", cl::ZeroOrMore, - cl::desc("Disable the slp vectorization pass")); - -/// LDC LICENSE START -#ifdef LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES -void addPass(llvm::PassManagerBase &pm, llvm::Pass *pass) { - pm.add(pass); - - if (verifyEach) { - pm.add(llvm::createVerifierPass()); - } -} - -void addStripExternalsPass(const llvm::PassManagerBuilder &builder, - llvm::PassManagerBase &pm) { - if (builder.OptLevel >= 1) { - addPass(pm, createStripExternalsPass()); - addPass(pm, llvm::createGlobalDCEPass()); - } -} - -void addSimplifyDRuntimeCallsPass(const llvm::PassManagerBuilder &builder, - llvm::PassManagerBase &pm) { - if (builder.OptLevel >= 2 && builder.SizeLevel == 0) { - addPass(pm, createSimplifyDRuntimeCalls()); - } -} - -void addGarbageCollect2StackPass(const llvm::PassManagerBuilder &builder, - llvm::PassManagerBase &pm) { - if (builder.OptLevel >= 2 && builder.SizeLevel == 0) { - addPass(pm, createGarbageCollect2Stack()); - } -} -#endif -/// LDC LICENSE END - -// 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) { - auto params = llvm::getInlineParams(optLevel, sizeLevel); - builder.Inliner = llvm::createFunctionInliningPass(params); - } else { - builder.Inliner = llvm::createAlwaysInlinerLegacyPass(); - } - - builder.DisableUnrollLoops = (disableLoopUnrolling.getNumOccurrences() > 0) - ? disableLoopUnrolling - : optLevel == 0; - - if (disableLoopVectorization) { - 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; - } - - builder.SLPVectorize = - disableSLPVectorization ? false : optLevel > 1 && sizeLevel < 2; - - // TODO: sanitizers support in jit? - // TODO: PGO support in jit? - - /// LDC LICENSE START -#ifdef LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES - if (!disableLangSpecificPasses) { - if (!disableSimplifyDruntimeCalls) { - builder.addExtension(llvm::PassManagerBuilder::EP_LoopOptimizerEnd, - addSimplifyDRuntimeCallsPass); - } - - if (!disableGCToStack) { - builder.addExtension(llvm::PassManagerBuilder::EP_LoopOptimizerEnd, - addGarbageCollect2StackPass); - } - } - - // EP_OptimizerLast does not exist in LLVM 3.0, add it manually below. - builder.addExtension(llvm::PassManagerBuilder::EP_OptimizerLast, - addStripExternalsPass); -#endif - /// LDC LICENSE END - - 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) { - 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(); } -}; - -void stripComdat(llvm::Module &module) { - for (auto &&func : module.functions()) { - func.setComdat(nullptr); - } - for (auto &&var : module.globals()) { - var.setComdat(nullptr); - } - module.getComdatSymbolTable().clear(); -} - -} // anon namespace - -void optimizeModule(const Context &context, llvm::TargetMachine &targetMachine, - const OptimizerSettings &settings, llvm::Module &module) { - // There is llvm bug related tp comdat and IR based pgo - // and anyway comdat is useless at this stage - stripComdat(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->getValueType(); - auto initializer = - parseInitializer(module.getDataLayout(), *type, init, - [&](const std::string &str) { fatal(context, str); }); - var->setConstant(true); - var->setInitializer(initializer); - var->setLinkage(llvm::GlobalValue::PrivateLinkage); - } -} diff --git a/runtime/jit-rt/cpp-so/optimizer.h b/runtime/jit-rt/cpp-so/optimizer.h index 327751f80e3..a37ee5e3afc 100644 --- a/runtime/jit-rt/cpp-so/optimizer.h +++ b/runtime/jit-rt/cpp-so/optimizer.h @@ -31,8 +31,8 @@ struct OptimizerSettings final { unsigned sizeLevel = 0; }; -void optimizeModule(const Context &context, llvm::TargetMachine &targetMachine, - const OptimizerSettings &settings, llvm::Module &module); +void optimizeModule(const OptimizerSettings &settings, llvm::Module *M, + llvm::TargetMachine *TM); void setRtCompileVar(const Context &context, llvm::Module &module, const char *name, const void *init); diff --git a/runtime/jit-rt/cpp/compile.cpp b/runtime/jit-rt/cpp/compile.cpp index 890bcd2a722..8e90db01e6a 100644 --- a/runtime/jit-rt/cpp/compile.cpp +++ b/runtime/jit-rt/cpp/compile.cpp @@ -58,7 +58,8 @@ EXTERNAL void JIT_API_ENTRYPOINT(const void *modlist_head, EXTERNAL void JIT_REG_BIND_PAYLOAD(DynamicCompilerContext *context, void *handle, void *originalFunc, - const ParamSlice *desc, size_t descSize); + void *exampleFunc, const ParamSlice *desc, + size_t descSize); EXTERNAL void JIT_UNREG_BIND_PAYLOAD(DynamicCompilerContext *context, void *handle); @@ -76,9 +77,10 @@ void rtCompileProcessImpl(const Context *context, std::size_t contextSize) { } void registerBindPayload(DynamicCompilerContext *context, void *handle, - void *originalFunc, const ParamSlice *desc, - size_t descSize) { - JIT_REG_BIND_PAYLOAD(context, handle, originalFunc, desc, descSize); + void *originalFunc, void *exampleFunc, + const ParamSlice *desc, size_t descSize) { + JIT_REG_BIND_PAYLOAD(context, handle, originalFunc, exampleFunc, desc, + descSize); } void unregisterBindPayload(DynamicCompilerContext *context, void *handle) { diff --git a/runtime/jit-rt/d/ldc/dynamic_compile.d b/runtime/jit-rt/d/ldc/dynamic_compile.d index 352498a1c19..813b391e31d 100644 --- a/runtime/jit-rt/d/ldc/dynamic_compile.d +++ b/runtime/jit-rt/d/ldc/dynamic_compile.d @@ -523,8 +523,8 @@ struct BindPayload(OF, F, int[] Index, Args...) static assert(Index.length == ParametersCount, "Invalid index size"); extern(C) private pure nothrow @nogc static { - pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null ); - pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p ); + pragma(mangle, "gc_addRange") void pureGcAddRange(const void* p, size_t sz, const TypeInfo ti = null); + pragma(mangle, "gc_removeRange") void pureGcRemoveRange(const void* p); } Base base; diff --git a/tests/dynamiccompile/asm_output.d b/tests/dynamiccompile/asm_output.d index f1c9eb3e738..0cbeda54438 100644 --- a/tests/dynamiccompile/asm_output.d +++ b/tests/dynamiccompile/asm_output.d @@ -47,6 +47,14 @@ void main(string[] args) assert(1 == count(dump.data, bar.mangleof)); assert(1 == count(dump.data, baz.mangleof)); assert(count(dump.data, value.mangleof) > 0); - assert(1 == count(dump.data, "7")); - assert(1 == count(dump.data, "8")); + + version (ARM) enum literalPrefix = "#"; + version (AArch64) enum literalPrefix = "#"; + version (X86) enum literalPrefix = "$"; + version (X86_64) enum literalPrefix = "$"; + static if (is(typeof(literalPrefix))) + { + assert(1 == count(dump.data, literalPrefix ~ "7")); + assert(1 == count(dump.data, literalPrefix ~ "8")); + } } diff --git a/tests/dynamiccompile/bind_bool.d b/tests/dynamiccompile/bind_bool.d index 24cb8a039ea..e1a7ad23956 100644 --- a/tests/dynamiccompile/bind_bool.d +++ b/tests/dynamiccompile/bind_bool.d @@ -28,7 +28,7 @@ void main(string[] args) settings.optLevel = 3; settings.dumpHandler = (DumpStage stage, in char[] str) { - if (DumpStage.FinalAsm == stage) + if (DumpStage.FinalAsm == stage || DumpStage.OptimizedModule == stage) { dump.put(str); write(str); diff --git a/tests/dynamiccompile/bind_func_opt.d b/tests/dynamiccompile/bind_func_opt.d index f71a3dfee83..b18b762bdef 100644 --- a/tests/dynamiccompile/bind_func_opt.d +++ b/tests/dynamiccompile/bind_func_opt.d @@ -42,7 +42,7 @@ void main(string[] args) settings.optLevel = 3; settings.dumpHandler = (DumpStage stage, in char[] str) { - if (DumpStage.FinalAsm == stage) + if (DumpStage.FinalAsm == stage || DumpStage.OptimizedModule == stage) { write(str); dump.put(str); diff --git a/tests/dynamiccompile/inline_asm.d b/tests/dynamiccompile/inline_asm.d new file mode 100644 index 00000000000..8577366eb72 --- /dev/null +++ b/tests/dynamiccompile/inline_asm.d @@ -0,0 +1,56 @@ +// RUN: %ldc -enable-dynamic-compile -run %s + +import std.array; +import std.string; +import std.stdio; + +import ldc.attributes; +import ldc.dynamic_compile; +import ldc.intrinsics; +import ldc.llvmasm; + +version (ARM) version = ARM_Any; +version (AArch64) version = ARM_Any; + +version (MIPS32) version = MIPS_Any; +version (MIPS64) version = MIPS_Any; + +version (PPC) version = PPC_Any; +version (PPC64) version = PPC_Any; + +version (RISCV32) version = RISCV_Any; +version (RISCV64) version = RISCV_Any; + +@dynamicCompile void* foo() +{ + version (X86) + return __asm!(void*)("movl %esp, $0", "=r"); + else version (X86_64) + return __asm!(void*)("movq %rsp, $0", "=r"); + else version (ARM_Any) + return __asm!(void*)("mov $0, sp", "=r"); + else version (PPC_Any) + return __asm!(void*)("mr $0, 1", "=r"); + else version (MIPS_Any) + return __asm!(void*)("move $0, $$sp", "=r"); + else + return llvm_frameaddress(0); // soft-skip the test +} + +void main(string[] args) +{ + auto dump = appender!(char[])(); + CompilerSettings settings; + settings.optLevel = 3; + settings.dumpHandler = (DumpStage stage, in char[] str) + { + if (DumpStage.FinalAsm == stage) + { + dump.put(str); + write(str); + } + }; + compileDynamicCode(settings); + void* result = foo(); + assert(result != null); +} diff --git a/tests/dynamiccompile/simd_simple_opt.d b/tests/dynamiccompile/simd_simple_opt.d new file mode 100644 index 00000000000..1cda8cb4d55 --- /dev/null +++ b/tests/dynamiccompile/simd_simple_opt.d @@ -0,0 +1,64 @@ +// RUN: %ldc -enable-dynamic-compile -run %s + +import std.stdio; +import std.array; +import std.string; +import ldc.simd; +import ldc.attributes; +import ldc.dynamic_compile; +import core.simd; + +@dynamicCompile +ubyte[32] hex_encode_16b(ubyte[16] binary) +{ + const ubyte ascii_a = 'a' - 9 - 1; + ubyte16 invec = loadUnaligned!ubyte16(binary.ptr); + ubyte[32] hex; + + ubyte16 masked1 = invec & 0xF; + ubyte16 masked2 = (invec >> 4) & 0xF; + + ubyte16 cmpmask1 = masked1 > 9; + ubyte16 cmpmask2 = masked2 > 9; + + ubyte16 masked1_r = ((cmpmask1 & ascii_a) | (~cmpmask1 & '0')) + masked1; + ubyte16 masked2_r = ((cmpmask2 & ascii_a) | (~cmpmask2 & '0')) + masked2; + ubyte16 res1 = shufflevector!(ubyte16, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23)( + masked2_r, masked1_r); + ubyte16 res2 = shufflevector!(ubyte16, 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31)( + masked2_r, masked1_r); + + storeUnaligned!ubyte16(res1, hex.ptr); + storeUnaligned!ubyte16(res2, hex.ptr + 16); + + return hex; +} + +void main(string[] args) +{ + const ubyte[16] binary = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 0xff, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf + ]; + const ubyte[32] expected = [ + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', + '0', '7', '0', '8', 'f', 'f', '0', 'a', '0', 'b', '0', 'c', '0', 'd', + '0', 'e', '0', 'f' + ]; + + CompilerSettings settings; + auto dump = appender!string(); + settings.dumpHandler = (DumpStage stage, in char[] str) { + if (DumpStage.FinalAsm == stage) + { + write(str); + dump.put(str); + } + }; + settings.optLevel = 3; + auto f = ldc.dynamic_compile.bind(&hex_encode_16b, binary); + compileDynamicCode(settings); + ubyte[32] hex = hex_encode_16b(binary); + assert(hex == expected); + + assert(f() == expected); +} diff --git a/tests/dynamiccompile/throw.d b/tests/dynamiccompile/throw.d index da92180b905..6e7c6221078 100644 --- a/tests/dynamiccompile/throw.d +++ b/tests/dynamiccompile/throw.d @@ -3,8 +3,8 @@ // win64 issue https://bugs.llvm.org//show_bug.cgi?id=24233 // XFAIL: Windows // -// Also, some issue on Arm -// XFAIL: host_ARM +// Also, some issue on macOS Arm64 +// XFAIL: host_Darwin_AArch64 // RUN: %ldc -enable-dynamic-compile -run %s import std.exception; diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index ed6ccec5e07..6fbfc1d3abd 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -48,6 +48,16 @@ config.test_format = lit.formats.ShTest(execute_external=False) # by individual lit.local.cfg files in the test subdirectories. config.suffixes = ['.d', '.i', '.c'] +# Set individual test timeout to 60 +supported, errormsg = lit_config.maxIndividualTestTimeIsSupported +if supported: + lit_config.maxIndividualTestTime = 60 +else: + lit_config.warning( + "Setting a timeout per test not supported: " + + errormsg + ) + # excludes: A list of directories to exclude from the testsuite. The 'inputs' # subdirectories contain auxiliary inputs for various tests in their parent # directories. @@ -115,6 +125,7 @@ if (platform.system() == 'Windows') and (config.default_target_bits == 64): # Examples: 'host_X86', 'host_ARM', 'host_PowerPC', 'host_AArch64' if (config.ldc_host_arch != ''): config.available_features.add('host_' + config.ldc_host_arch) + config.available_features.add('host_' + platform.system() + '_' + config.ldc_host_arch) # Add "LTO" feature if linker support and LTO plugin are available if (platform.system() == 'Windows'):