Skip to content

Commit

Permalink
incr.comp.: Allow for more fine-grained testing of CGU reuse and use …
Browse files Browse the repository at this point in the history
…it to test incremental ThinLTO.
  • Loading branch information
michaelwoerister committed Sep 18, 2018
1 parent b80cb47 commit ca19732
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 80 deletions.
145 changes: 145 additions & 0 deletions 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 <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
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
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
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
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
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
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

0 comments on commit ca19732

Please sign in to comment.