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 --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; +} -- 2.43.0