Fix complex arithmetic and signed zeros

Joseph S. Myers joseph@codesourcery.com
Thu May 7 18:27:00 GMT 2009


This patch implements the C99 requirements that arithmetic between one
real and one complex operand does not convert the real operand to a
complex type.  This avoids excess operations on zero values in such
cases, which are inefficient and can lead to the wrong sign of zero in
the result (recall that positive and negative zero add to positive
zero in all rounding modes except towards negative infinity).  There
appear to be no specific requirements regarding the result of
real/complex division that make converting both operands to a common
complex type there wrong, and that case might also need a new libgcc
function or other special handling to avoid the conversion, so nothing
is done about that case.

There is some code in tree-complex.c that tries to optimize complex
arithmetic with one operand real or imaginary, but if there are two
complex operands then that is different from the case of one real and
one complex operand because of the handling of signed zeros.  Thus
this must be disabled if flag_signed_zeros to get the correct
semantics.

Note that the absence of imaginary types means that expressions of the
form (a + b * I) may still yield unwanted results in some cases
because of b * I being b * (0.0 + 1.0i).  As far as I know this
(signed zeros in complex numbers) is the only use of imaginary types,
and they are not a generally implemented feature of C99 (Annex G is an
informative Annex, not normative; the Power ABI working group did not
find any sign of compilers for Power Architecture that implemented
these types).  So I do not propose to change anything regarding their
unimplemented state in GCC.

I believe the only piece needed after this patch to make complex
numbers support Done in c99status.html is fixing complex arithmetic
folding (PR 30789), likely using MPC.

Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  OK to
commit (the tree-complex.c part)?

The C front-end code checks flag_signed_zeros because it appears the
language-independent compiler is better with -fno-signed-zeros at
optimizing the complex arithmetic than at optimizing the accurately
represented real/complex arithmetic.  Without that check,
gcc.dg/builtins-20.c, gcc.dg/builtins-54.c and gcc.dg/pr30172-1.c fail
(the last is checking an early GIMPLE dump before most optimizations,
but the first two do suggest a real problem).  The complicated ENCODE
code in the testcases is because if you initialize both halves of a
complex number using __real__ and __imag__ assignments (the simplest
way of putting known values, including signed zeros, in both halves),
the compiler hasn't worked out that the complex number has a constant
value by the time it gets to lowering complex arithmetic, so tests
using __real__ and __imag__ assignments wouldn't detect the
problematic transformations for complex numbers with one part zero.

2009-05-07  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.

testsuite:
2009-05-07  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.

Index: tree-complex.c
===================================================================
--- tree-complex.c	(revision 147199)
+++ tree-complex.c	(working copy)
@@ -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);
Index: testsuite/gcc.dg/torture/complex-sign-mixed-add.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-mixed-add.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-mixed-add.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign-mixed-sub.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-mixed-sub.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-mixed-sub.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign-mul.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-mul.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-mul.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign.h
===================================================================
--- testsuite/gcc.dg/torture/complex-sign.h	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign.h	(revision 0)
@@ -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: testsuite/gcc.dg/torture/complex-sign-mixed-mul.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-mixed-mul.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-mixed-mul.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign-add.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-add.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-add.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign-sub.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-sub.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-sub.c	(revision 0)
@@ -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);
+}
Index: testsuite/gcc.dg/torture/complex-sign-mixed-div.c
===================================================================
--- testsuite/gcc.dg/torture/complex-sign-mixed-div.c	(revision 0)
+++ testsuite/gcc.dg/torture/complex-sign-mixed-div.c	(revision 0)
@@ -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);
+}
Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 147199)
+++ c-typeck.c	(working copy)
@@ -9246,7 +9246,9 @@ build_binary_op (location_t location, en
       (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, en
 	    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.

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Gcc-patches mailing list