Skip to content

Commit 317ae05

Browse files
committed
Auto merge of #54325 - michaelwoerister:incr-thinlto-tests, r=alexcrichton
incr.comp.: Allow for more fine-grained testing of CGU reuse and use it to test incremental ThinLTO. This adds some tests specifically targeted at incremental ThinLTO, plus the infrastructure for tracking the kind of cache hit/miss we had for a given CGU. @alexcrichton, let me know if you can think of any more tests to add. ThinLTO works rather reliably for small functions, so we should be able to test it in a robust way. I think after this lands it might time for a "Help us test incremental ThinLTO" post on irlo. r? @alexcrichton
2 parents 7714c43 + ca19732 commit 317ae05

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>>>,
@@ -1116,6 +1120,12 @@ pub fn build_session_(
11161120
};
11171121
let working_dir = file_path_mapping.map_prefix(working_dir);
11181122

1123+
let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph {
1124+
CguReuseTracker::new()
1125+
} else {
1126+
CguReuseTracker::new_disabled()
1127+
};
1128+
11191129
let sess = Session {
11201130
target: target_cfg,
11211131
host,
@@ -1146,6 +1156,7 @@ pub fn build_session_(
11461156
injected_panic_runtime: Once::new(),
11471157
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
11481158
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
1159+
cgu_reuse_tracker,
11491160
self_profiling: Lock::new(SelfProfiler::new()),
11501161
profile_channel: Lock::new(None),
11511162
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)