diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 76e343d5119..4af559e80e4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2007-09-12 Sa Liu + + * 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 * config/bfin/bfin.h (enum reg_class, REG_CLASS_CONTENTS, diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c index 3423cef4266..2dd78a099ac 100644 --- a/gcc/config/spu/spu.c +++ b/gcc/config/spu/spu.c @@ -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) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index a6ddaa7b753..a562e626aed 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-09-12 Ben Elliston + Ulrich Weigand + + * gcc.target/spu/dfcgt-nan.c: New test. + 2007-09-11 Hans-Peter Nilsson * 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 index 00000000000..18ce0135638 --- /dev/null +++ b/gcc/testsuite/gcc.target/spu/dfcgt-nan.c @@ -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); +}