[llvm] [ARM] Don't select hard-float fastcc for soft-float subtargets (PR #228394)

via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 04:09:27 PDT 2026


https://github.com/vaibhav-1207 created https://github.com/llvm/llvm-project/pull/228394

Since #221434, ARMBaseTargetMachine::createMachineFunctionInfo reports "calling convention is hard-float, but floating-point registers are unavailable" when useSoftFloat() is set and a function (or a call it makes) has an effective calling convention of ARM_AAPCS_VFP.

getEffectiveCallingConv() still maps fastcc and cxx_fast_tlscc to ARM_AAPCS_VFP whenever the subtarget has FP registers, without checking useSoftFloat(). A +soft-float subtarget that still has FP registers, such as Rust's armv7-unknown-linux-gnueabi target, therefore hits the error as soon as GlobalOpt turns an internal function into fastcc (i.e. at -O1 and above). The diagnostic is a false positive here: with soft-float, FP arguments are softened to integers and passed in core registers anyway.

Only pick ARM_AAPCS_VFP for fastcc/cxx_fast_tlscc when soft-float is not in use. The generated code does not change; the only effect is that the false error goes away.

Fixes #228390

Assisted-by: Jetski (Gemini)

>From 38023734d7fab2a4e01115b74d81b5684f8ba9db Mon Sep 17 00:00:00 2001
From: Vaibhav Shukla <vaibhavshukla at google.com>
Date: Fri, 2 Oct 2026 11:02:59 +0000
Subject: [PATCH] [ARM] Don't select hard-float fastcc for soft-float
 subtargets

Since #221434, ARMBaseTargetMachine::createMachineFunctionInfo reports
"calling convention is hard-float, but floating-point registers are
unavailable" when useSoftFloat() is set and a function (or a call it
makes) has an effective calling convention of ARM_AAPCS_VFP.

getEffectiveCallingConv() still maps fastcc and cxx_fast_tlscc to
ARM_AAPCS_VFP whenever the subtarget has FP registers, without checking
useSoftFloat(). A +soft-float subtarget that still has FP registers,
such as Rust's armv7-unknown-linux-gnueabi target, therefore hits the
error as soon as GlobalOpt turns an internal function into fastcc (i.e.
at -O1 and above). The diagnostic is a false positive here: with
soft-float, FP arguments are softened to integers and passed in core
registers anyway.

Only pick ARM_AAPCS_VFP for fastcc/cxx_fast_tlscc when soft-float is not
in use. The generated code does not change; the only effect is that the
false error goes away.

Fixes #228390

Assisted-by: Jetski (Gemini)
---
 llvm/lib/Target/ARM/ARMISelLowering.cpp    |  2 +-
 llvm/test/CodeGen/ARM/eabihf-no-fpregs.ll  | 20 +++++
 llvm/test/CodeGen/ARM/fastcc-soft-float.ll | 99 ++++++++++++++++++++++
 3 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/ARM/fastcc-soft-float.ll

diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 2f4a4b47677c56..185978d0eb9557 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -1718,7 +1718,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC,
         return CallingConv::Fast;
       return CallingConv::ARM_APCS;
     } else if (Subtarget->hasFPRegs() && !Subtarget->isThumb1Only() &&
-               !isVarArg)
+               !Subtarget->useSoftFloat() && !isVarArg)
       return CallingConv::ARM_AAPCS_VFP;
     else
       return CallingConv::ARM_AAPCS;
diff --git a/llvm/test/CodeGen/ARM/eabihf-no-fpregs.ll b/llvm/test/CodeGen/ARM/eabihf-no-fpregs.ll
index 022a9732173691..89891731ed1cea 100644
--- a/llvm/test/CodeGen/ARM/eabihf-no-fpregs.ll
+++ b/llvm/test/CodeGen/ARM/eabihf-no-fpregs.ll
@@ -2,6 +2,7 @@
 ; RUN: not llc --mtriple=armv7-none-eabihf --mattr=-fpregs < %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=CHECK,EABIHF --implicit-check-not=error:
 ; RUN: not llc --mtriple=thumbv6-none-eabihf --mcpu=arm1176jzf-s < %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=CHECK,EABIHF --implicit-check-not=error:
 ; RUN: not llc --mtriple=armv7-none-eabihf --mattr=+soft-float < %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=CHECK,EABIHF --implicit-check-not=error:
+; RUN: not llc --mtriple=armv7-none-eabi --mattr=+soft-float < %s -o /dev/null 2>&1 | FileCheck %s --implicit-check-not=error:
 
 ; EABIHF: error: <unknown>:0:0: in function default_pcs void (): calling convention is hard-float, but floating-point registers are unavailable
 define void @default_pcs() {
@@ -21,6 +22,17 @@ define void @variadic(...) {
   ret void
 }
 
+; fastcc and cxx_fast_tlscc only use the hard-float calling convention when
+; FP registers are usable, so they are never diagnosed here, including when
+; FP registers exist but +soft-float is used.
+define fastcc void @fast_pcs() {
+  ret void
+}
+
+define cxx_fast_tlscc void @fast_tls_pcs() {
+  ret void
+}
+
 ; CHECK: error: {{.*}} in function soft_to_hard {{.*}}: 'soft_to_hard' calls 'hard_callee', which expects a hard-float calling convention, but floating-point registers are unavailable
 ; CHECK: error: {{.*}} 'soft_to_hard' calls 'hard_callee2', which expects a hard-float calling convention, but floating-point registers are unavailable
 define arm_aapcscc void @soft_to_hard() {
@@ -35,6 +47,14 @@ define arm_aapcscc void @soft_to_default_hard() {
   ret void
 }
 
+define arm_aapcscc void @soft_to_fast() {
+  call fastcc void @fast_callee()
+  call cxx_fast_tlscc void @fast_tls_callee()
+  ret void
+}
+
 declare arm_aapcs_vfpcc void @hard_callee()
 declare arm_aapcs_vfpcc void @hard_callee2()
 declare void @default_callee()
+declare fastcc void @fast_callee()
+declare cxx_fast_tlscc void @fast_tls_callee()
diff --git a/llvm/test/CodeGen/ARM/fastcc-soft-float.ll b/llvm/test/CodeGen/ARM/fastcc-soft-float.ll
new file mode 100644
index 00000000000000..a092c4caefd35e
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/fastcc-soft-float.ll
@@ -0,0 +1,99 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=armv7-unknown-linux-gnueabi < %s | FileCheck %s
+
+; A soft-float subtarget can still have FP registers (e.g. Rust's
+; armv7-unknown-linux-gnueabi target uses "+v7,+thumb2,+soft-float,-neon").
+; fastcc must not be mapped to the hard-float ARM_AAPCS_VFP calling convention
+; in that case: that used to trigger the "calling convention is hard-float, but
+; floating-point registers are unavailable" error. FP arguments are softened
+; and passed in core registers.
+
+define internal fastcc i32 @fast_int(i32 %a, i32 %b) #0 {
+; CHECK-LABEL: fast_int:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    add r0, r0, r1
+; CHECK-NEXT:    bx lr
+  %r = add i32 %a, %b
+  ret i32 %r
+}
+
+define internal fastcc float @fast_float(float %a, float %b) #0 {
+; CHECK-LABEL: fast_float:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    .save {r11, lr}
+; CHECK-NEXT:    push {r11, lr}
+; CHECK-NEXT:    bl __aeabi_fadd
+; CHECK-NEXT:    pop {r11, pc}
+  %r = fadd float %a, %b
+  ret float %r
+}
+
+define internal fastcc double @fast_double(double %a, double %b) #0 {
+; CHECK-LABEL: fast_double:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    .save {r11, lr}
+; CHECK-NEXT:    push {r11, lr}
+; CHECK-NEXT:    bl __aeabi_dadd
+; CHECK-NEXT:    pop {r11, pc}
+  %r = fadd double %a, %b
+  ret double %r
+}
+
+define internal fastcc double @fast_mixed(i32 %i, float %f, double %d) #0 {
+; CHECK-LABEL: fast_mixed:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    .save {r4, r5, r6, lr}
+; CHECK-NEXT:    push {r4, r5, r6, lr}
+; CHECK-NEXT:    mov r4, r3
+; CHECK-NEXT:    mov r5, r2
+; CHECK-NEXT:    mov r6, r1
+; CHECK-NEXT:    bl __aeabi_i2f
+; CHECK-NEXT:    mov r1, r6
+; CHECK-NEXT:    bl __aeabi_fadd
+; CHECK-NEXT:    bl __aeabi_f2d
+; CHECK-NEXT:    mov r2, r5
+; CHECK-NEXT:    mov r3, r4
+; CHECK-NEXT:    bl __aeabi_dmul
+; CHECK-NEXT:    pop {r4, r5, r6, pc}
+  %fi = sitofp i32 %i to float
+  %s = fadd float %fi, %f
+  %e = fpext float %s to double
+  %r = fmul double %e, %d
+  ret double %r
+}
+
+define double @caller(i32 %i, float %f, double %d) #0 {
+; CHECK-LABEL: caller:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    .save {r4, r5, r6, r7, r11, lr}
+; CHECK-NEXT:    push {r4, r5, r6, r7, r11, lr}
+; CHECK-NEXT:    mov r6, r1
+; CHECK-NEXT:    mov r1, #1
+; CHECK-NEXT:    mov r4, r3
+; CHECK-NEXT:    mov r5, r2
+; CHECK-NEXT:    bl fast_int
+; CHECK-NEXT:    mov r7, r0
+; CHECK-NEXT:    mov r0, r6
+; CHECK-NEXT:    mov r1, #1073741824
+; CHECK-NEXT:    bl fast_float
+; CHECK-NEXT:    movw r3, #0
+; CHECK-NEXT:    mov r6, r0
+; CHECK-NEXT:    movt r3, #16392
+; CHECK-NEXT:    mov r0, r5
+; CHECK-NEXT:    mov r1, r4
+; CHECK-NEXT:    mov r2, #0
+; CHECK-NEXT:    bl fast_double
+; CHECK-NEXT:    mov r2, r0
+; CHECK-NEXT:    mov r3, r1
+; CHECK-NEXT:    mov r0, r7
+; CHECK-NEXT:    mov r1, r6
+; CHECK-NEXT:    bl fast_mixed
+; CHECK-NEXT:    pop {r4, r5, r6, r7, r11, pc}
+  %a = call fastcc i32 @fast_int(i32 %i, i32 1)
+  %b = call fastcc float @fast_float(float %f, float 2.0)
+  %c = call fastcc double @fast_double(double %d, double 3.0)
+  %m = call fastcc double @fast_mixed(i32 %a, float %b, double %c)
+  ret double %m
+}
+
+attributes #0 = { noinline nounwind "target-features"="+v7,+thumb2,+soft-float,-neon" }



More information about the llvm-commits mailing list