Put unwinding support behind a cargo feature

This commit is contained in:
bjorn3 2025-05-07 13:34:29 +00:00
parent 0c7e953885
commit e0ea4b016a
14 changed files with 231 additions and 150 deletions

View file

@ -46,6 +46,7 @@ smallvec = "1.8.1"
unstable-features = ["jit", "inline_asm_sym"]
jit = ["cranelift-jit", "libloading"]
inline_asm_sym = []
unwinding = [] # Not yet included in unstable-features for performance reasons
[package.metadata.rust-analyzer]
rustc_private = true

View file

@ -19,6 +19,7 @@ pub(crate) fn run(
cg_clif_dylib: &CodegenBackend,
rustup_toolchain_name: Option<&str>,
bootstrap_host_compiler: &Compiler,
panic_unwind_support: bool,
) {
std::fs::create_dir_all(&dirs.download_dir).unwrap();
ABI_CAFE_REPO.fetch(dirs);
@ -32,6 +33,7 @@ pub(crate) fn run(
bootstrap_host_compiler,
rustup_toolchain_name,
bootstrap_host_compiler.triple.clone(),
panic_unwind_support,
);
eprintln!("Running abi-cafe");

View file

@ -12,6 +12,7 @@ pub(crate) fn build_backend(
dirs: &Dirs,
bootstrap_host_compiler: &Compiler,
use_unstable_features: bool,
panic_unwind_support: bool,
) -> PathBuf {
let _group = LogGroup::guard("Build backend");
@ -31,6 +32,10 @@ pub(crate) fn build_backend(
cmd.arg("--features").arg("unstable-features");
}
if panic_unwind_support {
cmd.arg("--features").arg("unwinding");
}
cmd.arg("--release");
eprintln!("[BUILD] rustc_codegen_cranelift");

View file

@ -17,6 +17,7 @@ pub(crate) fn build_sysroot(
bootstrap_host_compiler: &Compiler,
rustup_toolchain_name: Option<&str>,
target_triple: String,
panic_unwind_support: bool,
) -> Compiler {
let _guard = LogGroup::guard("Build sysroot");
@ -52,6 +53,9 @@ pub(crate) fn build_sysroot(
.arg("-o")
.arg(&wrapper_path)
.arg("-Cstrip=debuginfo");
if panic_unwind_support {
build_cargo_wrapper_cmd.arg("--cfg").arg("support_panic_unwind");
}
if let Some(rustup_toolchain_name) = &rustup_toolchain_name {
build_cargo_wrapper_cmd
.env("TOOLCHAIN_NAME", rustup_toolchain_name)
@ -77,6 +81,7 @@ pub(crate) fn build_sysroot(
bootstrap_host_compiler.clone(),
&cg_clif_dylib_path,
sysroot_kind,
panic_unwind_support,
);
host.install_into_sysroot(dist_dir);
@ -91,6 +96,7 @@ pub(crate) fn build_sysroot(
},
&cg_clif_dylib_path,
sysroot_kind,
panic_unwind_support,
)
.install_into_sysroot(dist_dir);
}
@ -141,12 +147,15 @@ fn build_sysroot_for_triple(
compiler: Compiler,
cg_clif_dylib_path: &CodegenBackend,
sysroot_kind: SysrootKind,
panic_unwind_support: bool,
) -> SysrootTarget {
match sysroot_kind {
SysrootKind::None => build_rtstartup(dirs, &compiler)
.unwrap_or(SysrootTarget { triple: compiler.triple, libs: vec![] }),
SysrootKind::Llvm => build_llvm_sysroot_for_triple(compiler),
SysrootKind::Clif => build_clif_sysroot_for_triple(dirs, compiler, cg_clif_dylib_path),
SysrootKind::Clif => {
build_clif_sysroot_for_triple(dirs, compiler, cg_clif_dylib_path, panic_unwind_support)
}
}
}
@ -188,6 +197,7 @@ fn build_clif_sysroot_for_triple(
dirs: &Dirs,
mut compiler: Compiler,
cg_clif_dylib_path: &CodegenBackend,
panic_unwind_support: bool,
) -> SysrootTarget {
let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
@ -206,7 +216,10 @@ fn build_clif_sysroot_for_triple(
}
// Build sysroot
let mut rustflags = vec!["-Zforce-unstable-if-unmarked".to_owned(), "-Cpanic=abort".to_owned()];
let mut rustflags = vec!["-Zforce-unstable-if-unmarked".to_owned()];
if !panic_unwind_support {
rustflags.push("-Cpanic=abort".to_owned());
}
match cg_clif_dylib_path {
CodegenBackend::Local(path) => {
rustflags.push(format!("-Zcodegen-backend={}", path.to_str().unwrap()));

View file

@ -83,6 +83,7 @@ fn main() {
let mut download_dir = None;
let mut sysroot_kind = SysrootKind::Clif;
let mut use_unstable_features = true;
let mut panic_unwind_support = false;
let mut frozen = false;
let mut skip_tests = vec![];
let mut use_backend = None;
@ -108,6 +109,7 @@ fn main() {
}
}
"--no-unstable-features" => use_unstable_features = false,
"--panic-unwind-support" => panic_unwind_support = true,
"--frozen" => frozen = true,
"--skip-test" => {
// FIXME check that all passed in tests actually exist
@ -201,6 +203,7 @@ fn main() {
&dirs,
&bootstrap_host_compiler,
use_unstable_features,
panic_unwind_support,
))
};
match command {
@ -212,6 +215,7 @@ fn main() {
&dirs,
sysroot_kind,
use_unstable_features,
panic_unwind_support,
&skip_tests.iter().map(|test| &**test).collect::<Vec<_>>(),
&cg_clif_dylib,
&bootstrap_host_compiler,
@ -230,6 +234,7 @@ fn main() {
&cg_clif_dylib,
rustup_toolchain_name.as_deref(),
&bootstrap_host_compiler,
panic_unwind_support,
);
}
Command::Build => {
@ -240,6 +245,7 @@ fn main() {
&bootstrap_host_compiler,
rustup_toolchain_name.as_deref(),
target_triple,
panic_unwind_support,
);
}
Command::Bench => {
@ -250,6 +256,7 @@ fn main() {
&bootstrap_host_compiler,
rustup_toolchain_name.as_deref(),
target_triple,
panic_unwind_support,
);
bench::benchmark(&dirs, &compiler);
}

View file

@ -233,6 +233,7 @@ pub(crate) fn run_tests(
dirs: &Dirs,
sysroot_kind: SysrootKind,
use_unstable_features: bool,
panic_unwind_support: bool,
skip_tests: &[&str],
cg_clif_dylib: &CodegenBackend,
bootstrap_host_compiler: &Compiler,
@ -251,12 +252,14 @@ pub(crate) fn run_tests(
bootstrap_host_compiler,
rustup_toolchain_name,
target_triple.clone(),
panic_unwind_support,
);
let runner = TestRunner::new(
dirs.clone(),
target_compiler,
use_unstable_features,
panic_unwind_support,
skip_tests,
bootstrap_host_compiler.triple == target_triple,
stdlib_source.clone(),
@ -283,12 +286,14 @@ pub(crate) fn run_tests(
bootstrap_host_compiler,
rustup_toolchain_name,
target_triple.clone(),
panic_unwind_support,
);
let mut runner = TestRunner::new(
dirs.clone(),
target_compiler,
use_unstable_features,
panic_unwind_support,
skip_tests,
bootstrap_host_compiler.triple == target_triple,
stdlib_source,
@ -314,6 +319,7 @@ pub(crate) fn run_tests(
struct TestRunner<'a> {
is_native: bool,
jit_supported: bool,
panic_unwind_support: bool,
skip_tests: &'a [&'a str],
dirs: Dirs,
target_compiler: Compiler,
@ -325,6 +331,7 @@ impl<'a> TestRunner<'a> {
dirs: Dirs,
mut target_compiler: Compiler,
use_unstable_features: bool,
panic_unwind_support: bool,
skip_tests: &'a [&'a str],
is_native: bool,
stdlib_source: PathBuf,
@ -335,7 +342,15 @@ impl<'a> TestRunner<'a> {
let jit_supported =
use_unstable_features && is_native && !target_compiler.triple.contains("windows");
Self { is_native, jit_supported, skip_tests, dirs, target_compiler, stdlib_source }
Self {
is_native,
jit_supported,
panic_unwind_support,
skip_tests,
dirs,
target_compiler,
stdlib_source,
}
}
fn run_testsuite(&self, tests: &[TestCase]) {
@ -404,7 +419,9 @@ impl<'a> TestRunner<'a> {
cmd.arg("-Cdebuginfo=2");
cmd.arg("--target");
cmd.arg(&self.target_compiler.triple);
cmd.arg("-Cpanic=abort");
if !self.panic_unwind_support {
cmd.arg("-Cpanic=abort");
}
cmd.arg("--check-cfg=cfg(jit)");
cmd.args(args);
cmd

View file

@ -25,6 +25,10 @@ OPTIONS:
Some features are not yet ready for production usage. This option will disable these
features. This includes the JIT mode and inline assembly support.
--panic-unwind-support
Enable support for unwinding when -Cpanic=unwind is used. This currently regresses build
performance.
--frozen
Require Cargo.lock and cache are up to date

View file

@ -12,7 +12,11 @@ fn main() {
sysroot = sysroot.parent().unwrap();
}
let mut rustflags = vec!["-Cpanic=abort".to_owned(), "-Zpanic-abort-tests".to_owned()];
let mut rustflags = vec![];
if !cfg!(support_panic_unwind) {
rustflags.push("-Cpanic=abort".to_owned());
rustflags.push("-Zpanic-abort-tests".to_owned());
}
if let Some(name) = option_env!("BUILTIN_BACKEND") {
rustflags.push(format!("-Zcodegen-backend={name}"));
} else {

View file

@ -17,8 +17,10 @@ fn main() {
let passed_args = std::env::args_os().skip(1).collect::<Vec<_>>();
let mut args = vec![];
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
if !cfg!(support_panic_unwind) {
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
}
if let Some(name) = option_env!("BUILTIN_BACKEND") {
args.push(OsString::from(format!("-Zcodegen-backend={name}")))
} else {

View file

@ -17,8 +17,10 @@ fn main() {
let passed_args = std::env::args_os().skip(1).collect::<Vec<_>>();
let mut args = vec![];
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
if !cfg!(support_panic_unwind) {
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
}
if let Some(name) = option_env!("BUILTIN_BACKEND") {
args.push(OsString::from(format!("-Zcodegen-backend={name}")))
} else {

View file

@ -844,7 +844,7 @@ pub(crate) fn codegen_call_with_unwind_action(
fx: &mut FunctionCx<'_, '_, '_>,
span: Span,
func_ref: CallTarget,
unwind: UnwindAction,
mut unwind: UnwindAction,
call_args: &[Value],
target_block: Option<Block>,
) -> SmallVec<[Value; 2]> {
@ -856,6 +856,11 @@ pub(crate) fn codegen_call_with_unwind_action(
if target_block.is_some() {
assert!(fx.bcx.func.dfg.signatures[sig_ref].returns.is_empty());
}
if cfg!(not(feature = "unwinding")) {
unwind = UnwindAction::Unreachable;
}
match unwind {
UnwindAction::Continue | UnwindAction::Unreachable => {
let call_inst = match func_ref {

View file

@ -308,6 +308,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
}
if bb_data.is_cleanup {
if cfg!(not(feature = "unwinding")) {
continue;
}
fx.bcx.set_cold_block(block);
}
@ -542,13 +546,15 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
codegen_unwind_terminate(fx, source_info.span, *reason);
}
TerminatorKind::UnwindResume => {
let exception_ptr = fx.bcx.use_var(fx.exception_slot);
fx.lib_call(
"_Unwind_Resume",
vec![AbiParam::new(fx.pointer_type)],
vec![],
&[exception_ptr],
);
if cfg!(feature = "unwinding") {
let exception_ptr = fx.bcx.use_var(fx.exception_slot);
fx.lib_call(
"_Unwind_Resume",
vec![AbiParam::new(fx.pointer_type)],
vec![],
&[exception_ptr],
);
}
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
}
TerminatorKind::Unreachable => {

View file

@ -37,68 +37,74 @@ impl UnwindContext {
} else {
gimli::DW_EH_PE_absptr
};
let code_ptr_encoding = if pic_eh_frame {
if module.isa().triple().architecture == target_lexicon::Architecture::X86_64 {
gimli::DwEhPe(
gimli::DW_EH_PE_indirect.0
| gimli::DW_EH_PE_pcrel.0
| gimli::DW_EH_PE_sdata4.0,
)
} else if let target_lexicon::Architecture::Aarch64(_) =
module.isa().triple().architecture
{
gimli::DwEhPe(
gimli::DW_EH_PE_indirect.0
| gimli::DW_EH_PE_pcrel.0
| gimli::DW_EH_PE_sdata8.0,
)
} else {
todo!()
}
} else {
gimli::DwEhPe(gimli::DW_EH_PE_indirect.0 | gimli::DW_EH_PE_absptr.0)
};
cie.fde_address_encoding = ptr_encoding;
cie.lsda_encoding = Some(ptr_encoding);
// FIXME use eh_personality lang item instead
let personality = module
.declare_function(
"rust_eh_personality",
Linkage::Import,
&Signature {
params: vec![
AbiParam::new(types::I32),
AbiParam::new(types::I32),
AbiParam::new(types::I64),
AbiParam::new(module.target_config().pointer_type()),
AbiParam::new(module.target_config().pointer_type()),
],
returns: vec![AbiParam::new(types::I32)],
call_conv: module.target_config().default_call_conv,
},
)
.unwrap();
// FIXME only add personality function and lsda when necessary: https://github.com/rust-lang/rust/blob/1f76d219c906f0112bb1872f33aa977164c53fa6/compiler/rustc_codegen_ssa/src/mir/mod.rs#L200-L204
if cfg!(feature = "unwinding") {
let code_ptr_encoding = if pic_eh_frame {
if module.isa().triple().architecture == target_lexicon::Architecture::X86_64 {
gimli::DwEhPe(
gimli::DW_EH_PE_indirect.0
| gimli::DW_EH_PE_pcrel.0
| gimli::DW_EH_PE_sdata4.0,
)
} else if let target_lexicon::Architecture::Aarch64(_) =
module.isa().triple().architecture
{
gimli::DwEhPe(
gimli::DW_EH_PE_indirect.0
| gimli::DW_EH_PE_pcrel.0
| gimli::DW_EH_PE_sdata8.0,
)
} else {
todo!()
}
} else {
gimli::DwEhPe(gimli::DW_EH_PE_indirect.0 | gimli::DW_EH_PE_absptr.0)
};
// Use indirection here to support PIC the case where rust_eh_personality is defined in
// another DSO.
let personality_ref = module
.declare_data("DW.ref.rust_eh_personality", Linkage::Local, false, false)
.unwrap();
cie.lsda_encoding = Some(ptr_encoding);
let mut personality_ref_data = DataDescription::new();
// Note: Must not use define_zeroinit. The unwinder can't handle this being in the .bss
// section.
let pointer_bytes = usize::from(module.target_config().pointer_bytes());
personality_ref_data.define(vec![0; pointer_bytes].into_boxed_slice());
let personality_func_ref =
module.declare_func_in_data(personality, &mut personality_ref_data);
personality_ref_data.write_function_addr(0, personality_func_ref);
// FIXME use eh_personality lang item instead
let personality = module
.declare_function(
"rust_eh_personality",
Linkage::Import,
&Signature {
params: vec![
AbiParam::new(types::I32),
AbiParam::new(types::I32),
AbiParam::new(types::I64),
AbiParam::new(module.target_config().pointer_type()),
AbiParam::new(module.target_config().pointer_type()),
],
returns: vec![AbiParam::new(types::I32)],
call_conv: module.target_config().default_call_conv,
},
)
.unwrap();
module.define_data(personality_ref, &personality_ref_data).unwrap();
// Use indirection here to support PIC the case where rust_eh_personality is defined in
// another DSO.
let personality_ref = module
.declare_data("DW.ref.rust_eh_personality", Linkage::Local, false, false)
.unwrap();
let mut personality_ref_data = DataDescription::new();
// Note: Must not use define_zeroinit. The unwinder can't handle this being in the .bss
// section.
let pointer_bytes = usize::from(module.target_config().pointer_bytes());
personality_ref_data.define(vec![0; pointer_bytes].into_boxed_slice());
let personality_func_ref =
module.declare_func_in_data(personality, &mut personality_ref_data);
personality_ref_data.write_function_addr(0, personality_func_ref);
module.define_data(personality_ref, &personality_ref_data).unwrap();
cie.personality = Some((code_ptr_encoding, address_for_data(personality_ref)));
}
cie.personality = Some((code_ptr_encoding, address_for_data(personality_ref)));
Some(frame_table.add_cie(cie))
} else {
None
@ -134,88 +140,93 @@ impl UnwindContext {
match unwind_info {
UnwindInfo::SystemV(unwind_info) => {
let mut fde = unwind_info.to_fde(address_for_func(func_id));
// FIXME use unique symbol name derived from function name
let lsda = module.declare_anonymous_data(false, false).unwrap();
let encoding = Encoding {
format: Format::Dwarf32,
version: 1,
address_size: module.isa().frontend_config().pointer_bytes(),
};
// FIXME only add personality function and lsda when necessary: https://github.com/rust-lang/rust/blob/1f76d219c906f0112bb1872f33aa977164c53fa6/compiler/rustc_codegen_ssa/src/mir/mod.rs#L200-L204
if cfg!(feature = "unwinding") {
// FIXME use unique symbol name derived from function name
let lsda = module.declare_anonymous_data(false, false).unwrap();
let mut gcc_except_table_data = GccExceptTable {
call_sites: CallSiteTable(vec![]),
actions: ActionTable::new(),
type_info: TypeInfoTable::new(gimli::DW_EH_PE_udata4),
};
let catch_type = gcc_except_table_data.type_info.add(Address::Constant(0));
let catch_action = gcc_except_table_data
.actions
.add(Action { kind: ActionKind::Catch(catch_type), next_action: None });
for call_site in context.compiled_code().unwrap().buffer.call_sites() {
if call_site.exception_handlers.is_empty() {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: 0,
action_entry: None,
});
}
for &(tag, landingpad) in call_site.exception_handlers {
match tag.expand().unwrap().as_u32() {
EXCEPTION_HANDLER_CLEANUP => {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: u64::from(landingpad),
action_entry: None,
})
}
EXCEPTION_HANDLER_CATCH => {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: u64::from(landingpad),
action_entry: Some(catch_action),
})
}
_ => unreachable!(),
}
}
}
let mut gcc_except_table = super::emit::WriterRelocate::new(self.endian);
gcc_except_table_data.write(&mut gcc_except_table, encoding).unwrap();
let mut data = DataDescription::new();
data.define(gcc_except_table.writer.into_vec().into_boxed_slice());
data.set_segment_section("", ".gcc_except_table");
for reloc in &gcc_except_table.relocs {
match reloc.name {
DebugRelocName::Section(_id) => unreachable!(),
DebugRelocName::Symbol(id) => {
let id = id.try_into().unwrap();
if id & 1 << 31 == 0 {
let func_ref =
module.declare_func_in_data(FuncId::from_u32(id), &mut data);
data.write_function_addr(reloc.offset, func_ref);
} else {
let gv = module.declare_data_in_data(
DataId::from_u32(id & !(1 << 31)),
&mut data,
);
data.write_data_addr(reloc.offset, gv, 0);
}
}
let encoding = Encoding {
format: Format::Dwarf32,
version: 1,
address_size: module.isa().frontend_config().pointer_bytes(),
};
let mut gcc_except_table_data = GccExceptTable {
call_sites: CallSiteTable(vec![]),
actions: ActionTable::new(),
type_info: TypeInfoTable::new(gimli::DW_EH_PE_udata4),
};
let catch_type = gcc_except_table_data.type_info.add(Address::Constant(0));
let catch_action = gcc_except_table_data
.actions
.add(Action { kind: ActionKind::Catch(catch_type), next_action: None });
for call_site in context.compiled_code().unwrap().buffer.call_sites() {
if call_site.exception_handlers.is_empty() {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: 0,
action_entry: None,
});
}
for &(tag, landingpad) in call_site.exception_handlers {
match tag.expand().unwrap().as_u32() {
EXCEPTION_HANDLER_CLEANUP => {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: u64::from(landingpad),
action_entry: None,
})
}
EXCEPTION_HANDLER_CATCH => {
gcc_except_table_data.call_sites.0.push(CallSite {
start: u64::from(call_site.ret_addr - 1),
length: 1,
landing_pad: u64::from(landingpad),
action_entry: Some(catch_action),
})
}
_ => unreachable!(),
}
}
}
let mut gcc_except_table = super::emit::WriterRelocate::new(self.endian);
gcc_except_table_data.write(&mut gcc_except_table, encoding).unwrap();
let mut data = DataDescription::new();
data.define(gcc_except_table.writer.into_vec().into_boxed_slice());
data.set_segment_section("", ".gcc_except_table");
for reloc in &gcc_except_table.relocs {
match reloc.name {
DebugRelocName::Section(_id) => unreachable!(),
DebugRelocName::Symbol(id) => {
let id = id.try_into().unwrap();
if id & 1 << 31 == 0 {
let func_ref = module
.declare_func_in_data(FuncId::from_u32(id), &mut data);
data.write_function_addr(reloc.offset, func_ref);
} else {
let gv = module.declare_data_in_data(
DataId::from_u32(id & !(1 << 31)),
&mut data,
);
data.write_data_addr(reloc.offset, gv, 0);
}
}
};
}
module.define_data(lsda, &data).unwrap();
fde.lsda = Some(address_for_data(lsda));
}
module.define_data(lsda, &data).unwrap();
fde.lsda = Some(address_for_data(lsda));
self.frame_table.add_fde(self.cie_id.unwrap(), fde);
}
UnwindInfo::WindowsX64(_) | UnwindInfo::WindowsArm64(_) => {

View file

@ -1367,7 +1367,9 @@ fn codegen_regular_intrinsic_call<'tcx>(
returns: vec![],
});
if fx.tcx.sess.panic_strategy() == PanicStrategy::Abort {
if cfg!(not(feature = "unwinding"))
|| fx.tcx.sess.panic_strategy() == PanicStrategy::Abort
{
fx.bcx.ins().call_indirect(f_sig, f, &[data]);
let layout = fx.layout_of(fx.tcx.types.i32);