This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Bug in gcc-3.2.1 for mips
- From: "Jeff Rubin" <jbr at keyresearch dot com>
- To: <gcc-bugs at gcc dot gnu dot org>
- Date: Mon, 27 Jan 2003 14:15:54 -0800
- Subject: Bug in gcc-3.2.1 for mips
The bug is in gcc/config/mips/mips.md in the
routine "reload_incc". The code is trying
to reload a floating-point condition code
from an integer value. It does this by loading
0 into the low 32 bits of one floating-point
register and the value to be reloaded (0 or 1)
into the low 32 bits of a second floating-point
register. It then emits a c.lt.s to compare the
0 against the 0 or 1 and put the result in fcc0.
This works fine except for one thing. The 32-bit
value 0x1 in a single-precision floating-point
register is a denormalized number. The user
may have set a floating-point status bit that
flushes denorms to zero. In that case, this
compare will always return false and therefore
fail to reload the value. There are a couple of
possible fixes. The one I have provided below
causes the compiler to emit a cvt.s.w instruction
on the second floating-point register to turn
the value into a single-precision 1.0 before
the compare. Another possibility would be to
grab another integer register and shift the
integer cc value left by, say, 30 places to
put a one bit in the exponent, before moving
the value to a floating-point register (that
is, assuming that you can be sure that the original
integer register contains either 0 or 1; I don't
have the expertise or time right now for that
verification). That would guarantee the result
is a valid non-denorm non-negative non-zero single-
precision floating-point value. Another way
would be to not grab a new register, do the shift
in place, move to the floating-point register,
and then shift back. I'm sure you can come up
with plenty more.
My test case for this was the double precision
sine routine from glibc-2.2.5. Inside of s_sin.c
there is a routine called slow1 which exhibited
this bug. The effect was that in the line which
read
if (res == res+1.005*cor) return (x>0)?res:-res;
the x>0 test result was in an integer register
and needed to be reloaded into fcc0. The symptom
was that it returned the negative of the correct value
for sin(x) when x had the double-precision value
represented by the 64-bit number 0x3fe560be22bd96e8.
Jeff Rubin
jbr@keyresearch.com
--- mips.md.orig Mon Jan 27 13:48:31 2003
+++ mips.md Mon Jan 27 13:50:10 2003
@@ -5807,12 +5807,12 @@
|| ! FP_REG_P (true_regnum (operands[2])))
abort ();
- /* We need to get the source in SFmode so that the insn is
+ /* We need to get the source in SImode so that the insn is
recognized. */
if (GET_CODE (operands[1]) == MEM)
- source = adjust_address (operands[1], SFmode, 0);
+ source = adjust_address (operands[1], SImode, 0);
else if (GET_CODE (operands[1]) == REG || GET_CODE (operands[1]) ==
SUBREG)
- source = gen_rtx_REG (SFmode, true_regnum (operands[1]));
+ source = gen_rtx_REG (SImode, true_regnum (operands[1]));
else
source = operands[1];
@@ -5823,7 +5823,10 @@
fp1 = gen_rtx_REG (SFmode, regno);
fp2 = gen_rtx_REG (SFmode, regno + HARD_REGNO_NREGS (regno, DFmode));
- emit_insn (gen_move_insn (fp1, source));
+ /* jbr: this used to just load source into the low 32 bits of fp1,
but
+ if denorms are flushed to zero, then the condition code will
always
+ be zero. We need to get it to do cvt.s.w first. */
+ emit_insn (gen_floatsisf2 (fp1, source));
emit_insn (gen_move_insn (fp2, gen_rtx_REG (SFmode, 0)));
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_LT (CCmode, fp2, fp1)));