incr.comp.: Allow for more fine-grained testing of CGU reuse and use it to test incremental ThinLTO.
This commit is contained in:
parent
b80cb47889
commit
ca197323b9
145
src/librustc/dep_graph/cgu_reuse_tracker.rs
Normal file
145
src/librustc/dep_graph/cgu_reuse_tracker.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<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: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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,
|
||||
];
|
||||
|
@ -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<RefCell<FxHashMap<Span, (String, Span)>>>,
|
||||
|
||||
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
|
||||
/// 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<Option<mpsc::Sender<ProfileQueriesMsg>>>,
|
||||
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<PathBuf>,
|
||||
// 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<Box<dyn Any + Send>>,
|
||||
// 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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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::<BTreeSet<String>>();
|
||||
|
||||
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<String>,
|
||||
}
|
||||
|
||||
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::<Vec<String>>()
|
||||
.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::<Vec<_>>()
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
// revisions:rpass1 rpass2
|
||||
// aux-build:a.rs
|
||||
// compile-flags: -Zquery-dep-graph
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![crate_type = "bin"]
|
||||
|
57
src/test/incremental/thinlto/cgu_invalidated_via_import.rs
Normal file
57
src/test/incremental/thinlto/cgu_invalidated_via_import.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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()
|
||||
}
|
||||
}
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user