From aa9eb33cb1063ece6eb4556fa20e41516e419d22 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 10 Feb 2023 21:39:10 +0100 Subject: [PATCH 1/5] Be less conservative wrt. linker dead code elimination --- CHANGELOG.md | 1 + driver/linker-gcc.cpp | 26 +++++++++++++------------- driver/targetmachine.cpp | 16 ++++++++-------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b83b6161..8480e3b27b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # LDC master #### Big news +- Linker-level dead code elimination is enabled by default for Apple, wasm and *all* ELF targets too now. (#4320) #### Platform support diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index a53936a9aa..85bd276189 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -479,6 +479,8 @@ void ArgsBuilder::addSanitizers(const llvm::Triple &triple) { void ArgsBuilder::build(llvm::StringRef outputPath, const std::vector &defaultLibNames) { + const auto &triple = *global.params.targetTriple; + // object files for (auto objfile : global.params.objfiles) { args.push_back(objfile); @@ -494,7 +496,7 @@ void ArgsBuilder::build(llvm::StringRef outputPath, // Link with profile-rt library when generating an instrumented binary. if (opts::isInstrumentingForPGO()) { - addProfileRuntimeLinkFlags(*global.params.targetTriple); + addProfileRuntimeLinkFlags(triple); } if (opts::enableDynamicCompile) { @@ -521,10 +523,10 @@ void ArgsBuilder::build(llvm::StringRef outputPath, args.push_back("-o"); args.push_back(std::string(outputPath)); - addSanitizers(*global.params.targetTriple); + addSanitizers(triple); if (opts::fXRayInstrument) { - addXRayLinkFlags(*global.params.targetTriple); + addXRayLinkFlags(triple); } // Add LTO link flags before adding the user link switches, such that the user @@ -559,16 +561,14 @@ void ArgsBuilder::build(llvm::StringRef outputPath, addLdFlag("-rpath", rpath); } - if (global.params.targetTriple->getOS() == llvm::Triple::Linux || - (global.params.targetTriple->getOS() == llvm::Triple::FreeBSD && - (useInternalLLDForLinking() || - (!opts::linker.empty() && opts::linker != "bfd") || - (opts::linker.empty() && isLldDefaultLinker())))) { - // Make sure we don't do --gc-sections when generating a profile- - // instrumented binary. The runtime relies on magic sections, which - // would be stripped by gc-section on older version of ld, see bug: - // https://sourceware.org/bugzilla/show_bug.cgi?id=19161 - if (!opts::disableLinkerStripDead && !opts::isInstrumentingForPGO()) { + // Make sure we don't do --gc-sections when generating a profile- + // instrumented binary. The runtime relies on magic sections, which + // would be stripped by gc-section on older version of ld, see bug: + // https://sourceware.org/bugzilla/show_bug.cgi?id=19161 + if (!opts::disableLinkerStripDead && !opts::isInstrumentingForPGO()) { + if (triple.isOSBinFormatMachO()) { + addLdFlag("-dead_strip"); + } else { addLdFlag("--gc-sections"); } } diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 3bd7101d3d..9b45464dae 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -492,14 +492,14 @@ createTargetMachine(const std::string targetTriple, const std::string arch, break; } - // Right now, we only support linker-level dead code elimination on Linux - // and FreeBSD using GNU or LLD linkers (based on the --gc-sections flag). - // The Apple ld on OS X supports a similar flag (-dead_strip) that doesn't - // require emitting the symbols into different sections. The MinGW ld doesn't - // seem to support --gc-sections at all. - if (!noLinkerStripDead && (triple.getOS() == llvm::Triple::Linux || - triple.getOS() == llvm::Triple::FreeBSD || - triple.getOS() == llvm::Triple::Win32)) { + // Linker-level dead code elimination for ELF/wasm binaries using GNU or LLD + // linkers (based on the --gc-sections flag) requires a separate section per + // symbol. + // The Apple ld64 on macOS supports a similar flag (-dead_strip) that doesn't + // require emitting the symbols into different sections. + // On Windows, the MSVC link.exe / lld-link.exe has `/REF`; LLD enforces + // separate sections with LTO, so do the same here. + if (!noLinkerStripDead && !triple.isOSBinFormatMachO()) { targetOptions.FunctionSections = true; targetOptions.DataSections = true; } From df86c9f9a59ce1285442feacc6d240d9940d78ac Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 11 Feb 2023 17:40:19 +0100 Subject: [PATCH 2/5] Implicitly apply `@assumeUsed` to rt_{options,envvars_enabled,cmdline_enabled} druntime symbol overrides Required to fix lit-test `linking/rt_options.d` on Apple targets when linking against *shared* druntime. --- ir/irvar.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ir/irvar.cpp b/ir/irvar.cpp index 97339d2f9e..16343658d6 100644 --- a/ir/irvar.cpp +++ b/ir/irvar.cpp @@ -150,7 +150,12 @@ void IrGlobal::define() { // If this global is used from a naked function, we need to create an // artificial "use" for it, or it could be removed by the optimizer if // the only reference to it is in inline asm. - if (nakedUse) { + // Also prevent linker-level dead-symbol-elimination from stripping + // special `rt_*` druntime symbol overrides (e.g., from executables linked + // against *shared* druntime; required at least for Apple's ld64 linker). + const auto name = gvar->getName(); + if (nakedUse || name == "rt_options" || name == "rt_envvars_enabled" || + name == "rt_cmdline_enabled") { gIR->usedArray.push_back(gvar); } From c278f8238b9dd48855284d425eff813e5ad991b2 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 11 Feb 2023 18:28:57 +0100 Subject: [PATCH 3/5] CMake: For Apple targets, disable dead_strip for LDC itself when building with plugins support --- CMakeLists.txt | 63 ++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85405e0143..1881fed346 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -635,33 +635,6 @@ if(LDC_WITH_LLD) endif() endif() -# Plugin support -if(UNIX) - set(LDC_ENABLE_PLUGINS_DEFAULT ON) -else() - set(LDC_ENABLE_PLUGINS_DEFAULT OFF) -endif() -set(LDC_ENABLE_PLUGINS ${LDC_ENABLE_PLUGINS_DEFAULT} CACHE BOOL "Build LDC with plugin support (increases binary size)") -if(LDC_ENABLE_PLUGINS) - add_definitions(-DLDC_ENABLE_PLUGINS) - - if(APPLE) - # No extra link flags needed. - elseif(UNIX) - # For plugin support, we need to link with --export-dynamic on Unix. - # Make sure the linker supports --export-dynamic (on Solaris it is not supported and also not needed). - set(CMAKE_REQUIRED_QUIET_BAK ${CMAKE_REQUIRED_QUIET}) - set(CMAKE_REQUIRED_QUIET ON) # suppress status messages - CHECK_LINK_FLAG("--export-dynamic" LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) - set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_BAK}) - - if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) - set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic") - endif() - endif() -endif() -message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}") - if(NOT DEFINED LDC_LINK_MANUALLY) if(MSVC) # Use the D host compiler for linking D executables. @@ -678,6 +651,42 @@ if(LDC_LINK_MANUALLY AND NOT DEFINED D_LINKER_ARGS) message(STATUS "Host D compiler linker flags: ${D_LINKER_ARGS}") endif() +# Plugin support +if(UNIX) + set(LDC_ENABLE_PLUGINS_DEFAULT ON) +else() + set(LDC_ENABLE_PLUGINS_DEFAULT OFF) +endif() +set(LDC_ENABLE_PLUGINS ${LDC_ENABLE_PLUGINS_DEFAULT} CACHE BOOL "Build LDC with plugin support (increases binary size)") +if(LDC_ENABLE_PLUGINS) + add_definitions(-DLDC_ENABLE_PLUGINS) + + if(APPLE) + # Need to disable dead_strip with LDC host compilers. + if("${D_COMPILER_ID}" STREQUAL "LDMD") + if(LDC_LINK_MANUALLY) + # suboptimal - applies to all D executables (incl. ldmd2, ldc-build-runtime, ldc-prune-cache) + list(REMOVE_ITEM D_LINKER_ARGS "-Wl,-dead_strip") + else() + # just for ldc2 (and ldc2-unittest) + append("-disable-linker-strip-dead" DFLAGS_LDC) + endif() + endif() + elseif(UNIX) + # For plugin support, we need to link with --export-dynamic on Unix. + # Make sure the linker supports --export-dynamic (on Solaris it is not supported and also not needed). + set(CMAKE_REQUIRED_QUIET_BAK ${CMAKE_REQUIRED_QUIET}) + set(CMAKE_REQUIRED_QUIET ON) # suppress status messages + CHECK_LINK_FLAG("--export-dynamic" LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) + set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_BAK}) + + if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG) + set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic") + endif() + endif() +endif() +message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}") + build_d_executable( "${LDC_EXE}" "${LDC_EXE_FULL}" From c61516a501f24ed986a42ea70d039ae77c961e77 Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 11 Feb 2023 20:06:23 +0100 Subject: [PATCH 4/5] [tentative fix] --- runtime/druntime/test/profile/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/druntime/test/profile/Makefile b/runtime/druntime/test/profile/Makefile index 50c52d5cea..b0110c66e0 100644 --- a/runtime/druntime/test/profile/Makefile +++ b/runtime/druntime/test/profile/Makefile @@ -10,7 +10,8 @@ GREP:=grep .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) -$(ROOT)/profile.done: DFLAGS+=-profile +# LDC: enable assertions for BUILD=RELEASE (=> `-O -release`) +$(ROOT)/profile.done: DFLAGS+=-profile -check=assert=on $(ROOT)/profile.done: $(ROOT)/%.done: $(ROOT)/% @echo Testing $* @rm -f $(ROOT)/mytrace.log $(ROOT)/mytrace.def From 7f2498b6cd55b4488b8105d5aae2744a4a73301e Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Tue, 14 Feb 2023 13:46:10 -0800 Subject: [PATCH 5/5] Allow RISC-V ABI to be configured by mabi (#4322) --- driver/cl_options.cpp | 2 +- driver/targetmachine.cpp | 27 +++++++++++++----- driver/targetmachine.h | 2 ++ driver/tool.cpp | 58 ++++++++++++++++++++++++++++++++++++--- tests/driver/riscv_abi1.d | 6 ++++ tests/driver/riscv_abi2.d | 6 ++++ tests/driver/riscv_abi3.d | 6 ++++ 7 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 tests/driver/riscv_abi1.d create mode 100644 tests/driver/riscv_abi2.d create mode 100644 tests/driver/riscv_abi3.d diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 080df45895..68443f495a 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -419,7 +419,7 @@ cl::opt mTargetTriple("mtriple", cl::ZeroOrMore, cl::desc("Override target triple")); cl::opt - mABI("mabi", cl::ZeroOrMore, cl::Hidden, cl::init(""), + mABI("mabi", cl::ZeroOrMore, cl::init(""), cl::desc("The name of the ABI to be targeted from the backend")); static Strings *pModFileAliasStrings = &global.params.modFileAliasStrings; diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 3bd7101d3d..8a18b6fcd0 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -57,7 +57,7 @@ static llvm::cl::opt preserveDwarfLineSection( llvm::cl::init(false)); #endif -static const char *getABI(const llvm::Triple &triple) { +const char *getABI(const llvm::Triple &triple) { llvm::StringRef ABIName(opts::mABI); if (ABIName != "") { switch (triple.getArch()) { @@ -88,6 +88,22 @@ static const char *getABI(const llvm::Triple &triple) { if (ABIName.startswith("elfv2")) return "elfv2"; break; + case llvm::Triple::riscv64: + if (ABIName.startswith("lp64f")) + return "lp64f"; + if (ABIName.startswith("lp64d")) + return "lp64d"; + if (ABIName.startswith("lp64")) + return "lp64"; + break; + case llvm::Triple::riscv32: + if (ABIName.startswith("ilp32f")) + return "ilp32f"; + if (ABIName.startswith("ilp32d")) + return "ilp32d"; + if (ABIName.startswith("ilp32")) + return "ilp32"; + break; default: break; } @@ -104,7 +120,9 @@ static const char *getABI(const llvm::Triple &triple) { case llvm::Triple::ppc64le: return "elfv2"; case llvm::Triple::riscv64: - return "lp64d"; + return "lp64"; + case llvm::Triple::riscv32: + return "ilp32"; default: return ""; } @@ -421,11 +439,6 @@ createTargetMachine(const std::string targetTriple, const std::string arch, features.push_back("+cx16"); } - if (triple.getArch() == llvm::Triple::riscv64 && !hasFeature("d")) { - // Support Double-Precision Floating-Point by default. - features.push_back("+d"); - } - // Handle cases where LLVM picks wrong default relocModel #if LDC_LLVM_VER >= 1600 if (relocModel.has_value()) {} diff --git a/driver/targetmachine.h b/driver/targetmachine.h index a53d7960ad..17e664a2bf 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -72,3 +72,5 @@ MipsABI::Type getMipsABI(); // Looks up a target based on an arch name and a target triple. const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple, std::string &errorMsg); + +const char *getABI(const llvm::Triple &triple); diff --git a/driver/tool.cpp b/driver/tool.cpp index b14347241b..b5ceb226a9 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Target/TargetMachine.h" #ifdef _WIN32 #include @@ -121,12 +122,61 @@ void appendTargetArgsForGcc(std::vector &args) { return; case Triple::riscv64: - if (triple.isArch64Bit()) { - args.push_back("-march=rv64gc"); - args.push_back("-mabi=lp64d"); + { + std::string mabi = getABI(triple); + args.push_back("-mabi=" + mabi); + + extern llvm::TargetMachine* gTargetMachine; + auto featuresStr = gTargetMachine->getTargetFeatureString(); + llvm::SmallVector features; + featuresStr.split(features, ",", -1, false); + + // Returns true if 'feature' is enabled and false otherwise. Handles the + // case where the feature is specified multiple times ('+m,-m'), and + // takes the last occurrence. + auto hasFeature = [&features](llvm::StringRef feature) { + for (int i = features.size() - 1; i >= 0; i--) { + auto f = features[i]; + if (f.substr(1) == feature) { + return f[0] == '+'; + } + } + return false; + }; + + std::string march; + if (triple.isArch64Bit()) + march = "rv64"; + else + march = "rv32"; + bool m = hasFeature("m"); + bool a = hasFeature("a"); + bool f = hasFeature("f"); + bool d = hasFeature("d"); + bool c = hasFeature("c"); + bool g = false; + + if (m && a && f && d) { + march += "g"; + g = true; + } else { + march += "i"; + if (m) + march += "m"; + if (a) + march += "a"; + if (f) + march += "f"; + if (d) + march += "d"; + } + if (c) + march += "c"; + if (!g) + march += "_zicsr_zifencei"; + args.push_back("-march=" + march); } return; - default: break; } diff --git a/tests/driver/riscv_abi1.d b/tests/driver/riscv_abi1.d new file mode 100644 index 0000000000..3eeb2659de --- /dev/null +++ b/tests/driver/riscv_abi1.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-elf -mattr=+m,-m --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64 -march=rv64i_zicsr_zifencei + +void main() {} diff --git a/tests/driver/riscv_abi2.d b/tests/driver/riscv_abi2.d new file mode 100644 index 0000000000..f3fd361704 --- /dev/null +++ b/tests/driver/riscv_abi2.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv64-unknown-elf -mattr=+m,+a,+d,+f,+c -mabi=lp64d --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=lp64d -march=rv64gc + +void main() {} diff --git a/tests/driver/riscv_abi3.d b/tests/driver/riscv_abi3.d new file mode 100644 index 0000000000..0d47d921d0 --- /dev/null +++ b/tests/driver/riscv_abi3.d @@ -0,0 +1,6 @@ +// REQUIRES: target_RISCV + +// RUN: %ldc %s -mtriple=riscv32-unknown-elf -mattr=+c,+a,-a,+a,+m --gcc=echo > %t && FileCheck %s < %t +// CHECK: -mabi=ilp32 -march=rv32imac_zicsr_zifencei + +void main() {}