diff mbox series

c/106800 - support vector condition operation in C

Message ID 20240730131354.49DBD385B510@sourceware.org
State New
Headers show
Series c/106800 - support vector condition operation in C | expand

Commit Message

Richard Biener July 30, 2024, 1:13 p.m. UTC
The following adds support for vector conditionals in C.  The support
was nearly there already but c_objc_common_truthvalue_conversion
rejecting vector types.  Instead of letting them pass there unchanged
I chose to instead skip it when parsing conditionals instead as a
variant with less possible fallout.  The part missing was promotion
of a scalar operands to vector, I copied the logic from binary operator
handling for this and for the case of two scalars mimicked what the
C++ frontend does.

I've moved the testcases I could easily spot over to c-c++-common.

Changed from the first version this should now be more compatible
with what the C++ frontend accepts and rejects, in particular
two scalar operands are now promote to vectors before applying
standard conversions.  I've resorted to c_common_type here,
the C++ frontend performs a scalar ?: build but C++ doesn't seem
to involve integral promotions in that so this doesn't work for C.
This enabled c-c++-common/vector23.c to be "moved" from g++.dg/ext 
and one extra case in c-c++-common/vector19.c but with an
explicit conversion of the character literal.
        
Bootstrapped and tested on x86_64-unknown-linux-gnu, OK?

Thanks,
Richard.

	PR c/106800
gcc/
	* doc/extend.texi (Vector Extension): Document ternary ?:
	to be generally available.

gcc/c/
	* c-parser.cc (c_parser_conditional_expression): Skip
	truthvalue conversion for vector typed conditions.
	* c-typeck.cc (build_conditional_expr): Build a VEC_COND_EXPR
	for vector typed ifexpr.  Do basic diagnostics.

	* g++.dg/ext/pr56790-1.C: Move ...
	* c-c++-common/pr56790-1.c: ... here.
	* g++.dg/opt/vectcond-1.C: Move ...
	* c-c++-common/vectcond-1.c: ... here.
	* g++.dg/ext/vector21.C: Move ...
	* c-c++-common/vector21.c: ... here.
	* g++.dg/ext/vector22.C: Move ...
	* c-c++-common/vector22.c: ... here.
	* g++.dg/ext/vector35.C: Move ...
	* c-c++-common/vector35.c: ... here.
	* g++.dg/ext/vector19.C: Move common parts ...
	* c-c++-common/vector19.c: ... here.
	* gcc.dg/vector-19.c: Add c23 auto case.
	* c-c++-common/vector23.c: Duplicate here from
	g++.dg/ext/vector23.C, removing use of auto and adding
	explicit cast.

amend
---
 gcc/c/c-parser.cc                             |   7 +-
 gcc/c/c-typeck.cc                             | 121 +++++++++++++++++-
 gcc/doc/extend.texi                           |   2 +-
 .../pr56790-1.C => c-c++-common/pr56790-1.c}  |   0
 .../vectcond-1.c}                             |   0
 gcc/testsuite/c-c++-common/vector19.c         |  34 +++++
 .../vector21.C => c-c++-common/vector21.c}    |   0
 .../vector22.C => c-c++-common/vector22.c}    |   0
 gcc/testsuite/c-c++-common/vector23.c         |  28 ++++
 .../vector35.C => c-c++-common/vector35.c}    |   0
 gcc/testsuite/g++.dg/ext/vector19.C           |  25 ----
 gcc/testsuite/gcc.dg/vector-19.c              |  10 ++
 12 files changed, 191 insertions(+), 36 deletions(-)
 rename gcc/testsuite/{g++.dg/ext/pr56790-1.C => c-c++-common/pr56790-1.c} (100%)
 rename gcc/testsuite/{g++.dg/opt/vectcond-1.C => c-c++-common/vectcond-1.c} (100%)
 create mode 100644 gcc/testsuite/c-c++-common/vector19.c
 rename gcc/testsuite/{g++.dg/ext/vector21.C => c-c++-common/vector21.c} (100%)
 rename gcc/testsuite/{g++.dg/ext/vector22.C => c-c++-common/vector22.c} (100%)
 create mode 100644 gcc/testsuite/c-c++-common/vector23.c
 rename gcc/testsuite/{g++.dg/ext/vector35.C => c-c++-common/vector35.c} (100%)
 create mode 100644 gcc/testsuite/gcc.dg/vector-19.c
diff mbox series

Patch

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 9b9284b1ba4..c5eacfd7d97 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -9281,9 +9281,10 @@  c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
     }
   else
     {
-      cond.value
-	= c_objc_common_truthvalue_conversion
-	(cond_loc, default_conversion (cond.value));
+      /* Vector conditions see no default or truthvalue conversion.  */
+      if (!VECTOR_INTEGER_TYPE_P (TREE_TYPE (cond.value)))
+	cond.value = c_objc_common_truthvalue_conversion
+		       (cond_loc, default_conversion (cond.value));
       c_inhibit_evaluation_warnings += cond.value == truthvalue_false_node;
       exp1 = c_parser_expression_conv (parser);
       mark_exp_read (exp1.value);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 094e41fa202..b0cad0278df 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -5677,6 +5677,57 @@  build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
   if (ifexp_int_operands)
     ifexp = remove_c_maybe_const_expr (ifexp);
 
+  /* When this is a vector conditional but both alternatives are not vector
+     types promote them before applying default conversions.  */
+  if (VECTOR_TYPE_P (TREE_TYPE (ifexp))
+      && !VECTOR_TYPE_P (TREE_TYPE (op1))
+      && !VECTOR_TYPE_P (TREE_TYPE (op2)))
+    {
+      tree ifexp_type = TREE_TYPE (ifexp);
+      if (TREE_CODE (ifexp) == VEC_COND_EXPR
+	  && COMPARISON_CLASS_P (TREE_OPERAND (ifexp, 0)))
+	ifexp_type = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (ifexp, 0), 0));
+      tree stype;
+      if ((TREE_CODE (TREE_TYPE (op1)) != REAL_TYPE
+	   && TREE_CODE (TREE_TYPE (op1)) != INTEGER_TYPE)
+	  || (TREE_CODE (TREE_TYPE (op2)) != REAL_TYPE
+	      && TREE_CODE (TREE_TYPE (op2)) != INTEGER_TYPE)
+	  || ((stype = c_common_type (TREE_TYPE (op1), TREE_TYPE (op2)))
+	      == error_mark_node))
+	{
+	  error_at (colon_loc, "cannot infer scalar type from operands %qE, "
+		    "%qE and %qT", op1, op2, TREE_TYPE (ifexp_type));
+	  return error_mark_node;
+	}
+      if (TYPE_SIZE (stype) != TYPE_SIZE (TREE_TYPE (TREE_TYPE (ifexp)))
+	  || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype)))
+	{
+	  error_at (colon_loc, "inferred scalar type %qT is not an integer or "
+		    "floating-point type of the same size as %qT", stype,
+		    TREE_TYPE (ifexp_type));
+	  return error_mark_node;
+	}
+      tree vtype
+	= build_opaque_vector_type (stype,
+				    TYPE_VECTOR_SUBPARTS (TREE_TYPE (ifexp)));
+      if (unsafe_conversion_p (stype, op1, NULL_TREE, false))
+	{
+	  error_at (op1_loc, "conversion of scalar %qT to vector %qT "
+		    "involves truncation", TREE_TYPE (op1), vtype);
+	  return error_mark_node;
+	}
+      if (unsafe_conversion_p (stype, op2, NULL_TREE, false))
+	{
+	  error_at (op2_loc, "conversion of scalar %qT to vector %qT "
+		    "involves truncation", TREE_TYPE (op2), vtype);
+	  return error_mark_node;
+	}
+      op1 = convert_and_check (op1_loc, stype, op1, false);
+      op2 = convert_and_check (op2_loc, stype, op2, false);
+      op1 = build_vector_from_val (vtype, save_expr (op1));
+      op2 = build_vector_from_val (vtype, save_expr (op2));
+    }
+
   /* Promote both alternatives.  */
 
   if (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE)
@@ -5990,6 +6041,50 @@  build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 			 TYPE_MAIN_VARIANT (type2)))
     result_type = composite_type (TYPE_MAIN_VARIANT (type1),
 				  TYPE_MAIN_VARIANT (type2));
+  else if (VECTOR_TYPE_P (TREE_TYPE (ifexp))
+	   && ((gnu_vector_type_p (type1) && code2 != VECTOR_TYPE)
+	       || (gnu_vector_type_p (type2) && code1 != VECTOR_TYPE)))
+    {
+      enum stv_conv convert_flag = scalar_to_vector (colon_loc, VEC_COND_EXPR,
+						     orig_op1, orig_op2, true);
+      switch (convert_flag)
+	{
+	  case stv_error:
+	    return error_mark_node;
+	  case stv_firstarg:
+	    {
+	      bool maybe_const = true;
+	      tree sc;
+	      sc = c_fully_fold (op1, false, &maybe_const);
+	      sc = save_expr (sc);
+	      sc = convert (TREE_TYPE (type2), sc);
+	      op1 = build_vector_from_val (type2, sc);
+	      if (!maybe_const)
+		op1 = c_wrap_maybe_const (op1, true);
+	      type1 = TREE_TYPE (op1);
+	      code1 = TREE_CODE (type1);
+	      result_type = type2;
+	      break;
+	    }
+	  case stv_secondarg:
+	    {
+	      bool maybe_const = true;
+	      tree sc;
+	      sc = c_fully_fold (op2, false, &maybe_const);
+	      sc = save_expr (sc);
+	      sc = convert (TREE_TYPE (type1), sc);
+	      op2 = build_vector_from_val (type1, sc);
+	      if (!maybe_const)
+		op2 = c_wrap_maybe_const (op2, true);
+	      type2 = TREE_TYPE (op2);
+	      code2 = TREE_CODE (type2);
+	      result_type = type1;
+	      break;
+	    }
+	  default:
+	    break;
+	}
+    }
 
   if (!result_type)
     {
@@ -6036,18 +6131,30 @@  build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 		       && !TREE_OVERFLOW (orig_op2)));
     }
 
-  /* Need to convert condition operand into a vector mask.  */
-  if (VECTOR_TYPE_P (TREE_TYPE (ifexp)))
+  if (VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp)))
     {
+      /* Need to convert condition operand into a vector mask.  */
       tree vectype = TREE_TYPE (ifexp);
-      tree elem_type = TREE_TYPE (vectype);
-      tree zero = build_int_cst (elem_type, 0);
-      tree zero_vec = build_vector_from_val (vectype, zero);
+      tree zero_vec = build_zero_cst (vectype);
       tree cmp_type = truth_type_for (vectype);
       ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec);
-    }
 
-  if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST))
+      if (!VECTOR_TYPE_P (type1)
+	  || !VECTOR_TYPE_P (type2)
+	  || TYPE_MAIN_VARIANT (type1) != TYPE_MAIN_VARIANT (type2)
+	  || maybe_ne (TYPE_VECTOR_SUBPARTS (vectype),
+		       TYPE_VECTOR_SUBPARTS (type1))
+	  || TYPE_SIZE (vectype) != TYPE_SIZE (type1))
+	{
+	  error_at (op1_loc,
+		    "incompatible vector types in conditional expression: "
+		    "%qT, %qT and %qT", vectype, type1, type2);
+	  return error_mark_node;
+	}
+
+      ret = build3_loc (colon_loc, VEC_COND_EXPR, result_type, ifexp, op1, op2);
+    }
+  else if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST))
     ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2);
   else
     {
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 927aa24ab63..7405e89d4d3 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -13175,7 +13175,7 @@  c = a >  b;     /* The result would be @{0, 0,-1, 0@}  */
 c = a == b;     /* The result would be @{0,-1, 0,-1@}  */
 @end smallexample
 
-In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where
+The ternary operator @code{?:} is supported for vectors. @code{a?b:c}, where
 @code{b} and @code{c} are vectors of the same type and @code{a} is an
 integer vector with the same number of elements of the same size as @code{b}
 and @code{c}, computes all three arguments and creates a vector
diff --git a/gcc/testsuite/g++.dg/ext/pr56790-1.C b/gcc/testsuite/c-c++-common/pr56790-1.c
similarity index 100%
rename from gcc/testsuite/g++.dg/ext/pr56790-1.C
rename to gcc/testsuite/c-c++-common/pr56790-1.c
diff --git a/gcc/testsuite/g++.dg/opt/vectcond-1.C b/gcc/testsuite/c-c++-common/vectcond-1.c
similarity index 100%
rename from gcc/testsuite/g++.dg/opt/vectcond-1.C
rename to gcc/testsuite/c-c++-common/vectcond-1.c
diff --git a/gcc/testsuite/c-c++-common/vector19.c b/gcc/testsuite/c-c++-common/vector19.c
new file mode 100644
index 00000000000..47ec7e73e5d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/vector19.c
@@ -0,0 +1,34 @@ 
+/* { dg-do compile } */
+
+typedef double vec __attribute__((vector_size(2*sizeof(double))));
+typedef signed char vec2 __attribute__((vector_size(16)));
+typedef unsigned char vec2u __attribute__((vector_size(16)));
+
+void f (vec *x, vec *y, vec *z)
+{
+  *x = (*y < *z) ? *x : *y;
+}
+
+void g (vec *x, vec *y, vec *z)
+{
+  *x = (*y < *z) ? *x : 42;
+}
+
+void h (vec *x, vec *y, vec *z)
+{
+  *x = (*y < *z) ? 3. : *y;
+}
+
+void i2 (vec2 *x, vec2 *y, vec2u *z)
+{
+  *x = *y ? *x : *y;
+  *y = *z ? *x : *y;
+}
+
+void j (vec2 *x, vec2 *y, vec2 *z, vec *t)
+{
+  *z = (*x < *z) ? (char)'1' : (char)'0';
+  *x = (*y < *z) ? *x : 4.2; /* { dg-error "" } */
+  *y = (*x < *z) ? 2.5 : *y; /* { dg-error "" } */
+  *t = *t ? *t : *t; /* { dg-error "" } */
+}
diff --git a/gcc/testsuite/g++.dg/ext/vector21.C b/gcc/testsuite/c-c++-common/vector21.c
similarity index 100%
rename from gcc/testsuite/g++.dg/ext/vector21.C
rename to gcc/testsuite/c-c++-common/vector21.c
diff --git a/gcc/testsuite/g++.dg/ext/vector22.C b/gcc/testsuite/c-c++-common/vector22.c
similarity index 100%
rename from gcc/testsuite/g++.dg/ext/vector22.C
rename to gcc/testsuite/c-c++-common/vector22.c
diff --git a/gcc/testsuite/c-c++-common/vector23.c b/gcc/testsuite/c-c++-common/vector23.c
new file mode 100644
index 00000000000..e1f99a4e1da
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/vector23.c
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wsign-conversion" } */
+// Ignore warning on some powerpc-linux configurations.
+// { dg-prune-output "non-standard ABI extension" }
+// Ignore warning on Linux/x86
+// { dg-prune-output "changes the ABI" }
+
+typedef double vecd __attribute__((vector_size(4*sizeof(double))));
+typedef float vecf __attribute__((vector_size(8*sizeof(float))));
+typedef long vecl __attribute__((vector_size(4*sizeof(long))));
+typedef short vecs __attribute__((vector_size(8*sizeof(short))));
+typedef char vecc __attribute__((vector_size(16*sizeof(char))));
+
+vecf f(vecf*a,float d,long long i){
+  return (*a<0)?d:i; // { dg-error "truncation" }
+}
+vecc g(vecc*a){
+  return (*a<0)?3LL:42UL; // { dg-error "inferred scalar type" }
+}
+vecc h(vecd*a){
+  return (*a<0)?'a':'c'; // { dg-error "inferred scalar type \[^\\n\]*double" }
+}
+vecd i(vecc*a){
+  return (*a<0)?1:0.; // { dg-error "inferred scalar type" }
+}
+vecl j(vecl*a,long i,unsigned long k){
+  return (*a<0)?i:k; // { dg-warning "may change the sign" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/vector35.C b/gcc/testsuite/c-c++-common/vector35.c
similarity index 100%
rename from gcc/testsuite/g++.dg/ext/vector35.C
rename to gcc/testsuite/c-c++-common/vector35.c
diff --git a/gcc/testsuite/g++.dg/ext/vector19.C b/gcc/testsuite/g++.dg/ext/vector19.C
index a41e8d51706..ef94eff7210 100644
--- a/gcc/testsuite/g++.dg/ext/vector19.C
+++ b/gcc/testsuite/g++.dg/ext/vector19.C
@@ -4,38 +4,14 @@  typedef double vec __attribute__((vector_size(2*sizeof(double))));
 typedef signed char vec2 __attribute__((vector_size(16)));
 typedef unsigned char vec2u __attribute__((vector_size(16)));
 
-void f (vec *x, vec *y, vec *z)
-{
-  *x = (*y < *z) ? *x : *y;
-}
-
-void g (vec *x, vec *y, vec *z)
-{
-  *x = (*y < *z) ? *x : 42;
-}
-
-void h (vec *x, vec *y, vec *z)
-{
-  *x = (*y < *z) ? 3. : *y;
-}
-
 void i1 (vec *x, vec *y, vec *z)
 {
   auto c = *y < *z;
   *x = c ? *x : *y;
 }
 
-void i2 (vec2 *x, vec2 *y, vec2u *z)
-{
-  *x = *y ? *x : *y;
-  *y = *z ? *x : *y;
-}
-
 void j (vec2 *x, vec2 *y, vec2 *z, vec *t)
 {
-  *x = (*y < *z) ? *x : 4.2; /* { dg-error "" } */
-  *y = (*x < *z) ? 2.5 : *y; /* { dg-error "" } */
-  *t = *t ? *t : *t; /* { dg-error "" } */
   *z = (*x < *z) ? '1' : '0';
 }
 
@@ -48,4 +24,3 @@  void l (vec2 *v, double x)
 {
   k (v, x);
 }
-
diff --git a/gcc/testsuite/gcc.dg/vector-19.c b/gcc/testsuite/gcc.dg/vector-19.c
new file mode 100644
index 00000000000..623d0048eda
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vector-19.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23" } */
+
+typedef double vec __attribute__((vector_size(2*sizeof(double))));
+
+void i1 (vec *x, vec *y, vec *z)
+{
+  auto c = *y < *z;
+  *x = c ? *x : *y;
+}