From a40c6c7fd2390805af0bf850dc0015d4a6eb5b51 Mon Sep 17 00:00:00 2001 From: Ivan Butygin Date: Sun, 8 Sep 2019 09:16:05 +0300 Subject: [PATCH] Make LDC custom passes available to jit, add API for jit compiler options (#2758) --- CMakeLists.txt | 1 + LICENSE | 9 +- driver/main.cpp | 2 +- gen/dcompute/targetCUDA.cpp | 2 +- gen/passes/GarbageCollect2Stack.cpp | 4 +- gen/passes/Passes.h | 2 - gen/passes/SimplifyDRuntimeCalls.cpp | 38 +++++- gen/{ => passes}/metadata.h | 0 gen/typinf.cpp | 2 +- ir/irclass.cpp | 2 +- runtime/jit-rt/DefineBuildJitRT.cmake | 17 ++- runtime/jit-rt/cpp-so/compile.cpp | 8 ++ runtime/jit-rt/cpp-so/context.h | 4 + runtime/jit-rt/cpp-so/optimizer.cpp | 118 ++++++++++++++++-- runtime/jit-rt/cpp-so/options.cpp | 57 +++++++++ runtime/jit-rt/cpp-so/options.h | 23 ++++ runtime/jit-rt/cpp-so/slice.h | 24 ++++ runtime/jit-rt/cpp/compile.cpp | 19 ++- runtime/jit-rt/d/ldc/dynamic_compile.d | 40 +++++- tests/dynamiccompile/options.d | 52 ++++++++ tests/dynamiccompile/options_invalid.d | 23 ++++ .../dynamiccompile/options_multiple_changes.d | 58 +++++++++ 22 files changed, 478 insertions(+), 27 deletions(-) rename gen/{ => passes}/metadata.h (100%) create mode 100644 runtime/jit-rt/cpp-so/options.cpp create mode 100644 runtime/jit-rt/cpp-so/options.h create mode 100644 runtime/jit-rt/cpp-so/slice.h create mode 100644 tests/dynamiccompile/options.d create mode 100644 tests/dynamiccompile/options_invalid.d create mode 100644 tests/dynamiccompile/options_multiple_changes.d diff --git a/CMakeLists.txt b/CMakeLists.txt index c1d0a8851d..d34c7e2213 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,7 @@ include(HandleLTOPGOBuildOptions) # Enable Dynamic compilation if supported for this platform and LLVM version. # set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (True|False). Enabled by default.") +option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON) if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO") set(LDC_DYNAMIC_COMPILE False) # must be a valid Python boolean constant (case sensitive) if (NOT (LDC_LLVM_VER LESS 500)) diff --git a/LICENSE b/LICENSE index b9e9ed4ea5..6bf0ab09d1 100644 --- a/LICENSE +++ b/LICENSE @@ -23,8 +23,13 @@ Libraries (lib/ and import/ in binary packages): Phobos (runtime/phobos), is distributed under the terms of the Boost Software License. See the individual source files for author information. - - The ldc-jit-rt library is distributed under the terms of the - Boost Software license (but additionally makes use of LLVM code). + - Most of the ldc-jit-rt library is licensed under the terms of the + Boost Software license. Additionally it contains code derived from LLVM + governed by the University of Illinois Open Source License. + Finally, it incorporates code from the LDC compiler (gen/passes/* and parts + of gen/optimizer.cpp) which are under "three-clause BSD" LDC license. These + parts from the LDC compiler can be omitted if you build ldc-jit-rt with + LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES=OFF. - The profile-rt runtime library (runtime/profile-rt) is a copy of LLVM's compiler-rt/profile library with a small D source addition. It is dual diff --git a/driver/main.cpp b/driver/main.cpp index c8c7ec2c55..0f9a3136cc 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -42,10 +42,10 @@ #include "gen/llvm.h" #include "gen/llvmhelpers.h" #include "gen/logger.h" -#include "gen/metadata.h" #include "gen/modules.h" #include "gen/objcgen.h" #include "gen/optimizer.h" +#include "gen/passes/metadata.h" #include "gen/passes/Passes.h" #include "gen/runtime.h" #include "gen/uda.h" diff --git a/gen/dcompute/targetCUDA.cpp b/gen/dcompute/targetCUDA.cpp index e94fab8d6d..47765d33f9 100644 --- a/gen/dcompute/targetCUDA.cpp +++ b/gen/dcompute/targetCUDA.cpp @@ -11,7 +11,7 @@ #include "gen/dcompute/target.h" #include "gen/dcompute/druntime.h" -#include "gen/metadata.h" +#include "gen/passes/metadata.h" #include "gen/abi-nvptx.h" #include "gen/logger.h" #include "gen/optimizer.h" diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index ff67f14e03..c7346e289e 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -18,7 +18,7 @@ #endif #include "gen/attributes.h" -#include "gen/metadata.h" +#include "metadata.h" #include "gen/passes/Passes.h" #include "gen/runtime.h" #include "llvm/Pass.h" @@ -849,7 +849,7 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V, const unsigned paramHasAttr_firstArg = 0; #endif if (!CS.paramHasAttr(A - B + paramHasAttr_firstArg, - LLAttribute::NoCapture)) { + llvm::Attribute::AttrKind::NoCapture)) { // The parameter is not marked 'nocapture' - captured. return false; } diff --git a/gen/passes/Passes.h b/gen/passes/Passes.h index f1201933aa..2d968a3d93 100644 --- a/gen/passes/Passes.h +++ b/gen/passes/Passes.h @@ -13,8 +13,6 @@ #pragma once -#include "gen/metadata.h" - namespace llvm { class FunctionPass; class ModulePass; diff --git a/gen/passes/SimplifyDRuntimeCalls.cpp b/gen/passes/SimplifyDRuntimeCalls.cpp index c98caf16cc..02367f8c10 100644 --- a/gen/passes/SimplifyDRuntimeCalls.cpp +++ b/gen/passes/SimplifyDRuntimeCalls.cpp @@ -206,6 +206,33 @@ struct LLVM_LIBRARY_VISIBILITY AllocationOpt : public LibCallOptimization { } }; +// This module will also be used in jit runtime +// copy these function here to avoid dependencies on rest of compiler +LLIntegerType *DtoSize_t(llvm::LLVMContext &context, + const llvm::Triple &triple) { + // the type of size_t does not change once set + static LLIntegerType *t = nullptr; + if (t == nullptr) { + + if (triple.isArch64Bit()) { + t = LLType::getInt64Ty(context); + } else if (triple.isArch32Bit()) { + t = LLType::getInt32Ty(context); + } else if (triple.isArch16Bit()) { + t = LLType::getInt16Ty(context); + } else { + llvm_unreachable("Unsupported size_t width"); + } + } + return t; +} + +llvm::ConstantInt *DtoConstSize_t(llvm::LLVMContext &context, + const llvm::Triple &targetTriple, + uint64_t i) { + return LLConstantInt::get(DtoSize_t(context, targetTriple), i, false); +} + /// ArraySliceCopyOpt - Turn slice copies into llvm.memcpy when safe struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization { Value *CallOptimizer(Function *Callee, CallInst *CI, @@ -244,9 +271,12 @@ struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization { // Equal length and the pointers definitely don't alias, so it's safe to // replace the call with memcpy - auto Size = Sz != llvm::MemoryLocation::UnknownSize - ? DtoConstSize_t(Sz) - : B.CreateMul(DstLength, ElemSz); + auto Size = + Sz != llvm::MemoryLocation::UnknownSize + ? DtoConstSize_t( + Callee->getContext(), + llvm::Triple(Callee->getParent()->getTargetTriple()), Sz) + : B.CreateMul(DstLength, ElemSz); return EmitMemCpy(CI->getOperand(0), CI->getOperand(2), Size, 1, B); } }; @@ -391,7 +421,7 @@ bool SimplifyDRuntimeCalls::runOnce(Function &F, const DataLayout *DL, } LLVM_DEBUG(errs() << "SimplifyDRuntimeCalls simplified: " << *CI; - errs() << " into: " << *Result << "\n"); + errs() << " into: " << *Result << "\n"); // Something changed! Changed = true; diff --git a/gen/metadata.h b/gen/passes/metadata.h similarity index 100% rename from gen/metadata.h rename to gen/passes/metadata.h diff --git a/gen/typinf.cpp b/gen/typinf.cpp index e24878387f..b833837282 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -44,7 +44,7 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/mangling.h" -#include "gen/metadata.h" +#include "gen/passes/metadata.h" #include "gen/pragma.h" #include "gen/rttibuilder.h" #include "gen/runtime.h" diff --git a/ir/irclass.cpp b/ir/irclass.cpp index bdc6f85419..488519aaf7 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -23,7 +23,7 @@ #include "gen/logger.h" #include "gen/llvmhelpers.h" #include "gen/mangling.h" -#include "gen/metadata.h" +#include "gen/passes/metadata.h" #include "gen/pragma.h" #include "gen/runtime.h" #include "gen/tollvm.h" diff --git a/runtime/jit-rt/DefineBuildJitRT.cmake b/runtime/jit-rt/DefineBuildJitRT.cmake index 47bc86e10a..eff8c75364 100644 --- a/runtime/jit-rt/DefineBuildJitRT.cmake +++ b/runtime/jit-rt/DefineBuildJitRT.cmake @@ -6,6 +6,14 @@ if(LDC_DYNAMIC_COMPILE) 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_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) + file(GLOB LDC_JITRT_SO_PASSES_CXX ${CMAKE_SOURCE_DIR}/gen/passes/*.cpp) + file(GLOB LDC_JITRT_SO_PASSES_H ${CMAKE_SOURCE_DIR}/gen/passes/*.h) + else() + set(LDC_JITRT_SO_PASSES_CXX "") + set(LDC_JITRT_SO_PASSES_H "") + endif() # Set compiler-dependent flags if(MSVC) @@ -61,7 +69,7 @@ if(LDC_DYNAMIC_COMPILE) get_target_suffix("" "${path_suffix}" target_suffix) set(output_path ${CMAKE_BINARY_DIR}/lib${path_suffix}) - add_library(ldc-jit-rt-so${target_suffix} SHARED ${LDC_JITRT_SO_CXX} ${LDC_JITRT_SO_H}) + add_library(ldc-jit-rt-so${target_suffix} SHARED ${LDC_JITRT_SO_CXX} ${LDC_JITRT_SO_H} ${LDC_JITRT_SO_PASSES_CXX} ${LDC_JITRT_SO_PASSES_H}) set_common_library_properties(ldc-jit-rt-so${target_suffix} ldc-jit ${output_path} "${c_flags} ${LDC_CXXFLAGS} ${LLVM_CXXFLAGS} ${JITRT_EXTRA_FLAGS}" @@ -70,6 +78,13 @@ if(LDC_DYNAMIC_COMPILE) ) set_target_properties(ldc-jit-rt-so${target_suffix} PROPERTIES LINKER_LANGUAGE CXX) + if(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES) + target_compile_definitions(ldc-jit-rt-so${target_suffix} PRIVATE LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES) + endif() + + target_include_directories(ldc-jit-rt-so${target_suffix} + PRIVATE ${CMAKE_SOURCE_DIR}/gen/passes/) + target_link_libraries(ldc-jit-rt-so${target_suffix} ${JITRT_LLVM_LIBS}) set(jitrt_d_o "") diff --git a/runtime/jit-rt/cpp-so/compile.cpp b/runtime/jit-rt/cpp-so/compile.cpp index 4e3001a1c2..649f7572e7 100644 --- a/runtime/jit-rt/cpp-so/compile.cpp +++ b/runtime/jit-rt/cpp-so/compile.cpp @@ -25,6 +25,7 @@ #include "context.h" #include "jit_context.h" #include "optimizer.h" +#include "options.h" #include "utils.h" #include "llvm/Bitcode/BitcodeReader.h" @@ -461,4 +462,11 @@ EXTERNAL void JIT_UNREG_BIND_PAYLOAD(void *handle) { JITContext &myJit = getJit(); myJit.unregisterBind(handle); } + +EXTERNAL bool JIT_SET_OPTS(const Slice> *args, + void (*errs)(void *, const char *, size_t), + void *errsContext) { + assert(args != nullptr); + return parseOptions(*args, errs, errsContext); +} } diff --git a/runtime/jit-rt/cpp-so/context.h b/runtime/jit-rt/cpp-so/context.h index 9c4c5624d5..77b02b042b 100644 --- a/runtime/jit-rt/cpp-so/context.h +++ b/runtime/jit-rt/cpp-so/context.h @@ -18,6 +18,8 @@ #include "param_slice.h" +#include "slice.h" + enum class DumpStage : int { OriginalModule = 0, MergedModule = 1, @@ -43,6 +45,8 @@ enum { ApiVersion = LDC_DYNAMIC_COMPILE_API_VERSION }; #define JIT_UNREG_BIND_PAYLOAD \ MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \ LDC_DYNAMIC_COMPILE_API_VERSION) +#define JIT_SET_OPTS \ + MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION) typedef void (*InterruptPointHandlerT)(void *, const char *action, const char *object); diff --git a/runtime/jit-rt/cpp-so/optimizer.cpp b/runtime/jit-rt/cpp-so/optimizer.cpp index 15dea76bf7..7421c14119 100644 --- a/runtime/jit-rt/cpp-so/optimizer.cpp +++ b/runtime/jit-rt/cpp-so/optimizer.cpp @@ -4,6 +4,8 @@ // // 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. // //===----------------------------------------------------------------------===// @@ -22,6 +24,8 @@ #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" @@ -31,7 +35,86 @@ #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, @@ -57,26 +140,43 @@ void addOptimizationPasses(llvm::legacy::PassManagerBase &mpm, } builder.DisableUnitAtATime = false; - // TODO: Expose this option - builder.DisableUnrollLoops = optLevel == 0; + builder.DisableUnrollLoops = (disableLoopUnrolling.getNumOccurrences() > 0) + ? disableLoopUnrolling + : optLevel == 0; - // TODO: expose this option - if (/*disableLoopVectorization*/ false) { + 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; } - // TODO: expose this option builder.SLPVectorize = - /*disableSLPVectorization*/ false ? false : optLevel > 1 && sizeLevel < 2; + disableSLPVectorization ? false : optLevel > 1 && sizeLevel < 2; // TODO: sanitizers support in jit? - // TODO: lang specific passes support - // TODO: addStripExternalsPass? // 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); } @@ -92,7 +192,7 @@ void setupPasses(llvm::TargetMachine &targetMachine, fpm.add(llvm::createTargetTransformInfoWrapperPass( targetMachine.getTargetIRAnalysis())); - if (/*stripDebug*/ true) { + if (stripDebug) { mpm.add(llvm::createStripSymbolsPass(true)); } mpm.add(llvm::createStripDeadPrototypesPass()); diff --git a/runtime/jit-rt/cpp-so/options.cpp b/runtime/jit-rt/cpp-so/options.cpp new file mode 100644 index 0000000000..b1527a063b --- /dev/null +++ b/runtime/jit-rt/cpp-so/options.cpp @@ -0,0 +1,57 @@ +//===-- options.cpp -------------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the Boost Software License. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "options.h" + +#include "callback_ostream.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/CommandLine.h" + +bool parseOptions(Slice> args, + void (*errs)(void *, const char *, size_t), + void *errsContext) { + llvm::SmallVector tempStrs; + tempStrs.reserve(args.len); + llvm::SmallVector tempOpts; + tempOpts.reserve(args.len + 1); + tempOpts.push_back("jit"); // dummy app name + for (size_t i = 0; i < args.len; ++i) { + auto &arg = args.data[i]; + tempStrs.push_back(std::string(arg.data, arg.len)); + tempOpts.push_back(tempStrs.back().c_str()); + } + + auto callback = [&](const char *str, size_t len) { + if (errs != nullptr) { + errs(errsContext, str, len); + } + }; + CallbackOstream os(callback); + + // There is no Option::setDefault() before llvm 60 +#if LDC_LLVM_VER >= 600 + llvm::cl::ResetAllOptionOccurrences(); + for (auto &i : llvm::cl::getRegisteredOptions()) { + i.second->setDefault(); + } +#else + static bool changed = false; + if (changed) { + os << "Cannot set options more than once"; + os.flush(); + return false; + } + changed = true; +#endif + auto res = llvm::cl::ParseCommandLineOptions( + static_cast(tempOpts.size()), tempOpts.data(), "", &os); + os.flush(); + return res; +} diff --git a/runtime/jit-rt/cpp-so/options.h b/runtime/jit-rt/cpp-so/options.h new file mode 100644 index 0000000000..33438ecdd9 --- /dev/null +++ b/runtime/jit-rt/cpp-so/options.h @@ -0,0 +1,23 @@ +//===-- options.h - jit support ---------------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the Boost Software License. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Jit runtime - compilation options. +// +//===----------------------------------------------------------------------===// + +#ifndef OPTIONS_HPP +#define OPTIONS_HPP + +#include "slice.h" + +bool parseOptions(Slice> args, + void (*errs)(void *, const char *, size_t), + void *errsContext); + +#endif // OPTIONS_HPP diff --git a/runtime/jit-rt/cpp-so/slice.h b/runtime/jit-rt/cpp-so/slice.h new file mode 100644 index 0000000000..290cb1dcd9 --- /dev/null +++ b/runtime/jit-rt/cpp-so/slice.h @@ -0,0 +1,24 @@ +//===-- slice.h - jit support -----------------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the Boost Software License. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// +// +// Jit runtime - simple memory slice class. +// +//===----------------------------------------------------------------------===// + +#ifndef SLICE_HPP +#define SLICE_HPP + +#include //size_t + +template struct Slice final { + size_t len; + T *data; +}; + +#endif // SLICE_HPP diff --git a/runtime/jit-rt/cpp/compile.cpp b/runtime/jit-rt/cpp/compile.cpp index ce32f4f43b..f08ea916fe 100644 --- a/runtime/jit-rt/cpp/compile.cpp +++ b/runtime/jit-rt/cpp/compile.cpp @@ -20,9 +20,14 @@ struct ParamSlice; #ifdef _WIN32 #define EXTERNAL __declspec(dllimport) extern #else -#define EXTERNAL extern +#define EXTERNAL __attribute__((visibility("default"))) extern #endif +template struct Slice final { + size_t len; + T *data; +}; + #define MAKE_JIT_API_CALL_IMPL(prefix, version) prefix##version #define MAKE_JIT_API_CALL(prefix, version) \ MAKE_JIT_API_CALL_IMPL(prefix, version) @@ -33,6 +38,8 @@ struct ParamSlice; #define JIT_UNREG_BIND_PAYLOAD \ MAKE_JIT_API_CALL(unregisterBindPayloadImplSo, \ LDC_DYNAMIC_COMPILE_API_VERSION) +#define JIT_SET_OPTS \ + MAKE_JIT_API_CALL(setDynamicCompilerOptsImpl, LDC_DYNAMIC_COMPILE_API_VERSION) extern "C" { @@ -50,6 +57,10 @@ EXTERNAL void JIT_REG_BIND_PAYLOAD(void *handle, void *originalFunc, EXTERNAL void JIT_UNREG_BIND_PAYLOAD(void *handle); +EXTERNAL bool JIT_SET_OPTS(const Slice> *args, + void (*errs)(void *, const char *, size_t), + void *errsContext); + void rtCompileProcessImpl(const Context *context, std::size_t contextSize) { JIT_API_ENTRYPOINT(dynamiccompile_modules_head, context, contextSize); } @@ -60,4 +71,10 @@ void registerBindPayload(void *handle, void *originalFunc, } void unregisterBindPayload(void *handle) { JIT_UNREG_BIND_PAYLOAD(handle); } + +bool setDynamicCompilerOpts(const Slice> *args, + void (*errs)(void *, const char *, size_t), + void *errsContext) { + return JIT_SET_OPTS(args, errs, errsContext); +} } diff --git a/runtime/jit-rt/d/ldc/dynamic_compile.d b/runtime/jit-rt/d/ldc/dynamic_compile.d index ae61d91a34..dd5bb79585 100644 --- a/runtime/jit-rt/d/ldc/dynamic_compile.d +++ b/runtime/jit-rt/d/ldc/dynamic_compile.d @@ -252,6 +252,32 @@ public: } } +/+ + + Set options for dynamic compiler. + + Returns false on error. + + + + This function is not thread-safe. + + + + Example: + + --- + + import ldc.attributes, ldc.dynamic_compile; + + + + auto res = setDynamicCompilerOptions(["-disable-gc2stack"]); + + assert(res); + + + + res = setDynamicCompilerOptions(["-invalid_option"], (in char[] str) + + { + + writeln("Error: ", str); + + }); + + assert(!res); + +/ +bool setDynamicCompilerOptions(string[] args, scope ErrsHandler errs = null) +{ + auto errsFunc = (errs !is null ? &errsWrapper : null); + auto errsFuncContext = (errs !is null ? cast(void*)&errs : null); + return setDynamicCompilerOpts(&args, errsFunc, errsFuncContext); +} + private: auto bindImpl(F, Args...)(F func, Args args) { @@ -436,6 +462,8 @@ struct BindPayload(OF, F, int[] Index, Args...) alias toDelegate = base.toDelegate; } +alias ErrsHandler = void delegate(const(char)[]); + extern(C) { enum ParamType : uint { @@ -465,6 +493,13 @@ void dumpHandlerWrapper(void* context, DumpStage stage, const char* buff, size_t (*del)(stage, buff[0..len]); } +void errsWrapper(void* context, const char* str, size_t len) +{ + alias DelType = ErrsHandler; + auto del = cast(DelType*)context; + assert(str !is null); + (*del)(str[0..len]); +} // must be synchronized with cpp struct Context @@ -480,7 +515,8 @@ struct Context } extern void rtCompileProcessImpl(const ref Context context, size_t contextSize); -void registerBindPayload(void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize); -void unregisterBindPayload(void* handle); +extern void registerBindPayload(void* handle, void* originalFunc, void* exampleFunc, const ParamSlice* params, size_t paramsSize); +extern void unregisterBindPayload(void* handle); +extern bool setDynamicCompilerOpts(const(string[])* args, void function(void*, const char*, size_t) errs, void* errsContext); } diff --git a/tests/dynamiccompile/options.d b/tests/dynamiccompile/options.d new file mode 100644 index 0000000000..685431a076 --- /dev/null +++ b/tests/dynamiccompile/options.d @@ -0,0 +1,52 @@ + +// RUN: %ldc -enable-dynamic-compile -run %s + +import std.stdio; +import std.array; +import std.string; +import ldc.attributes; +import ldc.dynamic_compile; + +@dynamicCompile int foo() +{ + int* i = new int; + *i = 42; + return *i; +} + +void main(string[] args) +{ + foreach (bool add_opt; [false, true]) + { + auto dump = appender!string(); + CompilerSettings settings; + settings.optLevel = 3; + settings.dumpHandler = (DumpStage stage, in char[] str) + { + if (DumpStage.OptimizedModule == stage) + { + write(str); + dump.put(str); + } + }; + writeln("==========================================="); + if (add_opt) + { + auto res = setDynamicCompilerOptions(["-disable-gc2stack"]); + assert(res); + } + compileDynamicCode(settings); + writeln(); + writeln("==========================================="); + stdout.flush(); + + if (add_opt) + { + assert(count(dump.data, "_d_allocmemoryT") > 0); + } + else + { + assert(count(dump.data, "_d_allocmemoryT") == 0); + } + } +} diff --git a/tests/dynamiccompile/options_invalid.d b/tests/dynamiccompile/options_invalid.d new file mode 100644 index 0000000000..f1678adab2 --- /dev/null +++ b/tests/dynamiccompile/options_invalid.d @@ -0,0 +1,23 @@ + +// RUN: %ldc -enable-dynamic-compile -run %s + +import std.array; +import std.string; +import ldc.attributes; +import ldc.dynamic_compile; + +@dynamicCompile int foo() +{ + return 42; +} + +void main(string[] args) +{ + auto dump = appender!string(); + auto res = setDynamicCompilerOptions(["-invalid_option"], (in char[] str) + { + dump.put(str); + }); + assert(!res); + assert(dump.data.length > 0); +} diff --git a/tests/dynamiccompile/options_multiple_changes.d b/tests/dynamiccompile/options_multiple_changes.d new file mode 100644 index 0000000000..1212497124 --- /dev/null +++ b/tests/dynamiccompile/options_multiple_changes.d @@ -0,0 +1,58 @@ + +// RUN: %ldc -enable-dynamic-compile -run %s +// REQUIRES: atleast_llvm600 + +import std.stdio; +import std.array; +import std.string; +import ldc.attributes; +import ldc.dynamic_compile; + +@dynamicCompile int foo() +{ + int* i = new int; + *i = 42; + return *i; +} + +void main(string[] args) +{ + foreach (bool add_opt; [false, true, false, true, false]) + { + auto dump = appender!string(); + CompilerSettings settings; + settings.optLevel = 3; + settings.dumpHandler = (DumpStage stage, in char[] str) + { + if (DumpStage.OptimizedModule == stage) + { + write(str); + dump.put(str); + } + }; + writeln("==========================================="); + if (add_opt) + { + auto res = setDynamicCompilerOptions(["-disable-gc2stack"]); + assert(res); + } + else + { + auto res = setDynamicCompilerOptions([]); + assert(res); + } + compileDynamicCode(settings); + writeln(); + writeln("==========================================="); + stdout.flush(); + + if (add_opt) + { + assert(count(dump.data, "_d_allocmemoryT") > 0); + } + else + { + assert(count(dump.data, "_d_allocmemoryT") == 0); + } + } +}