Skip to content

Commit 3cf3ec6

Browse files
committed
Move thin LTO out of the main loop too
1 parent a6725ab commit 3cf3ec6

File tree

1 file changed

+167
-50
lines changed
  • compiler/rustc_codegen_ssa/src/back

1 file changed

+167
-50
lines changed

compiler/rustc_codegen_ssa/src/back/write.rs

Lines changed: 167 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
1515
use rustc_errors::emitter::Emitter;
1616
use rustc_errors::translation::Translator;
1717
use rustc_errors::{
18-
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalErrorMarker, Level, MultiSpan, Style,
19-
Suggestions,
18+
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level,
19+
MultiSpan, Style, Suggestions,
2020
};
2121
use rustc_fs_util::link_or_copy;
2222
use rustc_incremental::{
@@ -992,6 +992,155 @@ fn do_fat_lto<B: ExtraBackendMethods>(
992992
B::codegen(cgcx, module, &cgcx.module_config)
993993
}
994994

995+
fn do_thin_lto<'a, B: ExtraBackendMethods>(
996+
cgcx: &'a CodegenContext<B>,
997+
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
998+
exported_symbols_for_lto: Arc<Vec<String>>,
999+
each_linked_rlib_for_lto: Vec<PathBuf>,
1000+
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
1001+
lto_import_only_modules: Vec<(
1002+
SerializedModule<<B as WriteBackendMethods>::ModuleBuffer>,
1003+
WorkProduct,
1004+
)>,
1005+
) -> Vec<CompiledModule> {
1006+
check_lto_allowed(&cgcx);
1007+
1008+
let (coordinator_send, coordinator_receive) = channel();
1009+
1010+
// First up, convert our jobserver into a helper thread so we can use normal
1011+
// mpsc channels to manage our messages and such.
1012+
// After we've requested tokens then we'll, when we can,
1013+
// get tokens on `coordinator_receive` which will
1014+
// get managed in the main loop below.
1015+
let coordinator_send2 = coordinator_send.clone();
1016+
let helper = jobserver::client()
1017+
.into_helper_thread(move |token| {
1018+
drop(coordinator_send2.send(Message::Token::<B>(token)));
1019+
})
1020+
.expect("failed to spawn helper thread");
1021+
1022+
let mut work_items = vec![];
1023+
1024+
// We have LTO work to do. Perform the serial work here of
1025+
// figuring out what we're going to LTO and then push a
1026+
// bunch of work items onto our queue to do LTO. This all
1027+
// happens on the coordinator thread but it's very quick so
1028+
// we don't worry about tokens.
1029+
for (work, cost) in generate_thin_lto_work(
1030+
cgcx,
1031+
&exported_symbols_for_lto,
1032+
&each_linked_rlib_for_lto,
1033+
needs_thin_lto,
1034+
lto_import_only_modules,
1035+
) {
1036+
let insertion_index =
1037+
work_items.binary_search_by_key(&cost, |&(_, cost)| cost).unwrap_or_else(|e| e);
1038+
work_items.insert(insertion_index, (work, cost));
1039+
if cgcx.parallel {
1040+
helper.request_token();
1041+
}
1042+
}
1043+
1044+
let mut codegen_aborted = None;
1045+
1046+
// These are the Jobserver Tokens we currently hold. Does not include
1047+
// the implicit Token the compiler process owns no matter what.
1048+
let mut tokens = vec![];
1049+
1050+
// Amount of tokens that are used (including the implicit token).
1051+
let mut used_token_count = 0;
1052+
1053+
let mut compiled_modules = vec![];
1054+
1055+
// Run the message loop while there's still anything that needs message
1056+
// processing. Note that as soon as codegen is aborted we simply want to
1057+
// wait for all existing work to finish, so many of the conditions here
1058+
// only apply if codegen hasn't been aborted as they represent pending
1059+
// work to be done.
1060+
loop {
1061+
if codegen_aborted.is_none() {
1062+
if used_token_count == 0 && work_items.is_empty() {
1063+
// All codegen work is done.
1064+
break;
1065+
}
1066+
1067+
// Spin up what work we can, only doing this while we've got available
1068+
// parallelism slots and work left to spawn.
1069+
while used_token_count < tokens.len() + 1
1070+
&& let Some((item, _)) = work_items.pop()
1071+
{
1072+
spawn_work(&cgcx, coordinator_send.clone(), llvm_start_time, item);
1073+
used_token_count += 1;
1074+
}
1075+
} else {
1076+
// Don't queue up any more work if codegen was aborted, we're
1077+
// just waiting for our existing children to finish.
1078+
if used_token_count == 0 {
1079+
break;
1080+
}
1081+
}
1082+
1083+
// Relinquish accidentally acquired extra tokens. Subtract 1 for the implicit token.
1084+
tokens.truncate(used_token_count.saturating_sub(1));
1085+
1086+
match coordinator_receive.recv().unwrap() {
1087+
// Save the token locally and the next turn of the loop will use
1088+
// this to spawn a new unit of work, or it may get dropped
1089+
// immediately if we have no more work to spawn.
1090+
Message::Token(token) => match token {
1091+
Ok(token) => {
1092+
tokens.push(token);
1093+
}
1094+
Err(e) => {
1095+
let msg = &format!("failed to acquire jobserver token: {e}");
1096+
cgcx.diag_emitter.fatal(msg);
1097+
codegen_aborted = Some(FatalError);
1098+
}
1099+
},
1100+
1101+
Message::CodegenDone { .. }
1102+
| Message::CodegenComplete
1103+
| Message::CodegenAborted
1104+
| Message::AddImportOnlyModule { .. } => {
1105+
unreachable!()
1106+
}
1107+
1108+
Message::WorkItem { result } => {
1109+
// If a thread exits successfully then we drop a token associated
1110+
// with that worker and update our `used_token_count` count.
1111+
// We may later re-acquire a token to continue running more work.
1112+
// We may also not actually drop a token here if the worker was
1113+
// running with an "ephemeral token".
1114+
used_token_count -= 1;
1115+
1116+
match result {
1117+
Ok(WorkItemResult::Finished(compiled_module)) => {
1118+
compiled_modules.push(compiled_module);
1119+
}
1120+
Ok(WorkItemResult::NeedsFatLto(_)) | Ok(WorkItemResult::NeedsThinLto(_, _)) => {
1121+
unreachable!()
1122+
}
1123+
Err(Some(WorkerFatalError)) => {
1124+
// Like `CodegenAborted`, wait for remaining work to finish.
1125+
codegen_aborted = Some(FatalError);
1126+
}
1127+
Err(None) => {
1128+
// If the thread failed that means it panicked, so
1129+
// we abort immediately.
1130+
bug!("worker thread panicked");
1131+
}
1132+
}
1133+
}
1134+
}
1135+
}
1136+
1137+
if let Some(codegen_aborted) = codegen_aborted {
1138+
codegen_aborted.raise();
1139+
}
1140+
1141+
compiled_modules
1142+
}
1143+
9951144
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
9961145
cgcx: &CodegenContext<B>,
9971146
module: lto::ThinModule<B>,
@@ -1085,9 +1234,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
10851234
regular_config: Arc<ModuleConfig>,
10861235
allocator_config: Arc<ModuleConfig>,
10871236
allocator_module: Option<ModuleCodegen<B::Module>>,
1088-
tx_to_llvm_workers: Sender<Message<B>>,
1237+
coordinator_send: Sender<Message<B>>,
10891238
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
1090-
let coordinator_send = tx_to_llvm_workers;
10911239
let sess = tcx.sess;
10921240

10931241
let mut each_linked_rlib_for_lto = Vec::new();
@@ -1307,7 +1455,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
13071455
let mut needs_fat_lto = Vec::new();
13081456
let mut needs_thin_lto = Vec::new();
13091457
let mut lto_import_only_modules = Vec::new();
1310-
let mut started_lto = false;
13111458

13121459
/// Possible state transitions:
13131460
/// - Ongoing -> Completed
@@ -1397,48 +1544,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
13971544
if running_with_any_token(main_thread_state, running_with_own_token) == 0
13981545
&& work_items.is_empty()
13991546
{
1400-
// All codegen work is done. Do we have LTO work to do?
1401-
if needs_fat_lto.is_empty()
1402-
&& needs_thin_lto.is_empty()
1403-
&& lto_import_only_modules.is_empty()
1404-
{
1405-
// Nothing more to do!
1406-
break;
1407-
}
1408-
1409-
// We have LTO work to do. Perform the serial work here of
1410-
// figuring out what we're going to LTO and then push a
1411-
// bunch of work items onto our queue to do LTO. This all
1412-
// happens on the coordinator thread but it's very quick so
1413-
// we don't worry about tokens.
1414-
assert!(!started_lto);
1415-
started_lto = true;
1416-
1417-
if !needs_fat_lto.is_empty() {
1418-
// We're doing fat LTO outside of the main loop.
1419-
break;
1420-
}
1421-
1422-
check_lto_allowed(&cgcx);
1423-
1424-
let needs_thin_lto = mem::take(&mut needs_thin_lto);
1425-
let import_only_modules = mem::take(&mut lto_import_only_modules);
1426-
1427-
for (work, cost) in generate_thin_lto_work(
1428-
&cgcx,
1429-
&exported_symbols_for_lto,
1430-
&each_linked_rlib_file_for_lto,
1431-
needs_thin_lto,
1432-
import_only_modules,
1433-
) {
1434-
let insertion_index = work_items
1435-
.binary_search_by_key(&cost, |&(_, cost)| cost)
1436-
.unwrap_or_else(|e| e);
1437-
work_items.insert(insertion_index, (work, cost));
1438-
if cgcx.parallel {
1439-
helper.request_token();
1440-
}
1441-
}
1547+
// All codegen work is done.
1548+
break;
14421549
}
14431550

14441551
// In this branch, we know that everything has been codegened,
@@ -1576,12 +1683,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
15761683
compiled_modules.push(compiled_module);
15771684
}
15781685
Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => {
1579-
assert!(!started_lto);
15801686
assert!(needs_thin_lto.is_empty());
15811687
needs_fat_lto.push(fat_lto_input);
15821688
}
15831689
Ok(WorkItemResult::NeedsThinLto(name, thin_buffer)) => {
1584-
assert!(!started_lto);
15851690
assert!(needs_fat_lto.is_empty());
15861691
needs_thin_lto.push((name, thin_buffer));
15871692
}
@@ -1598,7 +1703,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
15981703
}
15991704

16001705
Message::AddImportOnlyModule { module_data, work_product } => {
1601-
assert!(!started_lto);
16021706
assert_eq!(codegen_state, Ongoing);
16031707
assert_eq!(main_thread_state, MainThreadState::Codegenning);
16041708
lto_import_only_modules.push((module_data, work_product));
@@ -1614,6 +1718,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
16141718
drop(codegen_state);
16151719
drop(tokens);
16161720
drop(helper);
1721+
assert!(work_items.is_empty());
16171722

16181723
if !needs_fat_lto.is_empty() {
16191724
assert!(compiled_modules.is_empty());
@@ -1628,6 +1733,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
16281733
lto_import_only_modules,
16291734
);
16301735
compiled_modules.push(module);
1736+
} else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() {
1737+
assert!(compiled_modules.is_empty());
1738+
assert!(needs_fat_lto.is_empty());
1739+
1740+
compiled_modules.extend(do_thin_lto(
1741+
&cgcx,
1742+
&mut llvm_start_time,
1743+
exported_symbols_for_lto,
1744+
each_linked_rlib_file_for_lto,
1745+
needs_thin_lto,
1746+
lto_import_only_modules,
1747+
));
16311748
}
16321749

16331750
// Drop to print timings

0 commit comments

Comments
 (0)