spu.c (spu_emit_branch_or_set): Handle NaN values as operands to DFmode GE or LE...
authorBen Elliston <bje@gcc.gnu.org>
Wed, 12 Sep 2007 00:48:49 +0000 (10:48 +1000)
committerBen Elliston <bje@gcc.gnu.org>
Wed, 12 Sep 2007 00:48:49 +0000 (10:48 +1000)
* config/spu/spu.c (spu_emit_branch_or_set): Handle NaN values as
operands to DFmode GE or LE compares.

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

From-SVN: r128404

gcc/ChangeLog
gcc/config/spu/spu.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/spu/dfcgt-nan.c [new file with mode: 0644]

index 76e343d511924d7af7a301bf567afa5ed362f68a..4af559e80e4a28bebd9ad402bc5ace30c15a8e73 100644 (file)
@@ -1,3 +1,8 @@
+2007-09-12  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.
+
 2007-09-12  Bernd Schmidt  <bernd.schmidt@analog.com>
 
        * config/bfin/bfin.h (enum reg_class, REG_CLASS_CONTENTS,
index 3423cef42669bf1b8118f7e77d289b56720bce1e..2dd78a099ac95357bd630ec17e95400b2ae05b1f 100644 (file)
@@ -720,13 +720,14 @@ spu_emit_branch_or_set (int is_set, enum rtx_code code, rtx operands[])
 {
   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 rtx_code code, rtx operands[])
          }
     }
 
+  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 rtx_code code, rtx operands[])
       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 rtx_code code, rtx operands[])
        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 a6ddaa7b7534ae081de010a52a54339662af7202..a562e626aed5b15ffb2f66271e76f7f00a04edd7 100644 (file)
@@ -1,3 +1,8 @@
+2007-09-12  Ben Elliston  <bje@au.ibm.com>
+           Ulrich Weigand  <uweigand@de.ibm.com>
+
+       * gcc.target/spu/dfcgt-nan.c: New test.
+
 2007-09-11  Hans-Peter Nilsson  <hp@axis.com>
 
        * gcc.dg/cpp/trad/include.c: Don't run for newlib targets.
diff --git a/gcc/testsuite/gcc.target/spu/dfcgt-nan.c b/gcc/testsuite/gcc.target/spu/dfcgt-nan.c
new file mode 100644 (file)
index 0000000..18ce013
--- /dev/null
@@ -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);
+}