This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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)));


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]