Hello,
I found a problem in GCC on MIPS r5900: When printf() is used with type float,
the converter function __extendsfdf2() is called. Parameters to printf() are
always passed as double and not float. The function __extendsfdf2() calls
itself to convert 32 bit float to 64 bit float. With Linux the __extendsfdf2()
leads to a segfault when the stack limit is reached.
Here is the wrong code (mipsel-linux-gnu):
00402af0 <__extendsfdf2>:
402af0: 3c1c0002 lui gp,0x2
402af4: 279c9b30 addiu gp,gp,-25808
402af8: 0399e021 addu gp,gp,t9
402afc: 8f9980a0 lw t9,-32608(gp) # Load pointer to
__extendsfdf2 into t9
402b00: 27bdffe0 addiu sp,sp,-32
402b04: afbf001c sw ra,28(sp)
402b08: 0320f809 jalr t9 # Calls __extendsfdf2
(itself)
402b0c: afbc0010 sw gp,16(sp)
402b10: 8fbf001c lw ra,28(sp)
402b14: 03e00008 jr ra
402b18: 27bd0020 addiu sp,sp,32
402b1c: 00000000 nop
The problem happens with the r5900 hard float configurations, e.g.:
configure --target=mipsel-linux-gnu --with-float=hard --with-fpu=single
--with-arch=r5900
I created the attached patch which fixes this problem for r5900 and another
problem explained later.
The fixed code generates the following code which should be correct
(mipsr5900el-ps2-elf):
00105440 <__extendsfdf2>:
105440: 27bdffc8 addiu sp,sp,-56
105444: 27a40028 addiu a0,sp,40
105448: 27a50018 addiu a1,sp,24
10544c: afbf0034 sw ra,52(sp)
105450: 0c0417b5 jal 105ed4 <__unpack_f>
105454: e7ac0028 swc1 $f12,40(sp)
105458: 8fa20024 lw v0,36(sp)
10545c: 8fa40018 lw a0,24(sp)
105460: 8fa5001c lw a1,28(sp)
105464: 8fa60020 lw a2,32(sp)
105468: 00021882 srl v1,v0,0x2
10546c: 00021780 sll v0,v0,0x1e
105470: afa20010 sw v0,16(sp)
105474: 0c041789 jal 105e24 <__make_dp>
105478: afa30014 sw v1,20(sp)
10547c: 8fbf0034 lw ra,52(sp)
105480: 03e00008 jr ra
105484: 27bd0038 addiu sp,sp,56
The default targets mipsr5900el and mips64r5900el are not affected by the
problem, because soft float is the default.
It also seems that the same problem occurs with the following configuration:
configure --target=mipsel-linux-gnu --with-float=hard --with-fpu=single
I expected that this combination should work and a problem should already be
detected. Can somebody confirm that the problem also occurs with default mipsel?
The second part of the patch fixes the following configuration:
configure --target=mipsel-linux-gnu --with-arch=r5900
It disables the mips16 stuff in the libgcc. This can't be compiled on r5900.
This was already disabled for targets mipsr5900*el. I detected the problem,
because the buildroot project uses this style which leads to less problems with
existing software (because "mipsel" or "mips64el" is hardcoded in most
configure scripts and don't expect "mipsr5900el" or "mips64r5900el").
Can someone please add the patch to the official GCC repository?
I am not sure whether I fixed all self-calling implementations. Does somebody
know a way of finding selfcalling implementations?
I tried to find a FPU testsuite, but the testsuites are not designed to test
non-standard FPUs or use double instead of float. So there can be more problems
with FPU on r5900 which I don't see at the moment.
Best regards
Jürgen Urban
--- gcc-4.9.0/libgcc/config/t-hardfp-sf 1970-01-01 01:00:00.000000000 +0100
+++ gcc-4.9.0-patched/libgcc/config/t-hardfp-sf 2014-05-07 00:14:27.093320928 +0200
@@ -0,0 +1,32 @@
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# This file is part of GCC.
+
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+hardfp_float_modes := sf
+# di and ti are provided by libgcc2.c where needed.
+hardfp_int_modes := si
+hardfp_extensions :=
+hardfp_truncations :=
+
+# Emulate 64 bit float:
+FPBIT = true
+DPBIT = true
+# Don't build functions handled by 32 bit hardware:
+LIB2FUNCS_EXCLUDE = _addsub_sf _mul_sf _div_sf \
+ _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
+ _lt_sf _le_sf _unord_sf _si_to_sf _sf_to_si _negate_sf \
+ _thenan_sf _sf_to_usi _usi_to_sf
--- gcc-4.9.0/libgcc/config.host 2014-03-27 16:40:31.000000000 +0100
+++ gcc-4.9.0-patched/libgcc/config.host 2014-05-07 00:10:16.565329373 +0200
@@ -140,11 +140,24 @@ microblaze*-*-*)
cpu_type=microblaze
;;
mips*-*-*)
- # All MIPS targets provide a full set of FP routines.
+ # All MIPS targets provide a full set of FP routines except r5900.
cpu_type=mips
tmake_file="mips/t-mips"
if test "${libgcc_cv_mips_hard_float}" = yes; then
- tmake_file="${tmake_file} t-hardfp-sfdf t-hardfp"
+ case ${host} in
+ mips64r5900* | mipsr5900*)
+ # r5900 doesn't support df.
+ tmake_file="${tmake_file} t-hardfp-sf t-hardfp"
+ ;;
+ *)
+ if test x$with_arch = xr5900; then
+ # r5900 doesn't support df.
+ tmake_file="${tmake_file} t-hardfp-sf t-hardfp"
+ else
+ tmake_file="${tmake_file} t-hardfp-sfdf t-hardfp"
+ fi
+ ;;
+ esac
else
tmake_file="${tmake_file} t-softfp-sfdf"
fi
@@ -794,7 +807,9 @@ mips*-*-linux*) # Linux MIPS, either
# instructions that are not supported on r5900.
;;
*)
- tmake_file="${tmake_file} mips/t-mips16 t-slibgcc-libgcc"
+ if test x$with_arch != xr5900; then
+ tmake_file="${tmake_file} mips/t-mips16 t-slibgcc-libgcc"
+ fi
;;
esac
md_unwind_header=mips/linux-unwind.h