[clang] [llvm] [ARM] Emit an error when the hard-float ABI is enabled but can't be used (PR #111334)
Simon Tatham via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 06:03:06 PDT 2026
statham-arm wrote:
Hi @chrisnc,
This new diagnostic caused some build failures in our Arm embedded toolchain. We build some embedded C/C++ libraries intended to run on a platform with hardware FP, but because they're embedded, the code begins running when the FPU is not enabled in hardware yet. So we tagged one of our initial setup functions with the `target("no-fpregs")` attribute to make sure it didn't accidentally use the FP registers:
```c++
[[gnu::target("no-fpregs")]] void setup() { ... }
```
But now this causes a compile error, because the compiler is running with `-mfloat-abi=hard`, and `setup()` isn't allowed to have the hard-float ABI and also not use the FP registers.
However, its function type is `void(void)`, which works the same with or without the hard-float ABI! The diagnostic is complaining that it doesn't have the same _formal_ ABI, even though it makes no difference in this case. We have to work around it by tagging the function with the `pcs("aapcs")` attribute too.
Another case of this occurs when the setup function calls a subroutine to write a system register. That subroutine must also be formally marked as `pcs("aapcs")` to avoid this new diagnostic. Again that function has no floating-point values in its signature. But not only that: it's also marked `[[clang::always_inline]]`, which ought to mean that its PCS _does not matter_, because no function call to it is ever emitted in output code!
What do you think? Would it make sense to suppress this diagnostic in one or both of the cases where
* the function's type is such that the two ABIs would agree anyway
* the function is marked always-inline, so that its ABI is irrelevant?
Separately, here's an oddity in the error reporting. Consider this input code:
```c++
__attribute__((target("no-fpregs"), pcs("aapcs"))) int f(void) {
extern int g(void);
return g() + 1;
}
```
Here, `f` is fine: it's not using the FP registers, but that's OK, it's also marked with the soft-float ABI. But `g` isn't, so the call to `g` provokes your new diagnostic. However, the diagnostic is localised to the start of `f`:
```
$ clang --target=armv8a-none-eabi -mfloat-abi=hard -c zz.c
zz.c:1:56: error: call to 'g' expects a hard-float calling convention, but
floating-point registers are unavailable
1 | __attribute__((target("no-fpregs"), pcs("aapcs"))) int f(void) {
| ^
1 error generated.
```
If `f` is a long function, it might not be easy to track down _where_ in it I've accidentally called `g`. Shouldn't the error message point at the objectionable function call, not the start of the containing function?
https://github.com/llvm/llvm-project/pull/111334
More information about the llvm-commits
mailing list