Repository navigation
ABI mismatch on powerpc64 (elfv1) for union containing floats #162011
Description
Activity
- addedO-PowerPCTarget: PowerPC processorsTarget: PowerPC processorsC-bugCategory: This is a bug.Category: This is a bug.A-ABIArea: Concerning the application binary interface (ABI)Area: Concerning the application binary interface (ABI)
on Aug 30, 2026 - addedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Aug 30, 2026 - addedT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundnessand removedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Aug 31, 2026 - addedI-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}Issue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
on Aug 31, 2026 - addedI-miscompileIssue: Correct Rust code lowers to incorrect machine codeIssue: Correct Rust code lowers to incorrect machine codeand removedI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
on Aug 31, 2026 - removedI-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}Issue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
on Aug 31, 2026 Assigning P-high (discussion on Zulip).
Ping target maintainers: @daltenty @gilamn5tr @amy-kwan @Gelbpunkt @famfo @neuschaefer
The ELFv1 specification says "Floating point registers f1 through f13 are used consecutively to pass [...] one member aggregates passed by value containing a floating point value" and the specification defines aggregates at the first use of the term as "Aggregates (structures and arrays)", so GCC and Clang are correct.
3 remaining items
Yes, we're still using it. Losing host tools would be unfortunate as it it has quite some enthusiasts still.
Not much we can do, unfortunately. Rust upstream is extremely strict and insist on two maintainers per target which smaller downstream projects can't really provide.
Luckily, gccrs is in the works and if Rust upstream ever decides to completely kill off older PowerPC ports, we will still have GCC.
Please note that the barrier for being a target maintainer is actually rather low, it certainly doesn't come with any time commitment. Our docs say:
A tier 2 target must have a designated team of developers (the "target maintainers") available to consult on target-specific build-breaking issues, or if necessary to develop target-specific language or library implementation details. This team must have at least 2 developers.
All that's really needed is somebody who knows about some target specifics, and knows who to get in touch with for issues that they themselves can't address. If they can work on the issues that's of course better, but it could also be e.g. a GCC/LLVM contributor who dabbles in Rust and doesn't mind some
./x buildwork to help figure out the occasional issue.I have to imagine it's possible to dig up two people like that for the target - please do reach out and point them at #113739 (comment) if so! If there really aren't two people like that around then it is not a good sign of this target's health.
I'll also note that this issue is not at all cause for demotion on its own. However, the target has accumulated a number of broken edge cases like this. Three years have gone by since #113739 was opened and in that time, nobody has expressed enough interest in keeping the target alive to sign up. Demotion would only be a reflection of that, and is completely reversible if that situation changes.
Fwiw I don't think we should demote now. But if at some point this target starts blocking something (breaks CI or blocks a new feature - IIRC this is problematic with tail calls) and we still don't have a target maintainer, I expect it would be demoted very rapidly.
From the
TwoFloatsexample in the initial report, clang pass the float in a GPR and rust passes it in an FPR.This bit of code seems likely to be involved: https://github.com/rust-lang/rust/blob/main/compiler/rustc_target/src/callconv/powerpc64.rs#L27-L44
Reacted by John Paul Adrian GlaubitzI'll also note that this issue is not at all cause for demotion on its own. However, the target has accumulated a number of broken edge cases like this. Three years have gone by since #113739 was opened and in that time, nobody has expressed enough interest in keeping the target alive to sign up. Demotion would only be a reflection of that, and is completely reversible if that situation changes.
It's a communications problem. Not every downstream developer can be aware of every upstream issue of every upstream project.
Upstream developers often make announcements about changes that affect downstream within their upstream channels only and then they're surprised that they're not getting any feedback. But we can't just be everywhere. Downstream developers from distributions like Debian and Gentoo try to contribute as much as they can, but we don't have the manpower to be everywhere.
What frustrates me is that IBM engineers could perfectly take care of the other PowerPC ports besides ppc64el like they already do in GCC and the kernel. But apparently the management side has decided that older PowerPC targets deserve no attention, so these ports and the people maintaining them downstream end up getting under pressure.
Fwiw I don't think we should demote now. But if at some point this target starts blocking something (breaks CI or blocks a new feature - IIRC this is problematic with tail calls) and we still don't have a target maintainer, I expect it would be demoted very rapidly.
Well, the first message that reached downstream regarding this issue was the threat of demotion and not the question to ask for help resolving these issues. So, maybe you can understand the frustration a bit.
Fwiw I don't think we should demote now. But if at some point this target starts blocking something (breaks CI or blocks a new feature - IIRC this is problematic with tail calls) and we still don't have a target maintainer, I expect it would be demoted very rapidly.
Well, the first message that reached downstream regarding this issue was the threat of demotion and not the question to ask for help resolving these issues. So, maybe you can understand the frustration a bit.
I was the one that took the initiative to talk about demoting this target (in rust-lang/compiler-team#1035) so I am in part responsible for the confusion, for which I apologize.
As discussed on Zulip we (Rust project) try to balance maintenance reality and downstream expectations. I think we have enough evidence that demoting the target would create concrete inconveniences downstream, so I will retract the proposal.
Last but not least, thank you @glaubitz for your hard work in the Debian project and for taking the time to interface with us as well!
I think I have a fix. I'll try to clean it up and add some tests.
I think I have a fix. I'll try to clean it up and add some tests.
It looks like you filed a Clang fix (thank you!), but per #162011 (comment) isn't Clang already doing the right thing?
Clang seems to have this bug only with unions containing a single element (e.g.
union { float a; }). This original bug report example is a union with two elements (e.g.union { float a; float b; }), which Rust doesn't handle correctly but Clang does.Reacted by Trevor GrossIt looks like you filed a Clang fix (thank you!), but per #162011 (comment) isn't Clang already doing the right thing?
For the reported case, yes. Clang gets single-member unions wrong, though.
The spec lists unions separately from aggregates everywhere ("Aggregates and Unions" in 3.1.6, "aggregates and unions passed by value" in 3.2.3), and the FPR rule only mentions "one member aggregates". So a union should never be passed in an FPR, even if it has one field. GCC does this. Clang unwraps single-member unions:
ELFv1 GCC Clang 22 rustc union { float a; float b; }GPR GPR FPR struct { union { float a; float b; } u; }GPR GPR FPR union { float a; }GPR FPR FPR struct { union { float a; } u; }GPR FPR FPR union { struct { float a; } s; }GPR FPR FPR It's the same with
double,long doubleand 128-bit vector members. In Clang this comes fromisSingleElementStructaccepting unions; llvm/llvm-project#223481 fixes that for ELFv1.My rustc change never unwraps a union on ELFv1, so it matches GCC in all of these cases (and Clang once that PR lands). It does change rustc's ABI for single-member unions.
Reacted by Anna WilcoxMy rustc change never unwraps a union on ELFv1
I look forward to seeing this. Does that mean you don't use
homogenous_aggregateat all on elfv1? That function is causing issues on some other targets (where an empty array should disqualify an aggregate) so it needs some work.Sorry, took a while to get the tests done.
- added a commit that references this issue
on Sep 25, 2026 - added a commit that references this issue
on Sep 25, 2026
with
https://godbolt.org/z/EYxKxTs4f
rustc emits
I.e. the argument is already in a float register, and it stays there for the return. But with GCC and Clang the equivalent
https://godbolt.org/z/Mx8GEzdvn
Has an explicit move from GPR to memory to FPR
You can see this difference in the LLVM IR emitted by rustc and clang too:
This came up in relation to #161987.