From 86f0d3ca5e6e3d536f97555abaf24b7f1797ef70 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 4 Jul 2025 07:42:28 +0000 Subject: [PATCH 1/6] Merge modules and cached_modules for fat LTO The modules vec can already contain serialized modules and there is no need to distinguish between cached and non-cached cgus at LTO time. --- compiler/rustc_codegen_gcc/src/back/lto.rs | 12 ------------ compiler/rustc_codegen_gcc/src/lib.rs | 3 +-- compiler/rustc_codegen_llvm/src/back/lto.rs | 13 +------------ compiler/rustc_codegen_llvm/src/lib.rs | 3 +-- compiler/rustc_codegen_ssa/src/back/write.rs | 15 +++++++++++---- compiler/rustc_codegen_ssa/src/traits/write.rs | 1 - 6 files changed, 14 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index e554dd2500bda..9f2842d7abc8f 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -175,7 +175,6 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { pub(crate) fn run_fat( cgcx: &CodegenContext, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); @@ -186,7 +185,6 @@ pub(crate) fn run_fat( cgcx, dcx, modules, - cached_modules, lto_data.upstream_modules, lto_data.tmp_path, //<o_data.symbols_below_threshold, @@ -197,7 +195,6 @@ fn fat_lto( cgcx: &CodegenContext, _dcx: DiagCtxtHandle<'_>, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], @@ -211,21 +208,12 @@ fn fat_lto( // modules that are serialized in-memory. // * `in_memory` contains modules which are already parsed and in-memory, // such as from multi-CGU builds. - // - // All of `cached_modules` (cached from previous incremental builds) can - // immediately go onto the `serialized_modules` modules list and then we can - // split the `modules` array into these two lists. let mut in_memory = Vec::new(); - serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| { - info!("pushing cached module {:?}", wp.cgu_name); - (buffer, CString::new(wp.cgu_name).unwrap()) - })); for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), FatLtoInput::Serialized { name, buffer } => { info!("pushing serialized module {:?}", name); - let buffer = SerializedModule::Local(buffer); serialized_modules.push((buffer, CString::new(name).unwrap())); } } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index d81bcc5977563..98673930f9bf6 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -356,14 +356,13 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, diff_fncs: Vec, ) -> Result, FatalError> { if !diff_fncs.is_empty() { unimplemented!(); } - back::lto::run_fat(cgcx, modules, cached_modules) + back::lto::run_fat(cgcx, modules) } fn run_thin_lto( diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 655e1c9537376..2dac29c4ee488 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -200,14 +200,13 @@ fn get_bitcode_slice_from_object_data<'a>( pub(crate) fn run_fat( cgcx: &CodegenContext, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); - fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold) + fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold) } /// Performs thin LTO by performing necessary global analysis and returning two @@ -245,7 +244,6 @@ fn fat_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, symbols_below_threshold: &[*const libc::c_char], ) -> Result, FatalError> { @@ -258,21 +256,12 @@ fn fat_lto( // modules that are serialized in-memory. // * `in_memory` contains modules which are already parsed and in-memory, // such as from multi-CGU builds. - // - // All of `cached_modules` (cached from previous incremental builds) can - // immediately go onto the `serialized_modules` modules list and then we can - // split the `modules` array into these two lists. let mut in_memory = Vec::new(); - serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| { - info!("pushing cached module {:?}", wp.cgu_name); - (buffer, CString::new(wp.cgu_name).unwrap()) - })); for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), FatLtoInput::Serialized { name, buffer } => { info!("pushing serialized module {:?}", name); - let buffer = SerializedModule::Local(buffer); serialized_modules.push((buffer, CString::new(name).unwrap())); } } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6db4e122ad6e8..5747e87e077ba 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -177,10 +177,9 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, diff_fncs: Vec, ) -> Result, FatalError> { - let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?; + let mut module = back::lto::run_fat(cgcx, modules)?; if !diff_fncs.is_empty() { builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 50a7cba300b4b..50e799a168738 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -808,7 +808,7 @@ pub(crate) enum WorkItemResult { } pub enum FatLtoInput { - Serialized { name: String, buffer: B::ModuleBuffer }, + Serialized { name: String, buffer: SerializedModule }, InMemory(ModuleCodegen), } @@ -897,7 +897,10 @@ fn execute_optimize_work_item( fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); - Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { name, buffer })) + Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { + name, + buffer: SerializedModule::Local(buffer), + })) } None => Ok(WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module))), }, @@ -990,12 +993,16 @@ fn execute_copy_from_cache_work_item( fn execute_fat_lto_work_item( cgcx: &CodegenContext, - needs_fat_lto: Vec>, + mut needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, autodiff: Vec, module_config: &ModuleConfig, ) -> Result, FatalError> { - let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?; + for (module, wp) in import_only_modules { + needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) + } + + let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, autodiff)?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 5e993640472d1..3e18cce316ef1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -25,7 +25,6 @@ pub trait WriteBackendMethods: Clone + 'static { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, modules: Vec>, - cached_modules: Vec<(SerializedModule, WorkProduct)>, diff_fncs: Vec, ) -> Result, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two From 125c7c7976988004888b41d983cdfd00a6810018 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 4 Jul 2025 07:52:30 +0000 Subject: [PATCH 2/6] Remove worker id It isn't used anywhere. Also inline free_worker into the only call site. --- compiler/rustc_codegen_ssa/src/back/write.rs | 75 +++++--------------- 1 file changed, 19 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 50e799a168738..8c3d17115191a 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1037,7 +1037,7 @@ pub(crate) enum Message { /// The backend has finished processing a work item for a codegen unit. /// Sent from a backend worker thread. - WorkItem { result: Result, Option>, worker_id: usize }, + WorkItem { result: Result, Option> }, /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the @@ -1355,18 +1355,6 @@ fn start_executing_work( // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || { - let mut worker_id_counter = 0; - let mut free_worker_ids = Vec::new(); - let mut get_worker_id = |free_worker_ids: &mut Vec| { - if let Some(id) = free_worker_ids.pop() { - id - } else { - let id = worker_id_counter; - worker_id_counter += 1; - id - } - }; - // This is where we collect codegen units that have gone all the way // through codegen and LLVM. let mut compiled_modules = vec![]; @@ -1447,12 +1435,7 @@ fn start_executing_work( let (item, _) = work_items.pop().expect("queue empty - queue_full_enough() broken?"); main_thread_state = MainThreadState::Lending; - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, &mut llvm_start_time, item); } } } else if codegen_state == Completed { @@ -1521,12 +1504,7 @@ fn start_executing_work( MainThreadState::Idle => { if let Some((item, _)) = work_items.pop() { main_thread_state = MainThreadState::Lending; - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, &mut llvm_start_time, item); } else { // There is no unstarted work, so let the main thread // take over for a running worker. Otherwise the @@ -1562,12 +1540,7 @@ fn start_executing_work( while running_with_own_token < tokens.len() && let Some((item, _)) = work_items.pop() { - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, &mut llvm_start_time, item); running_with_own_token += 1; } } @@ -1575,21 +1548,6 @@ fn start_executing_work( // Relinquish accidentally acquired extra tokens. tokens.truncate(running_with_own_token); - // If a thread exits successfully then we drop a token associated - // with that worker and update our `running_with_own_token` count. - // We may later re-acquire a token to continue running more work. - // We may also not actually drop a token here if the worker was - // running with an "ephemeral token". - let mut free_worker = |worker_id| { - if main_thread_state == MainThreadState::Lending { - main_thread_state = MainThreadState::Idle; - } else { - running_with_own_token -= 1; - } - - free_worker_ids.push(worker_id); - }; - let msg = coordinator_receive.recv().unwrap(); match *msg.downcast::>().ok().unwrap() { // Save the token locally and the next turn of the loop will use @@ -1658,8 +1616,17 @@ fn start_executing_work( codegen_state = Aborted; } - Message::WorkItem { result, worker_id } => { - free_worker(worker_id); + Message::WorkItem { result } => { + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running_with_own_token` count. + // We may later re-acquire a token to continue running more work. + // We may also not actually drop a token here if the worker was + // running with an "ephemeral token". + if main_thread_state == MainThreadState::Lending { + main_thread_state = MainThreadState::Idle; + } else { + running_with_own_token -= 1; + } match result { Ok(WorkItemResult::Finished(compiled_module)) => { @@ -1805,7 +1772,6 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext, llvm_start_time: &mut Option>, - worker_id: usize, work: WorkItem, ) { if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { @@ -1820,24 +1786,21 @@ fn spawn_work<'a, B: ExtraBackendMethods>( struct Bomb { coordinator_send: Sender>, result: Option, FatalError>>, - worker_id: usize, } impl Drop for Bomb { fn drop(&mut self) { - let worker_id = self.worker_id; let msg = match self.result.take() { - Some(Ok(result)) => Message::WorkItem:: { result: Ok(result), worker_id }, + Some(Ok(result)) => Message::WorkItem:: { result: Ok(result) }, Some(Err(FatalError)) => { - Message::WorkItem:: { result: Err(Some(WorkerFatalError)), worker_id } + Message::WorkItem:: { result: Err(Some(WorkerFatalError)) } } - None => Message::WorkItem:: { result: Err(None), worker_id }, + None => Message::WorkItem:: { result: Err(None) }, }; drop(self.coordinator_send.send(Box::new(msg))); } } - let mut bomb = - Bomb:: { coordinator_send: cgcx.coordinator_send.clone(), result: None, worker_id }; + let mut bomb = Bomb:: { coordinator_send: cgcx.coordinator_send.clone(), result: None }; // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. From f462a742b3777831efc98b90b4fa36038a25317e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:59:53 +0000 Subject: [PATCH 3/6] Move LTO symbol export calculation from backends to cg_ssa --- compiler/rustc_codegen_gcc/messages.ftl | 8 -- compiler/rustc_codegen_gcc/src/back/lto.rs | 74 ++--------------- compiler/rustc_codegen_gcc/src/errors.rs | 13 --- compiler/rustc_codegen_llvm/messages.ftl | 10 --- compiler/rustc_codegen_llvm/src/back/lto.rs | 91 ++++----------------- compiler/rustc_codegen_llvm/src/errors.rs | 17 ---- compiler/rustc_codegen_ssa/messages.ftl | 10 +++ compiler/rustc_codegen_ssa/src/back/lto.rs | 83 +++++++++++++++++++ compiler/rustc_codegen_ssa/src/errors.rs | 17 ++++ 9 files changed, 132 insertions(+), 191 deletions(-) diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 55a28bc9493e7..a70ac08f01aee 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -3,12 +3,4 @@ codegen_gcc_unwinding_inline_asm = codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err} -codegen_gcc_dynamic_linking_with_lto = - cannot prefer dynamic linking when performing LTO - .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO - -codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs - -codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` - codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 9f2842d7abc8f..e075aa8480aa9 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -24,36 +24,24 @@ use std::sync::Arc; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::symbol_export; +use rustc_codegen_ssa::back::lto::{ + SerializedModule, ThinModule, ThinShared, exported_symbols_for_lto, +}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; use rustc_errors::{DiagCtxtHandle, FatalError}; -use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; -use rustc_session::config::{CrateType, Lto}; +use rustc_session::config::Lto; use rustc_target::spec::RelocModel; use tempfile::{TempDir, tempdir}; use crate::back::write::save_temp_bitcode; -use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib}; +use crate::errors::LtoBitcodeFromRlib; use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; -pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::Sdylib => true, - CrateType::Rlib | CrateType::ProcMacro => false, - } -} - struct LtoData { // TODO(antoyo): use symbols_below_threshold. //symbols_below_threshold: Vec, @@ -65,15 +53,8 @@ fn prepare_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, ) -> Result { - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; + // FIXME(bjorn3): Limit LTO exports to these symbols + let _symbols_below_threshold = exported_symbols_for_lto(cgcx, dcx)?; let tmp_path = match tempdir() { Ok(tmp_path) => tmp_path, @@ -83,20 +64,6 @@ fn prepare_lto( } }; - let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { - Some(name.clone()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - let mut symbols_below_threshold = { - let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() - }; - info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); - // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode // from the archive. @@ -105,32 +72,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - dcx.emit_err(LtoDisallowed); - return Err(FatalError); - } - if *crate_type == CrateType::Dylib && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoDylib); - return Err(FatalError); - } - } - - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(DynamicLinkingWithLTO); - return Err(FatalError); - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = - cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - { - let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold"); - symbols_below_threshold - .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); - } - + for &(_cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { let archive_data = unsafe { Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib") }; diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index b7e7343460fbf..0aa16bd88b43a 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -14,19 +14,6 @@ pub(crate) struct CopyBitcode { pub err: std::io::Error, } -#[derive(Diagnostic)] -#[diag(codegen_gcc_dynamic_linking_with_lto)] -#[note] -pub(crate) struct DynamicLinkingWithLTO; - -#[derive(Diagnostic)] -#[diag(codegen_gcc_lto_disallowed)] -pub(crate) struct LtoDisallowed; - -#[derive(Diagnostic)] -#[diag(codegen_gcc_lto_dylib)] -pub(crate) struct LtoDylib; - #[derive(Diagnostic)] #[diag(codegen_gcc_lto_bitcode_from_rlib)] pub(crate) struct LtoBitcodeFromRlib { diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index f197ea7447326..3d5f17a60345a 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -2,10 +2,6 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z au codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} -codegen_llvm_dynamic_linking_with_lto = - cannot prefer dynamic linking when performing LTO - .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO - codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture @@ -18,12 +14,6 @@ codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$na codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err}) -codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs - -codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` - -codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto` - codegen_llvm_mismatch_data_layout = data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 2dac29c4ee488..633a944005e21 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,27 +7,24 @@ use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::symbol_export; +use rustc_codegen_ssa::back::lto::{ + SerializedModule, ThinModule, ThinShared, exported_symbols_for_lto, +}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_errors::{DiagCtxtHandle, FatalError}; -use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; -use rustc_session::config::{self, CrateType, Lto}; +use rustc_session::config::{self, Lto}; use tracing::{debug, info}; use crate::back::write::{ self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, }; -use crate::errors::{ - DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro, -}; +use crate::errors::{LlvmError, LtoBitcodeFromRlib}; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, build_string}; use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; @@ -36,45 +33,19 @@ use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; /// session to determine which CGUs we can reuse. const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; -fn crate_type_allows_lto(crate_type: CrateType) -> bool { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::ProcMacro - | CrateType::Sdylib => true, - CrateType::Rlib => false, - } -} - fn prepare_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec, Vec<(SerializedModule, CString)>), FatalError> { - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; + let mut symbols_below_threshold = exported_symbols_for_lto(cgcx, dcx)? + .into_iter() + .map(|symbol| CString::new(symbol).unwrap()) + .collect::>(); - let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { - Some(CString::new(name.as_str()).unwrap()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - let mut symbols_below_threshold = { - let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() - }; - info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); + // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to + // __llvm_profile_runtime, therefore we won't know until link time if this symbol + // should have default visibility. + symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode @@ -84,37 +55,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - dcx.emit_err(LtoDisallowed); - return Err(FatalError); - } else if *crate_type == CrateType::Dylib { - if !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoDylib); - return Err(FatalError); - } - } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoProcMacro); - return Err(FatalError); - } - } - - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(DynamicLinkingWithLTO); - return Err(FatalError); - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = - cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - { - let _timer = - cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold"); - symbols_below_threshold - .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); - } - + for &(_cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { let archive_data = unsafe { Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib")) .expect("couldn't map rlib") @@ -147,10 +88,6 @@ fn prepare_lto( } } - // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to - // __llvm_profile_runtime, therefore we won't know until link time if this symbol - // should have default visibility. - symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); Ok((symbols_below_threshold, upstream_modules)) } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 31d49e8631955..2a889888a39b5 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -20,11 +20,6 @@ pub(crate) struct SymbolAlreadyDefined<'a> { #[diag(codegen_llvm_sanitizer_memtag_requires_mte)] pub(crate) struct SanitizerMemtagRequiresMte; -#[derive(Diagnostic)] -#[diag(codegen_llvm_dynamic_linking_with_lto)] -#[note] -pub(crate) struct DynamicLinkingWithLTO; - pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { @@ -41,18 +36,6 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { #[diag(codegen_llvm_autodiff_without_enable)] pub(crate) struct AutoDiffWithoutEnable; -#[derive(Diagnostic)] -#[diag(codegen_llvm_lto_disallowed)] -pub(crate) struct LtoDisallowed; - -#[derive(Diagnostic)] -#[diag(codegen_llvm_lto_dylib)] -pub(crate) struct LtoDylib; - -#[derive(Diagnostic)] -#[diag(codegen_llvm_lto_proc_macro)] -pub(crate) struct LtoProcMacro; - #[derive(Diagnostic)] #[diag(codegen_llvm_lto_bitcode_from_rlib)] pub(crate) struct LtoBitcodeFromRlib { diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index c7bd6ffd1f27c..a70d0011d161f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -35,6 +35,10 @@ codegen_ssa_dlltool_fail_import_library = {$stdout} {$stderr} +codegen_ssa_dynamic_linking_with_lto = + cannot prefer dynamic linking when performing LTO + .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO + codegen_ssa_error_calling_dlltool = Error calling dlltool '{$dlltool_path}': {$error} @@ -191,6 +195,12 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} +codegen_ssa_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs + +codegen_ssa_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` + +codegen_ssa_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto` + codegen_ssa_malformed_cgu_name = found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case). diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index b49b6783bbd9a..474a5e08baf4f 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -2,7 +2,15 @@ use std::ffi::CString; use std::sync::Arc; use rustc_data_structures::memmap::Mmap; +use rustc_errors::{DiagCtxtHandle, FatalError}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; +use rustc_session::config::{CrateType, Lto}; +use tracing::info; +use crate::back::symbol_export; +use crate::back::write::CodegenContext; +use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; pub struct ThinModule { @@ -52,3 +60,78 @@ impl SerializedModule { } } } + +fn crate_type_allows_lto(crate_type: CrateType) -> bool { + match crate_type { + CrateType::Executable + | CrateType::Dylib + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::ProcMacro + | CrateType::Sdylib => true, + CrateType::Rlib => false, + } +} + +pub fn exported_symbols_for_lto<'a, B: WriteBackendMethods>( + cgcx: &'a CodegenContext, + dcx: DiagCtxtHandle<'_>, +) -> Result, FatalError> { + // FIXME move symbol filtering to cg_ssa + let export_threshold = match cgcx.lto { + // We're just doing LTO for our one crate + Lto::ThinLocal => SymbolExportLevel::Rust, + + // We're doing LTO for the entire crate graph + Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), + + Lto::No => panic!("didn't request LTO but we're doing LTO"), + }; + + let symbol_filter = &|&(ref name, info): &'a (String, SymbolExportInfo)| { + if info.level.is_below_threshold(export_threshold) || info.used { + Some(name.as_str()) + } else { + None + } + }; + let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); + let mut symbols_below_threshold = { + let _timer = cgcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() + }; + info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); + + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, include their exported symbols. + if cgcx.lto != Lto::ThinLocal { + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + return Err(dcx.emit_almost_fatal(LtoDisallowed)); + } else if *crate_type == CrateType::Dylib { + if !cgcx.opts.unstable_opts.dylib_lto { + return Err(dcx.emit_almost_fatal(LtoDylib)); + } + } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { + return Err(dcx.emit_almost_fatal(LtoProcMacro)); + } + } + + if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { + return Err(dcx.emit_almost_fatal(DynamicLinkingWithLTO)); + } + + for &(cnum, ref _path) in cgcx.each_linked_rlib_for_lto.iter() { + let exported_symbols = + cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); + { + let _timer = cgcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + symbols_below_threshold + .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); + } + } + } + + Ok(symbols_below_threshold) +} diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 9040915b6af34..3d787d8bdbde9 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1294,3 +1294,20 @@ pub(crate) struct FeatureNotValid<'a> { #[help] pub plus_hint: bool, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_disallowed)] +pub(crate) struct LtoDisallowed; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_dylib)] +pub(crate) struct LtoDylib; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_proc_macro)] +pub(crate) struct LtoProcMacro; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_dynamic_linking_with_lto)] +#[note] +pub(crate) struct DynamicLinkingWithLTO; From 097a84f6ceaed05a04ce96916d3abaca9a75ef4b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 6 Jul 2025 15:57:20 +0000 Subject: [PATCH 4/6] Merge exported_symbols computation into exported_symbols_for_lto And move exported_symbols_for_lto call from backends to cg_ssa. --- compiler/rustc_codegen_gcc/src/back/lto.rs | 6 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 11 +-- compiler/rustc_codegen_ssa/src/back/lto.rs | 97 +++++++++++--------- compiler/rustc_codegen_ssa/src/back/write.rs | 45 ++------- 4 files changed, 69 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index e075aa8480aa9..f957bb7f101ba 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -24,9 +24,7 @@ use std::sync::Arc; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{ - SerializedModule, ThinModule, ThinShared, exported_symbols_for_lto, -}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; @@ -54,7 +52,7 @@ fn prepare_lto( dcx: DiagCtxtHandle<'_>, ) -> Result { // FIXME(bjorn3): Limit LTO exports to these symbols - let _symbols_below_threshold = exported_symbols_for_lto(cgcx, dcx)?; + let _symbols_below_threshold = &cgcx.exported_symbols_for_lto; let tmp_path = match tempdir() { Ok(tmp_path) => tmp_path, diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 633a944005e21..4abf9b6842a05 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,9 +7,7 @@ use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{ - SerializedModule, ThinModule, ThinShared, exported_symbols_for_lto, -}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; @@ -37,9 +35,10 @@ fn prepare_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec, Vec<(SerializedModule, CString)>), FatalError> { - let mut symbols_below_threshold = exported_symbols_for_lto(cgcx, dcx)? - .into_iter() - .map(|symbol| CString::new(symbol).unwrap()) + let mut symbols_below_threshold = cgcx + .exported_symbols_for_lto + .iter() + .map(|symbol| CString::new(symbol.to_owned()).unwrap()) .collect::>(); // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 474a5e08baf4f..150a588ffed7d 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -1,14 +1,15 @@ use std::ffi::CString; +use std::path::PathBuf; use std::sync::Arc; use rustc_data_structures::memmap::Mmap; -use rustc_errors::{DiagCtxtHandle, FatalError}; -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; +use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, Lto}; use tracing::info; -use crate::back::symbol_export; +use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate}; use crate::back::write::CodegenContext; use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; @@ -73,65 +74,73 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { } } -pub fn exported_symbols_for_lto<'a, B: WriteBackendMethods>( - cgcx: &'a CodegenContext, - dcx: DiagCtxtHandle<'_>, -) -> Result, FatalError> { - // FIXME move symbol filtering to cg_ssa - let export_threshold = match cgcx.lto { +pub(super) fn exported_symbols_for_lto( + tcx: TyCtxt<'_>, + each_linked_rlib_for_lto: &[(CrateNum, PathBuf)], +) -> Vec { + let export_threshold = match tcx.sess.lto() { // We're just doing LTO for our one crate Lto::ThinLocal => SymbolExportLevel::Rust, // We're doing LTO for the entire crate graph - Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), + Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()), - Lto::No => panic!("didn't request LTO but we're doing LTO"), + Lto::No => return vec![], }; - let symbol_filter = &|&(ref name, info): &'a (String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { - Some(name.as_str()) - } else { - None - } + let copy_symbols = |cnum| { + tcx.exported_non_generic_symbols(cnum) + .iter() + .chain(tcx.exported_generic_symbols(cnum)) + .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| { + if info.level.is_below_threshold(export_threshold) || info.used { + Some(symbol_name_for_instance_in_crate(tcx, s, cnum)) + } else { + None + } + }) + .collect::>() }; - let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); let mut symbols_below_threshold = { - let _timer = cgcx.prof.generic_activity("lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() + let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + copy_symbols(LOCAL_CRATE) }; info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, include their exported symbols. - if cgcx.lto != Lto::ThinLocal { - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - return Err(dcx.emit_almost_fatal(LtoDisallowed)); - } else if *crate_type == CrateType::Dylib { - if !cgcx.opts.unstable_opts.dylib_lto { - return Err(dcx.emit_almost_fatal(LtoDylib)); - } - } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { - return Err(dcx.emit_almost_fatal(LtoProcMacro)); - } + if tcx.sess.lto() != Lto::ThinLocal { + for &(cnum, ref _path) in each_linked_rlib_for_lto { + let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + symbols_below_threshold.extend(copy_symbols(cnum)); } + } - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { - return Err(dcx.emit_almost_fatal(DynamicLinkingWithLTO)); - } + symbols_below_threshold +} + +pub(super) fn check_lto_allowed(cgcx: &CodegenContext) { + if cgcx.lto == Lto::ThinLocal { + // Crate local LTO is always allowed + return; + } - for &(cnum, ref _path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = - cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - { - let _timer = cgcx.prof.generic_activity("lto_generate_symbols_below_threshold"); - symbols_below_threshold - .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); + let dcx = cgcx.create_dcx(); + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + dcx.handle().emit_fatal(LtoDisallowed); + } else if *crate_type == CrateType::Dylib { + if !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(LtoDylib); } + } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(LtoProcMacro); } } - Ok(symbols_below_threshold) + if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(DynamicLinkingWithLTO); + } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8c3d17115191a..c507255dfe1e9 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -9,7 +9,7 @@ use std::{fs, io, mem, str, thread}; use rustc_abi::Size; use rustc_ast::attr; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; @@ -20,14 +20,13 @@ use rustc_errors::{ Suggestions, }; use rustc_fs_util::link_or_copy; -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::def_id::CrateNum; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; -use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{ @@ -40,7 +39,7 @@ use tracing::debug; use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; -use super::symbol_export::symbol_name_for_instance_in_crate; +use crate::back::lto::check_lto_allowed; use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir}; use crate::traits::*; use crate::{ @@ -330,8 +329,6 @@ pub type TargetMachineFactoryFn = Arc< + Sync, >; -type ExportedSymbols = FxHashMap>>; - /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] pub struct CodegenContext { @@ -341,10 +338,10 @@ pub struct CodegenContext { pub save_temps: bool, pub fewer_names: bool, pub time_trace: bool, - pub exported_symbols: Option>, pub opts: Arc, pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, + pub exported_symbols_for_lto: Arc>, pub output_filenames: Arc, pub invocation_temp: Option, pub regular_module_config: Arc, @@ -1126,34 +1123,8 @@ fn start_executing_work( })); // Compute the set of symbols we need to retain when doing LTO (if we need to) - let exported_symbols = { - let mut exported_symbols = FxHashMap::default(); - - let copy_symbols = |cnum| { - let symbols = tcx - .exported_non_generic_symbols(cnum) - .iter() - .chain(tcx.exported_generic_symbols(cnum)) - .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl)) - .collect(); - Arc::new(symbols) - }; - - match sess.lto() { - Lto::No => None, - Lto::ThinLocal => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - Some(Arc::new(exported_symbols)) - } - Lto::Fat | Lto::Thin => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - for &(cnum, ref _path) in &each_linked_rlib_for_lto { - exported_symbols.insert(cnum, copy_symbols(cnum)); - } - Some(Arc::new(exported_symbols)) - } - } - }; + let exported_symbols_for_lto = + Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto)); // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. @@ -1189,13 +1160,13 @@ fn start_executing_work( let cgcx = CodegenContext:: { crate_types: tcx.crate_types().to_vec(), each_linked_rlib_for_lto, + exported_symbols_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, time_trace: sess.opts.unstable_opts.llvm_time_trace, opts: Arc::new(sess.opts.clone()), prof: sess.prof.clone(), - exported_symbols, remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), @@ -1463,6 +1434,8 @@ fn start_executing_work( let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); + check_lto_allowed(&cgcx); + if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); From b9501bac43691662e035a4e3c342ba3800bfa27d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 6 Jul 2025 16:06:10 +0000 Subject: [PATCH 5/6] Move exported_symbols_for_lto out of CodegenContext --- compiler/rustc_codegen_gcc/src/back/lto.rs | 3 -- compiler/rustc_codegen_gcc/src/lib.rs | 4 +++ compiler/rustc_codegen_llvm/src/back/lto.rs | 12 +++++--- compiler/rustc_codegen_llvm/src/lib.rs | 6 ++-- compiler/rustc_codegen_ssa/src/back/write.rs | 29 ++++++++++++++----- .../rustc_codegen_ssa/src/traits/write.rs | 2 ++ 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index f957bb7f101ba..d107e56fa35a9 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -51,9 +51,6 @@ fn prepare_lto( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, ) -> Result { - // FIXME(bjorn3): Limit LTO exports to these symbols - let _symbols_below_threshold = &cgcx.exported_symbols_for_lto; - let tmp_path = match tempdir() { Ok(tmp_path) => tmp_path, Err(error) => { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 98673930f9bf6..a85e631db202b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -355,6 +355,8 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + // FIXME(bjorn3): Limit LTO exports to these symbols + _exported_symbols_for_lto: &[String], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError> { @@ -367,6 +369,8 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_thin_lto( cgcx: &CodegenContext, + // FIXME(bjorn3): Limit LTO exports to these symbols + _exported_symbols_for_lto: &[String], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4abf9b6842a05..f4879c6e52745 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -33,10 +33,10 @@ const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; fn prepare_lto( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec, Vec<(SerializedModule, CString)>), FatalError> { - let mut symbols_below_threshold = cgcx - .exported_symbols_for_lto + let mut symbols_below_threshold = exported_symbols_for_lto .iter() .map(|symbol| CString::new(symbol.to_owned()).unwrap()) .collect::>(); @@ -135,11 +135,13 @@ fn get_bitcode_slice_from_object_data<'a>( /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; + let (symbols_below_threshold, upstream_modules) = + prepare_lto(cgcx, exported_symbols_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold) @@ -150,12 +152,14 @@ pub(crate) fn run_fat( /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; + let (symbols_below_threshold, upstream_modules) = + prepare_lto(cgcx, exported_symbols_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); if cgcx.opts.cg.linker_plugin_lto.enabled() { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5747e87e077ba..44c1608dca075 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -176,10 +176,11 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError> { - let mut module = back::lto::run_fat(cgcx, modules)?; + let mut module = back::lto::run_fat(cgcx, exported_symbols_for_lto, modules)?; if !diff_fncs.is_empty() { builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; @@ -193,10 +194,11 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_thin_lto( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { - back::lto::run_thin(cgcx, modules, cached_modules) + back::lto::run_thin(cgcx, exported_symbols_for_lto, modules, cached_modules) } fn optimize( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c507255dfe1e9..45e90cd072bc0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -341,7 +341,6 @@ pub struct CodegenContext { pub opts: Arc, pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, - pub exported_symbols_for_lto: Arc>, pub output_filenames: Arc, pub invocation_temp: Option, pub regular_module_config: Arc, @@ -396,13 +395,15 @@ impl CodegenContext { fn generate_thin_lto_work( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Vec<(WorkItem, u64)> { let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); let (lto_modules, copy_jobs) = - B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + B::run_thin_lto(cgcx, exported_symbols_for_lto, needs_thin_lto, import_only_modules) + .unwrap_or_else(|e| e.raise()); lto_modules .into_iter() .map(|module| { @@ -718,6 +719,7 @@ pub(crate) enum WorkItem { CopyPostLtoArtifacts(CachedModuleCodegen), /// Performs fat LTO on the given module. FatLto { + exported_symbols_for_lto: Arc>, needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, autodiff: Vec, @@ -990,6 +992,7 @@ fn execute_copy_from_cache_work_item( fn execute_fat_lto_work_item( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], mut needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, autodiff: Vec, @@ -999,7 +1002,8 @@ fn execute_fat_lto_work_item( needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) } - let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, autodiff)?; + let module = + B::run_and_optimize_fat_lto(cgcx, exported_symbols_for_lto, needs_fat_lto, autodiff)?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } @@ -1160,7 +1164,6 @@ fn start_executing_work( let cgcx = CodegenContext:: { crate_types: tcx.crate_types().to_vec(), each_linked_rlib_for_lto, - exported_symbols_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, @@ -1441,6 +1444,7 @@ fn start_executing_work( work_items.push(( WorkItem::FatLto { + exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto), needs_fat_lto, import_only_modules, autodiff: autodiff_items.clone(), @@ -1456,9 +1460,12 @@ fn start_executing_work( dcx.handle().emit_fatal(AutodiffWithoutLto {}); } - for (work, cost) in - generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules) - { + for (work, cost) in generate_thin_lto_work( + &cgcx, + &exported_symbols_for_lto, + needs_thin_lto, + import_only_modules, + ) { let insertion_index = work_items .binary_search_by_key(&cost, |&(_, cost)| cost) .unwrap_or_else(|e| e); @@ -1797,12 +1804,18 @@ fn spawn_work<'a, B: ExtraBackendMethods>( ); Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config)) } - WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => { + WorkItem::FatLto { + exported_symbols_for_lto, + needs_fat_lto, + import_only_modules, + autodiff, + } => { let _timer = cgcx .prof .generic_activity_with_arg("codegen_module_perform_lto", "everything"); execute_fat_lto_work_item( &cgcx, + &exported_symbols_for_lto, needs_fat_lto, import_only_modules, autodiff, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 3e18cce316ef1..d0b3099191a8b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -24,6 +24,7 @@ pub trait WriteBackendMethods: Clone + 'static { /// if necessary and running any further optimizations fn run_and_optimize_fat_lto( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError>; @@ -32,6 +33,7 @@ pub trait WriteBackendMethods: Clone + 'static { /// can simply be copied over from the incr. comp. cache. fn run_thin_lto( cgcx: &CodegenContext, + exported_symbols_for_lto: &[String], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError>; From 02d63c080be62785c64a5f06679fd2fe88c25a72 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 6 Jul 2025 16:59:30 +0000 Subject: [PATCH 6/6] Remove each_linked_rlib_for_lto from CodegenContext --- compiler/rustc_codegen_gcc/src/back/lto.rs | 9 +++-- compiler/rustc_codegen_gcc/src/lib.rs | 7 ++-- compiler/rustc_codegen_llvm/src/back/lto.rs | 11 +++--- compiler/rustc_codegen_llvm/src/lib.rs | 14 ++++++-- compiler/rustc_codegen_ssa/src/back/lto.rs | 5 ++- compiler/rustc_codegen_ssa/src/back/write.rs | 36 ++++++++++++++----- .../rustc_codegen_ssa/src/traits/write.rs | 4 +++ 7 files changed, 63 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index d107e56fa35a9..d558dfbc1c455 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -49,6 +49,7 @@ struct LtoData { fn prepare_lto( cgcx: &CodegenContext, + each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, ) -> Result { let tmp_path = match tempdir() { @@ -67,7 +68,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - for &(_cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + for path in each_linked_rlib_for_lto { let archive_data = unsafe { Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib") }; @@ -111,11 +112,12 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext, + each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let lto_data = prepare_lto(cgcx, dcx)?; + let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?; /*let symbols_below_threshold = lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/ fat_lto( @@ -281,12 +283,13 @@ impl ModuleBufferMethods for ModuleBuffer { /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext, + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let lto_data = prepare_lto(cgcx, dcx)?; + let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?; if cgcx.opts.cg.linker_plugin_lto.enabled() { unreachable!( "We should never reach this case if the LTO step \ diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a85e631db202b..c72ecea93caea 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -81,6 +81,7 @@ mod type_of; use std::any::Any; use std::fmt::Debug; use std::ops::Deref; +use std::path::PathBuf; #[cfg(not(feature = "master"))] use std::sync::atomic::AtomicBool; #[cfg(not(feature = "master"))] @@ -357,6 +358,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError> { @@ -364,17 +366,18 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } - back::lto::run_fat(cgcx, modules) + back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules) } fn run_thin_lto( cgcx: &CodegenContext, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { - back::lto::run_thin(cgcx, modules, cached_modules) + back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules) } fn print_pass_timings(&self) { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index f4879c6e52745..e884170902369 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::ffi::{CStr, CString}; use std::fs::File; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use std::sync::Arc; use std::{io, iter, slice}; @@ -34,6 +34,7 @@ const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; fn prepare_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec, Vec<(SerializedModule, CString)>), FatalError> { let mut symbols_below_threshold = exported_symbols_for_lto @@ -54,7 +55,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - for &(_cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + for path in each_linked_rlib_for_lto { let archive_data = unsafe { Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib")) .expect("couldn't map rlib") @@ -136,12 +137,13 @@ fn get_bitcode_slice_from_object_data<'a>( pub(crate) fn run_fat( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = - prepare_lto(cgcx, exported_symbols_for_lto, dcx)?; + prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold) @@ -153,13 +155,14 @@ pub(crate) fn run_fat( pub(crate) fn run_thin( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = - prepare_lto(cgcx, exported_symbols_for_lto, dcx)?; + prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); if cgcx.opts.cg.linker_plugin_lto.enabled() { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 44c1608dca075..20616c0b9fede 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -22,6 +22,7 @@ use std::any::Any; use std::ffi::CStr; use std::mem::ManuallyDrop; +use std::path::PathBuf; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; @@ -177,10 +178,12 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError> { - let mut module = back::lto::run_fat(cgcx, exported_symbols_for_lto, modules)?; + let mut module = + back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?; if !diff_fncs.is_empty() { builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; @@ -195,10 +198,17 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn run_thin_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { - back::lto::run_thin(cgcx, exported_symbols_for_lto, modules, cached_modules) + back::lto::run_thin( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + modules, + cached_modules, + ) } fn optimize( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 150a588ffed7d..c95038375a1bd 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -1,5 +1,4 @@ use std::ffi::CString; -use std::path::PathBuf; use std::sync::Arc; use rustc_data_structures::memmap::Mmap; @@ -76,7 +75,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { pub(super) fn exported_symbols_for_lto( tcx: TyCtxt<'_>, - each_linked_rlib_for_lto: &[(CrateNum, PathBuf)], + each_linked_rlib_for_lto: &[CrateNum], ) -> Vec { let export_threshold = match tcx.sess.lto() { // We're just doing LTO for our one crate @@ -110,7 +109,7 @@ pub(super) fn exported_symbols_for_lto( // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, include their exported symbols. if tcx.sess.lto() != Lto::ThinLocal { - for &(cnum, ref _path) in each_linked_rlib_for_lto { + for &cnum in each_linked_rlib_for_lto { let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); symbols_below_threshold.extend(copy_symbols(cnum)); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 45e90cd072bc0..158001c402651 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -20,7 +20,6 @@ use rustc_errors::{ Suggestions, }; use rustc_fs_util::link_or_copy; -use rustc_hir::def_id::CrateNum; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; @@ -340,7 +339,6 @@ pub struct CodegenContext { pub time_trace: bool, pub opts: Arc, pub crate_types: Vec, - pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, pub output_filenames: Arc, pub invocation_temp: Option, pub regular_module_config: Arc, @@ -396,14 +394,20 @@ impl CodegenContext { fn generate_thin_lto_work( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Vec<(WorkItem, u64)> { let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); - let (lto_modules, copy_jobs) = - B::run_thin_lto(cgcx, exported_symbols_for_lto, needs_thin_lto, import_only_modules) - .unwrap_or_else(|e| e.raise()); + let (lto_modules, copy_jobs) = B::run_thin_lto( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_thin_lto, + import_only_modules, + ) + .unwrap_or_else(|e| e.raise()); lto_modules .into_iter() .map(|module| { @@ -720,6 +724,7 @@ pub(crate) enum WorkItem { /// Performs fat LTO on the given module. FatLto { exported_symbols_for_lto: Arc>, + each_linked_rlib_for_lto: Vec, needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, autodiff: Vec, @@ -993,6 +998,7 @@ fn execute_copy_from_cache_work_item( fn execute_fat_lto_work_item( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], mut needs_fat_lto: Vec>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, autodiff: Vec, @@ -1002,8 +1008,13 @@ fn execute_fat_lto_work_item( needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) } - let module = - B::run_and_optimize_fat_lto(cgcx, exported_symbols_for_lto, needs_fat_lto, autodiff)?; + let module = B::run_and_optimize_fat_lto( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_fat_lto, + autodiff, + )?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } @@ -1119,11 +1130,13 @@ fn start_executing_work( let autodiff_items = autodiff_items.to_vec(); let mut each_linked_rlib_for_lto = Vec::new(); + let mut each_linked_rlib_file_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { if link::ignored_for_lto(sess, crate_info, cnum) { return; } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + each_linked_rlib_for_lto.push(cnum); + each_linked_rlib_file_for_lto.push(path.to_path_buf()); })); // Compute the set of symbols we need to retain when doing LTO (if we need to) @@ -1163,7 +1176,6 @@ fn start_executing_work( let cgcx = CodegenContext:: { crate_types: tcx.crate_types().to_vec(), - each_linked_rlib_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, @@ -1436,6 +1448,8 @@ fn start_executing_work( let needs_fat_lto = mem::take(&mut needs_fat_lto); let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); + let each_linked_rlib_file_for_lto = + mem::take(&mut each_linked_rlib_file_for_lto); check_lto_allowed(&cgcx); @@ -1445,6 +1459,7 @@ fn start_executing_work( work_items.push(( WorkItem::FatLto { exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto), + each_linked_rlib_for_lto: each_linked_rlib_file_for_lto, needs_fat_lto, import_only_modules, autodiff: autodiff_items.clone(), @@ -1463,6 +1478,7 @@ fn start_executing_work( for (work, cost) in generate_thin_lto_work( &cgcx, &exported_symbols_for_lto, + &each_linked_rlib_file_for_lto, needs_thin_lto, import_only_modules, ) { @@ -1806,6 +1822,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( } WorkItem::FatLto { exported_symbols_for_lto, + each_linked_rlib_for_lto, needs_fat_lto, import_only_modules, autodiff, @@ -1816,6 +1833,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( execute_fat_lto_work_item( &cgcx, &exported_symbols_for_lto, + &each_linked_rlib_for_lto, needs_fat_lto, import_only_modules, autodiff, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index d0b3099191a8b..8e78cbe1963bf 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; @@ -25,6 +27,7 @@ pub trait WriteBackendMethods: Clone + 'static { fn run_and_optimize_fat_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, diff_fncs: Vec, ) -> Result, FatalError>; @@ -34,6 +37,7 @@ pub trait WriteBackendMethods: Clone + 'static { fn run_thin_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError>;