diff --git a/src/librustc/dep_graph/cgu_reuse_tracker.rs b/src/librustc/dep_graph/cgu_reuse_tracker.rs new file mode 100644 index 00000000000..2a0891787f8 --- /dev/null +++ b/src/librustc/dep_graph/cgu_reuse_tracker.rs @@ -0,0 +1,145 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Some facilities for tracking how codegen-units are reused during incremental +//! compilition. This is used for incremental compiliation tests and debug +//! output. + +use session::Session; +use rustc_data_structures::fx::FxHashMap; +use std::sync::{Arc, Mutex}; +use syntax_pos::Span; + +#[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, + expected_reuse: FxHashMap, +} + +// 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>>, +} + +impl CguReuseTracker { + pub fn new() -> CguReuseTracker { + let data = TrackerData { + actual_reuse: FxHashMap(), + expected_reuse: FxHashMap(), + }; + + 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: &str, + 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, sess: &Session) { + 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 `{}` is `{:?}` but \ + should be {}`{:?}`", + cgu_user_name, + actual_reuse, + at_least, + expected_reuse); + sess.span_err(error_span.0, &msg); + } + } else { + let msg = format!("CGU-reuse for `{}` (mangled: `{}`) was \ + not recorded", + cgu_user_name, + cgu_name); + sess.span_fatal(error_span.0, &msg); + } + } + } + } +} diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index 8a6f66911ec..158edc6c59e 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -16,6 +16,7 @@ mod query; mod safe; mod serialized; +pub mod cgu_reuse_tracker; pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, label_strs}; diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs index 6e5134c3c05..a23bab6226e 100644 --- a/src/librustc/ich/mod.rs +++ b/src/librustc/ich/mod.rs @@ -30,16 +30,7 @@ pub const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; pub const ATTR_PARTITION_REUSED: &'static str = "rustc_partition_reused"; pub const ATTR_PARTITION_CODEGENED: &'static str = "rustc_partition_codegened"; - - -pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ - ATTR_IF_THIS_CHANGED, - ATTR_THEN_THIS_WOULD_NEED, - ATTR_DIRTY, - ATTR_CLEAN, - ATTR_PARTITION_REUSED, - ATTR_PARTITION_CODEGENED, -]; +pub const ATTR_EXPECTED_CGU_REUSE: &'static str = "rustc_expected_cgu_reuse"; pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ "cfg", @@ -49,4 +40,5 @@ ATTR_CLEAN, ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED, + ATTR_EXPECTED_CGU_REUSE, ]; diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 619262abb0b..e749d0338d0 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -11,6 +11,7 @@ pub use self::code_stats::{DataTypeKind, SizeKind, FieldInfo, VariantInfo}; use self::code_stats::CodeStats; +use dep_graph::cgu_reuse_tracker::CguReuseTracker; use hir::def_id::CrateNum; use rustc_data_structures::fingerprint::Fingerprint; @@ -124,6 +125,9 @@ pub struct Session { pub imported_macro_spans: OneThread>>, incr_comp_session: OneThread>, + /// Used for incremental compilation tests. Will only be populated if + /// `-Zquery-dep-graph` is specified. + pub cgu_reuse_tracker: CguReuseTracker, /// Used by -Z profile-queries in util::common pub profile_channel: Lock>>, @@ -1109,6 +1113,12 @@ pub fn build_session_( }; let working_dir = file_path_mapping.map_prefix(working_dir); + let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { + CguReuseTracker::new() + } else { + CguReuseTracker::new_disabled() + }; + let sess = Session { target: target_cfg, host, @@ -1139,6 +1149,7 @@ pub fn build_session_( injected_panic_runtime: Once::new(), imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), + cgu_reuse_tracker, self_profiling: Lock::new(SelfProfiler::new()), profile_channel: Lock::new(None), perf_stats: PerfStats { diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 364b469738f..3ac22f4eaef 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -18,6 +18,7 @@ use llvm; use memmap; use rustc::dep_graph::WorkProduct; +use rustc::dep_graph::cgu_reuse_tracker::CguReuse; use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::exported_symbols::SymbolExportLevel; use rustc::session::config::{self, Lto}; @@ -538,6 +539,8 @@ fn thin_lto(cgcx: &CodegenContext, let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); + cgcx.cgu_reuse_tracker.set_actual_reuse(module_name, + CguReuse::PostLto); continue } } diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 7b78d4fb4ff..447b505e79c 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -21,6 +21,7 @@ use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess}; use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; +use rustc::dep_graph::cgu_reuse_tracker::CguReuseTracker; use rustc::middle::cstore::EncodedMetadata; use rustc::session::config::{self, OutputFilenames, OutputType, Passes, Sanitizer, Lto}; use rustc::session::Session; @@ -377,6 +378,8 @@ pub struct CodegenContext { // The incremental compilation session directory, or None if we are not // compiling incrementally pub incr_comp_session_dir: Option, + // Used to update CGU re-use information during the thinlto phase. + pub cgu_reuse_tracker: CguReuseTracker, // Channel back to the main control thread to send messages to coordinator_send: Sender>, // A reference to the TimeGraph so we can register timings. None means that @@ -1607,6 +1610,7 @@ fn start_executing_work(tcx: TyCtxt, remark: sess.opts.cg.remark.clone(), worker: 0, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(), coordinator_send, diag_emitter: shared_emitter.clone(), time_graph, @@ -2390,6 +2394,8 @@ pub(crate) fn join( } }; + sess.cgu_reuse_tracker.check_expected_reuse(sess); + sess.abort_if_errors(); if let Some(time_graph) = self.time_graph { diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index c1f6006e684..55dc43ec1fb 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -32,6 +32,7 @@ use back::write::{self, OngoingCodegen}; use llvm::{self, TypeKind, get_param}; use metadata; +use rustc::dep_graph::cgu_reuse_tracker::CguReuse; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::middle::lang_items::StartFnLangItem; use rustc::middle::weak_lang_items; @@ -697,25 +698,18 @@ pub fn iter_globals(llmod: &'ll llvm::Module) -> ValueIter<'ll> { } } -#[derive(Debug)] -enum CguReUsable { - PreLto, - PostLto, - No -} - fn determine_cgu_reuse<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cgu: &CodegenUnit<'tcx>) - -> CguReUsable { + -> CguReuse { if !tcx.dep_graph.is_fully_enabled() { - return CguReUsable::No + return CguReuse::No } let work_product_id = &cgu.work_product_id(); if tcx.dep_graph.previous_work_product(work_product_id).is_none() { // We don't have anything cached for this CGU. This can happen // if the CGU did not exist in the previous session. - return CguReUsable::No + return CguReuse::No } // Try to mark the CGU as green. If it we can do so, it means that nothing @@ -732,12 +726,12 @@ fn determine_cgu_reuse<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { // We can re-use either the pre- or the post-thinlto state if tcx.sess.lto() != Lto::No { - CguReUsable::PreLto + CguReuse::PreLto } else { - CguReUsable::PostLto + CguReuse::PostLto } } else { - CguReUsable::No + CguReuse::No } } @@ -894,8 +888,11 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_codegen.wait_for_signal_to_codegen_item(); ongoing_codegen.check_for_errors(tcx.sess); - let loaded_from_cache = match determine_cgu_reuse(tcx, &cgu) { - CguReUsable::No => { + let cgu_reuse = determine_cgu_reuse(tcx, &cgu); + tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse); + + match cgu_reuse { + CguReuse::No => { let _timing_guard = time_graph.as_ref().map(|time_graph| { time_graph.start(write::CODEGEN_WORKER_TIMELINE, write::CODEGEN_WORK_PACKAGE_KIND, @@ -907,14 +904,14 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, total_codegen_time += start_time.elapsed(); false } - CguReUsable::PreLto => { + CguReuse::PreLto => { write::submit_pre_lto_module_to_llvm(tcx, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.work_product(tcx), }); true } - CguReUsable::PostLto => { + CguReuse::PostLto => { write::submit_post_lto_module_to_llvm(tcx, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.work_product(tcx), @@ -922,12 +919,6 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, true } }; - - if tcx.dep_graph.is_fully_enabled() { - let dep_node = cgu.codegen_dep_node(tcx); - let dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); - tcx.dep_graph.mark_loaded_from_cache(dep_node_index, loaded_from_cache); - } } ongoing_codegen.codegen_finished(tcx); @@ -938,9 +929,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "codegen to LLVM IR", total_codegen_time); - if tcx.sess.opts.incremental.is_some() { - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); - } + rustc_incremental::assert_module_sources::assert_module_sources(tcx); symbol_names_test::report_symbol_names(tcx); diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs index 139159c1639..4ff2529b26d 100644 --- a/src/librustc_incremental/assert_module_sources.rs +++ b/src/librustc_incremental/assert_module_sources.rs @@ -26,19 +26,23 @@ //! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that //! the HIR doesn't change as a result of the annotations, which might //! perturb the reuse results. +//! +//! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] +//! allows for doing a more fine-grained check to see if pre- or post-lto data +//! was re-used. use rustc::hir::def_id::LOCAL_CRATE; -use rustc::dep_graph::{DepNode, DepConstructor}; +use rustc::dep_graph::cgu_reuse_tracker::*; use rustc::mir::mono::CodegenUnitNameBuilder; use rustc::ty::TyCtxt; +use std::collections::BTreeSet; use syntax::ast; -use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED}; +use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED, + ATTR_EXPECTED_CGU_REUSE}; const MODULE: &'static str = "module"; const CFG: &'static str = "cfg"; - -#[derive(Debug, PartialEq, Clone, Copy)] -enum Disposition { Reused, Codegened } +const KIND: &'static str = "kind"; pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.dep_graph.with_ignore(|| { @@ -46,7 +50,18 @@ pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { return; } - let ams = AssertModuleSource { tcx }; + let available_cgus = tcx + .collect_and_partition_mono_items(LOCAL_CRATE) + .1 + .iter() + .map(|cgu| format!("{}", cgu.name())) + .collect::>(); + + let ams = AssertModuleSource { + tcx, + available_cgus + }; + for attr in &tcx.hir.krate().attrs { ams.check_attr(attr); } @@ -54,19 +69,39 @@ pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { } struct AssertModuleSource<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx> + tcx: TyCtxt<'a, 'tcx, 'tcx>, + available_cgus: BTreeSet, } impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { fn check_attr(&self, attr: &ast::Attribute) { - let disposition = if attr.check_name(ATTR_PARTITION_REUSED) { - Disposition::Reused + let (expected_reuse, comp_kind) = if attr.check_name(ATTR_PARTITION_REUSED) { + (CguReuse::PreLto, ComparisonKind::AtLeast) } else if attr.check_name(ATTR_PARTITION_CODEGENED) { - Disposition::Codegened + (CguReuse::No, ComparisonKind::Exact) + } else if attr.check_name(ATTR_EXPECTED_CGU_REUSE) { + match &self.field(attr, KIND).as_str()[..] { + "no" => (CguReuse::No, ComparisonKind::Exact), + "pre-lto" => (CguReuse::PreLto, ComparisonKind::Exact), + "post-lto" => (CguReuse::PostLto, ComparisonKind::Exact), + "any" => (CguReuse::PreLto, ComparisonKind::AtLeast), + other => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unknown cgu-reuse-kind `{}` specified", other)); + } + } } else { return; }; + if !self.tcx.sess.opts.debugging_opts.query_dep_graph { + self.tcx.sess.span_fatal( + attr.span, + &format!("found CGU-reuse attribute but `-Zquery-dep-graph` \ + was not specified")); + } + if !self.check_config(attr) { debug!("check_attr: config does not match, ignoring attr"); return; @@ -101,43 +136,24 @@ fn check_attr(&self, attr: &ast::Attribute) { debug!("mapping '{}' to cgu name '{}'", self.field(attr, MODULE), cgu_name); - let dep_node = DepNode::new(self.tcx, - DepConstructor::CompileCodegenUnit(cgu_name)); - - if let Some(loaded_from_cache) = self.tcx.dep_graph.was_loaded_from_cache(&dep_node) { - match (disposition, loaded_from_cache) { - (Disposition::Reused, false) => { - self.tcx.sess.span_err( - attr.span, - &format!("expected module named `{}` to be Reused but is Codegened", - user_path)); - } - (Disposition::Codegened, true) => { - self.tcx.sess.span_err( - attr.span, - &format!("expected module named `{}` to be Codegened but is Reused", - user_path)); - } - (Disposition::Reused, true) | - (Disposition::Codegened, false) => { - // These are what we would expect. - } - } - } else { - let available_cgus = self.tcx - .collect_and_partition_mono_items(LOCAL_CRATE) - .1 - .iter() - .map(|cgu| format!("{}", cgu.name())) - .collect::>() - .join(", "); - + if !self.available_cgus.contains(&cgu_name.as_str()[..]) { self.tcx.sess.span_err(attr.span, - &format!("no module named `{}` (mangled: {}).\nAvailable modules: {}", + &format!("no module named `{}` (mangled: {}). \ + Available modules: {}", user_path, cgu_name, - available_cgus)); + self.available_cgus + .iter() + .cloned() + .collect::>() + .join(", "))); } + + self.tcx.sess.cgu_reuse_tracker.set_expectation(&cgu_name.as_str(), + &user_path, + attr.span, + expected_reuse, + comp_kind); } fn field(&self, attr: &ast::Attribute, name: &str) -> ast::Name { @@ -171,5 +187,4 @@ fn check_config(&self, attr: &ast::Attribute) -> bool { debug!("check_config: no match found"); return false; } - } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 060faa9856d..0c1e57c85eb 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -930,6 +930,12 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool { is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), + ("rustc_expected_cgu_reuse", Whitelisted, Gated(Stability::Unstable, + "rustc_attrs", + "this attribute \ + is just used for rustc unit tests \ + and will never be stable", + cfg_fn!(rustc_attrs))), ("rustc_synthetic", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "this attribute \ diff --git a/src/test/incremental/remove-private-item-cross-crate/main.rs b/src/test/incremental/remove-private-item-cross-crate/main.rs index d94cb403da8..68806561547 100644 --- a/src/test/incremental/remove-private-item-cross-crate/main.rs +++ b/src/test/incremental/remove-private-item-cross-crate/main.rs @@ -13,6 +13,7 @@ // revisions:rpass1 rpass2 // aux-build:a.rs +// compile-flags: -Zquery-dep-graph #![feature(rustc_attrs)] #![crate_type = "bin"] diff --git a/src/test/incremental/thinlto/cgu_invalidated_via_import.rs b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs new file mode 100644 index 00000000000..c9e1ab89e50 --- /dev/null +++ b/src/test/incremental/thinlto/cgu_invalidated_via_import.rs @@ -0,0 +1,57 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test checks that the LTO phase is re-done for CGUs that import something +// via ThinLTO and that imported thing changes while the definition of the CGU +// stays untouched. + +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph -O +// compile-pass + +#![feature(rustc_attrs)] +#![crate_type="rlib"] + +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-foo", + cfg="cfail2", + kind="no")] +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-foo", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-bar", + cfg="cfail2", + kind="pre-lto")] +#![rustc_expected_cgu_reuse(module="cgu_invalidated_via_import-bar", + cfg="cfail3", + kind="post-lto")] + +mod foo { + + // Trivial functions like this one are imported very reliably by ThinLTO. + #[cfg(cfail1)] + pub fn inlined_fn() -> u32 { + 1234 + } + + #[cfg(not(cfail1))] + pub fn inlined_fn() -> u32 { + 1234 + } +} + +pub mod bar { + use foo::inlined_fn; + + pub fn caller() -> u32 { + inlined_fn() + } +} diff --git a/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs b/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs new file mode 100644 index 00000000000..d0d6d6fdc24 --- /dev/null +++ b/src/test/incremental/thinlto/independent_cgus_dont_affect_each_other.rs @@ -0,0 +1,67 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// This test checks that a change in a CGU does not invalidate an unrelated CGU +// during incremental ThinLTO. + +// revisions: cfail1 cfail2 cfail3 +// compile-flags: -Z query-dep-graph -O +// compile-pass + +#![feature(rustc_attrs)] +#![crate_type="rlib"] + +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-foo", + cfg="cfail2", + kind="no")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-foo", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-bar", + cfg="cfail2", + kind="pre-lto")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-bar", + cfg="cfail3", + kind="post-lto")] + +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-baz", + cfg="cfail2", + kind="post-lto")] +#![rustc_expected_cgu_reuse(module="independent_cgus_dont_affect_each_other-baz", + cfg="cfail3", + kind="post-lto")] +mod foo { + + #[cfg(cfail1)] + pub fn inlined_fn() -> u32 { + 1234 + } + + #[cfg(not(cfail1))] + pub fn inlined_fn() -> u32 { + 1234 + } +} + +pub mod bar { + use foo::inlined_fn; + + pub fn caller() -> u32 { + inlined_fn() + } +} + +pub mod baz { + pub fn unrelated_to_other_fns() -> u64 { + 0xbeef + } +}