+2019-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/85052
+ * tree-vect-generic.c: Include insn-config.h and recog.h.
+ (expand_vector_piecewise): Add defaulted ret_type argument,
+ if non-NULL, use that in preference to type for the result type.
+ (expand_vector_parallel): Formatting fix.
+ (do_vec_conversion, do_vec_narrowing_conversion,
+ expand_vector_conversion): New functions.
+ (expand_vector_operations_1): Call expand_vector_conversion
+ for VEC_CONVERT ifn calls.
+ * internal-fn.def (VEC_CONVERT): New internal function.
+ * internal-fn.c (expand_VEC_CONVERT): New function.
+ * fold-const-call.c (fold_const_vec_convert): New function.
+ (fold_const_call): Use it for CFN_VEC_CONVERT.
+ * doc/extend.texi (__builtin_convertvector): Document.
+
2019-01-07 Tom de Vries <tdevries@suse.de>
* config/nvptx/nvptx-protos.h (nvptx_output_red_partition): Declare.
+2019-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/85052
+ * c-common.h (enum rid): Add RID_BUILTIN_CONVERTVECTOR.
+ (c_build_vec_convert): Declare.
+ * c-common.c (c_build_vec_convert): New function.
+
2019-01-04 Martin Sebor <msebor@redhat.com>
PR c/88546
RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
{ "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
{ "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
+ { "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 },
{ "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
{ "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
{ "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
return ret;
}
+/* Build a VEC_CONVERT ifn for __builtin_convertvector builtin. */
+
+tree
+c_build_vec_convert (location_t loc1, tree expr, location_t loc2, tree type,
+ bool complain)
+{
+ if (error_operand_p (type))
+ return error_mark_node;
+ if (error_operand_p (expr))
+ return error_mark_node;
+
+ if (!VECTOR_INTEGER_TYPE_P (TREE_TYPE (expr))
+ && !VECTOR_FLOAT_TYPE_P (TREE_TYPE (expr)))
+ {
+ if (complain)
+ error_at (loc1, "%<__builtin_convertvector%> first argument must "
+ "be an integer or floating vector");
+ return error_mark_node;
+ }
+
+ if (!VECTOR_INTEGER_TYPE_P (type) && !VECTOR_FLOAT_TYPE_P (type))
+ {
+ if (complain)
+ error_at (loc2, "%<__builtin_convertvector%> second argument must "
+ "be an integer or floating vector type");
+ return error_mark_node;
+ }
+
+ if (maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (expr)),
+ TYPE_VECTOR_SUBPARTS (type)))
+ {
+ if (complain)
+ error_at (loc1, "%<__builtin_convertvector%> number of elements "
+ "of the first argument vector and the second argument "
+ "vector type should be the same");
+ return error_mark_node;
+ }
+
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (expr)))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+ || (VECTOR_INTEGER_TYPE_P (TREE_TYPE (expr))
+ && VECTOR_INTEGER_TYPE_P (type)
+ && (TYPE_PRECISION (TREE_TYPE (TREE_TYPE (expr)))
+ == TYPE_PRECISION (TREE_TYPE (type)))))
+ return build1_loc (loc1, VIEW_CONVERT_EXPR, type, expr);
+
+ bool wrap = true;
+ bool maybe_const = false;
+ tree ret;
+ if (!c_dialect_cxx ())
+ {
+ /* Avoid C_MAYBE_CONST_EXPRs inside of VEC_CONVERT argument. */
+ expr = c_fully_fold (expr, false, &maybe_const);
+ wrap &= maybe_const;
+ }
+
+ ret = build_call_expr_internal_loc (loc1, IFN_VEC_CONVERT, type, 1, expr);
+
+ if (!wrap)
+ ret = c_wrap_maybe_const (ret, true);
+
+ return ret;
+}
+
/* Like tree.c:get_narrower, but retain conversion from C++0x scoped enum
to integral type. */
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_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE,
- RID_BUILTIN_TGMATH,
+ RID_BUILTIN_CONVERTVECTOR, RID_BUILTIN_TGMATH,
RID_BUILTIN_HAS_ATTRIBUTE,
RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
extern tree c_build_vec_perm_expr (location_t, tree, tree, tree, bool = true);
+extern tree c_build_vec_convert (location_t, tree, location_t, tree, bool = true);
extern void init_c_lex (void);
+2019-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/85052
+ * c-parser.c (c_parser_postfix_expression): Parse
+ __builtin_convertvector.
+
2019-01-01 Jakub Jelinek <jakub@redhat.com>
Update copyright years.
__builtin_shuffle ( assignment-expression ,
assignment-expression ,
assignment-expression, )
+ __builtin_convertvector ( assignment-expression , type-name )
offsetof-member-designator:
identifier
*p = convert_lvalue_to_rvalue (loc, *p, true, true);
if (vec_safe_length (cexpr_list) == 2)
- expr.value =
- c_build_vec_perm_expr
- (loc, (*cexpr_list)[0].value,
- NULL_TREE, (*cexpr_list)[1].value);
+ expr.value = c_build_vec_perm_expr (loc, (*cexpr_list)[0].value,
+ NULL_TREE,
+ (*cexpr_list)[1].value);
else if (vec_safe_length (cexpr_list) == 3)
- expr.value =
- c_build_vec_perm_expr
- (loc, (*cexpr_list)[0].value,
- (*cexpr_list)[1].value,
- (*cexpr_list)[2].value);
+ expr.value = c_build_vec_perm_expr (loc, (*cexpr_list)[0].value,
+ (*cexpr_list)[1].value,
+ (*cexpr_list)[2].value);
else
{
error_at (loc, "wrong number of arguments to "
set_c_expr_source_range (&expr, loc, close_paren_loc);
break;
}
+ case RID_BUILTIN_CONVERTVECTOR:
+ {
+ location_t start_loc = loc;
+ c_parser_consume_token (parser);
+ matching_parens parens;
+ if (!parens.require_open (parser))
+ {
+ expr.set_error ();
+ break;
+ }
+ e1 = c_parser_expr_no_commas (parser, NULL);
+ mark_exp_read (e1.value);
+ if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+ {
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+ expr.set_error ();
+ break;
+ }
+ loc = c_parser_peek_token (parser)->location;
+ t1 = c_parser_type_name (parser);
+ location_t end_loc = c_parser_peek_token (parser)->get_finish ();
+ c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+ "expected %<)%>");
+ if (t1 == NULL)
+ expr.set_error ();
+ else
+ {
+ tree type_expr = NULL_TREE;
+ expr.value = c_build_vec_convert (start_loc, e1.value, loc,
+ groktypename (t1, &type_expr,
+ NULL));
+ set_c_expr_source_range (&expr, start_loc, end_loc);
+ }
+ }
+ break;
case RID_AT_SELECTOR:
{
gcc_assert (c_dialect_objc ());
+2019-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/85052
+ * cp-tree.h (cp_build_vec_convert): Declare.
+ * parser.c (cp_parser_postfix_expression): Parse
+ __builtin_convertvector.
+ * constexpr.c: Include fold-const-call.h.
+ (cxx_eval_internal_function): Handle IFN_VEC_CONVERT.
+ (potential_constant_expression_1): Likewise.
+ * semantics.c (cp_build_vec_convert): New function.
+ * pt.c (tsubst_copy_and_build): Handle CALL_EXPR to
+ IFN_VEC_CONVERT.
+
2019-01-03 Jakub Jelinek <jakub@redhat.com>
PR c++/88636
#include "ubsan.h"
#include "gimple-fold.h"
#include "timevar.h"
+#include "fold-const-call.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
false, non_constant_p, overflow_p);
+ case IFN_VEC_CONVERT:
+ {
+ tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
+ false, non_constant_p,
+ overflow_p);
+ if (TREE_CODE (arg) == VECTOR_CST)
+ return fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg);
+ else
+ {
+ *non_constant_p = true;
+ return t;
+ }
+ }
+
default:
if (!ctx->quiet)
error_at (cp_expr_loc_or_loc (t, input_location),
case IFN_SUB_OVERFLOW:
case IFN_MUL_OVERFLOW:
case IFN_LAUNDER:
+ case IFN_VEC_CONVERT:
bail = false;
+ break;
default:
break;
extern bool lambda_static_thunk_p (tree);
extern tree finish_builtin_launder (location_t, tree,
tsubst_flags_t);
+extern tree cp_build_vec_convert (tree, location_t, tree,
+ tsubst_flags_t);
extern void start_lambda_scope (tree);
extern void record_lambda_scope (tree);
extern void record_null_lambda_scope (tree);
break;
}
+ case RID_BUILTIN_CONVERTVECTOR:
+ {
+ tree expression;
+ tree type;
+ /* Consume the `__builtin_convertvector' token. */
+ cp_lexer_consume_token (parser->lexer);
+ /* Look for the opening `('. */
+ matching_parens parens;
+ parens.require_open (parser);
+ /* Now, parse the assignment-expression. */
+ expression = cp_parser_assignment_expression (parser);
+ /* Look for the `,'. */
+ cp_parser_require (parser, CPP_COMMA, RT_COMMA);
+ location_t type_location
+ = cp_lexer_peek_token (parser->lexer)->location;
+ /* Parse the type-id. */
+ {
+ type_id_in_expr_sentinel s (parser);
+ type = cp_parser_type_id (parser);
+ }
+ /* Look for the closing `)'. */
+ parens.require_close (parser);
+ return cp_build_vec_convert (expression, type_location, type,
+ tf_warning_or_error);
+ }
+
default:
{
tree type;
(*call_args)[0], complain);
break;
+ case IFN_VEC_CONVERT:
+ gcc_assert (nargs == 1);
+ if (vec_safe_length (call_args) != 1)
+ {
+ error_at (cp_expr_loc_or_loc (t, input_location),
+ "wrong number of arguments to "
+ "%<__builtin_convertvector%>");
+ ret = error_mark_node;
+ break;
+ }
+ ret = cp_build_vec_convert ((*call_args)[0], input_location,
+ tsubst (TREE_TYPE (t), args,
+ complain, in_decl),
+ complain);
+ if (TREE_CODE (ret) == VIEW_CONVERT_EXPR)
+ {
+ release_tree_vector (call_args);
+ RETURN (ret);
+ }
+ break;
+
default:
/* Unsupported internal function with arguments. */
gcc_unreachable ();
TREE_TYPE (arg), 1, arg);
}
+/* Finish __builtin_convertvector (arg, type). */
+
+tree
+cp_build_vec_convert (tree arg, location_t loc, tree type,
+ tsubst_flags_t complain)
+{
+ if (error_operand_p (type))
+ return error_mark_node;
+ if (error_operand_p (arg))
+ return error_mark_node;
+
+ tree ret = NULL_TREE;
+ if (!type_dependent_expression_p (arg) && !dependent_type_p (type))
+ ret = c_build_vec_convert (cp_expr_loc_or_loc (arg, input_location), arg,
+ loc, type, (complain & tf_error) != 0);
+
+ if (!processing_template_decl)
+ return ret;
+
+ return build_call_expr_internal_loc (loc, IFN_VEC_CONVERT, type, 1, arg);
+}
+
#include "gt-cp-semantics.h"
You cannot operate between vectors of different lengths or different
signedness without a cast.
+@findex __builtin_convertvector
+Vector conversion is available using the
+@code{__builtin_convertvector (vec, vectype)}
+function. @var{vec} must be an expression with integral or floating
+vector type and @var{vectype} an integral or floating vector type with the
+same number of elements. The result has @var{vectype} type and value of
+a C cast of every element of @var{vec} to the element type of @var{vectype}.
+
+Consider the following example,
+@smallexample
+typedef int v4si __attribute__ ((vector_size (16)));
+typedef float v4sf __attribute__ ((vector_size (16)));
+typedef double v4df __attribute__ ((vector_size (32)));
+typedef unsigned long long v4di __attribute__ ((vector_size (32)));
+
+v4si a = @{1,-2,3,-4@};
+v4sf b = @{1.5f,-2.5f,3.f,7.f@};
+v4di c = @{1ULL,5ULL,0ULL,10ULL@};
+v4sf d = __builtin_convertvector (a, v4sf); /* d is @{1.f,-2.f,3.f,-4.f@} */
+/* Equivalent of:
+ v4sf d = @{ (float)a[0], (float)a[1], (float)a[2], (float)a[3] @}; */
+v4df e = __builtin_convertvector (a, v4df); /* e is @{1.,-2.,3.,-4.@} */
+v4df f = __builtin_convertvector (b, v4df); /* f is @{1.5,-2.5,3.,7.@} */
+v4si g = __builtin_convertvector (f, v4si); /* g is @{1,-2,3,7@} */
+v4si h = __builtin_convertvector (c, v4si); /* h is @{1,5,0,10@} */
+@end smallexample
+
@node Offsetof
@section Support for @code{offsetof}
@findex __builtin_offsetof
#include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO. */
#include "builtins.h"
#include "gimple-expr.h"
+#include "tree-vector-builder.h"
/* Functions that test for certain constant types, abstracting away the
decision about whether to check for overflow. */
return res;
}
+/* Fold a call to IFN_VEC_CONVERT (ARG) returning TYPE. */
+
+static tree
+fold_const_vec_convert (tree ret_type, tree arg)
+{
+ enum tree_code code = NOP_EXPR;
+ tree arg_type = TREE_TYPE (arg);
+ if (TREE_CODE (arg) != VECTOR_CST)
+ return NULL_TREE;
+
+ gcc_checking_assert (VECTOR_TYPE_P (ret_type) && VECTOR_TYPE_P (arg_type));
+
+ if (INTEGRAL_TYPE_P (TREE_TYPE (ret_type))
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (arg_type)))
+ code = FIX_TRUNC_EXPR;
+ else if (INTEGRAL_TYPE_P (TREE_TYPE (arg_type))
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (ret_type)))
+ code = FLOAT_EXPR;
+
+ tree_vector_builder elts;
+ elts.new_unary_operation (ret_type, arg, true);
+ unsigned int count = elts.encoded_nelts ();
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ tree elt = fold_unary (code, TREE_TYPE (ret_type),
+ VECTOR_CST_ELT (arg, i));
+ if (elt == NULL_TREE || !CONSTANT_CLASS_P (elt))
+ return NULL_TREE;
+ elts.quick_push (elt);
+ }
+
+ return elts.build ();
+}
+
/* Try to evaluate:
*RESULT = FN (*ARG)
case CFN_REDUC_XOR:
return fold_const_reduction (type, arg, BIT_XOR_EXPR);
+ case CFN_VEC_CONVERT:
+ return fold_const_vec_convert (type, arg);
+
default:
return fold_const_call_1 (fn, type, arg);
}
gcc_unreachable ();
}
+/* IFN_VEC_CONVERT is supposed to be expanded at pass_lower_vector. So this
+ dummy function should never be called. */
+
+static void
+expand_VEC_CONVERT (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
/* Expand the IFN_UNIQUE function according to its first argument. */
static void
DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (TSAN_FUNC_EXIT, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (VA_ARG, ECF_NOTHROW | ECF_LEAF, NULL)
+DEF_INTERNAL_FN (VEC_CONVERT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
/* An unduplicable, uncombinable function. Generally used to preserve
a CFG property in the face of jump threading, tail merging or
+2019-01-07 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/85052
+ * c-c++-common/builtin-convertvector-1.c: New test.
+ * c-c++-common/torture/builtin-convertvector-1.c: New test.
+ * g++.dg/ext/builtin-convertvector-1.C: New test.
+ * g++.dg/cpp0x/constexpr-builtin4.C: New test.
+
2018-12-26 Mateusz B <mateuszb@poczta.onet.pl>
PR target/88521
--- /dev/null
+typedef int v8si __attribute__((vector_size (8 * sizeof (int))));
+typedef long long v4di __attribute__((vector_size (4 * sizeof (long long))));
+
+void
+foo (v8si *x, v4di *y, int z)
+{
+ __builtin_convertvector (*y, v8si); /* { dg-error "number of elements of the first argument vector and the second argument vector type should be the same" } */
+ __builtin_convertvector (*x, v4di); /* { dg-error "number of elements of the first argument vector and the second argument vector type should be the same" } */
+ __builtin_convertvector (*x, int); /* { dg-error "second argument must be an integer or floating vector type" } */
+ __builtin_convertvector (z, v4di); /* { dg-error "first argument must be an integer or floating vector" } */
+ __builtin_convertvector (); /* { dg-error "expected" } */
+ __builtin_convertvector (*x); /* { dg-error "expected" } */
+ __builtin_convertvector (*x, *y); /* { dg-error "expected" } */
+ __builtin_convertvector (*x, v8si, 1);/* { dg-error "expected" } */
+}
--- /dev/null
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort (void);
+typedef int v4si __attribute__((vector_size (4 * sizeof (int))));
+typedef unsigned int v4usi __attribute__((vector_size (4 * sizeof (unsigned int))));
+typedef float v4sf __attribute__((vector_size (4 * sizeof (float))));
+typedef double v4df __attribute__((vector_size (4 * sizeof (double))));
+typedef long long v256di __attribute__((vector_size (256 * sizeof (long long))));
+typedef double v256df __attribute__((vector_size (256 * sizeof (double))));
+
+void
+f1 (v4usi *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+void
+f2 (v4sf *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+void
+f3 (v4si *x, v4sf *y)
+{
+ *y = __builtin_convertvector (*x, v4sf);
+}
+
+void
+f4 (v4df *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+void
+f5 (v4si *x, v4df *y)
+{
+ *y = __builtin_convertvector (*x, v4df);
+}
+
+void
+f6 (v256df *x, v256di *y)
+{
+ *y = __builtin_convertvector (*x, v256di);
+}
+
+void
+f7 (v256di *x, v256df *y)
+{
+ *y = __builtin_convertvector (*x, v256df);
+}
+
+void
+f8 (v4df *x)
+{
+ v4si a = { 1, 2, -3, -4 };
+ *x = __builtin_convertvector (a, v4df);
+}
+
+int
+main ()
+{
+ union U1 { v4si v; int a[4]; } u1;
+ union U2 { v4usi v; unsigned int a[4]; } u2;
+ union U3 { v4sf v; float a[4]; } u3;
+ union U4 { v4df v; double a[4]; } u4;
+ union U5 { v256di v; long long a[256]; } u5;
+ union U6 { v256df v; double a[256]; } u6;
+ int i;
+ for (i = 0; i < 4; i++)
+ u2.a[i] = i * 2;
+ f1 (&u2.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i * 2)
+ abort ();
+ else
+ u3.a[i] = i - 2.25f;
+ f2 (&u3.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != (i == 3 ? 0 : i - 2))
+ abort ();
+ else
+ u3.a[i] = i + 0.75f;
+ f2 (&u3.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i)
+ abort ();
+ else
+ u1.a[i] = 7 * i - 5;
+ f3 (&u1.v, &u3.v);
+ for (i = 0; i < 4; i++)
+ if (u3.a[i] != 7 * i - 5)
+ abort ();
+ else
+ u4.a[i] = i - 2.25;
+ f4 (&u4.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != (i == 3 ? 0 : i - 2))
+ abort ();
+ else
+ u4.a[i] = i + 0.75;
+ f4 (&u4.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i)
+ abort ();
+ else
+ u1.a[i] = 7 * i - 5;
+ f5 (&u1.v, &u4.v);
+ for (i = 0; i < 4; i++)
+ if (u4.a[i] != 7 * i - 5)
+ abort ();
+ for (i = 0; i < 256; i++)
+ u6.a[i] = i - 128.25;
+ f6 (&u6.v, &u5.v);
+ for (i = 0; i < 256; i++)
+ if (u5.a[i] != i - 128 - (i > 128))
+ abort ();
+ else
+ u5.a[i] = i - 128;
+ f7 (&u5.v, &u6.v);
+ for (i = 0; i < 256; i++)
+ if (u6.a[i] != i - 128)
+ abort ();
+ f8 (&u4.v);
+ for (i = 0; i < 4; i++)
+ if (u4.a[i] != (i >= 2 ? -1 - i : i + 1))
+ abort ();
+ return 0;
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-psabi" }
+
+typedef int v4si __attribute__((vector_size (4 * sizeof (int))));
+typedef float v4sf __attribute__((vector_size (4 * sizeof (float))));
+constexpr v4sf a = __builtin_convertvector (v4si { 1, 2, -3, -4 }, v4sf);
+
+constexpr v4sf
+foo (v4si x)
+{
+ return __builtin_convertvector (x, v4sf);
+}
+
+constexpr v4sf b = foo (v4si { 3, 4, -1, -2 });
+
+static_assert (a[0] == 1.0f && a[1] == 2.0f && a[2] == -3.0f && a[3] == -4.0f, "");
+static_assert (b[0] == 3.0f && b[1] == 4.0f && b[2] == -1.0f && b[3] == -2.0f, "");
--- /dev/null
+// { dg-do run }
+
+extern "C" void abort ();
+typedef int v4si __attribute__((vector_size (4 * sizeof (int))));
+typedef unsigned int v4usi __attribute__((vector_size (4 * sizeof (unsigned int))));
+typedef float v4sf __attribute__((vector_size (4 * sizeof (float))));
+typedef double v4df __attribute__((vector_size (4 * sizeof (double))));
+typedef long long v256di __attribute__((vector_size (256 * sizeof (long long))));
+typedef double v256df __attribute__((vector_size (256 * sizeof (double))));
+
+template <int N>
+void
+f1 (v4usi *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+template <typename T>
+void
+f2 (T *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+template <typename T>
+void
+f3 (v4si *x, T *y)
+{
+ *y = __builtin_convertvector (*x, T);
+}
+
+template <int N>
+void
+f4 (v4df *x, v4si *y)
+{
+ *y = __builtin_convertvector (*x, v4si);
+}
+
+template <typename T, typename U>
+void
+f5 (T *x, U *y)
+{
+ *y = __builtin_convertvector (*x, U);
+}
+
+template <typename T>
+void
+f6 (v256df *x, T *y)
+{
+ *y = __builtin_convertvector (*x, T);
+}
+
+template <int N>
+void
+f7 (v256di *x, v256df *y)
+{
+ *y = __builtin_convertvector (*x, v256df);
+}
+
+template <int N>
+void
+f8 (v4df *x)
+{
+ v4si a = { 1, 2, -3, -4 };
+ *x = __builtin_convertvector (a, v4df);
+}
+
+int
+main ()
+{
+ union U1 { v4si v; int a[4]; } u1;
+ union U2 { v4usi v; unsigned int a[4]; } u2;
+ union U3 { v4sf v; float a[4]; } u3;
+ union U4 { v4df v; double a[4]; } u4;
+ union U5 { v256di v; long long a[256]; } u5;
+ union U6 { v256df v; double a[256]; } u6;
+ int i;
+ for (i = 0; i < 4; i++)
+ u2.a[i] = i * 2;
+ f1<0> (&u2.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i * 2)
+ abort ();
+ else
+ u3.a[i] = i - 2.25f;
+ f2 (&u3.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != (i == 3 ? 0 : i - 2))
+ abort ();
+ else
+ u3.a[i] = i + 0.75f;
+ f2 (&u3.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i)
+ abort ();
+ else
+ u1.a[i] = 7 * i - 5;
+ f3 (&u1.v, &u3.v);
+ for (i = 0; i < 4; i++)
+ if (u3.a[i] != 7 * i - 5)
+ abort ();
+ else
+ u4.a[i] = i - 2.25;
+ f4<12> (&u4.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != (i == 3 ? 0 : i - 2))
+ abort ();
+ else
+ u4.a[i] = i + 0.75;
+ f4<13> (&u4.v, &u1.v);
+ for (i = 0; i < 4; i++)
+ if (u1.a[i] != i)
+ abort ();
+ else
+ u1.a[i] = 7 * i - 5;
+ f5 (&u1.v, &u4.v);
+ for (i = 0; i < 4; i++)
+ if (u4.a[i] != 7 * i - 5)
+ abort ();
+ for (i = 0; i < 256; i++)
+ u6.a[i] = i - 128.25;
+ f6 (&u6.v, &u5.v);
+ for (i = 0; i < 256; i++)
+ if (u5.a[i] != i - 128 - (i > 128))
+ abort ();
+ else
+ u5.a[i] = i - 128;
+ f7<-1> (&u5.v, &u6.v);
+ for (i = 0; i < 256; i++)
+ if (u6.a[i] != i - 128)
+ abort ();
+ f8<5> (&u4.v);
+ for (i = 0; i < 4; i++)
+ if (u4.a[i] != (i >= 2 ? -1 - i : i + 1))
+ abort ();
+ return 0;
+}
#include "tree-cfg.h"
#include "tree-vector-builder.h"
#include "vec-perm-indices.h"
+#include "insn-config.h"
+#include "recog.h" /* FIXME: for insn_data */
static void expand_vector_operations_1 (gimple_stmt_iterator *);
static tree
expand_vector_piecewise (gimple_stmt_iterator *gsi, elem_op_func f,
tree type, tree inner_type,
- tree a, tree b, enum tree_code code)
+ tree a, tree b, enum tree_code code,
+ tree ret_type = NULL_TREE)
{
vec<constructor_elt, va_gc> *v;
tree part_width = TYPE_SIZE (inner_type);
int i;
location_t loc = gimple_location (gsi_stmt (*gsi));
- if (types_compatible_p (gimple_expr_type (gsi_stmt (*gsi)), type))
+ if (ret_type
+ || types_compatible_p (gimple_expr_type (gsi_stmt (*gsi)), type))
warning_at (loc, OPT_Wvector_operation_performance,
"vector operation will be expanded piecewise");
else
warning_at (loc, OPT_Wvector_operation_performance,
"vector operation will be expanded in parallel");
+ if (!ret_type)
+ ret_type = type;
vec_alloc (v, (nunits + delta - 1) / delta);
for (i = 0; i < nunits;
i += delta, index = int_const_binop (PLUS_EXPR, index, part_width))
{
- tree result = f (gsi, inner_type, a, b, index, part_width, code, type);
+ tree result = f (gsi, inner_type, a, b, index, part_width, code,
+ ret_type);
constructor_elt ce = {NULL_TREE, result};
v->quick_push (ce);
}
- return build_constructor (type, v);
+ return build_constructor (ret_type, v);
}
/* Expand a vector operation to scalars with the freedom to use
in the vector type. */
static tree
expand_vector_parallel (gimple_stmt_iterator *gsi, elem_op_func f, tree type,
- tree a, tree b,
- enum tree_code code)
+ tree a, tree b, enum tree_code code)
{
tree result, compute_type;
int n_words = tree_to_uhwi (TYPE_SIZE_UNIT (type)) / UNITS_PER_WORD;
update_stmt (gsi_stmt (*gsi));
}
+/* Callback for expand_vector_piecewise to do VEC_CONVERT ifn call
+ lowering. If INNER_TYPE is not a vector type, this is a scalar
+ fallback. */
+
+static tree
+do_vec_conversion (gimple_stmt_iterator *gsi, tree inner_type, tree a,
+ tree decl, tree bitpos, tree bitsize,
+ enum tree_code code, tree type)
+{
+ a = tree_vec_extract (gsi, inner_type, a, bitsize, bitpos);
+ if (!VECTOR_TYPE_P (inner_type))
+ return gimplify_build1 (gsi, code, TREE_TYPE (type), a);
+ if (code == CALL_EXPR)
+ {
+ gimple *g = gimple_build_call (decl, 1, a);
+ tree lhs = make_ssa_name (TREE_TYPE (TREE_TYPE (decl)));
+ gimple_call_set_lhs (g, lhs);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ return lhs;
+ }
+ else
+ {
+ tree outer_type = build_vector_type (TREE_TYPE (type),
+ TYPE_VECTOR_SUBPARTS (inner_type));
+ return gimplify_build1 (gsi, code, outer_type, a);
+ }
+}
+
+/* Similarly, but for narrowing conversion. */
+
+static tree
+do_vec_narrow_conversion (gimple_stmt_iterator *gsi, tree inner_type, tree a,
+ tree, tree bitpos, tree, enum tree_code code,
+ tree type)
+{
+ tree itype = build_vector_type (TREE_TYPE (inner_type),
+ exact_div (TYPE_VECTOR_SUBPARTS (inner_type),
+ 2));
+ tree b = tree_vec_extract (gsi, itype, a, TYPE_SIZE (itype), bitpos);
+ tree c = tree_vec_extract (gsi, itype, a, TYPE_SIZE (itype),
+ int_const_binop (PLUS_EXPR, bitpos,
+ TYPE_SIZE (itype)));
+ tree outer_type = build_vector_type (TREE_TYPE (type),
+ TYPE_VECTOR_SUBPARTS (inner_type));
+ return gimplify_build2 (gsi, code, outer_type, b, c);
+}
+
+/* Expand VEC_CONVERT ifn call. */
+
+static void
+expand_vector_conversion (gimple_stmt_iterator *gsi)
+{
+ gimple *stmt = gsi_stmt (*gsi);
+ gimple *g;
+ tree lhs = gimple_call_lhs (stmt);
+ tree arg = gimple_call_arg (stmt, 0);
+ tree decl = NULL_TREE;
+ tree ret_type = TREE_TYPE (lhs);
+ tree arg_type = TREE_TYPE (arg);
+ tree new_rhs, compute_type = TREE_TYPE (arg_type);
+ enum tree_code code = NOP_EXPR;
+ enum tree_code code1 = ERROR_MARK;
+ enum { NARROW, NONE, WIDEN } modifier = NONE;
+ optab optab1 = unknown_optab;
+
+ gcc_checking_assert (VECTOR_TYPE_P (ret_type) && VECTOR_TYPE_P (arg_type));
+ gcc_checking_assert (tree_fits_uhwi_p (TYPE_SIZE (TREE_TYPE (ret_type))));
+ gcc_checking_assert (tree_fits_uhwi_p (TYPE_SIZE (TREE_TYPE (arg_type))));
+ if (INTEGRAL_TYPE_P (TREE_TYPE (ret_type))
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (arg_type)))
+ code = FIX_TRUNC_EXPR;
+ else if (INTEGRAL_TYPE_P (TREE_TYPE (arg_type))
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (ret_type)))
+ code = FLOAT_EXPR;
+ if (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (ret_type)))
+ < tree_to_uhwi (TYPE_SIZE (TREE_TYPE (arg_type))))
+ modifier = NARROW;
+ else if (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (ret_type)))
+ > tree_to_uhwi (TYPE_SIZE (TREE_TYPE (arg_type))))
+ modifier = WIDEN;
+
+ if (modifier == NONE && (code == FIX_TRUNC_EXPR || code == FLOAT_EXPR))
+ {
+ if (supportable_convert_operation (code, ret_type, arg_type, &decl,
+ &code1))
+ {
+ if (code1 == CALL_EXPR)
+ {
+ g = gimple_build_call (decl, 1, arg);
+ gimple_call_set_lhs (g, lhs);
+ }
+ else
+ g = gimple_build_assign (lhs, code1, arg);
+ gsi_replace (gsi, g, false);
+ return;
+ }
+ /* Can't use get_compute_type here, as supportable_convert_operation
+ doesn't necessarily use an optab and needs two arguments. */
+ tree vec_compute_type
+ = type_for_widest_vector_mode (TREE_TYPE (arg_type), mov_optab);
+ if (vec_compute_type
+ && VECTOR_MODE_P (TYPE_MODE (vec_compute_type))
+ && subparts_gt (arg_type, vec_compute_type))
+ {
+ unsigned HOST_WIDE_INT nelts
+ = constant_lower_bound (TYPE_VECTOR_SUBPARTS (vec_compute_type));
+ while (nelts > 1)
+ {
+ tree ret1_type = build_vector_type (TREE_TYPE (ret_type), nelts);
+ tree arg1_type = build_vector_type (TREE_TYPE (arg_type), nelts);
+ if (supportable_convert_operation (code, ret1_type, arg1_type,
+ &decl, &code1))
+ {
+ new_rhs = expand_vector_piecewise (gsi, do_vec_conversion,
+ ret_type, arg1_type, arg,
+ decl, code1);
+ g = gimple_build_assign (lhs, new_rhs);
+ gsi_replace (gsi, g, false);
+ return;
+ }
+ nelts = nelts / 2;
+ }
+ }
+ }
+ else if (modifier == NARROW)
+ {
+ switch (code)
+ {
+ CASE_CONVERT:
+ code1 = VEC_PACK_TRUNC_EXPR;
+ optab1 = optab_for_tree_code (code1, arg_type, optab_default);
+ break;
+ case FIX_TRUNC_EXPR:
+ code1 = VEC_PACK_FIX_TRUNC_EXPR;
+ /* The signedness is determined from output operand. */
+ optab1 = optab_for_tree_code (code1, ret_type, optab_default);
+ break;
+ case FLOAT_EXPR:
+ code1 = VEC_PACK_FLOAT_EXPR;
+ optab1 = optab_for_tree_code (code1, arg_type, optab_default);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (optab1)
+ compute_type = get_compute_type (code1, optab1, arg_type);
+ enum insn_code icode1;
+ if (VECTOR_TYPE_P (compute_type)
+ && ((icode1 = optab_handler (optab1, TYPE_MODE (compute_type)))
+ != CODE_FOR_nothing)
+ && VECTOR_MODE_P (insn_data[icode1].operand[0].mode))
+ {
+ tree cretd_type
+ = build_vector_type (TREE_TYPE (ret_type),
+ TYPE_VECTOR_SUBPARTS (compute_type) * 2);
+ if (insn_data[icode1].operand[0].mode == TYPE_MODE (cretd_type))
+ {
+ if (compute_type == arg_type)
+ {
+ new_rhs = gimplify_build2 (gsi, code1, cretd_type,
+ arg, build_zero_cst (arg_type));
+ new_rhs = tree_vec_extract (gsi, ret_type, new_rhs,
+ TYPE_SIZE (ret_type),
+ bitsize_int (0));
+ g = gimple_build_assign (lhs, new_rhs);
+ gsi_replace (gsi, g, false);
+ return;
+ }
+ tree dcompute_type
+ = build_vector_type (TREE_TYPE (compute_type),
+ TYPE_VECTOR_SUBPARTS (compute_type) * 2);
+ if (TYPE_MAIN_VARIANT (dcompute_type)
+ == TYPE_MAIN_VARIANT (arg_type))
+ new_rhs = do_vec_narrow_conversion (gsi, dcompute_type, arg,
+ NULL_TREE, bitsize_int (0),
+ NULL_TREE, code1,
+ ret_type);
+ else
+ new_rhs = expand_vector_piecewise (gsi,
+ do_vec_narrow_conversion,
+ arg_type, dcompute_type,
+ arg, NULL_TREE, code1,
+ ret_type);
+ g = gimple_build_assign (lhs, new_rhs);
+ gsi_replace (gsi, g, false);
+ return;
+ }
+ }
+ }
+ else if (modifier == WIDEN)
+ {
+ enum tree_code code2 = ERROR_MARK;
+ optab optab2 = unknown_optab;
+ switch (code)
+ {
+ CASE_CONVERT:
+ code1 = VEC_UNPACK_LO_EXPR;
+ code2 = VEC_UNPACK_HI_EXPR;
+ break;
+ case FIX_TRUNC_EXPR:
+ code1 = VEC_UNPACK_FIX_TRUNC_LO_EXPR;
+ code2 = VEC_UNPACK_FIX_TRUNC_HI_EXPR;
+ break;
+ case FLOAT_EXPR:
+ code1 = VEC_UNPACK_FLOAT_LO_EXPR;
+ code2 = VEC_UNPACK_FLOAT_HI_EXPR;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (BYTES_BIG_ENDIAN)
+ std::swap (code1, code2);
+
+ if (code == FIX_TRUNC_EXPR)
+ {
+ /* The signedness is determined from output operand. */
+ optab1 = optab_for_tree_code (code1, ret_type, optab_default);
+ optab2 = optab_for_tree_code (code2, ret_type, optab_default);
+ }
+ else
+ {
+ optab1 = optab_for_tree_code (code1, arg_type, optab_default);
+ optab2 = optab_for_tree_code (code2, arg_type, optab_default);
+ }
+
+ if (optab1 && optab2)
+ compute_type = get_compute_type (code1, optab1, arg_type);
+
+ enum insn_code icode1, icode2;
+ if (VECTOR_TYPE_P (compute_type)
+ && ((icode1 = optab_handler (optab1, TYPE_MODE (compute_type)))
+ != CODE_FOR_nothing)
+ && ((icode2 = optab_handler (optab2, TYPE_MODE (compute_type)))
+ != CODE_FOR_nothing)
+ && VECTOR_MODE_P (insn_data[icode1].operand[0].mode)
+ && (insn_data[icode1].operand[0].mode
+ == insn_data[icode2].operand[0].mode))
+ {
+ poly_uint64 nunits
+ = exact_div (TYPE_VECTOR_SUBPARTS (compute_type), 2);
+ tree cretd_type = build_vector_type (TREE_TYPE (ret_type), nunits);
+ if (insn_data[icode1].operand[0].mode == TYPE_MODE (cretd_type))
+ {
+ vec<constructor_elt, va_gc> *v;
+ tree part_width = TYPE_SIZE (compute_type);
+ tree index = bitsize_int (0);
+ int nunits = nunits_for_known_piecewise_op (arg_type);
+ int delta = tree_to_uhwi (part_width)
+ / tree_to_uhwi (TYPE_SIZE (TREE_TYPE (arg_type)));
+ int i;
+ location_t loc = gimple_location (gsi_stmt (*gsi));
+
+ if (compute_type != arg_type)
+ warning_at (loc, OPT_Wvector_operation_performance,
+ "vector operation will be expanded piecewise");
+ else
+ {
+ nunits = 1;
+ delta = 1;
+ }
+
+ vec_alloc (v, (nunits + delta - 1) / delta * 2);
+ for (i = 0; i < nunits;
+ i += delta, index = int_const_binop (PLUS_EXPR, index,
+ part_width))
+ {
+ tree a = arg;
+ if (compute_type != arg_type)
+ a = tree_vec_extract (gsi, compute_type, a, part_width,
+ index);
+ tree result = gimplify_build1 (gsi, code1, cretd_type, a);
+ constructor_elt ce = { NULL_TREE, result };
+ v->quick_push (ce);
+ ce.value = gimplify_build1 (gsi, code2, cretd_type, a);
+ v->quick_push (ce);
+ }
+
+ new_rhs = build_constructor (ret_type, v);
+ g = gimple_build_assign (lhs, new_rhs);
+ gsi_replace (gsi, g, false);
+ return;
+ }
+ }
+ }
+
+ new_rhs = expand_vector_piecewise (gsi, do_vec_conversion, arg_type,
+ TREE_TYPE (arg_type), arg,
+ NULL_TREE, code, ret_type);
+ g = gimple_build_assign (lhs, new_rhs);
+ gsi_replace (gsi, g, false);
+}
+
/* Process one statement. If we identify a vector operation, expand it. */
static void
/* Only consider code == GIMPLE_ASSIGN. */
gassign *stmt = dyn_cast <gassign *> (gsi_stmt (*gsi));
if (!stmt)
- return;
+ {
+ if (gimple_call_internal_p (gsi_stmt (*gsi), IFN_VEC_CONVERT))
+ expand_vector_conversion (gsi);
+ return;
+ }
code = gimple_assign_rhs_code (stmt);
rhs_class = get_gimple_rhs_class (code);