The helper functions used to implement double-precision arithmetic on ARM processors that only support single-precision arithmetic in hardware should use the soft-float ABI (i.e. passing and returning floating-point arguments in core registers), even when -mfloat-abi=hard is in effect. This patch tweaks the ABI for the affected functions so that is true.
Tested with cross to ARM EABI, and by manually observing compiler output. We've also been carrying this patch in our local tree for some time without issue. OK to apply? Thanks, Julian ChangeLog gcc/ * config/arm/arm.c (arm_libcall_uses_aapcs_base) (arm_init_cumulative_args): Use correct ABI for double-precision helper functions in hard-float mode if only single-precision arithmetic is supported in hardware.
commit 829dab1d743604e6025b929be7287c3215a57d21 Author: Julian Brown <jul...@henry8.codesourcery.com> Date: Fri May 27 04:49:33 2011 -0700 Fix hard-float ABI for double-precision helpers on single-precision CPUs. diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 5a62802..47fba2c 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -3472,6 +3472,28 @@ arm_libcall_uses_aapcs_base (const_rtx libcall) convert_optab_libfunc (sfix_optab, DImode, SFmode)); add_libcall (libcall_htab, convert_optab_libfunc (ufix_optab, DImode, SFmode)); + + /* Values from double-precision helper functions are returned in core + registers if the selected core only supports single-precision + arithmetic, even if we are using the hard-float ABI. */ + if (TARGET_VFP) + { + add_libcall (libcall_htab, optab_libfunc (add_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (sdiv_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (smul_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (neg_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (sub_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (eq_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (lt_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (le_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (ge_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (gt_optab, DFmode)); + add_libcall (libcall_htab, optab_libfunc (unord_optab, DFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sext_optab, DFmode, SFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (trunc_optab, SFmode, DFmode)); + } } return libcall && htab_find (libcall_htab, libcall) != NULL; @@ -4438,6 +4460,31 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, if (arm_libcall_uses_aapcs_base (libname)) pcum->pcs_variant = ARM_PCS_AAPCS; + /* We must pass arguments to double-precision helper functions in core + registers if we only have hardware support for single-precision + arithmetic, even if we are using the hard-float ABI. */ + if (TARGET_VFP + && (rtx_equal_p (libname, optab_libfunc (add_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (sdiv_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (smul_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (neg_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (sub_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (eq_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (lt_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (le_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (ge_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (gt_optab, DFmode)) + || rtx_equal_p (libname, optab_libfunc (unord_optab, DFmode)) + || rtx_equal_p (libname, convert_optab_libfunc (sext_optab, + DFmode, SFmode)) + || rtx_equal_p (libname, convert_optab_libfunc (trunc_optab, + SFmode, DFmode)) + || rtx_equal_p (libname, convert_optab_libfunc (sfix_optab, + SImode, DFmode)) + || rtx_equal_p (libname, convert_optab_libfunc (ufix_optab, + SImode, DFmode)))) + pcum->pcs_variant = ARM_PCS_AAPCS; + pcum->aapcs_ncrn = pcum->aapcs_next_ncrn = 0; pcum->aapcs_reg = NULL_RTX; pcum->aapcs_partial = 0;