diff options
Diffstat (limited to 'compiler/rustc_session/src/cgu_reuse_tracker.rs')
-rw-r--r-- | compiler/rustc_session/src/cgu_reuse_tracker.rs | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs new file mode 100644 index 000000000..dd64e8ab7 --- /dev/null +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -0,0 +1,118 @@ +//! Some facilities for tracking how codegen-units are reused during incremental +//! compilation. This is used for incremental compilation tests and debug +//! output. + +use rustc_data_structures::fx::FxHashMap; +use rustc_span::{Span, Symbol}; +use std::sync::{Arc, Mutex}; +use tracing::debug; + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub enum CguReuse { + No, + PreLto, + PostLto, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ComparisonKind { + Exact, + AtLeast, +} + +struct TrackerData { + actual_reuse: FxHashMap<String, CguReuse>, + expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>, +} + +// Span does not implement `Send`, so we can't just store it in the shared +// `TrackerData` object. Instead of splitting up `TrackerData` into shared and +// non-shared parts (which would be complicated), we just mark the `Span` here +// explicitly as `Send`. That's safe because the span data here is only ever +// accessed from the main thread. +struct SendSpan(Span); +unsafe impl Send for SendSpan {} + +#[derive(Clone)] +pub struct CguReuseTracker { + data: Option<Arc<Mutex<TrackerData>>>, +} + +impl CguReuseTracker { + pub fn new() -> CguReuseTracker { + let data = + TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() }; + + CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) } + } + + pub fn new_disabled() -> CguReuseTracker { + CguReuseTracker { data: None } + } + + pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) { + if let Some(ref data) = self.data { + debug!("set_actual_reuse({cgu_name:?}, {kind:?})"); + + let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind); + + if let Some(prev_reuse) = prev_reuse { + // The only time it is legal to overwrite reuse state is when + // we discover during ThinLTO that we can actually reuse the + // post-LTO version of a CGU. + assert_eq!(prev_reuse, CguReuse::PreLto); + } + } + } + + pub fn set_expectation( + &self, + cgu_name: Symbol, + cgu_user_name: &str, + error_span: Span, + expected_reuse: CguReuse, + comparison_kind: ComparisonKind, + ) { + if let Some(ref data) = self.data { + debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})"); + let mut data = data.lock().unwrap(); + + data.expected_reuse.insert( + cgu_name.to_string(), + (cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind), + ); + } + } + + pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) { + if let Some(ref data) = self.data { + let data = data.lock().unwrap(); + + for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in + &data.expected_reuse + { + if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) { + let (error, at_least) = match comparison_kind { + ComparisonKind::Exact => (expected_reuse != actual_reuse, false), + ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true), + }; + + if error { + let at_least = if at_least { "at least " } else { "" }; + let msg = format!( + "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \ + should be {at_least}`{expected_reuse:?}`" + ); + diag.span_err(error_span.0, &msg); + } + } else { + let msg = format!( + "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \ + not recorded" + ); + diag.span_fatal(error_span.0, &msg) + } + } + } + } +} |