Patchwork Add __builtin_complex to construct complex values (C1X CMPLX* macros)

login
register
mail settings
Submitter Joseph S. Myers
Date Aug. 19, 2011, 3:55 p.m.
Message ID <Pine.LNX.4.64.1108191554450.6986@digraph.polyomino.org.uk>
Download mbox | patch
Permalink /patch/110692/
State New
Headers show

Comments

Joseph S. Myers - Aug. 19, 2011, 3:55 p.m.
This patch adds __builtin_complex to support generating values with
arbitrary real and imaginary parts, including in static initializers,
despite the absence of imaginary types.  (Recall that X + I * Y, in
the absence of imaginary types, is really X + Y * (0.0 + 1.0I),
resulting in a real part X + Y * 0.0 which yields incorrect results
when infinities or signed zero are used.)  This is intended to be used
by C library headers to define the C1X macros CMPLX*, with definitions
along the lines of:

#define CMPLX(X, Y) __builtin_complex ((double) (X), (double) (Y))

As requested in PR 48760 comment 12, this is purely a C front-end
built-in (actually, a keyword that looks like a built-in function)
providing syntax for COMPLEX_EXPR, rather than a middle-end built-in
function.  The C++ front end is using a different approach for this
issue, allowing list-initialization of _Complex values.  (Allowing
{ real, imag } initializers was one of the approaches considered for
C1X before the final macro approach was arrived at.  See N1464 for the
approach that was followed and references to previous proposals; the
initializer approach was proposal two in N1431.  Note that if you did
allow such initializers for C, it wouldn't provide *expressions*
usable in static initializers, since to make a braced initializer into
an expression you need a compound literal and compound literals can't
be used in static initializers.)

Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  Applied
to mainline.

2011-08-19  Joseph Myers  <joseph@codesourcery.com>

	* c-parser.c (c_parser_postfix_expression): Handle
	RID_BUILTIN_COMPLEX.
	* doc/extend.texi (__builtin_complex): Document.

c-family:
2011-08-19  Joseph Myers  <joseph@codesourcery.com>

	* c-common.c (c_common_reswords): Add __builtin_complex.
	* c-common.h (RID_BUILTIN_COMPLEX): New.

testsuite:
2011-08-19  Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/builtin-complex-err-1.c, gcc.dg/builtin-complex-err-2.c,
	gcc.dg/dfp/builtin-complex.c, gcc.dg/torture/builtin-complex-1.c:
	New tests.
Jakub Jelinek - Aug. 19, 2011, 6:33 p.m.
On Fri, Aug 19, 2011 at 03:55:12PM +0000, Joseph S. Myers wrote:
> Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  Applied
> to mainline.

The new tests ICE on i686-linux:
FAIL: gcc.dg/builtin-complex-err-1.c (internal compiler error)
FAIL: gcc.dg/builtin-complex-err-2.c (internal compiler error)
FAIL: gcc.dg/torture/builtin-complex-1.c  -O*  (internal compiler error)

All the ICEs are on
    case EXCESS_PRECISION_EXPR:
      /* Each case where an operand with excess precision may be
         encountered must remove the EXCESS_PRECISION_EXPR around
         inner operands and possibly put one around the whole
         expression or possibly convert to the semantic type (which
         c_fully_fold does); we cannot tell at this stage which is
         appropriate in any particular case.  */
      gcc_unreachable ();
in c_fully_fold_internal.

	Jakub
Gabriel Dos Reis - Aug. 19, 2011, 9:11 p.m.
On Fri, Aug 19, 2011 at 10:55 AM, Joseph S. Myers
<joseph@codesourcery.com> wrote:
>  Note that if you did
> allow such initializers for C, it wouldn't provide *expressions*
> usable in static initializers, since to make a braced initializer into
> an expression you need a compound literal and compound literals can't
> be used in static initializers.)

Thanks for the rationale.  I was puzzled until I read that bits.
I would have thought that the natural thing to do was to fix
C's compound literals so that they can be used in static initializers.
Do you know why WG14 did not want to do that?

-- Gaby
Joseph S. Myers - Aug. 19, 2011, 9:58 p.m.
On Fri, 19 Aug 2011, Gabriel Dos Reis wrote:

> On Fri, Aug 19, 2011 at 10:55 AM, Joseph S. Myers
> <joseph@codesourcery.com> wrote:
> >  Note that if you did
> > allow such initializers for C, it wouldn't provide *expressions*
> > usable in static initializers, since to make a braced initializer into
> > an expression you need a compound literal and compound literals can't
> > be used in static initializers.)
> 
> Thanks for the rationale.  I was puzzled until I read that bits.
> I would have thought that the natural thing to do was to fix
> C's compound literals so that they can be used in static initializers.
> Do you know why WG14 did not want to do that?

A compound literal is essentially an anonymous variable with a given 
initializer, so I suppose it comes down to C not allowing const variables 
(to which const qualified compound literals are equivalent, except that 
they may share storage, like string constants and unlike named variables) 
in initializers and I don't know a specific rationale for that difference 
between C and C++.
Gary Funck - Aug. 22, 2011, 10:26 p.m.
On 08/19/11 15:55:12, Joseph S. Myers wrote:
> Index: gcc/c-family/c-common.h
> ===================================================================
> --- gcc/c-family/c-common.h	(revision 177894)
> +++ gcc/c-family/c-common.h	(working copy)
> @@ -103,7 +103,7 @@ enum rid
>    /* C extensions */
>    RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
>    RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
> -  RID_TYPES_COMPATIBLE_P,
> +  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,
>    RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
>    RID_FRACT, RID_ACCUM,

Joseph,

Does this comment also need to be adjusted?

/* Reserved identifiers.  This is the union of all the keywords for C,
   C++, and Objective-C.  All the type modifiers have to be in one
   block at the beginning, because they are used as mask bits.  There
   are 28 type modifiers; if we add many more we will have to redesign
   the mask mechanism.  */

That is: 28 -> 29?

BTW, unfortunately for GUPC, this will bump the number of bits
it uses to *32*.  Thus, any subsequent addition of RID's will
cause GUPC to exceed the 32-bit barrier.

Is it time to consider increasing the bit range of
this set of flag bits?

- Gary
Joseph S. Myers - Aug. 22, 2011, 10:39 p.m.
On Mon, 22 Aug 2011, Gary Funck wrote:

> 
> On 08/19/11 15:55:12, Joseph S. Myers wrote:
> > Index: gcc/c-family/c-common.h
> > ===================================================================
> > --- gcc/c-family/c-common.h	(revision 177894)
> > +++ gcc/c-family/c-common.h	(working copy)
> > @@ -103,7 +103,7 @@ enum rid
> >    /* C extensions */
> >    RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
> >    RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
> > -  RID_TYPES_COMPATIBLE_P,
> > +  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,
> >    RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
> >    RID_FRACT, RID_ACCUM,
> 
> Joseph,
> 
> Does this comment also need to be adjusted?
> 
> /* Reserved identifiers.  This is the union of all the keywords for C,
>    C++, and Objective-C.  All the type modifiers have to be in one
>    block at the beginning, because they are used as mask bits.  There
>    are 28 type modifiers; if we add many more we will have to redesign
>    the mask mechanism.  */
> 
> That is: 28 -> 29?

This isn't a type modifier; neither is __builtin_types_compatible_p.  It's 
not within the first 28.

> BTW, unfortunately for GUPC, this will bump the number of bits
> it uses to *32*.  Thus, any subsequent addition of RID's will
> cause GUPC to exceed the 32-bit barrier.
> 
> Is it time to consider increasing the bit range of
> this set of flag bits?

I don't believe the comment is accurate; I'm not aware of any code for any 
C-family front end that uses these values as mask bits at all.
Gary Funck - Aug. 22, 2011, 11:03 p.m.
On 08/22/11 22:39:04, Joseph S. Myers wrote:
[...]
> This isn't a type modifier; neither is __builtin_types_compatible_p.  It's 
> not within the first 28.
[...]
> I don't believe the comment is accurate; I'm not aware of any code for any 
> C-family front end that uses these values as mask bits at all.

OK, thanks for the clarification.

- Gary

Patch

Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 177894)
+++ gcc/doc/extend.texi	(working copy)
@@ -7511,6 +7511,18 @@  future revisions.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} @var{type} __builtin_complex (@var{real}, @var{imag})
+
+The built-in function @code{__builtin_complex} is provided for use in
+implementing the ISO C1X macros @code{CMPLXF}, @code{CMPLX} and
+@code{CMPLXL}.  @var{real} and @var{imag} must have the same type, a
+real binary floating-point type, and the result has the corresponding
+complex type with real and imaginary parts @var{real} and @var{imag}.
+Unlike @samp{@var{real} + I * @var{imag}}, this works even when
+infinities, NaNs and negative zeros are involved.
+
+@end deftypefn
+
 @deftypefn {Built-in Function} int __builtin_constant_p (@var{exp})
 You can use the built-in function @code{__builtin_constant_p} to
 determine if a value is known to be constant at compile-time and hence
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 177894)
+++ gcc/c-family/c-common.c	(working copy)
@@ -424,6 +424,7 @@  const struct c_common_resword c_common_r
   { "__attribute",	RID_ATTRIBUTE,	0 },
   { "__attribute__",	RID_ATTRIBUTE,	0 },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
+  { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
   { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
   { "__builtin_va_arg",	RID_VA_ARG,	0 },
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 177894)
+++ gcc/c-family/c-common.h	(working copy)
@@ -103,7 +103,7 @@  enum rid
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
-  RID_TYPES_COMPATIBLE_P,
+  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
   RID_FRACT, RID_ACCUM,
 
Index: gcc/testsuite/gcc.dg/dfp/builtin-complex.c
===================================================================
--- gcc/testsuite/gcc.dg/dfp/builtin-complex.c	(revision 0)
+++ gcc/testsuite/gcc.dg/dfp/builtin-complex.c	(revision 0)
@@ -0,0 +1,10 @@ 
+/* Test __builtin_complex errors with DFP.  */
+/* { dg-do compile } */
+
+_Decimal32 a, b;
+
+void
+f (void)
+{
+  __builtin_complex (a, b); /* { dg-error "not of real binary floating-point type" } */
+}
Index: gcc/testsuite/gcc.dg/builtin-complex-err-1.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-complex-err-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/builtin-complex-err-1.c	(revision 0)
@@ -0,0 +1,26 @@ 
+/* Test __builtin_complex errors.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c1x -pedantic-errors" } */
+
+typedef double D;
+
+double d;
+
+_Complex double dc = __builtin_complex (1.0, (D) 0.0);
+
+_Complex double dc2 = __builtin_complex (d, 0.0); /* { dg-error "not constant" } */
+
+_Complex float fc = __builtin_complex (1.0f, 1); /* { dg-error "not of real binary floating-point type" } */
+
+_Complex float fc2 = __builtin_complex (1, 1.0f); /* { dg-error "not of real binary floating-point type" } */
+
+_Complex float fc3 = __builtin_complex (1.0f, 1.0); /* { dg-error "different types" } */
+
+void
+f (void)
+{
+  __builtin_complex (0.0); /* { dg-error "expected" } */
+  __builtin_complex (0.0, 0.0, 0.0); /* { dg-error "expected" } */
+}
+
+void (*p) (void) = __builtin_complex; /* { dg-error "expected" } */
Index: gcc/testsuite/gcc.dg/torture/builtin-complex-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/builtin-complex-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/builtin-complex-1.c	(revision 0)
@@ -0,0 +1,117 @@ 
+/* Test __builtin_complex semantics.  */
+/* { dg-do run } */
+/* { dg-options "-std=c1x -pedantic-errors" } */
+
+extern void exit (int);
+extern void abort (void);
+
+#define COMPARE_BODY(A, B, TYPE, COPYSIGN)				\
+  do {									\
+    TYPE s1 = COPYSIGN ((TYPE) 1.0, A);					\
+    TYPE s2 = COPYSIGN ((TYPE) 1.0, B);					\
+    if (s1 != s2)							\
+      abort ();								\
+    if ((__builtin_isnan (A) != 0) != (__builtin_isnan (B) != 0))	\
+      abort ();								\
+    if ((A != B) != (__builtin_isnan (A) != 0))				\
+      abort ();								\
+  } while (0)
+
+void
+comparef (float a, float b)
+{
+  COMPARE_BODY (a, b, float, __builtin_copysignf);
+}
+
+void
+compare (double a, double b)
+{
+  COMPARE_BODY (a, b, double, __builtin_copysign);
+}
+
+void
+comparel (long double a, long double b)
+{
+  COMPARE_BODY (a, b, long double, __builtin_copysignl);
+}
+
+void
+comparecf (_Complex float a, float r, float i)
+{
+  comparef (__real__ a, r);
+  comparef (__imag__ a, i);
+}
+
+void
+comparec (_Complex double a, double r, double i)
+{
+  compare (__real__ a, r);
+  compare (__imag__ a, i);
+}
+
+void
+comparecl (_Complex long double a, long double r, long double i)
+{
+  comparel (__real__ a, r);
+  comparel (__imag__ a, i);
+}
+
+#define VERIFY(A, B, TYPE, COMPARE)			\
+  do {							\
+    TYPE a = A;						\
+    TYPE b = B;						\
+    _Complex TYPE cr = __builtin_complex (a, b);	\
+    static _Complex TYPE cs = __builtin_complex (A, B);	\
+    COMPARE (cr, A, B);					\
+    COMPARE (cs, A, B);					\
+  } while (0)
+
+#define ALL_CHECKS(PZ, NZ, NAN, INF, TYPE, COMPARE)	\
+  do {							\
+    VERIFY (PZ, PZ, TYPE, COMPARE);			\
+    VERIFY (PZ, NZ, TYPE, COMPARE);			\
+    VERIFY (PZ, NAN, TYPE, COMPARE);			\
+    VERIFY (PZ, INF, TYPE, COMPARE);			\
+    VERIFY (NZ, PZ, TYPE, COMPARE);			\
+    VERIFY (NZ, NZ, TYPE, COMPARE);			\
+    VERIFY (NZ, NAN, TYPE, COMPARE);			\
+    VERIFY (NZ, INF, TYPE, COMPARE);			\
+    VERIFY (NAN, PZ, TYPE, COMPARE);			\
+    VERIFY (NAN, NZ, TYPE, COMPARE);			\
+    VERIFY (NAN, NAN, TYPE, COMPARE);			\
+    VERIFY (NAN, INF, TYPE, COMPARE);			\
+    VERIFY (INF, PZ, TYPE, COMPARE);			\
+    VERIFY (INF, NZ, TYPE, COMPARE);			\
+    VERIFY (INF, NAN, TYPE, COMPARE);			\
+    VERIFY (INF, INF, TYPE, COMPARE);			\
+  } while (0)
+
+void
+check_float (void)
+{
+  ALL_CHECKS (0.0f, -0.0f, __builtin_nanf(""), __builtin_inff(),
+	      float, comparecf);
+}
+
+void
+check_double (void)
+{
+  ALL_CHECKS (0.0, -0.0, __builtin_nan(""), __builtin_inf(),
+	      double, comparec);
+}
+
+void
+check_long_double (void)
+{
+  ALL_CHECKS (0.0l, -0.0l, __builtin_nanl(""), __builtin_infl(),
+	      long double, comparecl);
+}
+
+int
+main (void)
+{
+  check_float ();
+  check_double ();
+  check_long_double ();
+  exit (0);
+}
Index: gcc/testsuite/gcc.dg/builtin-complex-err-2.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-complex-err-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/builtin-complex-err-2.c	(revision 0)
@@ -0,0 +1,10 @@ 
+/* Test __builtin_complex errors.  Verify it does nto allow quiet
+   creation of complex types in C90.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c90 -pedantic-errors" } */
+
+void
+f (void)
+{
+  __builtin_complex (0.0, 0.0); /* { dg-error "ISO C90 does not support complex types" } */
+}
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 177894)
+++ gcc/c-parser.c	(working copy)
@@ -6023,6 +6023,7 @@  c_parser_alignof_expression (c_parser *p
 			     assignment-expression ,
 			     assignment-expression )
      __builtin_types_compatible_p ( type-name , type-name )
+     __builtin_complex ( assignment-expression , assignment-expression )
 
    offsetof-member-designator:
      identifier
@@ -6405,6 +6406,52 @@  c_parser_postfix_expression (c_parser *p
 	      = comptypes (e1, e2) ? integer_one_node : integer_zero_node;
 	  }
 	  break;
+	case RID_BUILTIN_COMPLEX:
+	  c_parser_consume_token (parser);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+	    {
+	      expr.value = error_mark_node;
+	      break;
+	    }
+	  loc = c_parser_peek_token (parser)->location;
+	  e1 = c_parser_expr_no_commas (parser, NULL);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      break;
+	    }
+	  e2 = c_parser_expr_no_commas (parser, NULL);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				     "expected %<)%>");
+	  mark_exp_read (e1.value);
+	  mark_exp_read (e2.value);
+	  if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (e1.value))
+	      || DECIMAL_FLOAT_TYPE_P (TREE_TYPE (e1.value))
+	      || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (e2.value))
+	      || DECIMAL_FLOAT_TYPE_P (TREE_TYPE (e2.value)))
+	    {
+	      error_at (loc, "%<__builtin_complex%> operand "
+			"not of real binary floating-point type");
+	      expr.value = error_mark_node;
+	      break;
+	    }
+	  if (TYPE_MAIN_VARIANT (TREE_TYPE (e1.value))
+	      != TYPE_MAIN_VARIANT (TREE_TYPE (e2.value)))
+	    {
+	      error_at (loc,
+			"%<__builtin_complex%> operands of different types");
+	      expr.value = error_mark_node;
+	      break;
+	    }
+	  if (!flag_isoc99)
+	    pedwarn (loc, OPT_pedantic,
+		     "ISO C90 does not support complex types");
+	  expr.value = build2 (COMPLEX_EXPR,
+			       build_complex_type (TYPE_MAIN_VARIANT
+						   (TREE_TYPE (e1.value))),
+			       e1.value, e2.value);
+	  break;
 	case RID_AT_SELECTOR:
 	  gcc_assert (c_dialect_objc ());
 	  c_parser_consume_token (parser);