re PR c++/85052 (Implement support for clang's __builtin_convertvector)
authorJakub Jelinek <jakub@redhat.com>
Mon, 7 Jan 2019 08:49:08 +0000 (09:49 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Mon, 7 Jan 2019 08:49:08 +0000 (09:49 +0100)
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.
c-family/
* c-common.h (enum rid): Add RID_BUILTIN_CONVERTVECTOR.
(c_build_vec_convert): Declare.
* c-common.c (c_build_vec_convert): New function.
c/
* c-parser.c (c_parser_postfix_expression): Parse
__builtin_convertvector.
cp/
* 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.
testsuite/
* 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.

From-SVN: r267632

22 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-parser.c
gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/doc/extend.texi
gcc/fold-const-call.c
gcc/internal-fn.c
gcc/internal-fn.def
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/builtin-convertvector-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/builtin-convertvector-1.c [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/constexpr-builtin4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/builtin-convertvector-1.C [new file with mode: 0644]
gcc/tree-vect-generic.c

index 8fafc234bfedc5e5d21d34466990248040649136..25660bd1c8aa92ec124789344443388b166409f2 100644 (file)
@@ -1,3 +1,20 @@
+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.
index b407f1cc18e4bda0205bb4a6b114a9ac011be9e1..a7b56ff18e9fd89c81b67e148191e31a533ad15e 100644 (file)
@@ -1,3 +1,10 @@
+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
index 5c380c14ca48b9dbb263e7b075b1a99ee00e8535..d2ea384d65328b27c59e7e55659b8936127a8a0e 100644 (file)
@@ -376,6 +376,7 @@ const struct c_common_resword c_common_reswords[] =
     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 },
@@ -1072,6 +1073,70 @@ c_build_vec_perm_expr (location_t loc, tree v0, tree v1, tree mask,
   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.  */
 
index 3dec6f2741dacce1dc09e77c03bd277352a3f2df..db16ae94b64c97fdceef1087f03c4e96b749d473 100644 (file)
@@ -102,7 +102,7 @@ enum rid
   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,
 
@@ -1001,6 +1001,7 @@ extern bool lvalue_p (const_tree);
 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);
 
index ef11b4c62d7d516d769c07c71b7b87329ae15bee..fbd94f5607c9d30db099a569c61eedfc529106d8 100644 (file)
@@ -1,3 +1,9 @@
+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.
index 972b629c092a07a99a4480bccb9cb8e21d3faeb3..76f314e119056544ac365d740459e443f5c6c715 100644 (file)
@@ -8038,6 +8038,7 @@ enum tgmath_parm_kind
      __builtin_shuffle ( assignment-expression ,
                         assignment-expression ,
                         assignment-expression, )
+     __builtin_convertvector ( assignment-expression , type-name )
 
    offsetof-member-designator:
      identifier
@@ -9113,17 +9114,14 @@ c_parser_postfix_expression (c_parser *parser)
              *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 "
@@ -9133,6 +9131,41 @@ c_parser_postfix_expression (c_parser *parser)
            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 ());
index 32576c58b84ec71bf4dd525edec42762c76f0e57..cbb70140c6999efd76cd1a32b4813c9f878fb8be 100644 (file)
@@ -1,3 +1,16 @@
+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
index 148150b4b7ca46fb492c55e9f66021e17f63df30..ed4bbeeb157878bd08721820fc46444dc80dca8b 100644 (file)
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #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)                                             \
@@ -1449,6 +1450,20 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
       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),
@@ -5623,7 +5638,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                case IFN_SUB_OVERFLOW:
                case IFN_MUL_OVERFLOW:
                case IFN_LAUNDER:
+               case IFN_VEC_CONVERT:
                  bail = false;
+                 break;
 
                default:
                  break;
index 99e0734781c9eb503c43f86133b5cba6734883a8..794849b50662ccb2fa1dab19fc1f0eac28181e39 100644 (file)
@@ -7142,6 +7142,8 @@ extern bool is_lambda_ignored_entity            (tree);
 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);
index 6ad2282c881b417b8cac5655ad7e755964742ae2..bca1739ace39cc769e29372e6be8ef97cc1d3a0f 100644 (file)
@@ -7031,6 +7031,32 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
        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;
index 19594b74969689c19116ee4cd5a39c7b731945e7..e216ef5037810fc46b3ed54bcc9e7dfe5aefb79d 100644 (file)
@@ -18813,6 +18813,27 @@ tsubst_copy_and_build (tree t,
                                              (*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 ();
index 82fda5fd789ebd4b081f7b9128ecd1d15573fd21..bc9d53800f7cd2e76c483262455ce3ed094177be 100644 (file)
@@ -9933,4 +9933,26 @@ finish_builtin_launder (location_t loc, tree arg, tsubst_flags_t complain)
                                       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"
index 19ef6a6760d11215a8cf28382e980c3d5375659a..7f33be4f29c118fd856051f3e7235b5ad7eb85b7 100644 (file)
@@ -10596,6 +10596,33 @@ to and from other datatypes of the same size).
 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
index 004f94ec19a029f57a52e5e95c8b793653f18427..439043a85a0a483da421f1e03bc30b85ca44d10d 100644 (file)
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #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.  */
@@ -645,6 +646,40 @@ fold_const_reduction (tree type, tree arg, tree_code code)
   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)
@@ -1232,6 +1267,9 @@ fold_const_call (combined_fn fn, tree type, tree 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);
     }
index 103e0c13c165c48fab0e53edf52aaa89f4ead969..4f2ef45a0ff45ebbb8518e0f3e6e897b63d78001 100644 (file)
@@ -2581,6 +2581,15 @@ expand_VA_ARG (internal_fn, gcall *)
   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
index 7313f925abc4657597dbaf6ebba41701c5f388f1..e370eaa84767839c827b6ebd0c86303bcc36fa54 100644 (file)
@@ -296,6 +296,7 @@ DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 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
index c0bb2817873914b416abb0bcc97bb678a1c80a17..a0d74152badf1003a72ce9591565550e5bc98379 100644 (file)
@@ -1,3 +1,11 @@
+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
diff --git a/gcc/testsuite/c-c++-common/builtin-convertvector-1.c b/gcc/testsuite/c-c++-common/builtin-convertvector-1.c
new file mode 100644 (file)
index 0000000..4bd0e4c
--- /dev/null
@@ -0,0 +1,15 @@
+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" } */
+}
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-convertvector-1.c b/gcc/testsuite/c-c++-common/torture/builtin-convertvector-1.c
new file mode 100644 (file)
index 0000000..347dda7
--- /dev/null
@@ -0,0 +1,131 @@
+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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-builtin4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-builtin4.C
new file mode 100644 (file)
index 0000000..7f6bf3c
--- /dev/null
@@ -0,0 +1,17 @@
+// { 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, "");
diff --git a/gcc/testsuite/g++.dg/ext/builtin-convertvector-1.C b/gcc/testsuite/g++.dg/ext/builtin-convertvector-1.C
new file mode 100644 (file)
index 0000000..c803c06
--- /dev/null
@@ -0,0 +1,137 @@
+// { 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;
+}
index fb3d3414ea2435947736be0c4b8e83b16109d2cb..e9f5505acb39ec1fdc7c75ef60a11ce28f8553fd 100644 (file)
@@ -39,6 +39,8 @@ along with GCC; see the file COPYING3.  If not see
 #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 *);
@@ -267,7 +269,8 @@ do_negate (gimple_stmt_iterator *gsi, tree word_type, tree b,
 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);
@@ -278,23 +281,27 @@ expand_vector_piecewise (gimple_stmt_iterator *gsi, elem_op_func f,
   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
@@ -302,8 +309,7 @@ expand_vector_piecewise (gimple_stmt_iterator *gsi, elem_op_func f,
    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;
@@ -1547,6 +1553,299 @@ expand_vector_scalar_condition (gimple_stmt_iterator *gsi)
   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
@@ -1561,7 +1860,11 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
   /* 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);