From f609afa7c062eaf40fb905afcafedea111b38c19 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Mon, 26 Jan 2026 00:50:42 +0100 Subject: [PATCH] Add `--funified-lto` command-line option, same as clang. --- CHANGELOG.md | 1 + driver/cl_options.cpp | 2 ++ driver/cl_options.h | 4 ++- gen/optimizer.cpp | 72 +++++++++++++++++++++++++++---------- tests/linking/lto_unified.d | 25 +++++++++++++ 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 tests/linking/lto_unified.d diff --git a/CHANGELOG.md b/CHANGELOG.md index 096abad9eaf..1ad9f80fde3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - `clang --target=aarch64-linux-gnu` - The prebuilt arm64/universal macOS packages additionally bundle the arm64 iOS-*simulator* libraries, for out-of-the-box cross-compilation support via e.g. `-mtriple=arm64-apple-ios12.0-simulator`. (#4974) - New `--fdebug-prefix-map` command-line option and changed debuginfo file/directory name splitting logic (both now similar to clang), to aid reproducible builds. (#5039) +- New `--funified-lto` command-line option, same as clang. #### Platform support - Supports LLVM 15 - 21. diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 8fba3c62b9f..610904db196 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -732,6 +732,8 @@ cl::opt ltoFatObjects( "ffat-lto-objects", cl::ZeroOrMore, cl::desc("Include both IR and object code in object file output; only " "effective when compiling with -flto.")); +cl::opt ltoUnified("funified-lto", cl::ZeroOrMore, + cl::desc("Use unified LTO pipeline.")); cl::opt saveOptimizationRecord("fsave-optimization-record", diff --git a/driver/cl_options.h b/driver/cl_options.h index 964874bcced..dd65cdfb850 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -131,9 +131,11 @@ enum LTOKind { LTO_Thin, }; extern cl::opt ltoMode; +extern cl::opt ltoFatObjects; +extern cl::opt ltoUnified; inline bool isUsingLTO() { return ltoMode != LTO_None; } inline bool isUsingThinLTO() { return ltoMode == LTO_Thin; } -extern cl::opt ltoFatObjects; +inline bool prepareForThinLTO() { return isUsingThinLTO() || opts::ltoUnified; } extern cl::opt saveOptimizationRecord; diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 70299594528..ecdec8c9ce7 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -10,30 +10,24 @@ // //===----------------------------------------------------------------------===// -#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 +#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" +#else +#include "dmd/errors.h" #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" +#include "gen/logger.h" #endif #if LDC_LLVM_VER < 1700 @@ -355,7 +349,9 @@ static void addGarbageCollect2StackPass(ModulePassManager &mpm, } } +// Exclude these functions, that access cmdline options, from the JIT RT. #ifndef IN_JITRT + static llvm::Optional getPGOOptions() { // FIXME: Do we have these anywhere? bool debugInfoForProfiling = false; @@ -409,6 +405,44 @@ static llvm::Optional getPGOOptions() { return std::nullopt; #endif } + +// Imitate behavior of clang +static bool shouldEmitRegularLTOSummary(llvm::Module const &M) { + return opts::isUsingLTO() && + llvm::Triple(M.getTargetTriple()).getVendor() != llvm::Triple::Apple; +} + +/// Check whether we should emit a flag for UnifiedLTO. +/// The UnifiedLTO module flag should be set when UnifiedLTO is enabled for +/// ThinLTO or Full LTO with module summaries. +static bool shouldEmitUnifiedLTOModuleFlag(llvm::Module const &M) { + return opts::ltoUnified && + (opts::prepareForThinLTO() || shouldEmitRegularLTOSummary(M)); +} + +static void addLTOModuleFlags(llvm::Module *M) { + if (opts::ltoFatObjects) { + if (opts::prepareForThinLTO()) { +// TODO: implement support for EnableSplitLTOUnit +// if (!M->getModuleFlag("EnableSplitLTOUnit")) +// M->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit", +// CodeGenOpts.EnableSplitLTOUnit); + } else { + if (shouldEmitRegularLTOSummary(*M)) { + if (!M->getModuleFlag("ThinLTO") && !opts::ltoUnified) + M->addModuleFlag(llvm::Module::Error, "ThinLTO", uint32_t(0)); + if (!M->getModuleFlag("EnableSplitLTOUnit")) + M->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit", + uint32_t(1)); + } + } + + if (shouldEmitUnifiedLTOModuleFlag(*M) && + !M->getModuleFlag("UnifiedLTO")) + M->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1)); + } +} + #endif // !IN_JITRT static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsigned sizeLevelVal) { @@ -585,12 +619,11 @@ void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) { mpm = pb.buildO0DefaultPipeline(level, ltoPrelink); #if LDC_LLVM_VER >= 1700 } else if (opts::ltoFatObjects && opts::isUsingLTO()) { - mpm = pb.buildFatLTODefaultPipeline(level, - opts::isUsingThinLTO(), - opts::isUsingThinLTO() - ); + mpm = pb.buildFatLTODefaultPipeline(level, opts::prepareForThinLTO(), + opts::prepareForThinLTO() || + shouldEmitRegularLTOSummary(*M)); #endif - } else if (opts::isUsingThinLTO()) { + } else if (opts::prepareForThinLTO()) { mpm = pb.buildThinLTOPreLinkDefaultPipeline(level); } else if (opts::isUsingLTO()) { mpm = pb.buildLTOPreLinkDefaultPipeline(level); @@ -599,6 +632,9 @@ void runOptimizationPasses(llvm::Module *M, llvm::TargetMachine *TM) { mpm = pb.buildPerModuleDefaultPipeline(level); } +#ifndef IN_JITRT + addLTOModuleFlags(M); +#endif mpm.run(*M,mam); } diff --git a/tests/linking/lto_unified.d b/tests/linking/lto_unified.d new file mode 100644 index 00000000000..fb8e09db0c5 --- /dev/null +++ b/tests/linking/lto_unified.d @@ -0,0 +1,25 @@ +// Test unified lto commandline flag + +// REQUIRES: LTO +// REQUIRES: atleast_llvm1800 + +// RUN: split-file %s %t + +// RUN: %ldc %t/second.d -of=%t/second_thin%obj -c -flto=thin +// RUN: %ldc %t/third.d -of=%t/third_full%obj -c -flto=full +// RUN: %ldc -I%t %t/main.d %t/second_thin%obj %t/third_full%obj -flto=full -O + +//--- main.d +import second; +import third; +void main() +{ + foo(); + g(); +} + +//--- second.d +void foo() {} + +//--- third.d +void g() {}