re PR c/24581 (Complex arithmetic on special cases is incorrect.)
authorJoseph Myers <joseph@codesourcery.com>
Fri, 8 May 2009 10:22:08 +0000 (11:22 +0100)
committerJoseph Myers <jsm28@gcc.gnu.org>
Fri, 8 May 2009 10:22:08 +0000 (11:22 +0100)
PR c/24581
* c-typeck.c (build_binary_op): Handle arithmetic between one real
and one complex operand specially.
* tree-complex.c (some_nonzerop): Do not identify a real value as
zero if flag_signed_zeros.

testsuite:
* gcc.dg/torture/complex-sign.h: New header.
* gcc.dg/torture/complex-sign-add.c,
gcc.dg/torture/complex-sign-mixed-add.c,
gcc.dg/torture/complex-sign-mixed-div.c,
gcc.dg/torture/complex-sign-mixed-mul.c,
gcc.dg/torture/complex-sign-mixed-sub.c,
gcc.dg/torture/complex-sign-mul.c,
gcc.dg/torture/complex-sign-sub.c: New tests.

From-SVN: r147281

12 files changed:
gcc/ChangeLog
gcc/c-typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/torture/complex-sign-add.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-mixed-add.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-mixed-div.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-mixed-mul.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-mixed-sub.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-mul.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign-sub.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/complex-sign.h [new file with mode: 0644]
gcc/tree-complex.c

index 006c508..502da99 100644 (file)
@@ -1,3 +1,11 @@
+2009-05-08  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/24581
+       * c-typeck.c (build_binary_op): Handle arithmetic between one real
+       and one complex operand specially.
+       * tree-complex.c (some_nonzerop): Do not identify a real value as
+       zero if flag_signed_zeros.
+
 2009-05-08  Paolo Bonzini  <bonzini@gnu.org>
 
        PR rtl-optimization/33928
index bef5e38..4d07a88 100644 (file)
@@ -9246,7 +9246,9 @@ build_binary_op (location_t location, enum tree_code code,
       (code1 == INTEGER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE
        || code1 == FIXED_POINT_TYPE || code1 == VECTOR_TYPE))
     {
-      int none_complex = (code0 != COMPLEX_TYPE && code1 != COMPLEX_TYPE);
+      bool first_complex = (code0 == COMPLEX_TYPE);
+      bool second_complex = (code1 == COMPLEX_TYPE);
+      int none_complex = (!first_complex && !second_complex);
 
       if (shorten || common || short_compare)
        {
@@ -9255,6 +9257,89 @@ build_binary_op (location_t location, enum tree_code code,
            return error_mark_node;
        }
 
+      if (first_complex != second_complex
+         && (code == PLUS_EXPR
+             || code == MINUS_EXPR
+             || code == MULT_EXPR
+             || (code == TRUNC_DIV_EXPR && first_complex))
+         && TREE_CODE (TREE_TYPE (result_type)) == REAL_TYPE
+         && flag_signed_zeros)
+       {
+         /* An operation on mixed real/complex operands must be
+            handled specially, but the language-independent code can
+            more easily optimize the plain complex arithmetic if
+            -fno-signed-zeros.  */
+         tree real_type = TREE_TYPE (result_type);
+         tree real, imag;
+         if (type0 != orig_type0 || type1 != orig_type1)
+           {
+             gcc_assert (may_need_excess_precision && common);
+             real_result_type = c_common_type (orig_type0, orig_type1);
+           }
+         if (first_complex)
+           {
+             if (TREE_TYPE (op0) != result_type)
+               op0 = convert_and_check (result_type, op0);
+             if (TREE_TYPE (op1) != real_type)
+               op1 = convert_and_check (real_type, op1);
+           }
+         else
+           {
+             if (TREE_TYPE (op0) != real_type)
+               op0 = convert_and_check (real_type, op0);
+             if (TREE_TYPE (op1) != result_type)
+               op1 = convert_and_check (result_type, op1);
+           }
+         if (TREE_CODE (op0) == ERROR_MARK || TREE_CODE (op1) == ERROR_MARK)
+           return error_mark_node;
+         if (first_complex)
+           {
+             op0 = c_save_expr (op0);
+             real = build_unary_op (EXPR_LOCATION (orig_op0), REALPART_EXPR,
+                                    op0, 1);
+             imag = build_unary_op (EXPR_LOCATION (orig_op0), IMAGPART_EXPR,
+                                    op0, 1);
+             switch (code)
+               {
+               case MULT_EXPR:
+               case TRUNC_DIV_EXPR:
+                 imag = build2 (resultcode, real_type, imag, op1);
+                 /* Fall through.  */
+               case PLUS_EXPR:
+               case MINUS_EXPR:
+                 real = build2 (resultcode, real_type, real, op1);
+                 break;
+               default:
+                 gcc_unreachable();
+               }
+           }
+         else
+           {
+             op1 = c_save_expr (op1);
+             real = build_unary_op (EXPR_LOCATION (orig_op1), REALPART_EXPR,
+                                    op1, 1);
+             imag = build_unary_op (EXPR_LOCATION (orig_op1), IMAGPART_EXPR,
+                                    op1, 1);
+             switch (code)
+               {
+               case MULT_EXPR:
+                 imag = build2 (resultcode, real_type, op0, imag);
+                 /* Fall through.  */
+               case PLUS_EXPR:
+                 real = build2 (resultcode, real_type, op0, real);
+                 break;
+               case MINUS_EXPR:
+                 real = build2 (resultcode, real_type, op0, real);
+                 imag = build1 (NEGATE_EXPR, real_type, imag);
+                 break;
+               default:
+                 gcc_unreachable();
+               }
+           }
+         ret = build2 (COMPLEX_EXPR, result_type, real, imag);
+         goto return_build_binary_op;
+       }
+
       /* For certain operations (which identify themselves by shorten != 0)
         if both args were extended from the same smaller type,
         do the arithmetic in that type and then extend.
index fc1bccc..1f523be 100644 (file)
@@ -1,3 +1,15 @@
+2009-05-08  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/24581
+       * gcc.dg/torture/complex-sign.h: New header.
+       * gcc.dg/torture/complex-sign-add.c,
+       gcc.dg/torture/complex-sign-mixed-add.c,
+       gcc.dg/torture/complex-sign-mixed-div.c,
+       gcc.dg/torture/complex-sign-mixed-mul.c,
+       gcc.dg/torture/complex-sign-mixed-sub.c,
+       gcc.dg/torture/complex-sign-mul.c,
+       gcc.dg/torture/complex-sign-sub.c: New tests.
+
 2009-05-08  Janus Weil  <janus@gcc.gnu.org>
 
        PR fortran/39876
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-add.c b/gcc/testsuite/gcc.dg/torture/complex-sign-add.c
new file mode 100644 (file)
index 0000000..db92140
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Pure complex
+   addition.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_ADD(TYPE, COPY, ZERO, ZEROI)                     \
+  do {                                                         \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, +, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, +, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, +, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, +, -, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, -, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, -, +, -, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, -, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, +, -, -, -, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, +, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, +, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, +, -, +, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, +, -, -, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, -, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, -, +, -, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, -, -, +, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, +, -, -, -, -, -, -);        \
+  } while (0)
+
+void
+check_add_float (void)
+{
+  CHECK_ADD (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_add_double (void)
+{
+  CHECK_ADD (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_add_long_double (void)
+{
+  CHECK_ADD (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_add_float ();
+  check_add_double ();
+  check_add_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-add.c b/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-add.c
new file mode 100644 (file)
index 0000000..5548fe4
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Mixed real/complex
+   addition.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_ADD(TYPE, COPY, ZERO, ZEROI)                             \
+  do {                                                                 \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, +, +, +, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, +, +, -, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, +, -, +, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, +, -, -, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, -, +, +, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, -, +, -, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, -, -, +, -, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, +, -, -, -, -, -);                \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, +, +, +, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, +, +, -, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, +, -, +, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, +, -, -, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, -, +, +, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, -, +, -, ZERO, -, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, -, -, +, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, +, -, -, -, ZERO, -, -);  \
+  } while (0)
+
+void
+check_add_float (void)
+{
+  CHECK_ADD (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_add_double (void)
+{
+  CHECK_ADD (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_add_long_double (void)
+{
+  CHECK_ADD (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_add_float ();
+  check_add_double ();
+  check_add_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-div.c b/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-div.c
new file mode 100644 (file)
index 0000000..4a315dc
--- /dev/null
@@ -0,0 +1,45 @@
+/* Test complex arithmetic with signed zeros.  Mixed real/complex
+   division.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_DIV(TYPE, COPY, ZERO, ZEROI, ONE)                                \
+  do {                                                                 \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, +, +, +, ONE, +, +);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, +, +, -, ONE, -, -);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, +, -, +, ONE, +, -);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, +, -, -, ONE, -, +);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, -, +, +, ONE, -, +);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, -, +, -, ONE, +, -);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, -, -, +, ONE, -, -);   \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, /, -, -, -, ONE, +, +);   \
+  } while (0)
+
+void
+check_div_float (void)
+{
+  CHECK_DIV (float, __builtin_copysignf, 0.0f, 0.0if, 1.0f);
+}
+
+void
+check_div_double (void)
+{
+  CHECK_DIV (double, __builtin_copysign, 0.0, 0.0i, 1.0);
+}
+
+void
+check_div_long_double (void)
+{
+  CHECK_DIV (long double, __builtin_copysignl, 0.0l, 0.0il, 1.0l);
+}
+
+int
+main (void)
+{
+  check_div_float ();
+  check_div_double ();
+  check_div_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-mul.c b/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-mul.c
new file mode 100644 (file)
index 0000000..f5b1fc4
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Mixed real/complex
+   multiplication.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_MUL(TYPE, COPY, ZERO, ZEROI)                             \
+  do {                                                                 \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, +, +, +, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, +, +, -, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, +, -, +, -, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, +, -, -, -, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, -, +, +, -, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, -, +, -, -, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, -, -, +, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, *, -, -, -, +, +);                \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, +, +, +, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, +, +, -, ZERO, -, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, +, -, +, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, +, -, -, ZERO, -, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, -, +, +, ZERO, -, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, -, +, -, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, -, -, +, ZERO, -, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, *, -, -, -, ZERO, +, +);  \
+  } while (0)
+
+void
+check_mul_float (void)
+{
+  CHECK_MUL (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_mul_double (void)
+{
+  CHECK_MUL (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_mul_long_double (void)
+{
+  CHECK_MUL (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_mul_float ();
+  check_mul_double ();
+  check_mul_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-sub.c b/gcc/testsuite/gcc.dg/torture/complex-sign-mixed-sub.c
new file mode 100644 (file)
index 0000000..adc3845
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Mixed real/complex
+   subtraction.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_SUB(TYPE, COPY, ZERO, ZEROI)                             \
+  do {                                                                 \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, +, +, +, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, +, +, -, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, +, -, +, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, +, -, -, +, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, -, +, +, -, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, -, +, -, -, +);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, -, -, +, +, -);                \
+    CHECK_ARITH_RC (TYPE, COPY, ZERO, ZEROI, -, -, -, -, +, +);                \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, +, +, +, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, +, +, -, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, +, -, +, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, +, -, -, ZERO, +, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, -, +, +, ZERO, -, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, -, +, -, ZERO, +, +);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, -, -, +, ZERO, -, -);  \
+    CHECK_ARITH_CR (TYPE, COPY, ZERO, ZEROI, -, -, -, -, ZERO, +, -);  \
+  } while (0)
+
+void
+check_sub_float (void)
+{
+  CHECK_SUB (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_sub_double (void)
+{
+  CHECK_SUB (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_sub_long_double (void)
+{
+  CHECK_SUB (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_sub_float ();
+  check_sub_double ();
+  check_sub_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-mul.c b/gcc/testsuite/gcc.dg/torture/complex-sign-mul.c
new file mode 100644 (file)
index 0000000..d9a06a5
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Pure complex
+   multiplication.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_MUL(TYPE, COPY, ZERO, ZEROI)                     \
+  do {                                                         \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, +, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, +, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, +, -, +, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, +, -, -, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, -, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, -, +, -, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, -, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, +, -, -, -, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, +, +, +, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, +, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, +, -, +, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, +, -, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, -, +, +, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, -, +, -, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, -, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, *, -, -, -, -, +, +);        \
+  } while (0)
+
+void
+check_mul_float (void)
+{
+  CHECK_MUL (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_mul_double (void)
+{
+  CHECK_MUL (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_mul_long_double (void)
+{
+  CHECK_MUL (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_mul_float ();
+  check_mul_double ();
+  check_mul_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign-sub.c b/gcc/testsuite/gcc.dg/torture/complex-sign-sub.c
new file mode 100644 (file)
index 0000000..94ab17d
--- /dev/null
@@ -0,0 +1,53 @@
+/* Test complex arithmetic with signed zeros.  Pure complex
+   subtraction.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu99" } */
+
+#include "complex-sign.h"
+
+#define CHECK_SUB(TYPE, COPY, ZERO, ZEROI)                     \
+  do {                                                         \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, +, +, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, +, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, +, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, +, -, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, -, +, +, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, -, +, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, -, -, +, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, +, -, -, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, +, +, +, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, +, +, -, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, +, -, +, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, +, -, -, +, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, -, +, +, -, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, -, +, -, -, +);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, -, -, +, +, -);        \
+    CHECK_ARITH (TYPE, COPY, ZERO, ZEROI, -, -, -, -, -, +, +);        \
+  } while (0)
+
+void
+check_sub_float (void)
+{
+  CHECK_SUB (float, __builtin_copysignf, 0.0f, 0.0if);
+}
+
+void
+check_sub_double (void)
+{
+  CHECK_SUB (double, __builtin_copysign, 0.0, 0.0i);
+}
+
+void
+check_sub_long_double (void)
+{
+  CHECK_SUB (long double, __builtin_copysignl, 0.0l, 0.0il);
+}
+
+int
+main (void)
+{
+  check_sub_float ();
+  check_sub_double ();
+  check_sub_long_double ();
+  exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/complex-sign.h b/gcc/testsuite/gcc.dg/torture/complex-sign.h
new file mode 100644 (file)
index 0000000..f12e25d
--- /dev/null
@@ -0,0 +1,74 @@
+/* Common header for complex arithmetic sign tests.  */
+
+extern void abort (void);
+extern void exit (int);
+
+#define CHECK_RES(VALUE, COPY, SIGN_REAL, SIGN_IMAG)           \
+  do {                                                         \
+    if ((VALUE) != 0                                           \
+       || COPY (1.0, __real__ (VALUE)) != SIGN_REAL 1.0        \
+       || COPY (1.0, __imag__ (VALUE)) != SIGN_IMAG 1.0)       \
+      abort ();                                                        \
+  } while (0)
+
+/* This definition is intended to work with or without imaginary
+   types, as long as mixed real/complex arithmetic is handled
+   correctly.  */
+#define ENCODE(ZERO, ZEROI, SA, SB)                    \
+  (SA 1 == 1                                           \
+   ? SB 1 == 1 ? ZERO + ZEROI : ZERO - ZEROI           \
+   : SB 1 == 1 ? -(ZERO - ZEROI) : -(ZERO + ZEROI))
+
+#define CHECK_ARITH(TYPE, COPY, ZERO, ZEROI, OP, S1, S2, S3, S4, SR, SI) \
+  do {                                                                 \
+    _Complex TYPE a1, b1, c1;                                          \
+    volatile _Complex TYPE a2, b2, c2;                                 \
+    a1 = ENCODE(ZERO, ZEROI, S1, S2);                                  \
+    CHECK_RES (a1, COPY, S1, S2);                                      \
+    b1 = ENCODE(ZERO, ZEROI, S3, S4);                                  \
+    CHECK_RES (b1, COPY, S3, S4);                                      \
+    c1 = a1 OP b1;                                                     \
+    CHECK_RES (c1, COPY, SR, SI);                                      \
+    a2 = ENCODE(ZERO, ZEROI, S1, S2);                                  \
+    CHECK_RES (a2, COPY, S1, S2);                                      \
+    b2 = ENCODE(ZERO, ZEROI, S3, S4);                                  \
+    CHECK_RES (b2, COPY, S3, S4);                                      \
+    c2 = a2 OP b2;                                                     \
+    CHECK_RES (c2, COPY, SR, SI);                                      \
+  } while (0)
+
+#define CHECK_ARITH_RC(TYPE, COPY, ZERO, ZEROI, OP, S1, S3, S4, SR, SI) \
+  do {                                                                 \
+    TYPE a1;                                                           \
+    _Complex TYPE b1, c1;                                              \
+    volatile TYPE a2;                                                  \
+    volatile _Complex TYPE b2, c2;                                     \
+    a1 = S1 ZERO;                                                      \
+    b1 = ENCODE(ZERO, ZEROI, S3, S4);                                  \
+    CHECK_RES (b1, COPY, S3, S4);                                      \
+    c1 = a1 OP b1;                                                     \
+    CHECK_RES (c1, COPY, SR, SI);                                      \
+    a2 = S1 ZERO;                                                      \
+    b2 = ENCODE(ZERO, ZEROI, S3, S4);                                  \
+    CHECK_RES (b2, COPY, S3, S4);                                      \
+    c2 = a2 OP b2;                                                     \
+    CHECK_RES (c2, COPY, SR, SI);                                      \
+  } while (0)
+
+#define CHECK_ARITH_CR(TYPE, COPY, ZERO, ZEROI, OP, S1, S2, S3, V3, SR, SI) \
+  do {                                                                 \
+    _Complex TYPE a1, c1;                                              \
+    TYPE b1;                                                           \
+    volatile _Complex TYPE a2, c2;                                     \
+    volatile TYPE b2;                                                  \
+    a1 = ENCODE(ZERO, ZEROI, S1, S2);                                  \
+    CHECK_RES (a1, COPY, S1, S2);                                      \
+    b1 = S3 V3;                                                                \
+    c1 = a1 OP b1;                                                     \
+    CHECK_RES (c1, COPY, SR, SI);                                      \
+    a2 = ENCODE(ZERO, ZEROI, S1, S2);                                  \
+    CHECK_RES (a2, COPY, S1, S2);                                      \
+    b2 = S3 V3;                                                                \
+    c2 = a2 OP b2;                                                     \
+    CHECK_RES (c2, COPY, SR, SI);                                      \
+  } while (0)
index 7dbc63a..2835220 100644 (file)
@@ -99,7 +99,10 @@ some_nonzerop (tree t)
 {
   int zerop = false;
 
-  if (TREE_CODE (t) == REAL_CST)
+  /* Operations with real or imaginary part of a complex number zero
+     cannot be treated the same as operations with a real or imaginary
+     operand if we care about the signs of zeros in the result.  */
+  if (TREE_CODE (t) == REAL_CST && !flag_signed_zeros)
     zerop = REAL_VALUES_IDENTICAL (TREE_REAL_CST (t), dconst0);
   else if (TREE_CODE (t) == FIXED_CST)
     zerop = fixed_zerop (t);