Auto merge of #112827 - nnethercote:codegen-cleanups, r=tmiasko

Codegen cleanups

Some cleanups I found while looking closely at this code.

r? `@tmiasko`
This commit is contained in:
bors 2023-06-23 03:51:49 +00:00
commit fe37f37e4b
2 changed files with 105 additions and 119 deletions

View File

@ -320,7 +320,6 @@ pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo
#[derive(Clone)] #[derive(Clone)]
pub struct CodegenContext<B: WriteBackendMethods> { pub struct CodegenContext<B: WriteBackendMethods> {
// Resources needed when running LTO // Resources needed when running LTO
pub backend: B,
pub prof: SelfProfilerRef, pub prof: SelfProfilerRef,
pub lto: Lto, pub lto: Lto,
pub save_temps: bool, pub save_temps: bool,
@ -338,14 +337,10 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub msvc_imps_needed: bool, pub msvc_imps_needed: bool,
pub is_pe_coff: bool, pub is_pe_coff: bool,
pub target_can_use_split_dwarf: bool, pub target_can_use_split_dwarf: bool,
pub target_pointer_width: u32,
pub target_arch: String, pub target_arch: String,
pub debuginfo: config::DebugInfo,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
/// Number of cgus excluding the allocator/metadata modules
pub total_cgus: usize,
/// Handler to use for diagnostics produced during codegen. /// Handler to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter, pub diag_emitter: SharedEmitter,
/// LLVM optimizations for which we want to print remarks. /// LLVM optimizations for which we want to print remarks.
@ -441,7 +436,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
target_cpu: String, target_cpu: String,
metadata: EncodedMetadata, metadata: EncodedMetadata,
metadata_module: Option<CompiledModule>, metadata_module: Option<CompiledModule>,
total_cgus: usize,
) -> OngoingCodegen<B> { ) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel(); let (coordinator_send, coordinator_receive) = channel();
let sess = tcx.sess; let sess = tcx.sess;
@ -469,7 +463,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
shared_emitter, shared_emitter,
codegen_worker_send, codegen_worker_send,
coordinator_receive, coordinator_receive,
total_cgus,
sess.jobserver.clone(), sess.jobserver.clone(),
Arc::new(regular_config), Arc::new(regular_config),
Arc::new(metadata_config), Arc::new(metadata_config),
@ -685,7 +678,7 @@ fn produce_final_output_artifacts(
// These are used in linking steps and will be cleaned up afterward. // These are used in linking steps and will be cleaned up afterward.
} }
pub enum WorkItem<B: WriteBackendMethods> { pub(crate) enum WorkItem<B: WriteBackendMethods> {
/// Optimize a newly codegened, totally unoptimized module. /// Optimize a newly codegened, totally unoptimized module.
Optimize(ModuleCodegen<B::Module>), Optimize(ModuleCodegen<B::Module>),
/// Copy the post-LTO artifacts from the incremental cache to the output /// Copy the post-LTO artifacts from the incremental cache to the output
@ -731,7 +724,8 @@ impl<B: WriteBackendMethods> WorkItem<B> {
} }
} }
enum WorkItemResult<B: WriteBackendMethods> { /// A result produced by the backend.
pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
Compiled(CompiledModule), Compiled(CompiledModule),
NeedsLink(ModuleCodegen<B::Module>), NeedsLink(ModuleCodegen<B::Module>),
NeedsFatLTO(FatLTOInput<B>), NeedsFatLTO(FatLTOInput<B>),
@ -923,38 +917,41 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
} }
} }
pub enum Message<B: WriteBackendMethods> { /// Messages sent to the coordinator.
pub(crate) enum Message<B: WriteBackendMethods> {
/// A jobserver token has become available. Sent from the jobserver helper
/// thread.
Token(io::Result<Acquired>), Token(io::Result<Acquired>),
NeedsFatLTO {
result: FatLTOInput<B>, /// The backend has finished processing a work item for a codegen unit.
worker_id: usize, /// Sent from a backend worker thread.
}, WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
NeedsThinLTO {
name: String, /// The frontend has finished generating something (backend IR or a
thin_buffer: B::ThinBuffer, /// post-LTO artifact) for a codegen unit, and it should be passed to the
worker_id: usize, /// backend. Sent from the main thread.
}, CodegenDone { llvm_work_item: WorkItem<B>, cost: u64 },
NeedsLink {
module: ModuleCodegen<B::Module>, /// Similar to `CodegenDone`, but for reusing a pre-LTO artifact
worker_id: usize, /// Sent from the main thread.
},
Done {
result: Result<CompiledModule, Option<WorkerFatalError>>,
worker_id: usize,
},
CodegenDone {
llvm_work_item: WorkItem<B>,
cost: u64,
},
AddImportOnlyModule { AddImportOnlyModule {
module_data: SerializedModule<B::ModuleBuffer>, module_data: SerializedModule<B::ModuleBuffer>,
work_product: WorkProduct, work_product: WorkProduct,
}, },
/// The frontend has finished generating everything for all codegen units.
/// Sent from the main thread.
CodegenComplete, CodegenComplete,
CodegenItem,
/// Some normal-ish compiler error occurred, and codegen should be wound
/// down. Sent from the main thread.
CodegenAborted, CodegenAborted,
} }
/// A message sent from the coordinator thread to the main thread telling it to
/// process another codegen unit.
pub struct CguMessage;
type DiagnosticArgName<'source> = Cow<'source, str>; type DiagnosticArgName<'source> = Cow<'source, str>;
struct Diagnostic { struct Diagnostic {
@ -976,9 +973,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
crate_info: &CrateInfo, crate_info: &CrateInfo,
shared_emitter: SharedEmitter, shared_emitter: SharedEmitter,
codegen_worker_send: Sender<Message<B>>, codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Box<dyn Any + Send>>, coordinator_receive: Receiver<Box<dyn Any + Send>>,
total_cgus: usize,
jobserver: Client, jobserver: Client,
regular_config: Arc<ModuleConfig>, regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>, metadata_config: Arc<ModuleConfig>,
@ -1046,7 +1042,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
}; };
let backend_features = tcx.global_backend_features(()); let backend_features = tcx.global_backend_features(());
let cgcx = CodegenContext::<B> { let cgcx = CodegenContext::<B> {
backend: backend.clone(),
crate_types: sess.crate_types().to_vec(), crate_types: sess.crate_types().to_vec(),
each_linked_rlib_for_lto, each_linked_rlib_for_lto,
lto: sess.lto(), lto: sess.lto(),
@ -1067,13 +1062,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
metadata_module_config: metadata_config, metadata_module_config: metadata_config,
allocator_module_config: allocator_config, allocator_module_config: allocator_config,
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features), tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
total_cgus,
msvc_imps_needed: msvc_imps_needed(tcx), msvc_imps_needed: msvc_imps_needed(tcx),
is_pe_coff: tcx.sess.target.is_like_windows, is_pe_coff: tcx.sess.target.is_like_windows,
target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
target_pointer_width: tcx.sess.target.pointer_width,
target_arch: tcx.sess.target.arch.to_string(), target_arch: tcx.sess.target.arch.to_string(),
debuginfo: tcx.sess.opts.debuginfo,
split_debuginfo: tcx.sess.split_debuginfo(), split_debuginfo: tcx.sess.split_debuginfo(),
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
}; };
@ -1235,10 +1227,19 @@ fn start_executing_work<B: ExtraBackendMethods>(
let mut needs_thin_lto = Vec::new(); let mut needs_thin_lto = Vec::new();
let mut lto_import_only_modules = Vec::new(); let mut lto_import_only_modules = Vec::new();
let mut started_lto = false; let mut started_lto = false;
let mut codegen_aborted = false;
// This flag tracks whether all items have gone through codegens /// Possible state transitions:
let mut codegen_done = false; /// - Ongoing -> Completed
/// - Ongoing -> Aborted
/// - Completed -> Aborted
#[derive(Debug, PartialEq)]
enum CodegenState {
Ongoing,
Completed,
Aborted,
}
use CodegenState::*;
let mut codegen_state = Ongoing;
// This is the queue of LLVM work items that still need processing. // This is the queue of LLVM work items that still need processing.
let mut work_items = Vec::<(WorkItem<B>, u64)>::new(); let mut work_items = Vec::<(WorkItem<B>, u64)>::new();
@ -1258,10 +1259,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
// wait for all existing work to finish, so many of the conditions here // wait for all existing work to finish, so many of the conditions here
// only apply if codegen hasn't been aborted as they represent pending // only apply if codegen hasn't been aborted as they represent pending
// work to be done. // work to be done.
while !codegen_done while codegen_state == Ongoing
|| running > 0 || running > 0
|| main_thread_worker_state == MainThreadWorkerState::LLVMing || main_thread_worker_state == MainThreadWorkerState::LLVMing
|| (!codegen_aborted || (codegen_state == Completed
&& !(work_items.is_empty() && !(work_items.is_empty()
&& needs_fat_lto.is_empty() && needs_fat_lto.is_empty()
&& needs_thin_lto.is_empty() && needs_thin_lto.is_empty()
@ -1271,7 +1272,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
// While there are still CGUs to be codegened, the coordinator has // While there are still CGUs to be codegened, the coordinator has
// to decide how to utilize the compiler processes implicit Token: // to decide how to utilize the compiler processes implicit Token:
// For codegenning more CGU or for running them through LLVM. // For codegenning more CGU or for running them through LLVM.
if !codegen_done { if codegen_state == Ongoing {
if main_thread_worker_state == MainThreadWorkerState::Idle { if main_thread_worker_state == MainThreadWorkerState::Idle {
// Compute the number of workers that will be running once we've taken as many // Compute the number of workers that will be running once we've taken as many
// items from the work queue as we can, plus one for the main thread. It's not // items from the work queue as we can, plus one for the main thread. It's not
@ -1284,9 +1285,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
let anticipated_running = running + additional_running + 1; let anticipated_running = running + additional_running + 1;
if !queue_full_enough(work_items.len(), anticipated_running) { if !queue_full_enough(work_items.len(), anticipated_running) {
// The queue is not full enough, codegen more items: // The queue is not full enough, process more codegen units:
if codegen_worker_send.send(Message::CodegenItem).is_err() { if codegen_worker_send.send(CguMessage).is_err() {
panic!("Could not send Message::CodegenItem to main thread") panic!("Could not send CguMessage to main thread")
} }
main_thread_worker_state = MainThreadWorkerState::Codegenning; main_thread_worker_state = MainThreadWorkerState::Codegenning;
} else { } else {
@ -1308,10 +1309,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
spawn_work(cgcx, item); spawn_work(cgcx, item);
} }
} }
} else if codegen_aborted { } else if codegen_state == Completed {
// don't queue up any more work if codegen was aborted, we're
// just waiting for our existing children to finish
} else {
// If we've finished everything related to normal codegen // If we've finished everything related to normal codegen
// then it must be the case that we've got some LTO work to do. // then it must be the case that we've got some LTO work to do.
// Perform the serial work here of figuring out what we're // Perform the serial work here of figuring out what we're
@ -1378,11 +1376,15 @@ fn start_executing_work<B: ExtraBackendMethods>(
// Already making good use of that token // Already making good use of that token
} }
} }
} else {
// Don't queue up any more work if codegen was aborted, we're
// just waiting for our existing children to finish.
assert!(codegen_state == Aborted);
} }
// Spin up what work we can, only doing this while we've got available // Spin up what work we can, only doing this while we've got available
// parallelism slots and work left to spawn. // parallelism slots and work left to spawn.
while !codegen_aborted && !work_items.is_empty() && running < tokens.len() { while codegen_state != Aborted && !work_items.is_empty() && running < tokens.len() {
let (item, _) = work_items.pop().unwrap(); let (item, _) = work_items.pop().unwrap();
maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time); maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time);
@ -1434,8 +1436,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
Err(e) => { Err(e) => {
let msg = &format!("failed to acquire jobserver token: {}", e); let msg = &format!("failed to acquire jobserver token: {}", e);
shared_emitter.fatal(msg); shared_emitter.fatal(msg);
codegen_done = true; codegen_state = Aborted;
codegen_aborted = true;
} }
} }
} }
@ -1463,7 +1464,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
} }
Message::CodegenComplete => { Message::CodegenComplete => {
codegen_done = true; if codegen_state != Aborted {
codegen_state = Completed;
}
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
main_thread_worker_state = MainThreadWorkerState::Idle; main_thread_worker_state = MainThreadWorkerState::Idle;
} }
@ -1475,58 +1478,59 @@ fn start_executing_work<B: ExtraBackendMethods>(
// then conditions above will ensure no more work is spawned but // then conditions above will ensure no more work is spawned but
// we'll keep executing this loop until `running` hits 0. // we'll keep executing this loop until `running` hits 0.
Message::CodegenAborted => { Message::CodegenAborted => {
codegen_done = true; codegen_state = Aborted;
codegen_aborted = true;
} }
Message::Done { result: Ok(compiled_module), worker_id } => {
Message::WorkItem { result, worker_id } => {
free_worker(worker_id); free_worker(worker_id);
match compiled_module.kind {
ModuleKind::Regular => { match result {
compiled_modules.push(compiled_module); Ok(WorkItemResult::Compiled(compiled_module)) => {
match compiled_module.kind {
ModuleKind::Regular => {
compiled_modules.push(compiled_module);
}
ModuleKind::Allocator => {
assert!(compiled_allocator_module.is_none());
compiled_allocator_module = Some(compiled_module);
}
ModuleKind::Metadata => bug!("Should be handled separately"),
}
} }
ModuleKind::Allocator => { Ok(WorkItemResult::NeedsLink(module)) => {
assert!(compiled_allocator_module.is_none()); needs_link.push(module);
compiled_allocator_module = Some(compiled_module); }
Ok(WorkItemResult::NeedsFatLTO(fat_lto_input)) => {
assert!(!started_lto);
needs_fat_lto.push(fat_lto_input);
}
Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) => {
assert!(!started_lto);
needs_thin_lto.push((name, thin_buffer));
}
Err(Some(WorkerFatalError)) => {
// Like `CodegenAborted`, wait for remaining work to finish.
codegen_state = Aborted;
}
Err(None) => {
// If the thread failed that means it panicked, so
// we abort immediately.
bug!("worker thread panicked");
} }
ModuleKind::Metadata => bug!("Should be handled separately"),
} }
} }
Message::NeedsLink { module, worker_id } => {
free_worker(worker_id);
needs_link.push(module);
}
Message::NeedsFatLTO { result, worker_id } => {
assert!(!started_lto);
free_worker(worker_id);
needs_fat_lto.push(result);
}
Message::NeedsThinLTO { name, thin_buffer, worker_id } => {
assert!(!started_lto);
free_worker(worker_id);
needs_thin_lto.push((name, thin_buffer));
}
Message::AddImportOnlyModule { module_data, work_product } => { Message::AddImportOnlyModule { module_data, work_product } => {
assert!(!started_lto); assert!(!started_lto);
assert!(!codegen_done); assert_eq!(codegen_state, Ongoing);
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
lto_import_only_modules.push((module_data, work_product)); lto_import_only_modules.push((module_data, work_product));
main_thread_worker_state = MainThreadWorkerState::Idle; main_thread_worker_state = MainThreadWorkerState::Idle;
} }
// If the thread failed that means it panicked, so we abort immediately.
Message::Done { result: Err(None), worker_id: _ } => {
bug!("worker thread panicked");
}
Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => {
// Similar to CodegenAborted, wait for remaining work to finish.
free_worker(worker_id);
codegen_done = true;
codegen_aborted = true;
}
Message::CodegenItem => bug!("the coordinator should not receive codegen requests"),
} }
} }
if codegen_aborted { if codegen_state == Aborted {
return Err(()); return Err(());
} }
@ -1641,22 +1645,11 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
fn drop(&mut self) { fn drop(&mut self) {
let worker_id = self.worker_id; let worker_id = self.worker_id;
let msg = match self.result.take() { let msg = match self.result.take() {
Some(Ok(WorkItemResult::Compiled(m))) => { Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id },
Message::Done::<B> { result: Ok(m), worker_id }
}
Some(Ok(WorkItemResult::NeedsLink(m))) => {
Message::NeedsLink::<B> { module: m, worker_id }
}
Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
Message::NeedsFatLTO::<B> { result: m, worker_id }
}
Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
}
Some(Err(FatalError)) => { Some(Err(FatalError)) => {
Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id } Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id }
} }
None => Message::Done::<B> { result: Err(None), worker_id }, None => Message::WorkItem::<B> { result: Err(None), worker_id },
}; };
drop(self.coordinator_send.send(Box::new(msg))); drop(self.coordinator_send.send(Box::new(msg)));
} }
@ -1879,7 +1872,7 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> {
pub metadata: EncodedMetadata, pub metadata: EncodedMetadata,
pub metadata_module: Option<CompiledModule>, pub metadata_module: Option<CompiledModule>,
pub crate_info: CrateInfo, pub crate_info: CrateInfo,
pub codegen_worker_receive: Receiver<Message<B>>, pub codegen_worker_receive: Receiver<CguMessage>,
pub shared_emitter_main: SharedEmitterMain, pub shared_emitter_main: SharedEmitterMain,
pub output_filenames: Arc<OutputFilenames>, pub output_filenames: Arc<OutputFilenames>,
pub coordinator: Coordinator<B>, pub coordinator: Coordinator<B>,
@ -1953,10 +1946,9 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
pub fn wait_for_signal_to_codegen_item(&self) { pub fn wait_for_signal_to_codegen_item(&self) {
match self.codegen_worker_receive.recv() { match self.codegen_worker_receive.recv() {
Ok(Message::CodegenItem) => { Ok(CguMessage) => {
// Nothing to do // Ok to proceed.
} }
Ok(_) => panic!("unexpected message"),
Err(_) => { Err(_) => {
// One of the LLVM threads must have panicked, fall through so // One of the LLVM threads must have panicked, fall through so
// error handling can be reached. // error handling can be reached.

View File

@ -580,7 +580,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
) -> OngoingCodegen<B> { ) -> OngoingCodegen<B> {
// Skip crate items and just output metadata in -Z no-codegen mode. // Skip crate items and just output metadata in -Z no-codegen mode.
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None, 1); let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None);
ongoing_codegen.codegen_finished(tcx); ongoing_codegen.codegen_finished(tcx);
@ -631,14 +631,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
}) })
}); });
let ongoing_codegen = start_async_codegen( let ongoing_codegen =
backend.clone(), start_async_codegen(backend.clone(), tcx, target_cpu, metadata, metadata_module);
tcx,
target_cpu,
metadata,
metadata_module,
codegen_units.len(),
);
// Codegen an allocator shim, if necessary. // Codegen an allocator shim, if necessary.
if let Some(kind) = allocator_kind_for_codegen(tcx) { if let Some(kind) = allocator_kind_for_codegen(tcx) {