From 474b14cfea3430ca6a8cbed9248a138a671d06cf Mon Sep 17 00:00:00 2001 From: "Eddy (Eduard) Stefes" Date: Thu, 24 Sep 2026 13:27:04 +0200 Subject: [PATCH] s390x: replace -Ctarget-feature=+backchain with -Cforce-frame-pointers On s390x, the `backchain` LLVM function attribute saves the "unwind" (backchain) pointer (r15) into the caller's parameter area, enabling external stack unwinders (e.g. Linux perf) to walk the call chain. Previously this was enabled via `-Ctarget-feature=+backchain`, which piggy-backed on the target-feature machinery even though backchain is not a CPU or ISA feature. This commit introduces a cleaner mechanism: - `-Cforce-frame-pointers` now enables the `"backchain"` LLVM *function attribute* on s390x, rather than the generic `"frame-pointer"` attribute used on other architectures. - `-Ctarget-feature=+backchain` is completely removed and will be rejected. - When the backchain opt-in is active, the `"frame-pointer"` attribute is suppressed on s390x. Other reasons to set frame pointers (e.g. `-Zinstrument-mcount`) are unaffected and still emit `"frame-pointer"`. - All s390x-specific frame-pointer/backchain/packed-stack logic is consolidated in `s390x_fn_attrs()` in `attributes.rs`. Co-authored-by: Ralf Jung --- compiler/rustc_codegen_llvm/src/attributes.rs | 47 +++++++++++++------ .../rustc_codegen_llvm/src/diagnostics.rs | 4 +- compiler/rustc_span/src/symbol.rs | 1 - compiler/rustc_target/src/target_features.rs | 2 +- src/doc/rustc/src/codegen-options/index.md | 6 +++ tests/assembly-llvm/s390x-backchain-toggle.rs | 21 ++------- tests/codegen-llvm/backchain.rs | 5 +- tests/codegen-llvm/force-frame-pointers.rs | 3 ++ .../codegen-llvm/frame-pointer-cli-control.rs | 4 ++ tests/ui/check-cfg/target_feature.stderr | 1 - .../forbidden-target-feature-backchain.rs | 10 ++++ .../forbidden-target-feature-backchain.stderr | 4 ++ ...edstack-combinations.backchain_attr.stderr | 6 --- ...edstack-combinations.need_softfloat.stderr | 10 ---- .../packedstack-combinations.rs | 21 ++++----- 15 files changed, 77 insertions(+), 68 deletions(-) create mode 100644 tests/ui/target-feature/forbidden-target-feature-backchain.rs create mode 100644 tests/ui/target-feature/forbidden-target-feature-backchain.stderr delete mode 100644 tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr delete mode 100644 tests/ui/target-feature/packedstack-combinations.need_softfloat.stderr diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 4073d521293a9..f42baf5d80838 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -222,7 +222,11 @@ pub(crate) fn frame_pointer(sess: &Session) -> FramePointer { if let InstrumentMcount::Mcount(_) = opts.unstable_opts.instrument_mcount { fp.ratchet(FramePointer::Always); } - fp.ratchet(opts.cg.force_frame_pointers); + // On s390x, `-Cforce-frame-pointers` opts in to the `backchain` LLVM function attribute + // (set in `s390x_fn_attrs`) instead of the generic `frame-pointer` attribute. + if !(sess.target.arch == Arch::S390x) { + fp.ratchet(opts.cg.force_frame_pointers); + } fp } @@ -396,34 +400,44 @@ fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll A Some(sspattr.create_attr(cx.llcx)) } -fn packed_stack_attr<'ll>( +/// s390x specific function attributes +/// - `-Cforce-frame-pointers` emits "backchain" +/// - "backchain" + "packed_stack" enforces "softfloat" +fn s390x_fn_attrs<'ll>( cx: &SimpleCx<'ll>, sess: &Session, - function_attributes: &Vec, -) -> Option<&'ll Attribute> { + fn_target_features: &[TargetFeature], +) -> Vec<&'ll Attribute> { if sess.target.arch != Arch::S390x { - return None; + return vec![]; + } + + let mut attrs = Vec::new(); + + // `-Cforce-frame-pointers` enables `backchain` + let have_backchain = sess.opts.cg.force_frame_pointers != FramePointer::MayOmit; + if have_backchain { + attrs.push(llvm::CreateAttrString(cx.llcx, "backchain")); } + + // only continue if we have packed_stack if !sess.opts.unstable_opts.packed_stack { - return None; + return attrs; } - // The backchain and softfloat flags can be set via -Ctarget-features=... - // or via #[target_features(enable = ...)] so we have to check both possibilities - let have_backchain = sess.internal_target_features.contains(&sym::backchain) - || function_attributes.iter().any(|feature| feature.name == sym::backchain); let have_softfloat = sess.internal_target_features.contains(&sym::soft_float) - || function_attributes.iter().any(|feature| feature.name == sym::soft_float); + || fn_target_features.iter().any(|f| f.name == sym::soft_float); // If both, backchain and packedstack, are enabled LLVM cannot generate valid function entry points // with the default ABI. However if the softfloat flag is set LLVM will switch to the softfloat // ABI, where this works. if have_backchain && !have_softfloat { sess.dcx().emit_err(PackedStackBackchainNeedsSoftfloat); - return None; + return attrs; } - Some(llvm::CreateAttrString(cx.llcx, "packed-stack")) + attrs.push(llvm::CreateAttrString(cx.llcx, "packed-stack")); + attrs } pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute { @@ -650,9 +664,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } - if let Some(packed_stack) = packed_stack_attr(cx, sess, &codegen_fn_attrs.target_features) { - to_add.push(packed_stack); + + // Apply and check s390x-specific attributes: backchain and packed-stack. + if sess.target.arch == Arch::S390x { + to_add.extend(s390x_fn_attrs(cx, sess, &codegen_fn_attrs.target_features)); } + to_add.extend(patchable_function_entry_attrs( cx, sess, diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 08b96770e46d1..a06f22310c310 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -227,9 +227,9 @@ pub(crate) struct FixedX18InvalidArch<'a> { } #[derive(Diagnostic)] -#[diag("`-Zpacked-stack` is incompatible with `backchain` target feature")] +#[diag("`-Zpacked-stack` is incompatible with `-Cforce-frame-pointers` on s390x")] #[note( - "enabling both `-Zpacked-stack` and the `backchain` target feature is incompatible with the default s390x ABI. Switch to s390x-unknown-none-softfloat if you need both attributes" + "`-Zpacked-stack` and `-Cforce-frame-pointers` is incompatible with the default s390x ABI (note that force-frame-pointers is implemented via backchain)" )] pub(crate) struct PackedStackBackchainNeedsSoftfloat; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3debf3248ff80..57c20a8d610ff 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -542,7 +542,6 @@ symbols! { avx512bw, avx512f, await_macro, - backchain, backend_repr, begin_panic, bench, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 2ee7dea9f8af3..2ee73bf9fadd5 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -937,7 +937,7 @@ static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker - ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("backchain", InternalOnly { reason: "use `-Cforce-frame-pointers` compiler flag instead", hard_error: true }, &[]), ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]), ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]), ("enhanced-sort", Unstable(sym::s390x_target_feature), &[]), diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 0f85e6d00e36b..7a8a1ba7e3c76 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -170,6 +170,12 @@ values: The default behaviour, if frame pointers are not force-enabled, depends on the target. +#### Implementation notes + +On s390x targets, frame pointers cannot be used to walk the stack. Instead of enabling frame +pointers, this flag enables the `backchain`, which stores a pointer to the caller's frame in each +function's frame. + ## force-unwind-tables This flag forces the generation of unwind tables. It takes one of the following diff --git a/tests/assembly-llvm/s390x-backchain-toggle.rs b/tests/assembly-llvm/s390x-backchain-toggle.rs index baf7104727833..bc602d1baec8c 100644 --- a/tests/assembly-llvm/s390x-backchain-toggle.rs +++ b/tests/assembly-llvm/s390x-backchain-toggle.rs @@ -3,8 +3,8 @@ //@ assembly-output: emit-asm //@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu //@ needs-llvm-components: systemz -//@[enable-backchain] compile-flags: -Ctarget-feature=+backchain -//@[disable-backchain] compile-flags: -Ctarget-feature=-backchain +//@[enable-backchain] compile-flags: -Cforce-frame-pointers +//@[disable-backchain] compile-flags: -Cforce-frame-pointers=no #![feature(no_core, lang_items)] #![no_std] #![no_core] @@ -31,20 +31,7 @@ extern "C" fn test_backchain() -> i32 { unsafe { extern_func(); } - // enable-backchain-NEXT: brasl %r{{.*}}, extern_func@PLT - // disable-backchain: brasl %r{{.*}}, extern_func@PLT - - // Make sure that the expected return value is written into %r2 (return register): - // enable-backchain-NEXT: lghi %r2, 1 - // disable-backchain: lghi %r2, 0 - // default-backchain: lghi %r2, 0 - #[cfg(target_feature = "backchain")] - { - 1 - } - #[cfg(not(target_feature = "backchain"))] - { - 0 - } + // NEXT: brasl %r{{.*}}, extern_func@PLT + 1 // CHECK: br %r{{.*}} } diff --git a/tests/codegen-llvm/backchain.rs b/tests/codegen-llvm/backchain.rs index fae494dcad1b4..1cb01f3cf0032 100644 --- a/tests/codegen-llvm/backchain.rs +++ b/tests/codegen-llvm/backchain.rs @@ -1,5 +1,6 @@ //@ add-minicore -//@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu -Ctarget-feature=+backchain +//@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu +//@ compile-flags: -Cforce-frame-pointers=yes //@ needs-llvm-components: systemz #![crate_type = "lib"] #![feature(no_core, lang_items)] @@ -12,4 +13,4 @@ use minicore::*; pub fn test_backchain() { // CHECK: @test_backchain() unnamed_addr #0 } -// CHECK: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+backchain{{.*}} } +// CHECK: attributes #0 = { {{.*}}"backchain"{{.*}} } diff --git a/tests/codegen-llvm/force-frame-pointers.rs b/tests/codegen-llvm/force-frame-pointers.rs index 2cdc37e691115..1d7cb2bea60aa 100644 --- a/tests/codegen-llvm/force-frame-pointers.rs +++ b/tests/codegen-llvm/force-frame-pointers.rs @@ -9,6 +9,9 @@ //@ [NonLeaf] ignore-x86_64-apple-darwin //@ [NonLeaf] ignore-windows-gnu //@ [NonLeaf] ignore-thumb +// On s390x, -Cforce-frame-pointers emits `"backchain"` not `"frame-pointer"`. +// Since this test passes -Zunstable-options globally, it must be skipped on s390x. +//@ ignore-s390x // result is platform-dependent based on platform's frame pointer settings #![crate_type = "lib"] diff --git a/tests/codegen-llvm/frame-pointer-cli-control.rs b/tests/codegen-llvm/frame-pointer-cli-control.rs index 79cdfc70f1ad7..47b2464bcd07c 100644 --- a/tests/codegen-llvm/frame-pointer-cli-control.rs +++ b/tests/codegen-llvm/frame-pointer-cli-control.rs @@ -2,6 +2,7 @@ //@ compile-flags: --crate-type=rlib -Copt-level=0 //@ revisions: force-on aarch64-apple aarch64-apple-on aarch64-apple-off //@ [force-on] compile-flags: -Cforce-frame-pointers=on +//@ [force-on] ignore-s390x //@ [aarch64-apple] needs-llvm-components: aarch64 //@ [aarch64-apple] compile-flags: --target=aarch64-apple-darwin //@ [aarch64-apple-on] needs-llvm-components: aarch64 @@ -39,6 +40,9 @@ Specific cases where platforms or tools rely on frame pointers for sound or corr - openbsd: - i686-msvc - i686-mingw: + +S390x is a special case because on this platform framepointers cannot be use for unwinding. +On s390x `-Cforce-frame-pointers=on' will emit a 'backchain' attribute which is tested elsewhere. */ #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 13c49fe00ef1f..ca6fdd4995c2a 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -56,7 +56,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `avxvnniint16` `avxvnniint8` `b` -`backchain` `bf16` `bmi1` `bmi2` diff --git a/tests/ui/target-feature/forbidden-target-feature-backchain.rs b/tests/ui/target-feature/forbidden-target-feature-backchain.rs new file mode 100644 index 0000000000000..b868352321e6c --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-backchain.rs @@ -0,0 +1,10 @@ +//! Ensure `backchain` cannot be enabled via `-Ctarget-feature` on s390x. +//@ compile-flags: --crate-type=lib --target=s390x-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=+backchain +//@ needs-llvm-components: systemz +//@ ignore-backends: gcc + +#![feature(no_core)] +#![no_core] + +//~? ERROR target feature `backchain` cannot be enabled with `-Ctarget-feature`: use `-Cforce-frame-pointers` compiler flag instead diff --git a/tests/ui/target-feature/forbidden-target-feature-backchain.stderr b/tests/ui/target-feature/forbidden-target-feature-backchain.stderr new file mode 100644 index 0000000000000..38847e3d8fe4e --- /dev/null +++ b/tests/ui/target-feature/forbidden-target-feature-backchain.stderr @@ -0,0 +1,4 @@ +error: target feature `backchain` cannot be enabled with `-Ctarget-feature`: use `-Cforce-frame-pointers` compiler flag instead + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr b/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr deleted file mode 100644 index 6270e43e93255..0000000000000 --- a/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: `-Zpacked-stack` is incompatible with `backchain` target feature - | - = note: enabling both `-Zpacked-stack` and the `backchain` target feature is incompatible with the default s390x ABI. Switch to s390x-unknown-none-softfloat if you need both attributes - -error: aborting due to 1 previous error - diff --git a/tests/ui/target-feature/packedstack-combinations.need_softfloat.stderr b/tests/ui/target-feature/packedstack-combinations.need_softfloat.stderr deleted file mode 100644 index 6f4531945ee07..0000000000000 --- a/tests/ui/target-feature/packedstack-combinations.need_softfloat.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `backchain` - | - = note: this feature is not stably supported; its behavior can change in the future - -error: packedstack with backchain needs softfloat - | - = note: enabling both `-Zpacked-stack` and the `backchain` target feature is incompatible with the default s390x ABI. Switch to s390x-unknown-none-softfloat if you need both attributes - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/target-feature/packedstack-combinations.rs b/tests/ui/target-feature/packedstack-combinations.rs index dc45ac1ff5e83..4898cc6ba672e 100644 --- a/tests/ui/target-feature/packedstack-combinations.rs +++ b/tests/ui/target-feature/packedstack-combinations.rs @@ -1,5 +1,6 @@ +// backchain on s390x is enabled with -Cforce-frame-pointers //@ add-minicore -//@ revisions: wrong_arch only_packedstack backchain_attr backchain_cli with_softfloat +//@ revisions: wrong_arch only_packedstack backchain_cli with_softfloat //@ compile-flags: -Zpacked-stack --crate-type=rlib //@ ignore-backends: gcc @@ -11,32 +12,26 @@ //@ [only_packedstack] build-pass //@ [only_packedstack] needs-llvm-components: systemz -//@ [backchain_attr] compile-flags: --target=s390x-unknown-linux-gnu -//@ [backchain_attr] build-fail -//@ [backchain_attr] needs-llvm-components: systemz - -//@ [backchain_cli] compile-flags: -Ctarget-feature=+backchain --target=s390x-unknown-linux-gnu +//@ [backchain_cli] compile-flags: --target=s390x-unknown-linux-gnu +//@ [backchain_cli] compile-flags: -Cforce-frame-pointers //@ [backchain_cli] should-fail //@ [backchain_cli] needs-llvm-components: systemz -//@ [with_softfloat] compile-flags: -Ctarget-feature=+backchain +//@ [with_softfloat] compile-flags: -Cforce-frame-pointers //@ [with_softfloat] compile-flags: --target=s390x-unknown-none-softfloat //@ [with_softfloat] build-pass //@ [with_softfloat] needs-llvm-components: systemz #![feature(s390x_target_feature)] #![crate_type = "rlib"] -#![feature(no_core,lang_items)] +#![feature(no_core, lang_items)] #![no_core] extern crate minicore; use minicore::*; #[no_mangle] -#[cfg_attr(backchain_attr,target_feature(enable = "backchain"))] -pub fn test() { -} +pub fn test() {} //[wrong_arch]~? ERROR `-Zpacked-stack` is only supported on s390x -//[backchain_cli]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature -//[backchain_attr]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature +//[backchain_cli]~? ERROR `-Zpacked-stack` is incompatible with `-Cforce-frame-pointers` (backchain)