Rollup merge of #145814 - bjorn3:codegen_worker_fatal_error, r=petrochenkov

Handle unwinding fatal errors in codegen workers

Also directly unwind on fatal errors at the point they are emitted inside the codegen backends.

Fixes the coordinator ICE of https://github.com/rust-lang/rust/issues/132240, https://github.com/rust-lang/rust/issues/135075 and https://github.com/rust-lang/rust/issues/145800.
This commit is contained in:
Stuart Cook
2025-08-26 14:19:18 +10:00
committed by GitHub
8 changed files with 133 additions and 151 deletions

View File

@@ -14,7 +14,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::memmap::Mmap;
use rustc_errors::{DiagCtxtHandle, FatalError};
use rustc_errors::DiagCtxtHandle;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
use rustc_session::config::{self, Lto};
@@ -36,7 +36,7 @@ fn prepare_lto(
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
dcx: DiagCtxtHandle<'_>,
) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
) -> (Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>) {
let mut symbols_below_threshold = exported_symbols_for_lto
.iter()
.map(|symbol| CString::new(symbol.to_owned()).unwrap())
@@ -79,16 +79,13 @@ fn prepare_lto(
let module = SerializedModule::FromRlib(data.to_vec());
upstream_modules.push((module, CString::new(name).unwrap()));
}
Err(e) => {
dcx.emit_err(e);
return Err(FatalError);
}
Err(e) => dcx.emit_fatal(e),
}
}
}
}
Ok((symbols_below_threshold, upstream_modules))
(symbols_below_threshold, upstream_modules)
}
fn get_bitcode_slice_from_object_data<'a>(
@@ -123,11 +120,11 @@ pub(crate) fn run_fat(
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
) -> ModuleCodegen<ModuleLlvm> {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
let (symbols_below_threshold, upstream_modules) =
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx);
let symbols_below_threshold =
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold)
@@ -142,11 +139,11 @@ pub(crate) fn run_thin(
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, ThinBuffer)>,
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
) -> (Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>) {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
let (symbols_below_threshold, upstream_modules) =
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx);
let symbols_below_threshold =
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
if cgcx.opts.cg.linker_plugin_lto.enabled() {
@@ -173,7 +170,7 @@ fn fat_lto(
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
symbols_below_threshold: &[*const libc::c_char],
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
) -> ModuleCodegen<ModuleLlvm> {
let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
info!("going for a fat lto");
@@ -224,7 +221,7 @@ fn fat_lto(
assert!(!serialized_modules.is_empty(), "must have at least one serialized module");
let (buffer, name) = serialized_modules.remove(0);
info!("no in-memory regular modules to choose from, parsing {:?}", name);
let llvm_module = ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx)?;
let llvm_module = ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx);
ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module)
}
};
@@ -265,7 +262,9 @@ fn fat_lto(
});
info!("linking {:?}", name);
let data = bc_decoded.data();
linker.add(data).map_err(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }))?;
linker
.add(data)
.unwrap_or_else(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }));
}
drop(linker);
save_temp_bitcode(cgcx, &module, "lto.input");
@@ -282,7 +281,7 @@ fn fat_lto(
save_temp_bitcode(cgcx, &module, "lto.after-restriction");
}
Ok(module)
module
}
pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>);
@@ -352,7 +351,7 @@ fn thin_lto(
serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
symbols_below_threshold: &[*const libc::c_char],
) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
) -> (Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>) {
let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
unsafe {
info!("going for that thin, thin LTO");
@@ -422,7 +421,7 @@ fn thin_lto(
symbols_below_threshold.as_ptr(),
symbols_below_threshold.len(),
)
.ok_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext))?;
.unwrap_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext));
let data = ThinData(data);
@@ -492,10 +491,10 @@ fn thin_lto(
if let Some(path) = key_map_path
&& let Err(err) = curr_key_map.save_to_file(&path)
{
return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err });
}
Ok((opt_jobs, copy_jobs))
(opt_jobs, copy_jobs)
}
}
@@ -550,7 +549,7 @@ pub(crate) fn run_pass_manager(
dcx: DiagCtxtHandle<'_>,
module: &mut ModuleCodegen<ModuleLlvm>,
thin: bool,
) -> Result<(), FatalError> {
) {
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
let config = cgcx.config(module.kind);
@@ -582,7 +581,7 @@ pub(crate) fn run_pass_manager(
}
unsafe {
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?;
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage);
}
if enable_gpu && !thin {
@@ -596,7 +595,7 @@ pub(crate) fn run_pass_manager(
let stage = write::AutodiffStage::PostAD;
if !config.autodiff.contains(&config::AutoDiff::NoPostopt) {
unsafe {
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?;
write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage);
}
}
@@ -608,7 +607,6 @@ pub(crate) fn run_pass_manager(
}
debug!("lto done");
Ok(())
}
pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer);
@@ -701,7 +699,7 @@ impl Drop for ThinBuffer {
pub(crate) fn optimize_thin_module(
thin_module: ThinModule<LlvmCodegenBackend>,
cgcx: &CodegenContext<LlvmCodegenBackend>,
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
) -> ModuleCodegen<ModuleLlvm> {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
@@ -712,7 +710,7 @@ pub(crate) fn optimize_thin_module(
// into that context. One day, however, we may do this for upstream
// crates but for locally codegened modules we may be able to reuse
// that LLVM Context and Module.
let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx)?;
let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx);
let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
// Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here.
if cgcx.config(ModuleKind::Regular).embed_bitcode() {
@@ -746,7 +744,7 @@ pub(crate) fn optimize_thin_module(
.generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) }
{
return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
}
save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
}
@@ -757,7 +755,7 @@ pub(crate) fn optimize_thin_module(
.generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) }
{
return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
}
save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
}
@@ -768,7 +766,7 @@ pub(crate) fn optimize_thin_module(
if unsafe {
!llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw())
} {
return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
}
save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
}
@@ -780,11 +778,11 @@ pub(crate) fn optimize_thin_module(
// little differently.
{
info!("running thin lto passes over {}", module.name);
run_pass_manager(cgcx, dcx, &mut module, true)?;
run_pass_manager(cgcx, dcx, &mut module, true);
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
}
}
Ok(module)
module
}
/// Maps LLVM module identifiers to their corresponding LLVM LTO cache keys
@@ -850,9 +848,9 @@ pub(crate) fn parse_module<'a>(
name: &CStr,
data: &[u8],
dcx: DiagCtxtHandle<'_>,
) -> Result<&'a llvm::Module, FatalError> {
) -> &'a llvm::Module {
unsafe {
llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr())
.ok_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode))
.unwrap_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode))
}
}

View File

@@ -20,7 +20,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{DiagCtxtHandle, FatalError, Level};
use rustc_errors::{DiagCtxtHandle, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@@ -46,10 +46,10 @@ use crate::llvm::{self, DiagnosticInfo};
use crate::type_::Type;
use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util};
pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> FatalError {
pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> ! {
match llvm::last_error() {
Some(llvm_err) => dcx.emit_almost_fatal(WithLlvmError(err, llvm_err)),
None => dcx.emit_almost_fatal(err),
Some(llvm_err) => dcx.emit_fatal(WithLlvmError(err, llvm_err)),
None => dcx.emit_fatal(err),
}
}
@@ -63,7 +63,7 @@ fn write_output_file<'ll>(
file_type: llvm::FileType,
self_profiler_ref: &SelfProfilerRef,
verify_llvm_ir: bool,
) -> Result<(), FatalError> {
) {
debug!("write_output_file output={:?} dwo_output={:?}", output, dwo_output);
let output_c = path_to_c_string(output);
let dwo_output_c;
@@ -100,7 +100,7 @@ fn write_output_file<'ll>(
}
}
result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output }))
result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output }))
}
pub(crate) fn create_informational_target_machine(
@@ -112,7 +112,7 @@ pub(crate) fn create_informational_target_machine(
// system/tcx is set up.
let features = llvm_util::global_llvm_features(sess, false, only_base_features);
target_machine_factory(sess, config::OptLevel::No, &features)(config)
.unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise())
.unwrap_or_else(|err| llvm_err(sess.dcx(), err))
}
pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTargetMachine {
@@ -139,7 +139,7 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar
tcx.backend_optimization_level(()),
tcx.global_backend_features(()),
)(config)
.unwrap_or_else(|err| llvm_err(tcx.dcx(), err).raise())
.unwrap_or_else(|err| llvm_err(tcx.dcx(), err))
}
fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize) {
@@ -565,7 +565,7 @@ pub(crate) unsafe fn llvm_optimize(
opt_level: config::OptLevel,
opt_stage: llvm::OptStage,
autodiff_stage: AutodiffStage,
) -> Result<(), FatalError> {
) {
// Enzyme:
// The whole point of compiler based AD is to differentiate optimized IR instead of unoptimized
// source code. However, benchmarks show that optimizations increasing the code size
@@ -704,7 +704,7 @@ pub(crate) unsafe fn llvm_optimize(
llvm_plugins.len(),
)
};
result.into_result().map_err(|()| llvm_err(dcx, LlvmError::RunLlvmPasses))
result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::RunLlvmPasses))
}
// Unsafe due to LLVM calls.
@@ -713,7 +713,7 @@ pub(crate) fn optimize(
dcx: DiagCtxtHandle<'_>,
module: &mut ModuleCodegen<ModuleLlvm>,
config: &ModuleConfig,
) -> Result<(), FatalError> {
) {
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name);
let llcx = &*module.module_llvm.llcx;
@@ -765,7 +765,7 @@ pub(crate) fn optimize(
opt_stage,
autodiff_stage,
)
}?;
};
if let Some(thin_lto_buffer) = thin_lto_buffer {
let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) };
module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec());
@@ -793,14 +793,13 @@ pub(crate) fn optimize(
}
}
}
Ok(())
}
pub(crate) fn codegen(
cgcx: &CodegenContext<LlvmCodegenBackend>,
module: ModuleCodegen<ModuleLlvm>,
config: &ModuleConfig,
) -> Result<CompiledModule, FatalError> {
) -> CompiledModule {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
@@ -909,7 +908,9 @@ pub(crate) fn codegen(
record_artifact_size(&cgcx.prof, "llvm_ir", &out);
}
result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteIr { path: &out }))?;
result
.into_result()
.unwrap_or_else(|()| llvm_err(dcx, LlvmError::WriteIr { path: &out }));
}
if config.emit_asm {
@@ -940,7 +941,7 @@ pub(crate) fn codegen(
llvm::FileType::AssemblyFile,
&cgcx.prof,
config.verify_llvm_ir,
)?;
);
}
match config.emit_obj {
@@ -976,7 +977,7 @@ pub(crate) fn codegen(
llvm::FileType::ObjectFile,
&cgcx.prof,
config.verify_llvm_ir,
)?;
);
}
EmitObj::Bitcode => {
@@ -1009,7 +1010,7 @@ pub(crate) fn codegen(
&& cgcx.target_can_use_split_dwarf
&& cgcx.split_debuginfo != SplitDebuginfo::Off
&& cgcx.split_dwarf_kind == SplitDwarfKind::Split;
Ok(module.into_compiled_module(
module.into_compiled_module(
config.emit_obj != EmitObj::None,
dwarf_object_emitted,
config.emit_bc,
@@ -1017,7 +1018,7 @@ pub(crate) fn codegen(
config.emit_ir,
&cgcx.output_filenames,
cgcx.invocation_temp.as_deref(),
))
)
}
fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data: &[u8]) -> Vec<u8> {

View File

@@ -37,7 +37,7 @@ use rustc_codegen_ssa::back::write::{
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{DiagCtxtHandle, FatalError};
use rustc_errors::DiagCtxtHandle;
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::ty::TyCtxt;
@@ -165,15 +165,15 @@ impl WriteBackendMethods for LlvmCodegenBackend {
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<Self>>,
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
) -> ModuleCodegen<Self::Module> {
let mut module =
back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?;
back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules);
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
back::lto::run_pass_manager(cgcx, dcx, &mut module, false)?;
back::lto::run_pass_manager(cgcx, dcx, &mut module, false);
Ok(module)
module
}
fn run_thin_lto(
cgcx: &CodegenContext<Self>,
@@ -181,7 +181,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, Self::ThinBuffer)>,
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> {
) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
back::lto::run_thin(
cgcx,
exported_symbols_for_lto,
@@ -195,20 +195,20 @@ impl WriteBackendMethods for LlvmCodegenBackend {
dcx: DiagCtxtHandle<'_>,
module: &mut ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> Result<(), FatalError> {
) {
back::write::optimize(cgcx, dcx, module, config)
}
fn optimize_thin(
cgcx: &CodegenContext<Self>,
thin: ThinModule<Self>,
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
) -> ModuleCodegen<Self::Module> {
back::lto::optimize_thin_module(thin, cgcx)
}
fn codegen(
cgcx: &CodegenContext<Self>,
module: ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> Result<CompiledModule, FatalError> {
) -> CompiledModule {
back::write::codegen(cgcx, module, config)
}
fn prepare_thin(
@@ -407,12 +407,12 @@ impl ModuleLlvm {
cgcx: &CodegenContext<LlvmCodegenBackend>,
name: &str,
dcx: DiagCtxtHandle<'_>,
) -> Result<OwnedTargetMachine, FatalError> {
) -> OwnedTargetMachine {
let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, name);
match (cgcx.tm_factory)(tm_factory_config) {
Ok(m) => Ok(m),
Ok(m) => m,
Err(e) => {
return Err(dcx.emit_almost_fatal(ParseTargetMachineConfig(e)));
dcx.emit_fatal(ParseTargetMachineConfig(e));
}
}
}
@@ -422,13 +422,13 @@ impl ModuleLlvm {
name: &CStr,
buffer: &[u8],
dcx: DiagCtxtHandle<'_>,
) -> Result<Self, FatalError> {
) -> Self {
unsafe {
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx)?;
let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx)?;
let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx);
let tm = ModuleLlvm::tm_from_cgcx(cgcx, name.to_str().unwrap(), dcx);
Ok(ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) })
ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }
}
}