Skip to content

Commit ca19732

Browse files
incr.comp.: Allow for more fine-grained testing of CGU reuse and use it to test incremental ThinLTO.
1 parent b80cb47 commit ca19732

File tree

12 files changed

+373
-80
lines changed

12 files changed

+373
-80
lines changed
+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Some facilities for tracking how codegen-units are reused during incremental
12+
//! compilition. This is used for incremental compiliation tests and debug
13+
//! output.
14+
15+
use session::Session;
16+
use rustc_data_structures::fx::FxHashMap;
17+
use std::sync::{Arc, Mutex};
18+
use syntax_pos::Span;
19+
20+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
21+
pub enum CguReuse {
22+
No,
23+
PreLto,
24+
PostLto,
25+
}
26+
27+
#[derive(Copy, Clone, Debug, PartialEq)]
28+
pub enum ComparisonKind {
29+
Exact,
30+
AtLeast,
31+
}
32+
33+
struct TrackerData {
34+
actual_reuse: FxHashMap<String, CguReuse>,
35+
expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>,
36+
}
37+
38+
// Span does not implement `Send`, so we can't just store it in the shared
39+
// `TrackerData` object. Instead of splitting up `TrackerData` into shared and
40+
// non-shared parts (which would be complicated), we just mark the `Span` here
41+
// explicitly as `Send`. That's safe because the span data here is only ever
42+
// accessed from the main thread.
43+
struct SendSpan(Span);
44+
unsafe impl Send for SendSpan {}
45+
46+
#[derive(Clone)]
47+
pub struct CguReuseTracker {
48+
data: Option<Arc<Mutex<TrackerData>>>,
49+
}
50+
51+
impl CguReuseTracker {
52+
pub fn new() -> CguReuseTracker {
53+
let data = TrackerData {
54+
actual_reuse: FxHashMap(),
55+
expected_reuse: FxHashMap(),
56+
};
57+
58+
CguReuseTracker {
59+
data: Some(Arc::new(Mutex::new(data))),
60+
}
61+
}
62+
63+
pub fn new_disabled() -> CguReuseTracker {
64+
CguReuseTracker {
65+
data: None,
66+
}
67+
}
68+
69+
pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) {
70+
if let Some(ref data) = self.data {
71+
debug!("set_actual_reuse({:?}, {:?})", cgu_name, kind);
72+
73+
let prev_reuse = data.lock()
74+
.unwrap()
75+
.actual_reuse
76+
.insert(cgu_name.to_string(), kind);
77+
78+
if let Some(prev_reuse) = prev_reuse {
79+
// The only time it is legal to overwrite reuse state is when
80+
// we discover during ThinLTO that we can actually reuse the
81+
// post-LTO version of a CGU.
82+
assert_eq!(prev_reuse, CguReuse::PreLto);
83+
}
84+
}
85+
}
86+
87+
pub fn set_expectation(&self,
88+
cgu_name: &str,
89+
cgu_user_name: &str,
90+
error_span: Span,
91+
expected_reuse: CguReuse,
92+
comparison_kind: ComparisonKind) {
93+
if let Some(ref data) = self.data {
94+
debug!("set_expectation({:?}, {:?}, {:?})", cgu_name,
95+
expected_reuse,
96+
comparison_kind);
97+
let mut data = data.lock().unwrap();
98+
99+
data.expected_reuse.insert(cgu_name.to_string(),
100+
(cgu_user_name.to_string(),
101+
SendSpan(error_span),
102+
expected_reuse,
103+
comparison_kind));
104+
}
105+
}
106+
107+
pub fn check_expected_reuse(&self, sess: &Session) {
108+
if let Some(ref data) = self.data {
109+
let data = data.lock().unwrap();
110+
111+
for (cgu_name, &(ref cgu_user_name,
112+
ref error_span,
113+
expected_reuse,
114+
comparison_kind)) in &data.expected_reuse {
115+
if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
116+
let (error, at_least) = match comparison_kind {
117+
ComparisonKind::Exact => {
118+
(expected_reuse != actual_reuse, false)
119+
}
120+
ComparisonKind::AtLeast => {
121+
(actual_reuse < expected_reuse, true)
122+
}
123+
};
124+
125+
if error {
126+
let at_least = if at_least { "at least " } else { "" };
127+
let msg = format!("CGU-reuse for `{}` is `{:?}` but \
128+
should be {}`{:?}`",
129+
cgu_user_name,
130+
actual_reuse,
131+
at_least,
132+
expected_reuse);
133+
sess.span_err(error_span.0, &msg);
134+
}
135+
} else {
136+
let msg = format!("CGU-reuse for `{}` (mangled: `{}`) was \
137+
not recorded",
138+
cgu_user_name,
139+
cgu_name);
140+
sess.span_fatal(error_span.0, &msg);
141+
}
142+
}
143+
}
144+
}
145+
}

src/librustc/dep_graph/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod prev;
1616
mod query;
1717
mod safe;
1818
mod serialized;
19+
pub mod cgu_reuse_tracker;
1920

2021
pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
2122
pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, label_strs};

src/librustc/ich/mod.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,7 @@ pub const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
3030
pub const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
3131
pub const ATTR_PARTITION_REUSED: &'static str = "rustc_partition_reused";
3232
pub const ATTR_PARTITION_CODEGENED: &'static str = "rustc_partition_codegened";
33-
34-
35-
pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[
36-
ATTR_IF_THIS_CHANGED,
37-
ATTR_THEN_THIS_WOULD_NEED,
38-
ATTR_DIRTY,
39-
ATTR_CLEAN,
40-
ATTR_PARTITION_REUSED,
41-
ATTR_PARTITION_CODEGENED,
42-
];
33+
pub const ATTR_EXPECTED_CGU_REUSE: &'static str = "rustc_expected_cgu_reuse";
4334

4435
pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[
4536
"cfg",
@@ -49,4 +40,5 @@ pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[
4940
ATTR_CLEAN,
5041
ATTR_PARTITION_REUSED,
5142
ATTR_PARTITION_CODEGENED,
43+
ATTR_EXPECTED_CGU_REUSE,
5244
];

src/librustc/session/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
pub use self::code_stats::{DataTypeKind, SizeKind, FieldInfo, VariantInfo};
1212
use self::code_stats::CodeStats;
1313

14+
use dep_graph::cgu_reuse_tracker::CguReuseTracker;
1415
use hir::def_id::CrateNum;
1516
use rustc_data_structures::fingerprint::Fingerprint;
1617

@@ -124,6 +125,9 @@ pub struct Session {
124125
pub imported_macro_spans: OneThread<RefCell<FxHashMap<Span, (String, Span)>>>,
125126

126127
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
128+
/// Used for incremental compilation tests. Will only be populated if
129+
/// `-Zquery-dep-graph` is specified.
130+
pub cgu_reuse_tracker: CguReuseTracker,
127131

128132
/// Used by -Z profile-queries in util::common
129133
pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
@@ -1109,6 +1113,12 @@ pub fn build_session_(
11091113
};
11101114
let working_dir = file_path_mapping.map_prefix(working_dir);
11111115

1116+
let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph {
1117+
CguReuseTracker::new()
1118+
} else {
1119+
CguReuseTracker::new_disabled()
1120+
};
1121+
11121122
let sess = Session {
11131123
target: target_cfg,
11141124
host,
@@ -1139,6 +1149,7 @@ pub fn build_session_(
11391149
injected_panic_runtime: Once::new(),
11401150
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
11411151
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
1152+
cgu_reuse_tracker,
11421153
self_profiling: Lock::new(SelfProfiler::new()),
11431154
profile_channel: Lock::new(None),
11441155
perf_stats: PerfStats {

src/librustc_codegen_llvm/back/lto.rs

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use llvm::{True, False};
1818
use llvm;
1919
use memmap;
2020
use rustc::dep_graph::WorkProduct;
21+
use rustc::dep_graph::cgu_reuse_tracker::CguReuse;
2122
use rustc::hir::def_id::LOCAL_CRATE;
2223
use rustc::middle::exported_symbols::SymbolExportLevel;
2324
use rustc::session::config::{self, Lto};
@@ -538,6 +539,8 @@ fn thin_lto(cgcx: &CodegenContext,
538539
let work_product = green_modules[module_name].clone();
539540
copy_jobs.push(work_product);
540541
info!(" - {}: re-used", module_name);
542+
cgcx.cgu_reuse_tracker.set_actual_reuse(module_name,
543+
CguReuse::PostLto);
541544
continue
542545
}
543546
}

src/librustc_codegen_llvm/back/write.rs

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use memmap;
2121
use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir,
2222
in_incr_comp_dir, in_incr_comp_dir_sess};
2323
use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind};
24+
use rustc::dep_graph::cgu_reuse_tracker::CguReuseTracker;
2425
use rustc::middle::cstore::EncodedMetadata;
2526
use rustc::session::config::{self, OutputFilenames, OutputType, Passes, Sanitizer, Lto};
2627
use rustc::session::Session;
@@ -377,6 +378,8 @@ pub struct CodegenContext {
377378
// The incremental compilation session directory, or None if we are not
378379
// compiling incrementally
379380
pub incr_comp_session_dir: Option<PathBuf>,
381+
// Used to update CGU re-use information during the thinlto phase.
382+
pub cgu_reuse_tracker: CguReuseTracker,
380383
// Channel back to the main control thread to send messages to
381384
coordinator_send: Sender<Box<dyn Any + Send>>,
382385
// A reference to the TimeGraph so we can register timings. None means that
@@ -1607,6 +1610,7 @@ fn start_executing_work(tcx: TyCtxt,
16071610
remark: sess.opts.cg.remark.clone(),
16081611
worker: 0,
16091612
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
1613+
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
16101614
coordinator_send,
16111615
diag_emitter: shared_emitter.clone(),
16121616
time_graph,
@@ -2390,6 +2394,8 @@ impl OngoingCodegen {
23902394
}
23912395
};
23922396

2397+
sess.cgu_reuse_tracker.check_expected_reuse(sess);
2398+
23932399
sess.abort_if_errors();
23942400

23952401
if let Some(time_graph) = self.time_graph {

src/librustc_codegen_llvm/base.rs

+15-26
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use abi;
3232
use back::write::{self, OngoingCodegen};
3333
use llvm::{self, TypeKind, get_param};
3434
use metadata;
35+
use rustc::dep_graph::cgu_reuse_tracker::CguReuse;
3536
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
3637
use rustc::middle::lang_items::StartFnLangItem;
3738
use rustc::middle::weak_lang_items;
@@ -697,25 +698,18 @@ pub fn iter_globals(llmod: &'ll llvm::Module) -> ValueIter<'ll> {
697698
}
698699
}
699700

700-
#[derive(Debug)]
701-
enum CguReUsable {
702-
PreLto,
703-
PostLto,
704-
No
705-
}
706-
707701
fn determine_cgu_reuse<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
708702
cgu: &CodegenUnit<'tcx>)
709-
-> CguReUsable {
703+
-> CguReuse {
710704
if !tcx.dep_graph.is_fully_enabled() {
711-
return CguReUsable::No
705+
return CguReuse::No
712706
}
713707

714708
let work_product_id = &cgu.work_product_id();
715709
if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
716710
// We don't have anything cached for this CGU. This can happen
717711
// if the CGU did not exist in the previous session.
718-
return CguReUsable::No
712+
return CguReuse::No
719713
}
720714

721715
// 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>,
732726
if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() {
733727
// We can re-use either the pre- or the post-thinlto state
734728
if tcx.sess.lto() != Lto::No {
735-
CguReUsable::PreLto
729+
CguReuse::PreLto
736730
} else {
737-
CguReUsable::PostLto
731+
CguReuse::PostLto
738732
}
739733
} else {
740-
CguReUsable::No
734+
CguReuse::No
741735
}
742736
}
743737

@@ -894,8 +888,11 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
894888
ongoing_codegen.wait_for_signal_to_codegen_item();
895889
ongoing_codegen.check_for_errors(tcx.sess);
896890

897-
let loaded_from_cache = match determine_cgu_reuse(tcx, &cgu) {
898-
CguReUsable::No => {
891+
let cgu_reuse = determine_cgu_reuse(tcx, &cgu);
892+
tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);
893+
894+
match cgu_reuse {
895+
CguReuse::No => {
899896
let _timing_guard = time_graph.as_ref().map(|time_graph| {
900897
time_graph.start(write::CODEGEN_WORKER_TIMELINE,
901898
write::CODEGEN_WORK_PACKAGE_KIND,
@@ -907,27 +904,21 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
907904
total_codegen_time += start_time.elapsed();
908905
false
909906
}
910-
CguReUsable::PreLto => {
907+
CguReuse::PreLto => {
911908
write::submit_pre_lto_module_to_llvm(tcx, CachedModuleCodegen {
912909
name: cgu.name().to_string(),
913910
source: cgu.work_product(tcx),
914911
});
915912
true
916913
}
917-
CguReUsable::PostLto => {
914+
CguReuse::PostLto => {
918915
write::submit_post_lto_module_to_llvm(tcx, CachedModuleCodegen {
919916
name: cgu.name().to_string(),
920917
source: cgu.work_product(tcx),
921918
});
922919
true
923920
}
924921
};
925-
926-
if tcx.dep_graph.is_fully_enabled() {
927-
let dep_node = cgu.codegen_dep_node(tcx);
928-
let dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node);
929-
tcx.dep_graph.mark_loaded_from_cache(dep_node_index, loaded_from_cache);
930-
}
931922
}
932923

933924
ongoing_codegen.codegen_finished(tcx);
@@ -938,9 +929,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
938929
"codegen to LLVM IR",
939930
total_codegen_time);
940931

941-
if tcx.sess.opts.incremental.is_some() {
942-
::rustc_incremental::assert_module_sources::assert_module_sources(tcx);
943-
}
932+
rustc_incremental::assert_module_sources::assert_module_sources(tcx);
944933

945934
symbol_names_test::report_symbol_names(tcx);
946935

0 commit comments

Comments
 (0)