-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The (stable) neon
aarch64 target feature is unsound: it changes the float ABI
#131058
Comments
|
I don't think LLVM currently supports this. On other architectures, it is possible to force a soft-float ABI while still having FP instructions and registers available inside functions (e.g. |
WG-prioritization assigning priority (Zulip discussion). @rustbot label -I-prioritize +P-high |
imo the major reason that we support |
However, it's also a super dangerous attribute. If that function takes or returns But given current LLVM I don't think there is a sound way to tell it to build code that makes use of the FPU on an aarch64 softfloat target. So seems like the proper fix is to match what other targets do, and add a |
I filed an LLVM issue; I hope I used the right words here since there's still a lot I don't understand around target features. ;) |
Note for aarch64 there is NO defined soft-float ABI. Even though advanced simd (and fp) are optional part of armv8-a, there is no parts out there where it is not included. I have no idea why LLVM has a softfloat aarch64 target. There is a way to forbid all simd and fp usage (in both LLVM and GCC) but that does not change the ABI. |
I took the position that we should do this because
There is no situation where one is enabled and the other is not. |
It does change the ABI though, as one can easily verify on godbolt: https://rust.godbolt.org/z/aP55vT1bf |
Yeah that makes perfect sense if one just looks at which features the hardware supports, but sadly LLVM conflates that aspect with whether a softfloat or hardfloat ABI should be used. |
LLVM has many bugs, yes. |
arguably the first bug is "having a soft-float ABI for AArch64". |
That's the argument being brought up in the LLVM issue, yeah. |
Actually it is defined here: ARM-software/abi-aa#232 . But it is only for the R cores rather than the A cores. |
From the ABI: "This variant is incompatible with the base procedure call standard and toolchains are not required to support it." |
huh, interesting. |
The problen is that LLVM does (pretend to?) support it, just in an incomplete way. So it seems reasonable to ask whether the existing support could be made a bit less incomplete.
|
Given that there is no standard softfloat ABI for this target, maybe the right thing to do is to make sure that on an aarch64 softfloat target, we never use the LLVM-defined ABI for floats, but instead use (Suggested by @Amanieu , or at least I think that is what they meant.) We'd still have to forbid disabling |
The other (valid) question being raised on the LLVM side is, what is even the usecase for ever allowing the use of neon or the FPU on the softfloat target? The target exists for kernels that want to avoid the cost of saving float registers on a context switch. So that usecase certainly does not want to ever enable the |
If code is compiled such that it uses float registers implicitly for a kernel, then on EVERY entry and exit of the switch into and from the kernel, it may run into code that has to use those. So it has to pay the cost of saving those registers on EVERY switch, even if it only uses integer operations in actual fact. If a kernel is a truly hyperminimalist interpretation of a microkernel, this is effectively the end of the story. Such a kernel never does anything that can be done by user code, so it only ever does the absolute minimum in the escalated privilege state. ...However, most kernels are not hyper-minimal. This is not because they are poorly designed, but rather because if something would require an egregious number of context switches to implement this way, it would not be efficient, compared to staying in kernelspace and running a bit more code. So they may need to do something that would be made significant factors faster by paying the cost anyways, like a bit of cryptographic or rendering code. So they explicitly do an additional context save of the extended register state. Then they perform the kernelspace computation with the float register state. Then they restore the float register state. Obviously, this requires tightly guarding the regions where this is done, but there are many such tightly-roped-off segments in e.g. the Linux kernel, and CPUs even build in additional support for doing this faster! |
Is that possible to do with clang? I guess you have to build separate files with separate flags, then you can mix some code that has FPU access with other code that does not. |
Correct. That is exactly what they do:
And yes, the kernel is built with clang for aarch64 devices. Those instructions are actually in the "Arm" folder and not the "Arm64" folder, but they follow the same protocol for AArch64, e.g. |
'-mfpu=neon -mfloat-abi=softfp'
That part does not work on aarch64 though, -mfloat-abi is for arm targets only AFAIK. So what do they do there?
|
...I mean we can just do the RFL ping at this point :^) |
It looks like on the C side they throw the and so parts alternate between...? https://github.com/Rust-for-Linux/linux/blob/a2f11547052001bd448ccec81dd1e68409078fbb/arch/arm64/lib/Makefile#L8-L12 hmm. and they definitely use |
I guess they are using pointers or integer types to pass data into "FPU land", so the question of a softfloat ABI does not come up. |
Right now we don't have any Rust code using the FPU, so we just always turn off
which removes the flags in |
I'd expect they're smuggling everything in structs or longs, yeah. |
In practice, it seems like most users of |
My current plan for this issue is:
|
…dtwco,wesleywiser aarch64 softfloat target: always pass floats in int registers This is a part of rust-lang#131058: on softfloat aarch64 targets, the float registers may be unavailable. And yet, LLVM will happily use them to pass float types if the corresponding target features are enabled. That's a problem as it means enabling/disabling `neon` instructions can change the ABI. Other targets have a `soft-float` target feature that forces the use of the soft-float ABI no matter whether float registers are enabled or not; aarch64 has nothing like that. So we follow the aarch64 [softfloat ABI](rust-lang#131058 (comment)) and treat floats like integers for `extern "C"` functions. For the "Rust" ABI, we do the same for scalars, and then just do something reasonable for ScalarPair that avoids the pointer indirection. Cc `@workingjubilee`
…dtwco,wesleywiser aarch64 softfloat target: always pass floats in int registers This is a part of rust-lang#131058: on softfloat aarch64 targets, the float registers may be unavailable. And yet, LLVM will happily use them to pass float types if the corresponding target features are enabled. That's a problem as it means enabling/disabling `neon` instructions can change the ABI. Other targets have a `soft-float` target feature that forces the use of the soft-float ABI no matter whether float registers are enabled or not; aarch64 has nothing like that. So we follow the aarch64 [softfloat ABI](rust-lang#131058 (comment)) and treat floats like integers for `extern "C"` functions. For the "Rust" ABI, we do the same for scalars, and then just do something reasonable for ScalarPair that avoids the pointer indirection. Cc ``@workingjubilee``
…dtwco,wesleywiser aarch64 softfloat target: always pass floats in int registers This is a part of rust-lang#131058: on softfloat aarch64 targets, the float registers may be unavailable. And yet, LLVM will happily use them to pass float types if the corresponding target features are enabled. That's a problem as it means enabling/disabling `neon` instructions can change the ABI. Other targets have a `soft-float` target feature that forces the use of the soft-float ABI no matter whether float registers are enabled or not; aarch64 has nothing like that. So we follow the aarch64 [softfloat ABI](rust-lang#131058 (comment)) and treat floats like integers for `extern "C"` functions. For the "Rust" ABI, we do the same for scalars, and then just do something reasonable for ScalarPair that avoids the pointer indirection. Cc ```@workingjubilee```
Rollup merge of rust-lang#133102 - RalfJung:aarch64-softfloat, r=davidtwco,wesleywiser aarch64 softfloat target: always pass floats in int registers This is a part of rust-lang#131058: on softfloat aarch64 targets, the float registers may be unavailable. And yet, LLVM will happily use them to pass float types if the corresponding target features are enabled. That's a problem as it means enabling/disabling `neon` instructions can change the ABI. Other targets have a `soft-float` target feature that forces the use of the soft-float ABI no matter whether float registers are enabled or not; aarch64 has nothing like that. So we follow the aarch64 [softfloat ABI](rust-lang#131058 (comment)) and treat floats like integers for `extern "C"` functions. For the "Rust" ABI, we do the same for scalars, and then just do something reasonable for ScalarPair that avoids the pointer indirection. Cc ```@workingjubilee```
Unfortunately #133102 isn't sufficient to make |
Damn... I opened #134375 for this. |
reject aarch64 target feature toggling that would change the float ABI ~~Stacked on top of rust-lang/rust#133099. Only the last two commits are new.~~ The first new commit lays the groundwork for separately controlling whether a feature may be enabled or disabled. The second commit uses that to make it illegal to *disable* the `neon` feature (which is only possible via `-Ctarget-feature`, and so the new check just adds a warning). Enabling the `neon` feature remains allowed on targets that don't disable `neon` or `fp-armv8`, which is all our built-in targets. This way, the entire PR is not a breaking change. Fixes rust-lang/rust#131058 for hardfloat targets (together with rust-lang/rust#133102 which fixed it for softfloat targets). Part of rust-lang/rust#116344.
This is an instance of #116344, but since it affects a target feature marked as "stable" I made a separate issue. See rust-lang/compiler-team#780 for the MCP that approved forbidding the toggling of target features that are unsound due to their ABI impact. Stabilization of
neon
happened in #90621. I am not sure where the FCP for this occurred.The summary of the problem is that code compiled with
-C target-feature=-neon
on an aarch64 target, if it calls any pre-compiled function from the standard library that involvesf32
/f64
arguments, will use the wrong ABI and hence cause UB. I am working towards marking such features as "forbidden" so that we can fix this soundness hole. But now we are hitting the same where a relevant feature is also already stable, so making it "forbidden" would be a breaking change... so we'll have to figure out something more clever. Like maybe only forbidding disabling the feature? Note however that on theaarch64-unknown-none-softfloat
target,+neon
is unsound for the same reason.Cc @Amanieu @workingjubilee
The text was updated successfully, but these errors were encountered: