From 1f1ae2427812c4e387ab8332080d71115a80ed21 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 21 Apr 2023 15:22:07 -0400 Subject: [PATCH] Add -Zsplit-thin-lto-unit flag to enable ThinLTO unit splitting Clang enables the EnableSplitLTOUnit LLVM module flag when compiling for ThinLTO when: * -fsplit-lto-unit is specified, or * -fsanitize=cfi is specified, or * -fwhole-program-vtables. When linking Rust and C++ together and using ThinLTO, the setting for the LLVM module flag "EnableSplitLTOUnit" must match or linking simply fails. The ability to enable this LLVM module flag is then required in order to link Rust objects with C++ objects with ThinLTO for any project that uses the above flags (like Chromium does). --- compiler/rustc_codegen_gcc/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 15 ++++++++++----- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_llvm/src/lib.rs | 7 +++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 +++++- compiler/rustc_codegen_ssa/src/back/write.rs | 4 +++- compiler/rustc_codegen_ssa/src/traits/write.rs | 5 ++++- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 6 +++++- compiler/rustc_session/src/options.rs | 2 ++ 9 files changed, 36 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 1cabb05de975b..991e46f5b8879 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -255,7 +255,7 @@ impl WriteBackendMethods for GccCodegenBackend { back::write::codegen(cgcx, diag_handler, module, config) } - fn prepare_thin(_module: ModuleCodegen) -> (String, Self::ThinBuffer) { + fn prepare_thin(_module: ModuleCodegen, _config: &ModuleConfig) -> (String, Self::ThinBuffer) { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index d2e01708a37bc..f3b59d73fd59e 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,7 +7,9 @@ use crate::{LlvmCodegenBackend, ModuleLlvm}; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig}; +use rustc_codegen_ssa::back::write::{ + CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, +}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -212,9 +214,12 @@ pub(crate) fn run_thin( ) } -pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBuffer) { +pub(crate) fn prepare_thin( + module: ModuleCodegen, + config: &ModuleConfig, +) -> (String, ThinBuffer) { let name = module.name; - let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); + let buffer = ThinBuffer::new(module.module_llvm.llmod(), true, config.split_thin_lto_unit); (name, buffer) } @@ -656,9 +661,9 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { + pub fn new(m: &llvm::Module, is_thin: bool, split_lto_unit: bool) -> ThinBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); + let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, split_lto_unit); ThinBuffer(buffer) } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 7136f750f39d9..d5a430885ffb5 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -615,7 +615,7 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name); - let thin = ThinBuffer::new(llmod, config.emit_thin_lto); + let thin = ThinBuffer::new(llmod, config.emit_thin_lto, config.split_thin_lto_unit); let data = thin.data(); if let Some(bitcode_filename) = bc_out.file_name() { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 8305a0a4c286d..a1036b306c4d2 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -230,8 +230,11 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result { back::write::codegen(cgcx, diag_handler, module, config) } - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module) + fn prepare_thin( + module: ModuleCodegen, + config: &ModuleConfig, + ) -> (String, Self::ThinBuffer) { + back::lto::prepare_thin(module, config) } fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c95148013eb74..294f431336c37 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2431,7 +2431,11 @@ extern "C" { #[allow(improper_ctypes)] pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); - pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferCreate( + M: &Module, + is_thin: bool, + split_lto_unit: bool, + ) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c42d59bd51c0a..d8a03bccd78d2 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -104,6 +104,7 @@ pub struct ModuleConfig { pub emit_asm: bool, pub emit_obj: EmitObj, pub emit_thin_lto: bool, + pub split_thin_lto_unit: bool, pub bc_cmdline: String, // Miscellaneous flags. These are mostly copied from command-line @@ -223,6 +224,7 @@ impl ModuleConfig { ), emit_obj, emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto, + split_thin_lto_unit: sess.opts.unstable_opts.split_thin_lto_unit, bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(), verify_llvm_ir: sess.verify_llvm_ir(), @@ -839,7 +841,7 @@ fn execute_optimize_work_item( match lto_type { ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), ComputedLtoType::Thin => { - let (name, thin_buffer) = B::prepare_thin(module); + let (name, thin_buffer) = B::prepare_thin(module, module_config); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 9826256a4c5d5..7df671a32aa7c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -55,7 +55,10 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { module: ModuleCodegen, config: &ModuleConfig, ) -> Result; - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer); + fn prepare_thin( + module: ModuleCodegen, + config: &ModuleConfig, + ) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1acdc95ca8d2d..9b6f29449b6bd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1361,7 +1361,7 @@ struct LLVMRustThinLTOBuffer { }; extern "C" LLVMRustThinLTOBuffer* -LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { +LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool split_lto_unit) { auto Ret = std::make_unique(); { raw_string_ostream OS(Ret->data); @@ -1378,6 +1378,10 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; + if (!unwrap(M)->getModuleFlag("EnableSplitLTOUnit")) { + unwrap(M)->addModuleFlag( + Module::Error, "EnableSplitLTOUnit", split_lto_unit); + } MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 775fad1a365c2..87b50f4751d23 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1707,6 +1707,8 @@ options! { file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker"), + split_thin_lto_unit: bool = (false, parse_bool, [UNTRACKED], + "control module splitting during thin LTO (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")]