From 39a66e757920dfc94fe7dcb1b026ec18af0c6405 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:09:13 +0000 Subject: [PATCH 1/6] Remove unused method --- compiler/rustc_middle/src/dep_graph/graph.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index dcb5775f20595..f93b80cc6c95c 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -861,12 +861,6 @@ impl DepGraph { self.data.as_ref().and_then(|data| data.previous_work_products.get(v).cloned()) } - /// Access the map of work-products created during the cached run. Only - /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> &WorkProductMap { - &self.data.as_ref().unwrap().previous_work_products - } - pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool { self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) } From dc513a511a7c7569d5db790d538bdf0e8f6d8243 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 25 Sep 2026 09:47:37 +0000 Subject: [PATCH 2/6] Move some LLVM specific options from Linker::link to cg_llvm --- .../src/back/llvm_backend.rs | 39 +++++++++++++------ .../rustc_codegen_ssa/src/traits/backend.rs | 8 ---- compiler/rustc_interface/src/queries.rs | 24 ------------ 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 8780af2f6d595..7fa7086d57456 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -377,21 +377,36 @@ impl CodegenBackend for LlvmCodegenBackend { }); } - (compiled_modules, work_products) - } + if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { + let timings = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); + print!("{timings}"); + } - fn print_pass_timings(&self) { - let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); - print!("{timings}"); - } + if sess.print_llvm_stats() { + let stats = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); + print!("{stats}"); + } - fn print_statistics(&self) { - let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); - print!("{stats}"); - } + if let Some(out_path) = sess.print_llvm_stats_json() { + let llvm_stats_json = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap(); - fn print_statistics_json(&self) -> String { - llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap() + if !llvm_stats_json.is_empty() { + if let Err(e) = std::fs::write(&out_path, llvm_stats_json) { + sess.dcx().err(format!("failed to write stats to {}: {}", out_path, e)); + } + } else { + sess.dcx().warn(format!( + "requested to print LLVM statistics to JSON file {}, but the codegen backend \ + did not provide any statistics", + out_path, + )); + } + } + + (compiled_modules, work_products) } fn link( diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e06ac36fd758a..bf5d67c213573 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -120,14 +120,6 @@ pub trait CodegenBackend { crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap); - fn print_pass_timings(&self) {} - - fn print_statistics(&self) {} - - fn print_statistics_json(&self) -> String { - String::new() - } - /// This is called on the returned [`CompiledModules`] from [`join_codegen`](Self::join_codegen). fn link( &self, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 759297bc69592..94cc1484586c8 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -67,30 +67,6 @@ impl Linker { } }); - if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { - codegen_backend.print_pass_timings() - } - - if sess.print_llvm_stats() { - codegen_backend.print_statistics() - } - - if let Some(out_path) = sess.print_llvm_stats_json() { - let llvm_stats_json = codegen_backend.print_statistics_json(); - - if !llvm_stats_json.is_empty() { - if let Err(e) = std::fs::write(&out_path, llvm_stats_json) { - sess.dcx().err(format!("failed to write stats to {}: {}", out_path, e)); - } - } else { - sess.dcx().warn(format!( - "requested to print LLVM statistics to JSON file {}, but the codegen backend \ - did not provide any statistics", - out_path, - )); - } - } - sess.timings.end_section(sess.dcx(), TimingSection::Codegen); if sess.opts.incremental.is_some() From 2b7a3ab6b282c0fb66173e367f7d521e4126cc30 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 30 Sep 2026 13:47:13 +0200 Subject: [PATCH 3/6] Apply suggestion Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com> --- compiler/rustc_codegen_llvm/src/back/llvm_backend.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 7fa7086d57456..e8bf1cf9a6f45 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -395,13 +395,12 @@ impl CodegenBackend for LlvmCodegenBackend { if !llvm_stats_json.is_empty() { if let Err(e) = std::fs::write(&out_path, llvm_stats_json) { - sess.dcx().err(format!("failed to write stats to {}: {}", out_path, e)); + sess.dcx().err(format!("failed to write stats to {out_path}: {e}")); } } else { sess.dcx().warn(format!( - "requested to print LLVM statistics to JSON file {}, but the codegen backend \ + "requested to print LLVM statistics to JSON file {out_path}, but the codegen backend \ did not provide any statistics", - out_path, )); } } From e243b6db3ffc86c98b482d8a9266cddd2dae664c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:38:04 +0000 Subject: [PATCH 4/6] Move incr comp session dirs out of CodegenContext CodegenContext will in the future be serialized and deserialized into a different rustc instance when -Zno-link/-Zlink-only is used. A separate incr comp session has to be used for both rustc invocations. --- .../rustc_codegen_cranelift/src/driver/aot.rs | 3 +- compiler/rustc_codegen_gcc/src/lib.rs | 5 +- .../src/back/llvm_backend.rs | 6 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 23 +++--- compiler/rustc_codegen_ssa/src/back/write.rs | 77 ++++++++++++------- .../rustc_codegen_ssa/src/traits/write.rs | 3 +- compiler/rustc_incremental/src/persist/fs.rs | 2 +- compiler/rustc_session/src/session.rs | 39 ++++++++++ 8 files changed, 115 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index a62dff641cfec..463b2d5cb2a37 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -22,8 +22,8 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; -use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, OutputType}; +use rustc_session::{BorrowedIncrCompSession, Session}; use rustc_span::Symbol; use crate::base::CodegenedFunction; @@ -337,6 +337,7 @@ impl WriteBackendMethods for AotDriver { fn run_thin_lto( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, + _incr_comp_session: Option<&BorrowedIncrCompSession>, _dcx: rustc_errors::DiagCtxtHandle<'_>, _exported_symbols_for_lto: &[String], _each_linked_rlib_for_lto: &[PathBuf], diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index b82d05dc211aa..59585c520a6cd 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -93,7 +93,9 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OptLevel, OutputFilenames}; -use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; +use rustc_session::{ + BorrowedIncrCompSession, CodegenBackendInit, EarlySession, IncrCompSession, Session, +}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TargetTuple}; use tempfile::TempDir; @@ -404,6 +406,7 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_thin_lto( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, + _incr_comp_session: Option<&BorrowedIncrCompSession>, _dcx: DiagCtxtHandle<'_>, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index e8bf1cf9a6f45..14309aa345150 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -20,7 +20,9 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; +use rustc_session::{ + BorrowedIncrCompSession, CodegenBackendInit, EarlySession, IncrCompSession, Session, +}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; @@ -120,6 +122,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn run_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -128,6 +131,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::lto::run_thin( cgcx, prof, + incr_comp_session, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index d52e34d30fb7d..29be4b970dfaf 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -19,7 +19,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::WorkProduct; -use rustc_session::config; +use rustc_session::{BorrowedIncrCompSession, config}; use rustc_span::bug; use rustc_structures::SanitizerSet; use tracing::{debug, info}; @@ -184,6 +184,7 @@ pub(crate) fn run_fat( pub(crate) fn run_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -199,7 +200,7 @@ pub(crate) fn run_thin( is deferred to the linker" ); } - thin_lto(cgcx, prof, dcx, modules, upstream_modules, &symbols_below_threshold) + thin_lto(prof, incr_comp_session, dcx, modules, upstream_modules, &symbols_below_threshold) } fn fat_lto( @@ -371,8 +372,8 @@ fn fat_lto( /// all of the `LtoModuleCodegen` units returned below and destroyed once /// they all go out of scope. fn thin_lto( - cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, modules: Vec>, serialized_modules: Vec<(SerializedModule, CString)>, @@ -463,13 +464,12 @@ fn thin_lto( info!("thin LTO data created"); - let new_key_map_path = cgcx - .new_incr_comp_session_dir - .as_ref() - .map(|dir| dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME)); + let new_key_map_path = incr_comp_session.as_ref().map(|incr_comp_session| { + incr_comp_session.new_session_directory.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME) + }); - let prev_key_map = if let Some(ref old_incr_comp_session_dir) = - cgcx.old_incr_comp_session_dir + let prev_key_map = if let Some(ref old_incr_comp_session_dir) = incr_comp_session + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.as_deref()) { let old_path = old_incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); @@ -481,7 +481,7 @@ fn thin_lto( assert!(green_modules.is_empty()); None }; - let curr_key_map = if cgcx.new_incr_comp_session_dir.is_some() { + let curr_key_map = if incr_comp_session.is_some() { ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names) } else { assert!(green_modules.is_empty()); @@ -506,8 +506,7 @@ fn thin_lto( if let (Some(prev_key_map), true) = (prev_key_map.as_ref(), green_modules.contains_key(module_name)) { - assert!(cgcx.old_incr_comp_session_dir.is_some()); - assert!(cgcx.new_incr_comp_session_dir.is_some()); + assert!(incr_comp_session.unwrap().old_session_directory.is_some()); // If a module exists in both the current and the previous session, // and has the same LTO cache key in both sessions, then we can re-use it diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 04be7956e8278..1b659ea9974d4 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::{ self, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, }; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{BorrowedIncrCompSession, IncrCompSession, Session}; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, InnerSpan, Span, SyntaxContext, bug}; use rustc_structures::CrateType; @@ -348,12 +348,6 @@ pub struct CodegenContext { /// Directory into which should the LLVM optimization remarks be written. /// If `None`, they will be written to stderr. pub remark_dir: Option, - /// The previous incremental compilation session directory, or None if we - /// are not compiling incrementally or there is no previous session. - pub old_incr_comp_session_dir: Option, - /// The incremental compilation session directory, or None if we are not - /// compiling incrementally - pub new_incr_comp_session_dir: Option, /// `Some(limit)` if the codegen should be run in parallel. /// /// Depends on [`WriteBackendMethods::supports_parallel()`] and `--jobs-backend`. @@ -363,6 +357,7 @@ pub struct CodegenContext { fn generate_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -373,6 +368,7 @@ fn generate_thin_lto_work( let (lto_modules, copy_jobs) = B::run_thin_lto( cgcx, prof, + incr_comp_session, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, @@ -819,6 +815,7 @@ pub(crate) fn compute_per_cgu_lto_type( fn execute_optimize_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, + new_incr_comp_session_dir: Option<&Path>, shared_emitter: SharedEmitter, mut module: ModuleCodegen, ) -> WorkItemResult { @@ -838,7 +835,7 @@ fn execute_optimize_work_item( // save our module to disk first. let bitcode = if cgcx.module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); - cgcx.new_incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) + new_incr_comp_session_dir.map(|path| path.join(&filename)) } else { None }; @@ -876,6 +873,7 @@ fn execute_optimize_work_item( fn execute_copy_from_cache_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, + old_incr_comp_session_dir: &Path, shared_emitter: SharedEmitter, module: CachedModuleCodegen, ) -> CompiledModule { @@ -885,10 +883,8 @@ fn execute_copy_from_cache_work_item( let dcx = DiagCtxt::new(Box::new(shared_emitter)); let dcx = dcx.handle(); - let incr_comp_session_dir = cgcx.old_incr_comp_session_dir.as_ref().unwrap(); - let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { - let source_file_in_incr_comp_dir = incr_comp_session_dir.join(saved_path); + let source_file_in_incr_comp_dir = old_incr_comp_session_dir.join(saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", module.name, @@ -988,6 +984,7 @@ fn do_fat_lto( fn do_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], @@ -1031,6 +1028,7 @@ fn do_thin_lto( for (i, (work, cost)) in generate_thin_lto_work::( cgcx, prof, + incr_comp_session.as_ref(), dcx, &exported_symbols_for_lto, &each_linked_rlib_for_lto, @@ -1080,6 +1078,12 @@ fn do_thin_lto( spawn_thin_lto_work( &cgcx, prof, + incr_comp_session + .as_ref() + .and_then(|incr_comp_session| { + incr_comp_session.old_session_directory.as_deref() + }) + .map(ToOwned::to_owned), shared_emitter.clone(), Arc::clone(&tm_factory), coordinator_send.clone(), @@ -1213,6 +1217,8 @@ fn start_executing_work( ) -> thread::JoinHandle, ()>> { let sess = tcx.sess; let prof = sess.prof.clone(); + let incr_comp_session = + tcx.incr_comp_session.map(|incr_comp_session| incr_comp_session.borrow()); // Compute the set of symbols we need to retain when doing thin local LTO (if we need to) let exported_symbols_for_lto = @@ -1265,15 +1271,6 @@ fn start_executing_work( time_trace: sess.opts.unstable_opts.llvm_time_trace, remark: sess.opts.cg.remark.clone(), remark_dir, - old_incr_comp_session_dir: tcx - .incr_comp_session - .as_ref() - .and_then(|incr_comp_session| incr_comp_session.old_session_directory.as_deref()) - .map(ToOwned::to_owned), - new_incr_comp_session_dir: tcx - .incr_comp_session - .as_ref() - .map(|incr_comp_session| (&*incr_comp_session.new_session_directory).to_owned()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, @@ -1512,6 +1509,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1538,6 +1536,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1582,6 +1581,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1750,6 +1750,7 @@ fn start_executing_work( compiled_modules.extend(do_thin_lto::( &cgcx, &prof, + incr_comp_session, shared_emitter.clone(), tm_factory, &exported_symbols_for_lto, @@ -1847,6 +1848,7 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: WriteBackendMethods>( cgcx: &CodegenContext, prof: &'a SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, shared_emitter: SharedEmitter, coordinator_send: Sender>, llvm_start_time: &mut Option>, @@ -1857,6 +1859,11 @@ fn spawn_work<'a, B: WriteBackendMethods>( *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); } + let old_incr_comp_session_dir = incr_comp_session + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.clone()); + let new_incr_comp_session_dir = + incr_comp_session.map(|incr_comp_session| incr_comp_session.new_session_directory.clone()); + let cgcx = cgcx.clone(); let prof = prof.clone(); @@ -1865,10 +1872,22 @@ fn spawn_work<'a, B: WriteBackendMethods>( let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m), - WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( - execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m), + WorkItem::Optimize(m) => execute_optimize_work_item( + &cgcx, + &prof, + new_incr_comp_session_dir.as_deref(), + shared_emitter, + m, ), + WorkItem::CopyPostLtoArtifacts(m) => { + WorkItemResult::Finished(execute_copy_from_cache_work_item( + &cgcx, + &prof, + old_incr_comp_session_dir.as_deref().unwrap(), + shared_emitter, + m, + )) + } })); let msg = match result { @@ -1895,6 +1914,7 @@ fn spawn_work<'a, B: WriteBackendMethods>( fn spawn_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, + old_incr_comp_session_dir: Option, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, coordinator_send: Sender, @@ -1909,9 +1929,13 @@ fn spawn_thin_lto_work( let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { - execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m) - } + ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item( + &cgcx, + &prof, + old_incr_comp_session_dir.as_deref().unwrap(), + shared_emitter, + m, + ), ThinLtoWorkItem::ThinLto(m) => { let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()); B::optimize_and_codegen_thin(&cgcx, &prof, &shared_emitter, tm_factory, m) @@ -2139,6 +2163,7 @@ impl OngoingCodegen { modules: do_thin_lto::( &cgcx, &sess.prof, + incr_comp_session.map(|incr_comp_session| incr_comp_session.borrow()), shared_emitter, tm_factory, &crate_info.exported_symbols_for_lto, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index bb63e189d20d3..4c0df97a41985 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; -use rustc_session::{Session, config}; +use rustc_session::{BorrowedIncrCompSession, Session, config}; use crate::back::lto::ThinModule; use crate::back::write::{ @@ -50,6 +50,7 @@ pub trait WriteBackendMethods: Clone + 'static { fn run_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 3f896584eaef2..038bddaa021d2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -268,7 +268,7 @@ pub(crate) fn prepare_session_directory( None }; - IncrCompSession { old_session_directory, new_session_directory } + IncrCompSession::new(old_session_directory, new_session_directory) } /// This function finalizes and thus 'publishes' the session directory by diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f068aaa584dce..d86ee5e5a4898 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1867,6 +1867,45 @@ pub struct IncrCompSession { /// The directory to which cached data for the current session can be /// written to. pub new_session_directory: flock::LockedDir, + borrows: Arc<()>, +} + +impl IncrCompSession { + pub fn new( + old_session_directory: Option, + new_session_directory: flock::LockedDir, + ) -> Self { + IncrCompSession { old_session_directory, new_session_directory, borrows: Arc::new(()) } + } + + pub fn borrow(&self) -> BorrowedIncrCompSession { + BorrowedIncrCompSession { + old_session_directory: self.old_session_directory.as_deref().map(ToOwned::to_owned), + new_session_directory: (&*self.new_session_directory).to_owned(), + _borrows: Arc::clone(&self.borrows), + } + } +} + +impl Drop for IncrCompSession { + fn drop(&mut self) { + // Check that there are no worker threads remaining that use the incr + // comp session before we unlock the old and new session dir. If there + // does exist a worker thread, there is not much we can do, but at + // least we will unconditionally complain rather than the worker thread + // sometimes crashing depending on what other rustc instances run. + assert!( + Arc::strong_count(&self.borrows) == 1, + "Incr comp session dropped while there are still references", + ); + } +} + +/// A runtime tracked borrow of the incr comp session. Can be sent to worker threads. +pub struct BorrowedIncrCompSession { + pub old_session_directory: Option, + pub new_session_directory: PathBuf, + _borrows: Arc<()>, } /// A wrapper around an [`DiagCtxt`] that is used for early error emissions. From dfb328156dc809ea64a90c2fec4a42d746d82121 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:17:05 +0000 Subject: [PATCH 5/6] Improve work product tracking for fat LTO Previously we would copy post LTO artifacts into the incr comp cache for fat LTO despite them never getting used. Also the pre LTO bitcode didn't get tracked and thus determine_cgu_reuse believed it had to regenerate bitcode for all CGUs even when all CGUs would be green. --- compiler/rustc_codegen_gcc/src/back/lto.rs | 6 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 6 +- compiler/rustc_codegen_ssa/src/back/write.rs | 87 ++++++++++++++------ 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index baf1fda02e258..28cabc36dbe63 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -148,11 +148,11 @@ fn fat_lto( for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), - FatLtoInput::Serialized { name, bitcode_path } => { - info!("pushing serialized module {:?}", name); + FatLtoInput::Serialized { wp, bitcode_path } => { + info!("pushing serialized module {:?}", wp.cgu_name); serialized_modules.push(( SerializedModule::from_file(&bitcode_path), - CString::new(name).unwrap(), + CString::new(wp.cgu_name).unwrap(), )); } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 29be4b970dfaf..309451b7e9772 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -226,11 +226,11 @@ fn fat_lto( for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), - FatLtoInput::Serialized { name, bitcode_path } => { - info!("pushing serialized module {:?}", name); + FatLtoInput::Serialized { wp, bitcode_path } => { + info!("pushing serialized module {:?}", wp.cgu_name); serialized_modules.push(( SerializedModule::from_file(&bitcode_path), - CString::new(name).unwrap(), + CString::new(wp.cgu_name).unwrap(), )); } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1b659ea9974d4..b9b5c7ae36b19 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -9,6 +9,7 @@ use std::{assert_matches, fs, io, str, thread}; use rustc_abi::Size; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; +use rustc_data_structures::unord::UnordMap; use rustc_errors::emitter::Emitter; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagInner, FatalError, FatalErrorMarker, Level, @@ -20,7 +21,7 @@ use rustc_incremental::{ }; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; -use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ self, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, @@ -769,7 +770,7 @@ pub(crate) enum WorkItemResult { } pub enum FatLtoInput { - Serialized { name: String, bitcode_path: PathBuf }, + Serialized { wp: WorkProduct, bitcode_path: PathBuf }, InMemory(ModuleCodegen), } @@ -861,7 +862,13 @@ fn execute_optimize_work_item( panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { - name: module.name, + wp: WorkProduct { + cgu_name: module.name.clone(), + saved_files: UnordMap::from_iter([( + PRE_LTO_BC_EXT.to_owned(), + pre_lto_bitcode_filename(&module.name), + )]), + }, bitcode_path: path, }) } @@ -1734,7 +1741,7 @@ fn start_executing_work( } for (bitcode_path, wp) in lto_import_only_modules { - needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, bitcode_path }) + needs_fat_lto.push(FatLtoInput::Serialized { wp, bitcode_path }) } return Ok(MaybeLtoModules::FatLto { cgcx, needs_fat_lto }); @@ -2135,31 +2142,54 @@ impl OngoingCodegen { let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); // Catch fatal errors to ensure shared_emitter_main.check() can emit the actual diagnostics - let compiled_modules = catch_fatal_errors(|| match maybe_lto_modules { + let compilation_output = catch_fatal_errors(|| match maybe_lto_modules { MaybeLtoModules::NoLto(compiled_modules) => { drop(shared_emitter); - compiled_modules + + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess, + incr_comp_session, + &compiled_modules, + ); + + (compiled_modules, work_products) } MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); - CompiledModules { - modules: vec![do_fat_lto( - sess, - &cgcx, - shared_emitter, - tm_factory, - &crate_info.exported_symbols_for_lto, - &crate_info.each_linked_rlib_file_for_lto, - needs_fat_lto, - )], - allocator_module: None, + let mut work_products = WorkProductMap::default(); + if sess.opts.incremental.is_some() { + for module in &needs_fat_lto { + match module { + FatLtoInput::Serialized { wp, bitcode_path: _ } => { + work_products + .insert(WorkProductId::from_cgu_name(&wp.cgu_name), wp.clone()); + } + FatLtoInput::InMemory(_) => {} + } + } } + + ( + CompiledModules { + modules: vec![do_fat_lto( + sess, + &cgcx, + shared_emitter, + tm_factory, + &crate_info.exported_symbols_for_lto, + &crate_info.each_linked_rlib_file_for_lto, + needs_fat_lto, + )], + allocator_module: None, + }, + work_products, + ) } MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); - CompiledModules { + let compiled_modules = CompiledModules { modules: do_thin_lto::( &cgcx, &sess.prof, @@ -2172,7 +2202,17 @@ impl OngoingCodegen { sess.opts.recommended_stack_size, ), allocator_module: None, - } + }; + + // FIXME include pre-LTO bitcode in workproduct tracking + // FIXME add separate incr comp session for post-LTO outputs to use during link step + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess, + incr_comp_session, + &compiled_modules, + ); + + (compiled_modules, work_products) } }); @@ -2180,19 +2220,14 @@ impl OngoingCodegen { sess.dcx().abort_if_errors(); - let mut compiled_modules = - compiled_modules.expect("fatal error emitted but not sent to SharedEmitter"); + let (mut compiled_modules, work_products) = + compilation_output.expect("fatal error emitted but not sent to SharedEmitter"); // Regardless of what order these modules completed in, report them to // the backend in the same order every time to ensure that we're handing // out deterministic results. compiled_modules.modules.sort_by(|a, b| a.name.cmp(&b.name)); - let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( - sess, - incr_comp_session, - &compiled_modules, - ); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); (compiled_modules, work_products) From 5951d04eba1f8f56fc01ba6d0ae1449770b07212 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 25 Sep 2026 12:06:29 +0000 Subject: [PATCH 6/6] Add test for incr comp fat LTO and fix assert_module_sources testing --- .../rustc_codegen_ssa/src/assert_module_sources.rs | 2 +- tests/incremental/lto.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 431783552bcd0..792ab9873378a 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -111,7 +111,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !self.check_config(cfg) { debug!("check_attr: config does not match, ignoring attr"); - return; + continue; } let user_path = module.as_str(); diff --git a/tests/incremental/lto.rs b/tests/incremental/lto.rs index ddeddf95f8aee..7e3c5f2f42bf6 100644 --- a/tests/incremental/lto.rs +++ b/tests/incremental/lto.rs @@ -1,11 +1,20 @@ //@ no-prefer-dynamic //@ revisions:rpass1 rpass2 -//@ compile-flags: -C lto +//@ compile-flags: -Z query-dep-graph -C lto=fat //@ ignore-backends: gcc +#![feature(rustc_attrs)] +#![rustc_partition_codegened(module = "lto", cfg = "rpass1")] +#![rustc_partition_codegened(module = "lto-x", cfg = "rpass1")] +#![rustc_partition_codegened(module = "lto-y", cfg = "rpass1")] +#![rustc_partition_reused(module = "lto", cfg = "rpass2")] +#![rustc_partition_codegened(module = "lto-x", cfg = "rpass2")] +#![rustc_partition_reused(module = "lto-y", cfg = "rpass2")] + mod x { pub struct X { - x: u32, y: u32, + x: u32, + y: u32, } #[cfg(rpass1)]