add rustc option -Zpacked-stack

this enables packed-stack just as -mpacked-stack in clang and gcc.
packed-stack is needed on s390x for kernel development.

Co-authored-by: Ralf Jung <post@ralfj.de>
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index f300ee5..1f59d25 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -3,16 +3,17 @@
 use rustc_hir::def_id::DefId;
 use rustc_hir::find_attr;
 use rustc_middle::middle::codegen_fn_attrs::{
-    CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
+    CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, TargetFeature,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
+use rustc_span::sym;
 use rustc_symbol_mangling::mangle_internal_symbol;
 use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector};
 use smallvec::SmallVec;
 
 use crate::context::SimpleCx;
-use crate::errors::SanitizerMemtagRequiresMte;
+use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte};
 use crate::llvm::AttributePlace::Function;
 use crate::llvm::{
     self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value,
@@ -302,6 +303,36 @@ fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll A
     Some(sspattr.create_attr(cx.llcx))
 }
 
+fn packed_stack_attr<'ll>(
+    cx: &SimpleCx<'ll>,
+    sess: &Session,
+    function_attributes: &Vec<TargetFeature>,
+) -> Option<&'ll Attribute> {
+    if sess.target.arch != Arch::S390x {
+        return None;
+    }
+    if !sess.opts.unstable_opts.packed_stack {
+        return None;
+    }
+
+    // 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.unstable_target_features.contains(&sym::backchain)
+        || function_attributes.iter().any(|feature| feature.name == sym::backchain);
+    let have_softfloat = sess.unstable_target_features.contains(&sym::soft_float)
+        || function_attributes.iter().any(|feature| feature.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;
+    }
+
+    Some(llvm::CreateAttrString(cx.llcx, "packed-stack"))
+}
+
 pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute {
     let target_cpu = llvm_util::target_cpu(sess);
     llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
@@ -517,6 +548,9 @@ 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);
+    }
     to_add.extend(patchable_function_entry_attrs(
         cx,
         sess,
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 880abb3..caec20d 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -204,3 +204,10 @@ pub(crate) struct MismatchedDataLayout<'a> {
 pub(crate) struct FixedX18InvalidArch<'a> {
     pub arch: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag("`-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"
+)]
+pub(crate) struct PackedStackBackchainNeedsSoftfloat;
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index d1899de..295d9c3 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -530,3 +530,7 @@ pub(crate) struct UnexpectedBuiltinCfg {
 #[derive(Diagnostic)]
 #[diag("ThinLTO is not supported by the codegen backend, using fat LTO instead")]
 pub(crate) struct ThinLtoNotSupportedByBackend;
+
+#[derive(Diagnostic)]
+#[diag("`-Zpacked-stack` is only supported on s390x")]
+pub(crate) struct UnsupportedPackedStack;
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 8771dbd..027a704 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2557,6 +2557,8 @@ pub(crate) fn parse_assert_incr_state(
         "pass `-install_name @rpath/...` to the macOS linker (default: no)"),
     packed_bundled_libs: bool = (false, parse_bool, [TRACKED],
         "change rlib format to store native libraries as archives"),
+    packed_stack: bool = (false, parse_bool, [TRACKED],
+        "use packed stack frames (s390x only) (default: no)"),
     panic_abort_tests: bool = (false, parse_bool, [TRACKED],
         "support compiling tests with panic=abort (default: no)"),
     panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 53eae0d..a9e7f15 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1363,6 +1363,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
             }
         }
     }
+
+    if sess.opts.unstable_opts.packed_stack {
+        if sess.target.arch != Arch::S390x {
+            sess.dcx().emit_err(errors::UnsupportedPackedStack);
+        }
+    }
 }
 
 /// Holds data on the current incremental compilation session, if there is one.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7b359dc..87b5b05 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -505,6 +505,7 @@
         avx512bw,
         avx512f,
         await_macro,
+        backchain,
         bang,
         begin_panic,
         bench,
@@ -1919,6 +1920,7 @@
         slice_len_fn,
         slice_patterns,
         slicing_syntax,
+        soft_float: "soft-float",
         sparc,
         sparc64,
         sparc_target_feature,
diff --git a/tests/assembly-llvm/s390x-packedstack-toggle.rs b/tests/assembly-llvm/s390x-packedstack-toggle.rs
new file mode 100644
index 0000000..d53f98b
--- /dev/null
+++ b/tests/assembly-llvm/s390x-packedstack-toggle.rs
@@ -0,0 +1,37 @@
+//@ add-minicore
+//@ revisions: enable-packedstack default-packedstack
+//@ assembly-output: emit-asm
+//@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu -Cforce-unwind-tables=no
+//@ needs-llvm-components: systemz
+//@[enable-packedstack] compile-flags: -Zpacked-stack
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+extern "C" {
+    fn extern_func() -> i32;
+}
+
+// CHECK-LABEL: test_packedstack
+#[no_mangle]
+extern "C" fn test_packedstack() -> i32 {
+    // test the creation of call stack with and without packed-stack
+
+    // without packed-stack we always reserve a least the maximal space of 160 bytes
+    // default-packedstack: stmg %r14, %r15, 112(%r15)
+    // default-packedstack-NEXT: aghi %r15, -160
+    // default-packedstack-NEXT: brasl %r14, extern_func
+
+    // with packed-stack only the actually needed registers are reserved on the stack
+    // enable-packedstack: stmg %r14, %r15, 144(%r15)
+    // enable-packedstack-NEXT: aghi %r15, -16
+    // enable-packedstack-NEXT: brasl %r14, extern_func
+    unsafe {
+        extern_func();
+    }
+    1
+    // CHECK: br %r{{.*}}
+}
diff --git a/tests/codegen-llvm/packedstack.rs b/tests/codegen-llvm/packedstack.rs
new file mode 100644
index 0000000..11a01e6
--- /dev/null
+++ b/tests/codegen-llvm/packedstack.rs
@@ -0,0 +1,18 @@
+//@ add-minicore
+//@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-none-softfloat -Zpacked-stack
+//@ needs-llvm-components: systemz
+#![feature(s390x_target_feature)]
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
+
+pub fn test_packedstack() {
+    // CHECK: @test_packedstack() unnamed_addr #0
+}
+
+// CHECK: attributes #0 = { {{.*}}"packed-stack"{{.*}} }
diff --git a/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr b/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr
new file mode 100644
index 0000000..6270e43
--- /dev/null
+++ b/tests/ui/target-feature/packedstack-combinations.backchain_attr.stderr
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 0000000..6f45319
--- /dev/null
+++ b/tests/ui/target-feature/packedstack-combinations.need_softfloat.stderr
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 0000000..5dc0f6a
--- /dev/null
+++ b/tests/ui/target-feature/packedstack-combinations.rs
@@ -0,0 +1,44 @@
+//@ add-minicore
+//@ revisions: wrong_arch only_packedstack backchain_attr backchain_cli with_softfloat
+//@ compile-flags: -Zpacked-stack --crate-type=rlib
+//@ ignore-backends: gcc
+
+//@ [wrong_arch] compile-flags: --target=x86_64-unknown-linux-gnu
+//@ [wrong_arch] should-fail
+//@ [wrong_arch] needs-llvm-components: x86
+
+//@ [only_packedstack] compile-flags: --target=s390x-unknown-linux-gnu
+//@ [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] should-fail
+//@ [backchain_cli] needs-llvm-components: systemz
+
+//@ [with_softfloat] compile-flags: -Ctarget-feature=+backchain
+//@ [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)]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
+#[cfg_attr(backchain_attr,target_feature(enable = "backchain"))]
+pub fn test() {
+}
+
+//[wrong_arch]~? ERROR `-Zpacked-stack` is only supported on s390x
+//[backchain_cli]~? WARN unstable feature specified for `-Ctarget-feature`: `backchain`
+//[backchain_cli]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature
+//[backchain_attr]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature
+//[with_softfloat]~? WARN unstable feature specified for `-Ctarget-feature`: `backchain`
diff --git a/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr b/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr
new file mode 100644
index 0000000..b8c06fc
--- /dev/null
+++ b/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr
@@ -0,0 +1,6 @@
+warning: unstable feature specified for `-Ctarget-feature`: `backchain`
+   |
+   = note: this feature is not stably supported; its behavior can change in the future
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/packedstack-combinations.wrong_arch.stderr b/tests/ui/target-feature/packedstack-combinations.wrong_arch.stderr
new file mode 100644
index 0000000..aa178ad
--- /dev/null
+++ b/tests/ui/target-feature/packedstack-combinations.wrong_arch.stderr
@@ -0,0 +1,4 @@
+error: `-Zpacked-stack` is only supported on s390x
+
+error: aborting due to 1 previous error
+