This is the mail archive of the gcc-patches@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]

[PATCH, spu]: Fix celledp >= and <= compares w/ NaNs


GCC targeting the SPU currently transforms an "a <= b" test into "! (a >
b)" when compiling with -march=celledp, so that the dfcgt instruction
can be used to implement the comparison.

However, this transformation violates the IEEE-754 standard in the
presence of NaN values.  If either a or b is a NaN, a <= b should
evaluate to false according to IEEE rules.  However, after the
transformation, a > b as implemented by dfcgt itself returns false, so
the transformed test returns true.

The following patch corrects this behaviour and has been well tested in
our tree with no regressions.  A test case is included.

Okay for mainline?

Ben

2007-09-11  Sa Liu  <saliu@de.ibm.com>

        * config/spu/spu.c (spu_emit_branch_or_set): Handle NaN values as
        operands to DFmode GE or LE compares.

testsuite/
2007-09-11  Ben Elliston  <bje@au.ibm.com>
            Ulrich Weigand  <uweigand@de.ibm.com>

        * gcc.target/spu/dfcgt-nan.c: New test.


Index: gcc/config/spu/spu.c
===================================================================
--- gcc/config/spu/spu.c	(revision 128361)
+++ gcc/config/spu/spu.c	(working copy)
@@ -720,13 +720,14 @@ spu_emit_branch_or_set (int is_set, enum
 {
   int reverse_compare = 0;
   int reverse_test = 0;
-  rtx compare_result;
-  rtx comp_rtx;
+  rtx compare_result, eq_result;
+  rtx comp_rtx, eq_rtx;
   rtx target = operands[0];
   enum machine_mode comp_mode;
   enum machine_mode op_mode;
-  enum spu_comp_code scode;
+  enum spu_comp_code scode, eq_code, ior_code;
   int index;
+  int eq_test = 0;
 
   /* When spu_compare_op1 is a CONST_INT change (X >= C) to (X > C-1),
      and so on, to keep the constant in operand 1. */
@@ -757,17 +758,40 @@ spu_emit_branch_or_set (int is_set, enum
 	  }
     }
 
+  comp_mode = SImode;
+  op_mode = GET_MODE (spu_compare_op0);
+
   switch (code)
     {
     case GE:
-      reverse_compare = 1;
-      reverse_test = 1;
       scode = SPU_GT;
+      if (HONOR_NANS (op_mode) && spu_arch == PROCESSOR_CELLEDP)
+	{
+	  reverse_compare = 0;
+	  reverse_test = 0;
+	  eq_test = 1;
+	  eq_code = SPU_EQ; 
+	}
+      else
+	{
+	  reverse_compare = 1;
+	  reverse_test = 1; 
+	}
       break;
     case LE:
-      reverse_compare = 0;
-      reverse_test = 1;
       scode = SPU_GT;
+      if (HONOR_NANS (op_mode) && spu_arch == PROCESSOR_CELLEDP)
+	{
+	  reverse_compare = 1;
+	  reverse_test = 0;
+	  eq_test = 1; 
+	  eq_code = SPU_EQ;
+	}
+      else
+	{
+	  reverse_compare = 0;
+	  reverse_test = 1; 
+	}
       break;
     case LT:
       reverse_compare = 1;
@@ -809,9 +833,6 @@ spu_emit_branch_or_set (int is_set, enum
       break;
     }
 
-  comp_mode = SImode;
-  op_mode = GET_MODE (spu_compare_op0);
-
   switch (op_mode)
     {
     case QImode:
@@ -916,6 +937,20 @@ spu_emit_branch_or_set (int is_set, enum
 	abort ();
       emit_insn (comp_rtx);
 
+      if (eq_test)
+        {
+          eq_result = gen_reg_rtx (comp_mode);
+          eq_rtx = GEN_FCN (spu_comp_icode[index][eq_code]) (eq_result,
+							     spu_compare_op0,
+							     spu_compare_op1);
+          if (eq_rtx == 0)
+	    abort ();
+          emit_insn (eq_rtx);
+          ior_code = ior_optab->handlers[(int)comp_mode].insn_code;
+          gcc_assert (ior_code != CODE_FOR_nothing);
+          emit_insn (GEN_FCN (ior_code)
+		     (compare_result, compare_result, eq_result));
+        }
     }
 
   if (is_set == 0)

Index: gcc/testsuite/gcc.target/spu/dfcgt-nan.c
===================================================================
--- gcc/testsuite/gcc.target/spu/dfcgt-nan.c    (revision 0)
+++ gcc/testsuite/gcc.target/spu/dfcgt-nan.c    (revision 0)
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-march=celledp -O1" } */
+/* { dg-final { scan-assembler "dfceq" } } */
+
+/* GCC previously transformed an "a <= b" test into "! (a > b)" when
+   compiling with -march=celledp, so that the dfcgt instruction can be
+   used to implement the comparison.
+
+   However, this transformation violates the IEEE-754 standard in the
+   presence of NaN values.  If either a or b is a NaN, a <= b should
+   evaluate to false according to IEEE rules.  However, after the
+   transformation, a > b as implemented by dfcgt itself returns false,
+   so the transformed test returns true.
+
+   Note that the equivalent transformation is valid for single-
+   precision floating-point values on the Cell SPU, because the format
+   does not have NaNs.  It is invalid for double-precision, even on
+   Cell, however.  */
+
+int test (double a, double b) __attribute__ ((noinline));
+int test (double a, double b)
+{
+  return a <= b;
+}
+
+int main (void)
+{
+  double x = 0.0;
+  double y = 0.0/0.0;
+  return test (x, y);
+}



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