Skip to content

Conversation

@fneddy
Copy link
Contributor

@fneddy fneddy commented Jan 7, 2026

as we want to enable rust in kernel on s390x we need to do some things:

  • allow the use of soft-float feature only if also soft-float ABI is enabled
  • add a -packed-stack rustc option. this adds the packed-stack function-attribute to every function call. I implemented it the same way as it is implemented in clang12. In clang this is a cli argument. It is not a part of the target feature. Therefore we cannot pass it via the features field in the target.json.
  • added a check for packed-stack && backchain** && !soft-float as this combination should never occur. However I don't know if I put it in the right spot. 3

Footnotes

  1. https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mpacked-stack

  2. https://github.com/llvm/llvm-project/blob/release/20/clang/lib/CodeGen/CodeGenFunction.cpp#L1202-L1208

  3. compiler/rustc_target/src/spec/mod.rs

@rustbot
Copy link
Collaborator

rustbot commented Jan 7, 2026

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 7, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 7, 2026

r? @nnethercote

rustbot has assigned @nnethercote.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@fneddy fneddy marked this pull request as draft January 7, 2026 14:04
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 7, 2026
@fneddy fneddy force-pushed the new_target_s390x_softfloat branch from 7fdc148 to 30628ca Compare January 7, 2026 14:09
@fneddy fneddy marked this pull request as ready for review January 7, 2026 14:10
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 7, 2026
Comment on lines 490 to 493
## packed-stack

This flag enables packed StackFrames on s390x.
If backchain is also enabled, switch to the soft-float ABI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should say something about allowed values and what the default is. (if only for consistency).

Also i like your phrasing from above better: enabling both packed-stack and backchain is incompatible with hard-float, but is compatible with soft-float. The current text could be read to mean that soft-float would be switched to automatically.

Comment on lines +1214 to +1215
// On s390x only the hard-float ABI is valid. However for kernel compilation we need to
// allow soft-float if and only if the ABI is explicitly set to soft-float.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this refer to the linux kernel? Also I feel like this mixes up the SoftFloat ABI and the soft-float feature, maybe you can clarify which way the implication goes?

Copy link
Contributor

@tgross35 tgross35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having flags that change ABI is a problem because everything quietly breaks if you don't rebuild the world (among other problems); I don't think we want to add this to any more targets, we're trying to rip it out. See context at #116344, cc @RalfJung.

The thing to do is to submit an MCP to create a new separate -softfloat target, like we have with aarch64-unknown-none-softfloat, loongarch64-unknown-none-softfloat, x86_64-unknown-none (softfloat is implied there), and others.

Also cc @uweigand @cuviper target maintainers

View changes since this review

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 8, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 8, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

FeatureConstraints { required: &[], incompatible: &["soft-float"] }
// On s390x only the hard-float ABI is valid. However for kernel compilation we need to
// allow soft-float if and only if the ABI is explicitly set to soft-float.
if matches!(self.abi, Abi::SoftFloat) {
Copy link
Member

@RalfJung RalfJung Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.abi is just used for cfg(target_abi), it doesn't actually control the ABI.

It looks like s390x is just as bad as x86 when it comes to ABI handling so the logic should probably be similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is only one valid abi on s390x. This ABI uses hard-float.

The idea is that soft-floatremains incompatible unless the user explicitly requests to use the soft-float abi(e.g.1). This will not change the abi, as there is no other ABI specified. And to my knowledge there wont be a soft-float abi specified that really could be used by llvm.

It is used as a gate, so if you want to use soft-float you also have to explicitly change the ABI. And make it clear that this code is incompatible with the normal code.

Footnotes

  1. https://github.com/torvalds/linux/compare/master...fneddy:linux:add_rustc_s390x#diff-8b2b79dc9fe8130845984440e8df685c928623dfe1ebd12fe937efe4ab352aa9R267

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not change the abi, as there is no other ABI specified.

What exactly do you mean by this - floats still get passed in vregs even with soft-float? What does the flag do then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might only be one official ABI, but the soft-float ABI is real and we're not going to pretend that it isn't. The soft-float ABI is whatever LLVM does to pass float args across function barriers when the soft-float target feature is on. So there might be no specification for such an ABI, but that doesn't stop LLVM from using it.

That's what happened on x86, anyway. I suspect it is similar on s390x.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if you want to use soft-float you also have to explicitly change the ABI.

Exactly. But self.abi does not change the ABI, it just tells cfg what the ABI is. llvm_abiname, llvm_floatabi, and rustc_abi change the ABI.

(Well, that's not entirely true. self.abi actually is used in some places in the ABI adjustment logic. It's all a bit messy unfortunately...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fneddy I assume that by saying there is only "one" "valid" ABI, you may be trying to say something like "the soft-float ABI is not required to be stable, as it is an implementation detail of the (Linux?) kernel, which is allowed to have an unstable ABI that changes from version to version"? If so, that is not something very important to us here. That describes the situation for Rust too.

For the Rust compiler's purposes, all that matters is that an ABI can be used by anything for an external call. Sometimes Linux code calls across kernel object boundaries, and sometimes Rust code calls across object boundaries, too. They both manage, despite the need to recompile everything per-version. Stability between compiler or kernel versions is not a required component for us to care about an ABI.

Copy link
Member

@workingjubilee workingjubilee Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...it does technically mean that any Rust target tuple supported since around 2015 has at least 3652... eh, 3660? Rust ABIs. "Legally" speaking, of course, we aren't changing every target's code literally every night.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that further up this file it still says

("soft-float", Forbidden { reason: "currently unsupported ABI-configuration feature" }, &[]),

So using the soft-float target feature still doesn't work even with this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that note, it would be great to have codegen tests for these kind of flags.

}

if sess.opts.cg.packed_stack {
Some(llvm::CreateAttrString(cx.llcx, "packed-stack"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the ABI considerations for this flag? Is it possible to freely mix code built with and without this flag? If not, it needs to be made a "target modifier" to avoid unsoundness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes packed-stack and non packed stack code can be mixed. It just affects the calling code and not the called code.

overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
"use overflow checks for integer arithmetic"),
packed_stack: bool = (false, parse_bool, [TRACKED],
"use packed stack frames (s390x only) (default: no)"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's bad UI to just silently ignore the flag for targets where it does not make sense. Please ensure it raises an error instead.

@rust-log-analyzer
Copy link
Collaborator

The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
    |
319 |     if sess.opts.cg.packed_stack {
    |                     ^^^^^^^^^^^^ unknown field
    |
    = note: available fields are: `ar`, `code_model`, `codegen_units`, `collapse_macro_debuginfo`, `control_flow_guard` ... and 48 others

For more information about this error, try `rustc --explain E0609`.
[RUSTC-TIMING] rustc_codegen_llvm test:false 3.395
error: could not compile `rustc_codegen_llvm` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

@nnethercote
Copy link
Contributor

I don't know anything about this stuff, so I will hand this over to a target maintainer:

r? @cuviper

@rustbot rustbot assigned cuviper and unassigned nnethercote Jan 8, 2026
@cuviper
Copy link
Member

cuviper commented Jan 8, 2026

cc @uweigand, in case you're available to look at this, otherwise I'll try to review it soon.

@workingjubilee
Copy link
Member

workingjubilee commented Jan 9, 2026

@fneddy

Hello. This should probably be two PRs: one for packed-stack because it contains potentially-soundly-miscible code (I haven't heard the full story so to be clear I have not concluded whether it is or not), and one for soft-float which is definitely not something we want to support as a Rust target feature. LLVM's target features are allowed to be unsound to additively compose in a binary, because they are "merely" code generation flags and it's the user's responsibility to use them correctly, but we do not want that here as they are exposed in the language's surface and compiler feature-flags and there are assumptions about what feature compositions may be done correctly.

Copy link
Member

@workingjubilee workingjubilee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-mbackchain
-mno-backchain
Store (do not store) the address of the caller's frame as backchain pointer into the callee's stack frame. A backchain may be needed to allow debugging using tools that do not understand DWARF 2 call frame information. When -mno-packed-stack is in effect, the backchain pointer is stored at the bottom of the stack frame; when -mpacked-stack is in effect, the backchain is placed into the topmost word of the 96/160 byte register save area.

I was looking at the gcc documentation for -mbackchain and... we don't even support emitting DWARF 1, lol? Is this combination actually relevant? Can anything that doesn't support this actually debug Rust, in the kernel or otherwise?

View changes since this review

@uweigand
Copy link
Contributor

uweigand commented Jan 9, 2026

cc @uweigand, in case you're available to look at this, otherwise I'll try to review it soon.

Let me try to summarize my understanding of the situation here. First of all, I agree with

This should probably be two PRs: one for packed-stack because it contains potentially-soundly-miscible code (I haven't heard the full story so to be clear I have not concluded whether it is or not), and one for soft-float which is definitely not something we want to support as a Rust target feature.

that this should probably be two PRs.

As to the soft-float question, the current situation on s390x is rather similar to x86, and the intent was to treat it in a similar fashion in Rust. Currently, LLVM supports the target feature soft-float (and associated command line option -msoft-float also supported by GCC), which has two effects: it prevents the compiler from ever emitting any instruction that touches any floating-point or vector register, and it changes the ABI to pass floating-point values in GPRs instead of FPRs. The only known and intended use of this option / feature is to build the Linux kernel.

I understand that Rust does not allow using any target feature that implicitly changes the ABI. Therefore, Rust will enforce that the target feature passed to LLVM to indicate soft- vs hard-float matches the Rust compiler's understanding of the ABI that is currently targeted; the latter is determined by some target property (not a feature). Where it becomes a bit confusing to me is that different targets currently use a bunch of different mechanisms to indicate this property: x86 uses rustc_abi, 32-bit Arm uses llvm_floatabi, 64-bit Arm uses abi, RISC-V and Loongarch use llvm_abiname. It's not immediately obvious to me which of those -if any- we should be using on s390x.

llvm_floatabi and llvm_abiname appear to refer to LLVM settings - but we do not have those on s390x in LLVM, so we probably cannot use those. This leaves the Rust-specific rustc_abi or abi. @fneddy's patch uses abi like AArch64 - I guess we can instead use rustc_abi like x86 if this is preferable?

In any case, I would have hoped to avoid creating an official Rust target for this, for two reasons: while soft-float does implicitly use a variant ABI, this ABI is nowhere officially specified or documented; and we would prefer to keep usage of soft-float confined to the Linux kernel rather than encourage other uses (even a hypothetical s390x-none target should use hard float!). My understanding is that the Linux kernel use case does not require an official Rust target, because the kernel uses the RFC 131 target specification mechanism via custom JSON file (https://rust-lang.github.io/rfcs/0131-target-specification.html). This is how it indicates the soft-float requirement on x86 (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/generate_rust_target.rs), and we'd have done it the same way on s390x.

Does this make sense?

Now as to packed-stack, this is currently an LLVM target feature, associated with a command-line option -mpacked-stack (also supported by GCC). This option modifies how the compiler-generated function prolog/epilog code makes use of the 160 byte register save area provided by a caller to the callee (as required by the ABI), but this variant is not actually incompatible with the ABI - packed-stack and regular functions can freely call each other without ABI issues. The only purpose of the option is to avoid "wasting" stack space for unneeded parts of the register save area; this is only of concern in environments where stack space it scarce, most notably the Linux kernel.

The only thing that requires extra attention is the combination with -mbackchain. Now, talking for a moment about -mbackchain on its own, and replying to

I was looking at the gcc documentation for -mbackchain and... we don't even support emitting DWARF 1, lol? Is this combination actually relevant? Can anything that doesn't support this actually debug Rust, in the kernel or otherwise?

this option provides a way to generate stack backtraces in scenarios where DWARF Call Frame Information cannot be used. Note that this nothing to do with DWARF 1 vs 2., it's about contexts where DWARF CFI parsing is not supported at all, e.g. when creating backtraces in Linux kernel code during profiling timer interrupts etc. This is where e.g. on Intel you'd typically use -fno-omit-frame-pointer and create a backtrace by following a frame pointer chain. That method does not work on s390x because even if we force frame pointer registers to be used, those are not stored on the stack in a way that forms a linked list that can be followed. The -mbackchain option simply generates prolog code that manages an equivalent linked list of stack frames at runtime.

Now going back to the combination of -mpacked-stack and -mbackchain - because of the variant use of the register save area due to -mpacked-stack, the location in the stack frame where the backchain link ought to be stored is not available. That is why this combination is not supported at all with the default ABI, and the compiler should error out. However, in the special case of also using soft-float, our (implied) soft-float ABI provides a different location for the backchain that is compatible with -mpacked-stack, so that combination should be supported - and in fact it must be supported as the Linux kernel relies on it (the Linux kernel being one example where DWARF CFI based unwinding does not usually work).

Happy to clarify any further questions!

@RalfJung
Copy link
Member

RalfJung commented Jan 9, 2026

Where it becomes a bit confusing to me is that different targets currently use a bunch of different mechanisms to indicate this property

Yeah, sadly LLVM has no uniform way of treating ABIs. RISC-V does it really well, ARM at least has explicit hard/soft-float modes (but using a different knob than the RISC-V ABIs), and the rest just does some ad-hoc hacks with target features that cause massive headaches.

On the Rust side we have rustc_abi because on x86 we needed fine-grained control over the "Rust" ABI that is not reflected in abi (which is the user-visible cfg(target_abi)). So the original idea was to use abi for the cfg and llvm_*/rustc_abi to control what actually happens -- surprisingly often, those are entirely different axes (e.g. the RISC-V ABI is not always reflected in cfg(target_abi)). Arm64 is breaking that pattern (and #150468 proposes to do the same for PowerPC). We should probably have a compiler team discussion on how to represent this. Given that the cfg is also about things that are entirely unrelated to the kind of ABI we are talking about here (e.g. the elfv1 vs elfv2 distinction on PowerPC), it may be a good idea to keep the flags for the cfg value and for codegen entirely separate.

In any case, I would have hoped to avoid creating an official Rust target for this,

I would rather not have flags in Rust target specs that no official target uses and that are therefore entirely untested and prone to removal by cleanup. That seems like a bad idea to me. So if we support building soft-float code for s390x, there should be an upstream target for that.

Now as to packed-stack, this is currently an LLVM target feature,

It it's a target feature, then there's no good reason to make it a separate flag the way this PR does, is there?

@uweigand
Copy link
Contributor

uweigand commented Jan 9, 2026

On the Rust side we have rustc_abi because on x86 we needed fine-grained control over the "Rust" ABI that is not reflected in abi (which is the user-visible cfg(target_abi)). So the original idea was to use abi for the cfg and llvm_*/rustc_abi to control what actually happens -- surprisingly often, those are entirely different axes (e.g. the RISC-V ABI is not always reflected in cfg(target_abi)). Arm64 is breaking that pattern (and #150468 proposes to do the same for PowerPC). We should probably have a compiler team discussion on how to represent this. Given that the cfg is also about things that are entirely unrelated to the kind of ABI we are talking about here (e.g. the elfv1 vs elfv2 distinction on PowerPC), it may be a good idea to keep the flags for the cfg value and for codegen entirely separate.

So if we shouldn't use rustc_abi because that is x86 specific, and we shouldn't use abi because that is intended for a different purpose - what flag should we be using? Even if we add an official target, the effect of that target would still have to be to set some flag in that structure?

In any case, I would have hoped to avoid creating an official Rust target for this,

I would rather not have flags in Rust target specs that no official target uses and that are therefore entirely untested and prone to removal by cleanup. That seems like a bad idea to me. So if we support building soft-float code for s390x, there should be an upstream target for that.

Fair enough.

Now as to packed-stack, this is currently an LLVM target feature,

It it's a target feature, then there's no good reason to make it a separate flag the way this PR does, is there?

Ah sorry, I mis-spoke / mis-remembered. It is actually not a target feature - LLVM supports this via a function attribute, and Clang has a command-line option that causes this function attribute to be set on each function.

@RalfJung
Copy link
Member

RalfJung commented Jan 9, 2026

So if we shouldn't use rustc_abi because that is x86 specific,

Nono, sorry for being unclear. It's not meant to be x86-specific, it's meant to be extended with more variants if more targets need their own ABI control.

Seeing as how soft-float ABIs are a common theme, we might want to just have one variant that all targets can use for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants