Skip to content

incr.comp.: Allow for more fine-grained testing of CGU reuse and use it to test incremental ThinLTO. #54325

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/librustc/dep_graph/cgu_reuse_tracker.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
}
1 change: 1 addition & 0 deletions src/librustc/dep_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod prev;
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};
Expand Down
12 changes: 2 additions & 10 deletions src/librustc/ich/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,7 @@ pub const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
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",
Expand All @@ -49,4 +40,5 @@ pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[
ATTR_CLEAN,
ATTR_PARTITION_REUSED,
ATTR_PARTITION_CODEGENED,
ATTR_EXPECTED_CGU_REUSE,
];
11 changes: 11 additions & 0 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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>>>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_codegen_llvm/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use llvm::{True, False};
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};
Expand Down Expand Up @@ -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
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_codegen_llvm/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use memmap;
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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -2390,6 +2394,8 @@ impl OngoingCodegen {
}
};

sess.cgu_reuse_tracker.check_expected_reuse(sess);

sess.abort_if_errors();

if let Some(time_graph) = self.time_graph {
Expand Down
41 changes: 15 additions & 26 deletions src/librustc_codegen_llvm/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use abi;
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;
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -907,27 +904,21 @@ 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),
});
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);
Expand All @@ -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);

Expand Down
Loading