Rebased all three patches and made some small changes to the second one:
- removed sub and abd optabs from commutative_optab_p, I suspect this was a copy paste mistake, - removed what I believe to be a superfluous switch case in vectorizable conversion, the one that was here:
+  if (code.is_fn_code ())
+     {
+      internal_fn ifn = as_internal_fn (code.as_fn_code ());
+      int ecf_flags = internal_fn_flags (ifn);
+      gcc_assert (ecf_flags & ECF_MULTI);
+
+      switch (code.as_fn_code ())
+       {
+       case CFN_VEC_WIDEN_PLUS:
+         break;
+       case CFN_VEC_WIDEN_MINUS:
+         break;
+       case CFN_LAST:
+       default:
+         return false;
+       }
+
+      internal_fn lo, hi;
+      lookup_multi_internal_fn (ifn, &lo, &hi);
+      *code1 = as_combined_fn (lo);
+      *code2 = as_combined_fn (hi);
+      optab1 = lookup_multi_ifn_optab (lo, !TYPE_UNSIGNED (vectype));
+      optab2 = lookup_multi_ifn_optab (hi, !TYPE_UNSIGNED (vectype));
     }

I don't think we need to check they are a specfic fn code, as we look-up optabs and if they succeed then surely we can vectorize?

OK for trunk?

Kind regards,
Andre
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 
8802141cd6edb298866025b8a55843eae1f0eb17..68dfba266d679c9738a3d5d70551a91cbdafcf66
 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -25,6 +25,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-fold.h"
 #include "ssa.h"
 #include "expmed.h"
 #include "optabs-tree.h"
@@ -1391,7 +1393,7 @@ vect_recog_sad_pattern (vec_info *vinfo,
 static gimple *
 vect_recog_widen_op_pattern (vec_info *vinfo,
                             stmt_vec_info last_stmt_info, tree *type_out,
-                            tree_code orig_code, tree_code wide_code,
+                            tree_code orig_code, code_helper wide_code,
                             bool shift_p, const char *name)
 {
   gimple *last_stmt = last_stmt_info->stmt;
@@ -1434,7 +1436,7 @@ vect_recog_widen_op_pattern (vec_info *vinfo,
       vecctype = get_vectype_for_scalar_type (vinfo, ctype);
     }
 
-  enum tree_code dummy_code;
+  code_helper dummy_code;
   int dummy_int;
   auto_vec<tree> dummy_vec;
   if (!vectype
@@ -1455,8 +1457,7 @@ vect_recog_widen_op_pattern (vec_info *vinfo,
                       2, oprnd, half_type, unprom, vectype);
 
   tree var = vect_recog_temp_ssa_var (itype, NULL);
-  gimple *pattern_stmt = gimple_build_assign (var, wide_code,
-                                             oprnd[0], oprnd[1]);
+  gimple *pattern_stmt = vect_gimple_build (var, wide_code, oprnd[0], 
oprnd[1]);
 
   if (vecctype != vecitype)
     pattern_stmt = vect_convert_output (vinfo, last_stmt_info, ctype,
@@ -6406,3 +6407,28 @@ vect_pattern_recog (vec_info *vinfo)
   /* After this no more add_stmt calls are allowed.  */
   vinfo->stmt_vec_info_ro = true;
 }
+
+/* Build a GIMPLE_ASSIGN or GIMPLE_CALL with the tree_code,
+   or internal_fn contained in ch, respectively.  */
+gimple *
+vect_gimple_build (tree lhs, code_helper ch, tree op0, tree op1)
+{
+  if (op0 == NULL_TREE)
+    return NULL;
+  if (ch.is_tree_code ())
+    return op1 == NULL_TREE ? gimple_build_assign (lhs, ch.safe_as_tree_code 
(),
+                                                  op0) :
+                             gimple_build_assign (lhs, ch.safe_as_tree_code (),
+                                                  op0, op1);
+  else
+  {
+    internal_fn fn = as_internal_fn (ch.safe_as_fn_code ());
+    gimple* stmt;
+    if (op1 == NULL_TREE)
+      stmt = gimple_build_call_internal (fn, 1, op0);
+    else
+      stmt = gimple_build_call_internal (fn, 2, op0, op1);
+    gimple_call_set_lhs (stmt, lhs);
+    return stmt;
+  }
+}
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 
6b7dbfd4a231baec24e740ffe0ce0b0bf7a1de6b..715ec2e30a4de620b8a5076c0e7f2f7fd1b0654e
 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -4768,7 +4768,7 @@ vectorizable_simd_clone_call (vec_info *vinfo, 
stmt_vec_info stmt_info,
    STMT_INFO is the original scalar stmt that we are vectorizing.  */
 
 static gimple *
-vect_gen_widened_results_half (vec_info *vinfo, enum tree_code code,
+vect_gen_widened_results_half (vec_info *vinfo, code_helper ch,
                                tree vec_oprnd0, tree vec_oprnd1, int op_type,
                               tree vec_dest, gimple_stmt_iterator *gsi,
                               stmt_vec_info stmt_info)
@@ -4777,12 +4777,11 @@ vect_gen_widened_results_half (vec_info *vinfo, enum 
tree_code code,
   tree new_temp;
 
   /* Generate half of the widened result:  */
-  gcc_assert (op_type == TREE_CODE_LENGTH (code));
   if (op_type != binary_op)
     vec_oprnd1 = NULL;
-  new_stmt = gimple_build_assign (vec_dest, code, vec_oprnd0, vec_oprnd1);
+  new_stmt = vect_gimple_build (vec_dest, ch, vec_oprnd0, vec_oprnd1);
   new_temp = make_ssa_name (vec_dest, new_stmt);
-  gimple_assign_set_lhs (new_stmt, new_temp);
+  gimple_set_lhs (new_stmt, new_temp);
   vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
 
   return new_stmt;
@@ -4861,8 +4860,8 @@ vect_create_vectorized_promotion_stmts (vec_info *vinfo,
                                        vec<tree> *vec_oprnds1,
                                        stmt_vec_info stmt_info, tree vec_dest,
                                        gimple_stmt_iterator *gsi,
-                                       enum tree_code code1,
-                                       enum tree_code code2, int op_type)
+                                       code_helper ch1,
+                                       code_helper ch2, int op_type)
 {
   int i;
   tree vop0, vop1, new_tmp1, new_tmp2;
@@ -4878,10 +4877,10 @@ vect_create_vectorized_promotion_stmts (vec_info *vinfo,
        vop1 = NULL_TREE;
 
       /* Generate the two halves of promotion operation.  */
-      new_stmt1 = vect_gen_widened_results_half (vinfo, code1, vop0, vop1,
+      new_stmt1 = vect_gen_widened_results_half (vinfo, ch1, vop0, vop1,
                                                 op_type, vec_dest, gsi,
                                                 stmt_info);
-      new_stmt2 = vect_gen_widened_results_half (vinfo, code2, vop0, vop1,
+      new_stmt2 = vect_gen_widened_results_half (vinfo, ch2, vop0, vop1,
                                                 op_type, vec_dest, gsi,
                                                 stmt_info);
       if (is_gimple_call (new_stmt1))
@@ -4978,8 +4977,9 @@ vectorizable_conversion (vec_info *vinfo,
   tree scalar_dest;
   tree op0, op1 = NULL_TREE;
   loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo);
-  enum tree_code code, code1 = ERROR_MARK, code2 = ERROR_MARK;
-  enum tree_code codecvt1 = ERROR_MARK, codecvt2 = ERROR_MARK;
+  tree_code tc1;
+  code_helper code, code1, code2;
+  code_helper codecvt1 = ERROR_MARK, codecvt2 = ERROR_MARK;
   tree new_temp;
   enum vect_def_type dt[2] = {vect_unknown_def_type, vect_unknown_def_type};
   int ndts = 2;
@@ -5008,31 +5008,43 @@ vectorizable_conversion (vec_info *vinfo,
       && ! vec_stmt)
     return false;
 
-  gassign *stmt = dyn_cast <gassign *> (stmt_info->stmt);
-  if (!stmt)
+  gimple* stmt = stmt_info->stmt;
+  if (!(is_gimple_assign (stmt) || is_gimple_call (stmt)))
     return false;
 
-  if (TREE_CODE (gimple_assign_lhs (stmt)) != SSA_NAME)
+  if (gimple_get_lhs (stmt) == NULL_TREE
+      || TREE_CODE (gimple_get_lhs (stmt)) != SSA_NAME)
     return false;
 
-  code = gimple_assign_rhs_code (stmt);
-  if (!CONVERT_EXPR_CODE_P (code)
-      && code != FIX_TRUNC_EXPR
-      && code != FLOAT_EXPR
-      && code != WIDEN_PLUS_EXPR
-      && code != WIDEN_MINUS_EXPR
-      && code != WIDEN_MULT_EXPR
-      && code != WIDEN_LSHIFT_EXPR)
+  if (TREE_CODE (gimple_get_lhs (stmt)) != SSA_NAME)
+    return false;
+
+  if (is_gimple_assign (stmt))
+    {
+      code = gimple_assign_rhs_code (stmt);
+      op_type = TREE_CODE_LENGTH (code.safe_as_tree_code ());
+    }
+  else if (gimple_call_internal_p (stmt))
+    {
+      code = gimple_call_internal_fn (stmt);
+      op_type = gimple_call_num_args (stmt);
+    }
+  else
     return false;
 
   bool widen_arith = (code == WIDEN_PLUS_EXPR
-                     || code == WIDEN_MINUS_EXPR
-                     || code == WIDEN_MULT_EXPR
-                     || code == WIDEN_LSHIFT_EXPR);
-  op_type = TREE_CODE_LENGTH (code);
+                || code == WIDEN_MINUS_EXPR
+                || code == WIDEN_MULT_EXPR
+                || code == WIDEN_LSHIFT_EXPR);
+
+  if (!widen_arith
+      && !CONVERT_EXPR_CODE_P (code)
+      && code != FIX_TRUNC_EXPR
+      && code != FLOAT_EXPR)
+    return false;
 
   /* Check types of lhs and rhs.  */
-  scalar_dest = gimple_assign_lhs (stmt);
+  scalar_dest = gimple_get_lhs (stmt);
   lhs_type = TREE_TYPE (scalar_dest);
   vectype_out = STMT_VINFO_VECTYPE (stmt_info);
 
@@ -5070,10 +5082,14 @@ vectorizable_conversion (vec_info *vinfo,
 
   if (op_type == binary_op)
     {
-      gcc_assert (code == WIDEN_MULT_EXPR || code == WIDEN_LSHIFT_EXPR
-                 || code == WIDEN_PLUS_EXPR || code == WIDEN_MINUS_EXPR);
+      gcc_assert (code == WIDEN_MULT_EXPR
+                 || code == WIDEN_LSHIFT_EXPR
+                 || code == WIDEN_PLUS_EXPR
+                 || code == WIDEN_MINUS_EXPR);
+
 
-      op1 = gimple_assign_rhs2 (stmt);
+      op1 = is_gimple_assign (stmt) ? gimple_assign_rhs2 (stmt) :
+                                    gimple_call_arg (stmt, 0);
       tree vectype1_in;
       if (!vect_is_simple_use (vinfo, stmt_info, slp_node, 1,
                               &op1, &slp_op1, &dt[1], &vectype1_in))
@@ -5157,8 +5173,12 @@ vectorizable_conversion (vec_info *vinfo,
          && code != FLOAT_EXPR
          && !CONVERT_EXPR_CODE_P (code))
        return false;
-      if (supportable_convert_operation (code, vectype_out, vectype_in, 
&code1))
+      if (supportable_convert_operation (code.safe_as_tree_code (), 
vectype_out,
+                                        vectype_in, &tc1))
+      {
+       code1 = tc1;
        break;
+      }
       /* FALLTHRU */
     unsupported:
       if (dump_enabled_p ())
@@ -5169,9 +5189,11 @@ vectorizable_conversion (vec_info *vinfo,
     case WIDEN:
       if (known_eq (nunits_in, nunits_out))
        {
-         if (!supportable_half_widening_operation (code, vectype_out,
-                                                  vectype_in, &code1))
+         if (!supportable_half_widening_operation (code.safe_as_tree_code (),
+                                                   vectype_out, vectype_in,
+                                                   &tc1))
            goto unsupported;
+         code1 = tc1;
          gcc_assert (!(multi_step_cvt && op_type == binary_op));
          break;
        }
@@ -5205,14 +5227,17 @@ vectorizable_conversion (vec_info *vinfo,
 
          if (GET_MODE_SIZE (rhs_mode) == fltsz)
            {
-             if (!supportable_convert_operation (code, vectype_out,
-                                                 cvt_type, &codecvt1))
+             tc1 = ERROR_MARK;
+             if (!supportable_convert_operation (code.safe_as_tree_code (),
+                                                 vectype_out,
+                                                 cvt_type, &tc1))
                goto unsupported;
+             codecvt1 = tc1;
            }
-         else if (!supportable_widening_operation (vinfo, code, stmt_info,
-                                                   vectype_out, cvt_type,
-                                                   &codecvt1, &codecvt2,
-                                                   &multi_step_cvt,
+         else if (!supportable_widening_operation (vinfo, code,
+                                                   stmt_info, vectype_out,
+                                                   cvt_type, &codecvt1,
+                                                   &codecvt2, &multi_step_cvt,
                                                    &interm_types))
            continue;
          else
@@ -5220,8 +5245,9 @@ vectorizable_conversion (vec_info *vinfo,
 
          if (supportable_widening_operation (vinfo, NOP_EXPR, stmt_info,
                                              cvt_type,
-                                             vectype_in, &code1, &code2,
-                                             &multi_step_cvt, &interm_types))
+                                             vectype_in, &code1,
+                                             &code2, &multi_step_cvt,
+                                             &interm_types))
            {
              found_mode = true;
              break;
@@ -5243,10 +5269,15 @@ vectorizable_conversion (vec_info *vinfo,
 
     case NARROW:
       gcc_assert (op_type == unary_op);
-      if (supportable_narrowing_operation (code, vectype_out, vectype_in,
-                                          &code1, &multi_step_cvt,
+      if (supportable_narrowing_operation (code.safe_as_tree_code (),
+                                          vectype_out,
+                                          vectype_in,
+                                          &tc1, &multi_step_cvt,
                                           &interm_types))
-       break;
+       {
+         code1 = tc1;
+         break;
+       }
 
       if (code != FIX_TRUNC_EXPR
          || GET_MODE_SIZE (lhs_mode) >= GET_MODE_SIZE (rhs_mode))
@@ -5257,13 +5288,18 @@ vectorizable_conversion (vec_info *vinfo,
       cvt_type = get_same_sized_vectype (cvt_type, vectype_in);
       if (cvt_type == NULL_TREE)
        goto unsupported;
-      if (!supportable_convert_operation (code, cvt_type, vectype_in,
-                                         &codecvt1))
+      if (!supportable_convert_operation (code.safe_as_tree_code (), cvt_type,
+                                         vectype_in,
+                                         &tc1))
        goto unsupported;
+      codecvt1 = tc1;
       if (supportable_narrowing_operation (NOP_EXPR, vectype_out, cvt_type,
-                                          &code1, &multi_step_cvt,
+                                          &tc1, &multi_step_cvt,
                                           &interm_types))
-       break;
+       {
+         code1 = tc1;
+         break;
+       }
       goto unsupported;
 
     default:
@@ -5377,8 +5413,10 @@ vectorizable_conversion (vec_info *vinfo,
       FOR_EACH_VEC_ELT (vec_oprnds0, i, vop0)
        {
          /* Arguments are ready, create the new vector stmt.  */
-         gcc_assert (TREE_CODE_LENGTH (code1) == unary_op);
-         gassign *new_stmt = gimple_build_assign (vec_dest, code1, vop0);
+         gcc_assert (TREE_CODE_LENGTH ((tree_code) code1) == unary_op);
+         gassign *new_stmt = gimple_build_assign (vec_dest,
+                                                  code1.safe_as_tree_code (),
+                                                  vop0);
          new_temp = make_ssa_name (vec_dest, new_stmt);
          gimple_assign_set_lhs (new_stmt, new_temp);
          vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
@@ -5410,7 +5448,7 @@ vectorizable_conversion (vec_info *vinfo,
       for (i = multi_step_cvt; i >= 0; i--)
        {
          tree this_dest = vec_dsts[i];
-         enum tree_code c1 = code1, c2 = code2;
+         code_helper c1 = code1, c2 = code2;
          if (i == 0 && codecvt2 != ERROR_MARK)
            {
              c1 = codecvt1;
@@ -5420,7 +5458,8 @@ vectorizable_conversion (vec_info *vinfo,
            vect_create_half_widening_stmts (vinfo, &vec_oprnds0,
                                                    &vec_oprnds1, stmt_info,
                                                    this_dest, gsi,
-                                                   c1, op_type);
+                                                   c1.safe_as_tree_code (),
+                                                   op_type);
          else
            vect_create_vectorized_promotion_stmts (vinfo, &vec_oprnds0,
                                                    &vec_oprnds1, stmt_info,
@@ -5433,9 +5472,11 @@ vectorizable_conversion (vec_info *vinfo,
          gimple *new_stmt;
          if (cvt_type)
            {
-             gcc_assert (TREE_CODE_LENGTH (codecvt1) == unary_op);
+             gcc_assert (TREE_CODE_LENGTH ((tree_code) codecvt1) == unary_op);
              new_temp = make_ssa_name (vec_dest);
-             new_stmt = gimple_build_assign (new_temp, codecvt1, vop0);
+             new_stmt = gimple_build_assign (new_temp,
+                                             codecvt1.safe_as_tree_code (),
+                                             vop0);
              vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
            }
          else
@@ -5459,10 +5500,12 @@ vectorizable_conversion (vec_info *vinfo,
       if (cvt_type)
        FOR_EACH_VEC_ELT (vec_oprnds0, i, vop0)
          {
-           gcc_assert (TREE_CODE_LENGTH (codecvt1) == unary_op);
+           gcc_assert (TREE_CODE_LENGTH (((tree_code) codecvt1)) == unary_op);
            new_temp = make_ssa_name (vec_dest);
            gassign *new_stmt
-             = gimple_build_assign (new_temp, codecvt1, vop0);
+             = gimple_build_assign (new_temp,
+                                    codecvt1.safe_as_tree_code (),
+                                    vop0);
            vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
            vec_oprnds0[i] = new_temp;
          }
@@ -5470,7 +5513,8 @@ vectorizable_conversion (vec_info *vinfo,
       vect_create_vectorized_demotion_stmts (vinfo, &vec_oprnds0,
                                             multi_step_cvt,
                                             stmt_info, vec_dsts, gsi,
-                                            slp_node, code1);
+                                            slp_node,
+                                            code1.safe_as_tree_code ());
       break;
     }
   if (!slp_node)
@@ -12151,9 +12195,11 @@ vect_maybe_update_slp_op_vectype (slp_tree op, tree 
vectype)
 
 bool
 supportable_widening_operation (vec_info *vinfo,
-                               enum tree_code code, stmt_vec_info stmt_info,
+                               code_helper code,
+                               stmt_vec_info stmt_info,
                                tree vectype_out, tree vectype_in,
-                                enum tree_code *code1, enum tree_code *code2,
+                               code_helper *code1,
+                               code_helper *code2,
                                 int *multi_step_cvt,
                                 vec<tree> *interm_types)
 {
@@ -12164,7 +12210,7 @@ supportable_widening_operation (vec_info *vinfo,
   optab optab1, optab2;
   tree vectype = vectype_in;
   tree wide_vectype = vectype_out;
-  enum tree_code c1, c2;
+  code_helper c1 = MAX_TREE_CODES, c2 = MAX_TREE_CODES;
   int i;
   tree prev_type, intermediate_type;
   machine_mode intermediate_mode, prev_mode;
@@ -12174,7 +12220,7 @@ supportable_widening_operation (vec_info *vinfo,
   if (loop_info)
     vect_loop = LOOP_VINFO_LOOP (loop_info);
 
-  switch (code)
+  switch (code.safe_as_tree_code ())
     {
     case WIDEN_MULT_EXPR:
       /* The result of a vectorized widening operation usually requires
@@ -12215,8 +12261,9 @@ supportable_widening_operation (vec_info *vinfo,
          && !nested_in_vect_loop_p (vect_loop, stmt_info)
          && supportable_widening_operation (vinfo, VEC_WIDEN_MULT_EVEN_EXPR,
                                             stmt_info, vectype_out,
-                                            vectype_in, code1, code2,
-                                            multi_step_cvt, interm_types))
+                                            vectype_in, code1,
+                                            code2, multi_step_cvt,
+                                            interm_types))
         {
           /* Elements in a vector with vect_used_by_reduction property cannot
              be reordered if the use chain with this property does not have the
@@ -12279,6 +12326,9 @@ supportable_widening_operation (vec_info *vinfo,
       c2 = VEC_UNPACK_FIX_TRUNC_HI_EXPR;
       break;
 
+    case MAX_TREE_CODES:
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -12289,10 +12339,12 @@ supportable_widening_operation (vec_info *vinfo,
   if (code == FIX_TRUNC_EXPR)
     {
       /* The signedness is determined from output operand.  */
-      optab1 = optab_for_tree_code (c1, vectype_out, optab_default);
-      optab2 = optab_for_tree_code (c2, vectype_out, optab_default);
+      optab1 = optab_for_tree_code (c1.safe_as_tree_code (), vectype_out,
+                                   optab_default);
+      optab2 = optab_for_tree_code (c2.safe_as_tree_code (), vectype_out,
+                                   optab_default);
     }
-  else if (CONVERT_EXPR_CODE_P (code)
+  else if (CONVERT_EXPR_CODE_P (code.safe_as_tree_code ())
           && VECTOR_BOOLEAN_TYPE_P (wide_vectype)
           && VECTOR_BOOLEAN_TYPE_P (vectype)
           && TYPE_MODE (wide_vectype) == TYPE_MODE (vectype)
@@ -12305,8 +12357,10 @@ supportable_widening_operation (vec_info *vinfo,
     }
   else
     {
-      optab1 = optab_for_tree_code (c1, vectype, optab_default);
-      optab2 = optab_for_tree_code (c2, vectype, optab_default);
+      optab1 = optab_for_tree_code (c1.safe_as_tree_code (), vectype,
+                                   optab_default);
+      optab2 = optab_for_tree_code (c2.safe_as_tree_code (), vectype,
+                                   optab_default);
     }
 
   if (!optab1 || !optab2)
@@ -12317,8 +12371,12 @@ supportable_widening_operation (vec_info *vinfo,
        || (icode2 = optab_handler (optab2, vec_mode)) == CODE_FOR_nothing)
     return false;
 
-  *code1 = c1;
-  *code2 = c2;
+  if (code.is_tree_code ())
+  {
+    *code1 = c1;
+    *code2 = c2;
+  }
+
 
   if (insn_data[icode1].operand[0].mode == TYPE_MODE (wide_vectype)
       && insn_data[icode2].operand[0].mode == TYPE_MODE (wide_vectype))
@@ -12339,7 +12397,7 @@ supportable_widening_operation (vec_info *vinfo,
   prev_type = vectype;
   prev_mode = vec_mode;
 
-  if (!CONVERT_EXPR_CODE_P (code))
+  if (!CONVERT_EXPR_CODE_P ((tree_code) code))
     return false;
 
   /* We assume here that there will not be more than MAX_INTERM_CVT_STEPS
@@ -12379,8 +12437,12 @@ supportable_widening_operation (vec_info *vinfo,
        }
       else
        {
-         optab3 = optab_for_tree_code (c1, intermediate_type, optab_default);
-         optab4 = optab_for_tree_code (c2, intermediate_type, optab_default);
+         optab3 = optab_for_tree_code (c1.safe_as_tree_code (),
+                                       intermediate_type,
+                                       optab_default);
+         optab4 = optab_for_tree_code (c2.safe_as_tree_code (),
+                                       intermediate_type,
+                                       optab_default);
        }
 
       if (!optab3 || !optab4
@@ -12439,7 +12501,7 @@ supportable_widening_operation (vec_info *vinfo,
 bool
 supportable_narrowing_operation (enum tree_code code,
                                 tree vectype_out, tree vectype_in,
-                                enum tree_code *code1, int *multi_step_cvt,
+                                tree_code *code1, int *multi_step_cvt,
                                  vec<tree> *interm_types)
 {
   machine_mode vec_mode;
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 
9cf2fb23fe397b467d89aa7cc5ebeaa293ed4cce..d241eba6ef3302225bbe37b374baa11e6472c280
 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -2139,13 +2139,12 @@ extern bool vect_is_simple_use (vec_info *, 
stmt_vec_info, slp_tree,
                                enum vect_def_type *,
                                tree *, stmt_vec_info * = NULL);
 extern bool vect_maybe_update_slp_op_vectype (slp_tree, tree);
-extern bool supportable_widening_operation (vec_info *,
-                                           enum tree_code, stmt_vec_info,
-                                           tree, tree, enum tree_code *,
-                                           enum tree_code *, int *,
-                                           vec<tree> *);
+extern bool supportable_widening_operation (vec_info*, code_helper,
+                                           stmt_vec_info, tree, tree,
+                                           code_helper*, code_helper*,
+                                           int*, vec<tree> *);
 extern bool supportable_narrowing_operation (enum tree_code, tree, tree,
-                                            enum tree_code *, int *,
+                                            tree_code *, int *,
                                             vec<tree> *);
 
 extern unsigned record_stmt_cost (stmt_vector_for_cost *, int,
@@ -2583,4 +2582,7 @@ vect_is_integer_truncation (stmt_vec_info stmt_info)
          && TYPE_PRECISION (lhs_type) < TYPE_PRECISION (rhs_type));
 }
 
+/* Build a GIMPLE_ASSIGN or GIMPLE_CALL with the tree_code,
+   or internal_fn contained in ch, respectively.  */
+gimple * vect_gimple_build (tree, code_helper, tree, tree);
 #endif  /* GCC_TREE_VECTORIZER_H  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 
abcdb5638d49aea4ccc46efa8e540b1fa78aa27a..a250a80e0321241e1158086acb2dd837d5827e10
 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -93,6 +93,8 @@ public:
   bool is_internal_fn () const;
   bool is_builtin_fn () const;
   int get_rep () const { return rep; }
+  enum tree_code safe_as_tree_code () const;
+  combined_fn safe_as_fn_code () const;
   bool operator== (const code_helper &other) { return rep == other.rep; }
   bool operator!= (const code_helper &other) { return rep != other.rep; }
   bool operator== (tree_code c) { return rep == code_helper (c).rep; }
@@ -102,6 +104,17 @@ private:
   int rep;
 };
 
+inline enum tree_code
+code_helper::safe_as_tree_code () const
+{
+  return is_tree_code () ? (tree_code)* this : MAX_TREE_CODES;
+}
+
+inline combined_fn
+code_helper::safe_as_fn_code () const {
+  return is_fn_code () ? (combined_fn) *this : CFN_LAST;
+}
+
 inline code_helper::operator internal_fn () const
 {
   return as_internal_fn (combined_fn (*this));
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index 
6e81dc05e0e0714256759b0594816df451415a2d..e4d815cd577d266d2bccf6fb68d62aac91a8b4cf
 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public 
License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+#define INCLUDE_MAP
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -70,6 +71,26 @@ const int internal_fn_flags_array[] = {
   0
 };
 
+const enum internal_fn internal_fn_hilo_keys_array[] = {
+#undef DEF_INTERNAL_OPTAB_HILO_FN
+#define DEF_INTERNAL_OPTAB_HILO_FN(NAME, FLAGS, OPTAB, SOPTAB, UOPTAB, TYPE) \
+  IFN_##NAME##_LO, \
+  IFN_##NAME##_HI,
+#include "internal-fn.def"
+  IFN_LAST
+#undef DEF_INTERNAL_OPTAB_HILO_FN
+};
+
+const optab internal_fn_hilo_values_array[] = {
+#undef DEF_INTERNAL_OPTAB_HILO_FN
+#define DEF_INTERNAL_OPTAB_HILO_FN(NAME, FLAGS, OPTAB, SOPTAB, UOPTAB, TYPE) \
+  SOPTAB##_lo_optab, UOPTAB##_lo_optab, \
+  SOPTAB##_hi_optab, UOPTAB##_hi_optab,
+#include "internal-fn.def"
+  unknown_optab, unknown_optab
+#undef DEF_INTERNAL_OPTAB_HILO_FN
+};
+
 /* Return the internal function called NAME, or IFN_LAST if there's
    no such function.  */
 
@@ -90,6 +111,61 @@ lookup_internal_fn (const char *name)
   return entry ? *entry : IFN_LAST;
 }
 
+static int
+ifn_cmp (const void *a_, const void *b_)
+{
+  typedef std::pair<enum internal_fn, unsigned> ifn_pair;
+  auto *a = (const std::pair<ifn_pair, optab> *)a_;
+  auto *b = (const std::pair<ifn_pair, optab> *)b_;
+  return (int) (a->first.first) - (b->first.first);
+}
+
+/* Return the optab belonging to the given internal function NAME for the given
+   SIGN or unknown_optab.  */
+
+optab
+lookup_hilo_ifn_optab (enum internal_fn fn, unsigned sign)
+{
+  typedef std::pair<enum internal_fn, unsigned> ifn_pair;
+  typedef auto_vec <std::pair<ifn_pair, optab>>fn_to_optab_map_type;
+  static fn_to_optab_map_type *fn_to_optab_map;
+
+  if (!fn_to_optab_map)
+    {
+      unsigned num
+       = sizeof (internal_fn_hilo_keys_array) / sizeof (enum internal_fn);
+      fn_to_optab_map = new fn_to_optab_map_type ();
+      for (unsigned int i = 0; i < num - 1; ++i)
+       {
+         enum internal_fn fn = internal_fn_hilo_keys_array[i];
+         optab v1 = internal_fn_hilo_values_array[2*i];
+         optab v2 = internal_fn_hilo_values_array[2*i + 1];
+         ifn_pair key1 (fn, 0);
+         fn_to_optab_map->safe_push ({key1, v1});
+         ifn_pair key2 (fn, 1);
+         fn_to_optab_map->safe_push ({key2, v2});
+       }
+       fn_to_optab_map->qsort (ifn_cmp);
+    }
+
+  ifn_pair new_pair (fn, sign ? 1 : 0);
+  optab tmp;
+  std::pair<ifn_pair,optab> pair_wrap (new_pair, tmp);
+  auto entry = fn_to_optab_map->bsearch (&pair_wrap, ifn_cmp);
+  return entry != fn_to_optab_map->end () ? entry->second : unknown_optab;
+}
+
+extern void
+lookup_hilo_internal_fn (enum internal_fn ifn, enum internal_fn *lo,
+                         enum internal_fn *hi)
+{
+  gcc_assert (decomposes_to_hilo_fn_p (ifn));
+
+  *lo = internal_fn (ifn + 1);
+  *hi = internal_fn (ifn + 2);
+}
+
+
 /* Fnspec of each internal function, indexed by function number.  */
 const_tree internal_fn_fnspec_array[IFN_LAST + 1];
 
@@ -3970,6 +4046,9 @@ commutative_binary_fn_p (internal_fn fn)
     case IFN_UBSAN_CHECK_MUL:
     case IFN_ADD_OVERFLOW:
     case IFN_MUL_OVERFLOW:
+    case IFN_VEC_WIDEN_PLUS:
+    case IFN_VEC_WIDEN_PLUS_LO:
+    case IFN_VEC_WIDEN_PLUS_HI:
       return true;
 
     default:
@@ -4043,6 +4122,42 @@ first_commutative_argument (internal_fn fn)
     }
 }
 
+/* Return true if FN has a wider output type than its argument types.  */
+
+bool
+widening_fn_p (internal_fn fn)
+{
+  switch (fn)
+    {
+    case IFN_VEC_WIDEN_PLUS:
+    case IFN_VEC_WIDEN_MINUS:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Return true if FN decomposes to _hi and _lo IFN.  If true this should also
+   be a widening function.  */
+
+bool
+decomposes_to_hilo_fn_p (internal_fn fn)
+{
+  if (!widening_fn_p (fn))
+    return false;
+
+  switch (fn)
+    {
+    case IFN_VEC_WIDEN_PLUS:
+    case IFN_VEC_WIDEN_MINUS:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
 /* Return true if IFN_SET_EDOM is supported.  */
 
 bool
@@ -4055,6 +4170,32 @@ set_edom_supported_p (void)
 #endif
 }
 
+#undef DEF_INTERNAL_OPTAB_HILO_FN
+#define DEF_INTERNAL_OPTAB_HILO_FN(CODE, FLAGS, OPTAB, SOPTAB, UOPTAB, TYPE) \
+  static void                                                  \
+  expand_##CODE (internal_fn, gcall *)                         \
+  {                                                            \
+    gcc_unreachable ();                                                \
+  }                                                            \
+  static void                                                  \
+  expand_##CODE##_LO (internal_fn fn, gcall *stmt)             \
+  {                                                            \
+    tree ty = TREE_TYPE (gimple_get_lhs (stmt));               \
+    if (!TYPE_UNSIGNED (ty))                                   \
+      expand_##TYPE##_optab_fn (fn, stmt, SOPTAB##_lo##_optab);        \
+    else                                                       \
+      expand_##TYPE##_optab_fn (fn, stmt, UOPTAB##_lo##_optab);        \
+  }                                                            \
+  static void                                                  \
+  expand_##CODE##_HI (internal_fn fn, gcall *stmt)             \
+  {                                                            \
+    tree ty = TREE_TYPE (gimple_get_lhs (stmt));               \
+    if (!TYPE_UNSIGNED (ty))                                   \
+      expand_##TYPE##_optab_fn (fn, stmt, SOPTAB##_hi##_optab);        \
+    else                                                       \
+      expand_##TYPE##_optab_fn (fn, stmt, UOPTAB##_hi##_optab);        \
+  }
+
 #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \
   static void                                          \
   expand_##CODE (internal_fn fn, gcall *stmt)          \
@@ -4071,6 +4212,7 @@ set_edom_supported_p (void)
     expand_##TYPE##_optab_fn (fn, stmt, which_optab);                  \
   }
 #include "internal-fn.def"
+#undef DEF_INTERNAL_OPTAB_HILO_FN
 
 /* Routines to expand each internal function, indexed by function number.
    Each routine has the prototype:
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 
7fe742c2ae713e7152ab05cfdfba86e4e0aa3456..347ed667d92620e0ee3ea15c58ecac6c242ebe73
 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -85,6 +85,13 @@ along with GCC; see the file COPYING3.  If not see
    says that the function extends the C-level BUILT_IN_<NAME>{,L,LL,IMAX}
    group of functions to any integral mode (including vector modes).
 
+   DEF_INTERNAL_OPTAB_HILO_FN is like DEF_INTERNAL_OPTAB_FN except it
+   provides convenience wrappers for defining conversions that require a
+   hi/lo split, like widening and narrowing operations.  Each definition
+   for <NAME> will require an optab named <OPTAB> and two other optabs that
+   you specify for signed and unsigned.
+
+
    Each entry must have a corresponding expander of the form:
 
      void expand_NAME (gimple_call stmt)
@@ -123,6 +130,14 @@ along with GCC; see the file COPYING3.  If not see
   DEF_INTERNAL_OPTAB_FN (NAME, FLAGS, OPTAB, TYPE)
 #endif
 
+#ifndef DEF_INTERNAL_OPTAB_HILO_FN
+#define DEF_INTERNAL_OPTAB_HILO_FN(NAME, FLAGS, OPTAB, SOPTAB, UOPTAB, TYPE) \
+  DEF_INTERNAL_OPTAB_FN (NAME, FLAGS, OPTAB, TYPE) \
+  DEF_INTERNAL_OPTAB_FN (NAME ## _LO, FLAGS, unknown, TYPE) \
+  DEF_INTERNAL_OPTAB_FN (NAME ## _HI, FLAGS, unknown, TYPE)
+#endif
+
+
 DEF_INTERNAL_OPTAB_FN (MASK_LOAD, ECF_PURE, maskload, mask_load)
 DEF_INTERNAL_OPTAB_FN (LOAD_LANES, ECF_CONST, vec_load_lanes, load_lanes)
 DEF_INTERNAL_OPTAB_FN (MASK_LOAD_LANES, ECF_PURE,
@@ -315,6 +330,14 @@ DEF_INTERNAL_OPTAB_FN (COMPLEX_ADD_ROT270, ECF_CONST, 
cadd270, binary)
 DEF_INTERNAL_OPTAB_FN (COMPLEX_MUL, ECF_CONST, cmul, binary)
 DEF_INTERNAL_OPTAB_FN (COMPLEX_MUL_CONJ, ECF_CONST, cmul_conj, binary)
 DEF_INTERNAL_OPTAB_FN (VEC_ADDSUB, ECF_CONST, vec_addsub, binary)
+DEF_INTERNAL_OPTAB_HILO_FN (VEC_WIDEN_PLUS,
+                           ECF_CONST | ECF_NOTHROW,
+                           vec_widen_add, vec_widen_saddl, vec_widen_uaddl,
+                            binary)
+DEF_INTERNAL_OPTAB_HILO_FN (VEC_WIDEN_MINUS,
+                           ECF_CONST | ECF_NOTHROW,
+                           vec_widen_sub, vec_widen_ssubl, vec_widen_usubl,
+                            binary)
 DEF_INTERNAL_OPTAB_FN (VEC_FMADDSUB, ECF_CONST, vec_fmaddsub, ternary)
 DEF_INTERNAL_OPTAB_FN (VEC_FMSUBADD, ECF_CONST, vec_fmsubadd, ternary)
 
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index 
08922ed4254898f5fffca3f33973e96ed9ce772f..6a5f8762e872ad2ef64ce2986a678e3b40622d81
 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -20,6 +20,10 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_INTERNAL_FN_H
 #define GCC_INTERNAL_FN_H
 
+#include "insn-codes.h"
+#include "insn-opinit.h"
+
+
 /* INTEGER_CST values for IFN_UNIQUE function arg-0.
 
    UNSPEC: Undifferentiated UNIQUE.
@@ -112,6 +116,9 @@ internal_fn_name (enum internal_fn fn)
 }
 
 extern internal_fn lookup_internal_fn (const char *);
+extern optab lookup_hilo_ifn_optab (enum internal_fn, unsigned);
+extern void lookup_hilo_internal_fn (enum internal_fn, enum internal_fn *,
+                                     enum internal_fn *);
 
 /* Return the ECF_* flags for function FN.  */
 
@@ -210,6 +217,8 @@ extern bool commutative_binary_fn_p (internal_fn);
 extern bool commutative_ternary_fn_p (internal_fn);
 extern int first_commutative_argument (internal_fn);
 extern bool associative_binary_fn_p (internal_fn);
+extern bool widening_fn_p (internal_fn);
+extern bool decomposes_to_hilo_fn_p (internal_fn);
 
 extern bool set_edom_supported_p (void);
 
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index 
c8e39c82d57a7d726e7da33d247b80f32ec9236c..d4dd7ee3d34d01c32ab432ae4e4ce9e4b522b2f7
 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -1314,7 +1314,12 @@ commutative_optab_p (optab binoptab)
          || binoptab == smul_widen_optab
          || binoptab == umul_widen_optab
          || binoptab == smul_highpart_optab
-         || binoptab == umul_highpart_optab);
+         || binoptab == umul_highpart_optab
+         || binoptab == vec_widen_add_optab
+         || binoptab == vec_widen_saddl_hi_optab
+         || binoptab == vec_widen_saddl_lo_optab
+         || binoptab == vec_widen_uaddl_hi_optab
+         || binoptab == vec_widen_uaddl_lo_optab);
 }
 
 /* X is to be used in mode MODE as operand OPN to BINOPTAB.  If we're
diff --git a/gcc/optabs.def b/gcc/optabs.def
index 
695f5911b300c9ca5737de9be809fa01aabe5e01..e064189103b3be70644468d11f3c91ac45ffe0d0
 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -78,6 +78,8 @@ OPTAB_CD(smsub_widen_optab, "msub$b$a4")
 OPTAB_CD(umsub_widen_optab, "umsub$b$a4")
 OPTAB_CD(ssmsub_widen_optab, "ssmsub$b$a4")
 OPTAB_CD(usmsub_widen_optab, "usmsub$a$b4")
+OPTAB_CD(vec_widen_add_optab, "add$a$b3")
+OPTAB_CD(vec_widen_sub_optab, "sub$a$b3")
 OPTAB_CD(vec_load_lanes_optab, "vec_load_lanes$a$b")
 OPTAB_CD(vec_store_lanes_optab, "vec_store_lanes$a$b")
 OPTAB_CD(vec_mask_load_lanes_optab, "vec_mask_load_lanes$a$b")
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-widen-add.c 
b/gcc/testsuite/gcc.target/aarch64/vect-widen-add.c
index 
220bd9352a4c7acd2e3713e441d74898d3e92b30..7037673d32bd780e1c9b58a51e58e2bac3b30b7e
 100644
--- a/gcc/testsuite/gcc.target/aarch64/vect-widen-add.c
+++ b/gcc/testsuite/gcc.target/aarch64/vect-widen-add.c
@@ -1,5 +1,5 @@
 /* { dg-do run } */
-/* { dg-options "-O3 -save-temps" } */
+/* { dg-options "-O3 -save-temps -fdump-tree-vect-all" } */
 #include <stdint.h>
 #include <string.h>
 
@@ -86,6 +86,8 @@ main()
     return 0;
 }
 
+/* { dg-final { scan-tree-dump "add new stmt.*VEC_WIDEN_PLUS_LO" "vect"   } } 
*/
+/* { dg-final { scan-tree-dump "add new stmt.*VEC_WIDEN_PLUS_HI" "vect"   } } 
*/
 /* { dg-final { scan-assembler-times {\tuaddl\t} 1} } */
 /* { dg-final { scan-assembler-times {\tuaddl2\t} 1} } */
 /* { dg-final { scan-assembler-times {\tsaddl\t} 1} } */
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-widen-sub.c 
b/gcc/testsuite/gcc.target/aarch64/vect-widen-sub.c
index 
a2bed63affbd091977df95a126da1f5b8c1d41d2..83bc1edb6105f47114b665e24a13e6194b2179a2
 100644
--- a/gcc/testsuite/gcc.target/aarch64/vect-widen-sub.c
+++ b/gcc/testsuite/gcc.target/aarch64/vect-widen-sub.c
@@ -1,5 +1,5 @@
 /* { dg-do run } */
-/* { dg-options "-O3 -save-temps" } */
+/* { dg-options "-O3 -save-temps -fdump-tree-vect-all" } */
 #include <stdint.h>
 #include <string.h>
 
@@ -86,6 +86,8 @@ main()
     return 0;
 }
 
+/* { dg-final { scan-tree-dump "add new stmt.*VEC_WIDEN_MINUS_LO" "vect"   } } 
*/
+/* { dg-final { scan-tree-dump "add new stmt.*VEC_WIDEN_MINUS_HI" "vect"   } } 
*/
 /* { dg-final { scan-assembler-times {\tusubl\t} 1} } */
 /* { dg-final { scan-assembler-times {\tusubl2\t} 1} } */
 /* { dg-final { scan-assembler-times {\tssubl\t} 1} } */
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 
68dfba266d679c9738a3d5d70551a91cbdafcf66..1a514461b2ca416f45a5fa9abe417980d33ef4df
 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -1394,14 +1394,16 @@ static gimple *
 vect_recog_widen_op_pattern (vec_info *vinfo,
                             stmt_vec_info last_stmt_info, tree *type_out,
                             tree_code orig_code, code_helper wide_code,
-                            bool shift_p, const char *name)
+                            bool shift_p, const char *name,
+                            enum optab_subtype *subtype = NULL)
 {
   gimple *last_stmt = last_stmt_info->stmt;
 
   vect_unpromoted_value unprom[2];
   tree half_type;
   if (!vect_widened_op_tree (vinfo, last_stmt_info, orig_code, orig_code,
-                            shift_p, 2, unprom, &half_type))
+                            shift_p, 2, unprom, &half_type, subtype))
+
     return NULL;
 
   /* Pattern detected.  */
@@ -1467,6 +1469,20 @@ vect_recog_widen_op_pattern (vec_info *vinfo,
                              type, pattern_stmt, vecctype);
 }
 
+static gimple *
+vect_recog_widen_op_pattern (vec_info *vinfo,
+                            stmt_vec_info last_stmt_info, tree *type_out,
+                            tree_code orig_code, internal_fn wide_ifn,
+                            bool shift_p, const char *name,
+                            enum optab_subtype *subtype = NULL)
+{
+  combined_fn ifn = as_combined_fn (wide_ifn);
+  return vect_recog_widen_op_pattern (vinfo, last_stmt_info, type_out,
+                                     orig_code, ifn, shift_p, name,
+                                     subtype);
+}
+
+
 /* Try to detect multiplication on widened inputs, converting MULT_EXPR
    to WIDEN_MULT_EXPR.  See vect_recog_widen_op_pattern for details.  */
 
@@ -1480,26 +1496,30 @@ vect_recog_widen_mult_pattern (vec_info *vinfo, 
stmt_vec_info last_stmt_info,
 }
 
 /* Try to detect addition on widened inputs, converting PLUS_EXPR
-   to WIDEN_PLUS_EXPR.  See vect_recog_widen_op_pattern for details.  */
+   to IFN_VEC_WIDEN_PLUS.  See vect_recog_widen_op_pattern for details.  */
 
 static gimple *
 vect_recog_widen_plus_pattern (vec_info *vinfo, stmt_vec_info last_stmt_info,
                               tree *type_out)
 {
+  enum optab_subtype subtype;
   return vect_recog_widen_op_pattern (vinfo, last_stmt_info, type_out,
-                                     PLUS_EXPR, WIDEN_PLUS_EXPR, false,
-                                     "vect_recog_widen_plus_pattern");
+                                     PLUS_EXPR, IFN_VEC_WIDEN_PLUS,
+                                     false, "vect_recog_widen_plus_pattern",
+                                     &subtype);
 }
 
 /* Try to detect subtraction on widened inputs, converting MINUS_EXPR
-   to WIDEN_MINUS_EXPR.  See vect_recog_widen_op_pattern for details.  */
+   to IFN_VEC_WIDEN_MINUS.  See vect_recog_widen_op_pattern for details.  */
 static gimple *
 vect_recog_widen_minus_pattern (vec_info *vinfo, stmt_vec_info last_stmt_info,
                               tree *type_out)
 {
+  enum optab_subtype subtype;
   return vect_recog_widen_op_pattern (vinfo, last_stmt_info, type_out,
-                                     MINUS_EXPR, WIDEN_MINUS_EXPR, false,
-                                     "vect_recog_widen_minus_pattern");
+                                     MINUS_EXPR, IFN_VEC_WIDEN_MINUS,
+                                     false, "vect_recog_widen_minus_pattern",
+                                     &subtype);
 }
 
 /* Function vect_recog_popcount_pattern
@@ -6067,6 +6087,7 @@ static vect_recog_func vect_vect_recog_func_ptrs[] = {
   { vect_recog_mask_conversion_pattern, "mask_conversion" },
   { vect_recog_widen_plus_pattern, "widen_plus" },
   { vect_recog_widen_minus_pattern, "widen_minus" },
+  /* These must come after the double widening ones.  */
 };
 
 const unsigned int NUM_PATTERNS = ARRAY_SIZE (vect_vect_recog_func_ptrs);
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 
715ec2e30a4de620b8a5076c0e7f2f7fd1b0654e..f4806073f48d4dedea3ac9bd855792b152d78919
 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -5035,7 +5035,9 @@ vectorizable_conversion (vec_info *vinfo,
   bool widen_arith = (code == WIDEN_PLUS_EXPR
                 || code == WIDEN_MINUS_EXPR
                 || code == WIDEN_MULT_EXPR
-                || code == WIDEN_LSHIFT_EXPR);
+                || code == WIDEN_LSHIFT_EXPR
+                || code == IFN_VEC_WIDEN_PLUS
+                || code == IFN_VEC_WIDEN_MINUS);
 
   if (!widen_arith
       && !CONVERT_EXPR_CODE_P (code)
@@ -5085,7 +5087,9 @@ vectorizable_conversion (vec_info *vinfo,
       gcc_assert (code == WIDEN_MULT_EXPR
                  || code == WIDEN_LSHIFT_EXPR
                  || code == WIDEN_PLUS_EXPR
-                 || code == WIDEN_MINUS_EXPR);
+                 || code == WIDEN_MINUS_EXPR
+                 || code == IFN_VEC_WIDEN_PLUS
+                 || code == IFN_VEC_WIDEN_MINUS);
 
 
       op1 = is_gimple_assign (stmt) ? gimple_assign_rhs2 (stmt) :
@@ -12355,14 +12359,50 @@ supportable_widening_operation (vec_info *vinfo,
       optab1 = vec_unpacks_sbool_lo_optab;
       optab2 = vec_unpacks_sbool_hi_optab;
     }
-  else
-    {
-      optab1 = optab_for_tree_code (c1.safe_as_tree_code (), vectype,
-                                   optab_default);
-      optab2 = optab_for_tree_code (c2.safe_as_tree_code (), vectype,
-                                   optab_default);
+
+  if (code.is_fn_code ())
+     {
+      internal_fn ifn = as_internal_fn (code.safe_as_fn_code ());
+      gcc_assert (decomposes_to_hilo_fn_p (ifn));
+
+      internal_fn lo, hi;
+      lookup_hilo_internal_fn (ifn, &lo, &hi);
+      *code1 = as_combined_fn (lo);
+      *code2 = as_combined_fn (hi);
+      optab1 = lookup_hilo_ifn_optab (lo, !TYPE_UNSIGNED (vectype));
+      optab2 = lookup_hilo_ifn_optab (hi, !TYPE_UNSIGNED (vectype));
     }
 
+  if (code.is_tree_code ())
+  {
+    if (code == FIX_TRUNC_EXPR)
+      {
+       /* The signedness is determined from output operand.  */
+       optab1 = optab_for_tree_code (c1.safe_as_tree_code (), vectype_out,
+                                     optab_default);
+       optab2 = optab_for_tree_code (c2.safe_as_tree_code (), vectype_out,
+                                     optab_default);
+      }
+    else if (CONVERT_EXPR_CODE_P (code.safe_as_tree_code ())
+            && VECTOR_BOOLEAN_TYPE_P (wide_vectype)
+            && VECTOR_BOOLEAN_TYPE_P (vectype)
+            && TYPE_MODE (wide_vectype) == TYPE_MODE (vectype)
+            && SCALAR_INT_MODE_P (TYPE_MODE (vectype)))
+      {
+       /* If the input and result modes are the same, a different optab
+          is needed where we pass in the number of units in vectype.  */
+       optab1 = vec_unpacks_sbool_lo_optab;
+       optab2 = vec_unpacks_sbool_hi_optab;
+      }
+    else
+      {
+       optab1 = optab_for_tree_code (c1.safe_as_tree_code (), vectype,
+                                     optab_default);
+       optab2 = optab_for_tree_code (c2.safe_as_tree_code (), vectype,
+                                     optab_default);
+      }
+  }
+
   if (!optab1 || !optab2)
     return false;
 
diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index 
1a1b26b1c6c23ce273bcd08dc9a973f777174007..25b1558dcb941ea491a19aeeb2cd8f4d2dbdf7c6
 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -5365,10 +5365,6 @@ expand_debug_expr (tree exp)
     case VEC_WIDEN_MULT_ODD_EXPR:
     case VEC_WIDEN_LSHIFT_HI_EXPR:
     case VEC_WIDEN_LSHIFT_LO_EXPR:
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
     case VEC_PERM_EXPR:
     case VEC_DUPLICATE_EXPR:
     case VEC_SERIES_EXPR:
@@ -5405,8 +5401,6 @@ expand_debug_expr (tree exp)
     case WIDEN_MULT_EXPR:
     case WIDEN_MULT_PLUS_EXPR:
     case WIDEN_MULT_MINUS_EXPR:
-    case WIDEN_PLUS_EXPR:
-    case WIDEN_MINUS_EXPR:
       if (SCALAR_INT_MODE_P (GET_MODE (op0))
          && SCALAR_INT_MODE_P (mode))
        {
@@ -5419,10 +5413,6 @@ expand_debug_expr (tree exp)
            op1 = simplify_gen_unary (ZERO_EXTEND, mode, op1, inner_mode);
          else
            op1 = simplify_gen_unary (SIGN_EXTEND, mode, op1, inner_mode);
-         if (TREE_CODE (exp) == WIDEN_PLUS_EXPR)
-           return simplify_gen_binary (PLUS, mode, op0, op1);
-         else if (TREE_CODE (exp) == WIDEN_MINUS_EXPR)
-           return simplify_gen_binary (MINUS, mode, op0, op1);
          op0 = simplify_gen_binary (MULT, mode, op0, op1);
          if (TREE_CODE (exp) == WIDEN_MULT_EXPR)
            return op0;
diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi
index 
2c14b7abce2db0a3da0a21e916907947cb56a265..3816abaaf4d364d604a44942317f96f3f303e5b6
 100644
--- a/gcc/doc/generic.texi
+++ b/gcc/doc/generic.texi
@@ -1811,10 +1811,6 @@ a value from @code{enum annot_expr_kind}, the third is 
an @code{INTEGER_CST}.
 @tindex VEC_RSHIFT_EXPR
 @tindex VEC_WIDEN_MULT_HI_EXPR
 @tindex VEC_WIDEN_MULT_LO_EXPR
-@tindex VEC_WIDEN_PLUS_HI_EXPR
-@tindex VEC_WIDEN_PLUS_LO_EXPR
-@tindex VEC_WIDEN_MINUS_HI_EXPR
-@tindex VEC_WIDEN_MINUS_LO_EXPR
 @tindex VEC_UNPACK_HI_EXPR
 @tindex VEC_UNPACK_LO_EXPR
 @tindex VEC_UNPACK_FLOAT_HI_EXPR
@@ -1861,33 +1857,6 @@ vector of @code{N/2} products. In the case of 
@code{VEC_WIDEN_MULT_LO_EXPR} the
 low @code{N/2} elements of the two vector are multiplied to produce the
 vector of @code{N/2} products.
 
-@item VEC_WIDEN_PLUS_HI_EXPR
-@itemx VEC_WIDEN_PLUS_LO_EXPR
-These nodes represent widening vector addition of the high and low parts of
-the two input vectors, respectively.  Their operands are vectors that contain
-the same number of elements (@code{N}) of the same integral type. The result
-is a vector that contains half as many elements, of an integral type whose size
-is twice as wide.  In the case of @code{VEC_WIDEN_PLUS_HI_EXPR} the high
-@code{N/2} elements of the two vectors are added to produce the vector of
-@code{N/2} products.  In the case of @code{VEC_WIDEN_PLUS_LO_EXPR} the low
-@code{N/2} elements of the two vectors are added to produce the vector of
-@code{N/2} products.
-
-@item VEC_WIDEN_MINUS_HI_EXPR
-@itemx VEC_WIDEN_MINUS_LO_EXPR
-These nodes represent widening vector subtraction of the high and low parts of
-the two input vectors, respectively.  Their operands are vectors that contain
-the same number of elements (@code{N}) of the same integral type. The high/low
-elements of the second vector are subtracted from the high/low elements of the
-first. The result is a vector that contains half as many elements, of an
-integral type whose size is twice as wide.  In the case of
-@code{VEC_WIDEN_MINUS_HI_EXPR} the high @code{N/2} elements of the second
-vector are subtracted from the high @code{N/2} of the first to produce the
-vector of @code{N/2} products.  In the case of
-@code{VEC_WIDEN_MINUS_LO_EXPR} the low @code{N/2} elements of the second
-vector are subtracted from the low @code{N/2} of the first to produce the
-vector of @code{N/2} products.
-
 @item VEC_UNPACK_HI_EXPR
 @itemx VEC_UNPACK_LO_EXPR
 These nodes represent unpacking of the high and low parts of the input vector,
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 
f8f5cc5a6ca67f291b3c8b7246d593c0be80272f..454d1391b19a7d2aa53f0a88876d1eaf0494de51
 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -9601,8 +9601,6 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode 
tmode,
                                          target, unsignedp);
       return target;
 
-    case WIDEN_PLUS_EXPR:
-    case WIDEN_MINUS_EXPR:
     case WIDEN_MULT_EXPR:
       /* If first operand is constant, swap them.
         Thus the following special case checks need only
@@ -10380,10 +10378,6 @@ expand_expr_real_2 (sepops ops, rtx target, 
machine_mode tmode,
        return temp;
       }
 
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
     case VEC_WIDEN_MULT_HI_EXPR:
     case VEC_WIDEN_MULT_LO_EXPR:
     case VEC_WIDEN_MULT_EVEN_EXPR:
diff --git a/gcc/gimple-pretty-print.cc b/gcc/gimple-pretty-print.cc
index 
300e9d7ed1e7be73f30875e08c461a8880c3134e..d903826894e7f0dfd34dc0caad92eea3caa45e05
 100644
--- a/gcc/gimple-pretty-print.cc
+++ b/gcc/gimple-pretty-print.cc
@@ -459,10 +459,6 @@ dump_binary_rhs (pretty_printer *buffer, const gassign 
*gs, int spc,
     case VEC_PACK_FLOAT_EXPR:
     case VEC_WIDEN_LSHIFT_HI_EXPR:
     case VEC_WIDEN_LSHIFT_LO_EXPR:
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
     case VEC_SERIES_EXPR:
       for (p = get_tree_code_name (code); *p; p++)
        pp_character (buffer, TOUPPER (*p));
diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
index 
4ca32a7b5d52f8426b09d1446a336650e143b41f..5ae7f7596c6fc6f901e4e47ae44f00185f4602b2
 100644
--- a/gcc/gimple-range-op.cc
+++ b/gcc/gimple-range-op.cc
@@ -797,12 +797,6 @@ gimple_range_op_handler::maybe_non_standard ()
   if (gimple_code (m_stmt) == GIMPLE_ASSIGN)
     switch (gimple_assign_rhs_code (m_stmt))
       {
-       case WIDEN_PLUS_EXPR:
-       {
-         signed_op = ptr_op_widen_plus_signed;
-         unsigned_op = ptr_op_widen_plus_unsigned;
-       }
-       gcc_fallthrough ();
        case WIDEN_MULT_EXPR:
        {
          m_valid = false;
diff --git a/gcc/optabs-tree.cc b/gcc/optabs-tree.cc
index 
8010046c6a8b3e809c989ddef7a06ddaa68ae32a..ee1aa8c9676ee9c67edbf403e6295da391826a62
 100644
--- a/gcc/optabs-tree.cc
+++ b/gcc/optabs-tree.cc
@@ -190,22 +190,6 @@ optab_for_tree_code (enum tree_code code, const_tree type,
       return (TYPE_UNSIGNED (type)
              ? vec_widen_ushiftl_lo_optab : vec_widen_sshiftl_lo_optab);
 
-    case VEC_WIDEN_PLUS_LO_EXPR:
-      return (TYPE_UNSIGNED (type)
-             ? vec_widen_uaddl_lo_optab : vec_widen_saddl_lo_optab);
-
-    case VEC_WIDEN_PLUS_HI_EXPR:
-      return (TYPE_UNSIGNED (type)
-             ? vec_widen_uaddl_hi_optab : vec_widen_saddl_hi_optab);
-
-    case VEC_WIDEN_MINUS_LO_EXPR:
-      return (TYPE_UNSIGNED (type)
-             ? vec_widen_usubl_lo_optab : vec_widen_ssubl_lo_optab);
-
-    case VEC_WIDEN_MINUS_HI_EXPR:
-      return (TYPE_UNSIGNED (type)
-             ? vec_widen_usubl_hi_optab : vec_widen_ssubl_hi_optab);
-
     case VEC_UNPACK_HI_EXPR:
       return (TYPE_UNSIGNED (type)
              ? vec_unpacku_hi_optab : vec_unpacks_hi_optab);
@@ -312,8 +296,6 @@ optab_for_tree_code (enum tree_code code, const_tree type,
    'hi'/'lo' pair using codes such as VEC_WIDEN_MINUS_HI/LO.
 
    Supported widening operations:
-    WIDEN_MINUS_EXPR
-    WIDEN_PLUS_EXPR
     WIDEN_MULT_EXPR
     WIDEN_LSHIFT_EXPR
 
@@ -345,12 +327,6 @@ supportable_half_widening_operation (enum tree_code code, 
tree vectype_out,
     case WIDEN_LSHIFT_EXPR:
       *code1 = LSHIFT_EXPR;
       break;
-    case WIDEN_MINUS_EXPR:
-      *code1 = MINUS_EXPR;
-      break;
-    case WIDEN_PLUS_EXPR:
-      *code1 = PLUS_EXPR;
-      break;
     case WIDEN_MULT_EXPR:
       *code1 = MULT_EXPR;
       break;
diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index 
a9fcc7fd050f871437ef336ecfb8d6cc81280ee0..f80cd1465df83b5540492e619e56b9af249e9f31
 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -4017,8 +4017,6 @@ verify_gimple_assign_binary (gassign *stmt)
         return false;
       }
 
-    case WIDEN_PLUS_EXPR:
-    case WIDEN_MINUS_EXPR:
     case PLUS_EXPR:
     case MINUS_EXPR:
       {
@@ -4139,10 +4137,6 @@ verify_gimple_assign_binary (gassign *stmt)
         return false;
       }
 
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
     case VEC_WIDEN_MULT_HI_EXPR:
     case VEC_WIDEN_MULT_LO_EXPR:
     case VEC_WIDEN_MULT_EVEN_EXPR:
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 
c702f0032a19203a7c536a01c1e7f47fc7b77add..6e5fd45a0c2435109dd3d50e8fc8e1d4969a1fd0
 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -4273,8 +4273,6 @@ estimate_operator_cost (enum tree_code code, eni_weights 
*weights,
 
     case REALIGN_LOAD_EXPR:
 
-    case WIDEN_PLUS_EXPR:
-    case WIDEN_MINUS_EXPR:
     case WIDEN_SUM_EXPR:
     case WIDEN_MULT_EXPR:
     case DOT_PROD_EXPR:
@@ -4283,10 +4281,6 @@ estimate_operator_cost (enum tree_code code, eni_weights 
*weights,
     case WIDEN_MULT_MINUS_EXPR:
     case WIDEN_LSHIFT_EXPR:
 
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
     case VEC_WIDEN_MULT_HI_EXPR:
     case VEC_WIDEN_MULT_LO_EXPR:
     case VEC_WIDEN_MULT_EVEN_EXPR:
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 
7947f9647a15110b52d195643ad7d28ee32d4236..9941d8bf80535a98e647b8928619a6bf08bc434c
 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -2874,8 +2874,6 @@ dump_generic_node (pretty_printer *pp, tree node, int 
spc, dump_flags_t flags,
       break;
 
       /* Binary arithmetic and logic expressions.  */
-    case WIDEN_PLUS_EXPR:
-    case WIDEN_MINUS_EXPR:
     case WIDEN_SUM_EXPR:
     case WIDEN_MULT_EXPR:
     case MULT_EXPR:
@@ -3831,10 +3829,6 @@ dump_generic_node (pretty_printer *pp, tree node, int 
spc, dump_flags_t flags,
     case VEC_SERIES_EXPR:
     case VEC_WIDEN_MULT_HI_EXPR:
     case VEC_WIDEN_MULT_LO_EXPR:
-    case VEC_WIDEN_PLUS_HI_EXPR:
-    case VEC_WIDEN_PLUS_LO_EXPR:
-    case VEC_WIDEN_MINUS_HI_EXPR:
-    case VEC_WIDEN_MINUS_LO_EXPR:
     case VEC_WIDEN_MULT_EVEN_EXPR:
     case VEC_WIDEN_MULT_ODD_EXPR:
     case VEC_WIDEN_LSHIFT_HI_EXPR:
@@ -4352,12 +4346,6 @@ op_symbol_code (enum tree_code code)
     case WIDEN_LSHIFT_EXPR:
       return "w<<";
 
-    case WIDEN_PLUS_EXPR:
-      return "w+";
-
-    case WIDEN_MINUS_EXPR:
-      return "w-";
-
     case POINTER_PLUS_EXPR:
       return "+";
 
diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
index 
8daf7bd7dd34d043b1d7b4cba1779f0ecf9f520a..213a3899a6c145bb057cd118bec1df7a05728aef
 100644
--- a/gcc/tree-vect-data-refs.cc
+++ b/gcc/tree-vect-data-refs.cc
@@ -136,8 +136,6 @@ vect_get_smallest_scalar_type (stmt_vec_info stmt_info, 
tree scalar_type)
          || gimple_assign_rhs_code (assign) == WIDEN_SUM_EXPR
          || gimple_assign_rhs_code (assign) == WIDEN_MULT_EXPR
          || gimple_assign_rhs_code (assign) == WIDEN_LSHIFT_EXPR
-         || gimple_assign_rhs_code (assign) == WIDEN_PLUS_EXPR
-         || gimple_assign_rhs_code (assign) == WIDEN_MINUS_EXPR
          || gimple_assign_rhs_code (assign) == FLOAT_EXPR)
        {
          tree rhs_type = TREE_TYPE (gimple_assign_rhs1 (assign));
diff --git a/gcc/tree-vect-generic.cc b/gcc/tree-vect-generic.cc
index 
445da53292e9d1d2db62ca962fc017bb0e6c9bbe..342ffc5fa7f3b8f37e6bd4658d2f1fccf1d2c7fa
 100644
--- a/gcc/tree-vect-generic.cc
+++ b/gcc/tree-vect-generic.cc
@@ -2227,10 +2227,6 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi,
      arguments, not the widened result.  VEC_UNPACK_FLOAT_*_EXPR is
      calculated in the same way above.  */
   if (code == WIDEN_SUM_EXPR
-      || code == VEC_WIDEN_PLUS_HI_EXPR
-      || code == VEC_WIDEN_PLUS_LO_EXPR
-      || code == VEC_WIDEN_MINUS_HI_EXPR
-      || code == VEC_WIDEN_MINUS_LO_EXPR
       || code == VEC_WIDEN_MULT_HI_EXPR
       || code == VEC_WIDEN_MULT_LO_EXPR
       || code == VEC_WIDEN_MULT_EVEN_EXPR
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 
1a514461b2ca416f45a5fa9abe417980d33ef4df..13c69133d7ae565cf0334390cb0c303c89f98ac8
 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -561,21 +561,35 @@ vect_joust_widened_type (tree type, tree new_type, tree 
*common_type)
 
 static unsigned int
 vect_widened_op_tree (vec_info *vinfo, stmt_vec_info stmt_info, tree_code code,
-                     tree_code widened_code, bool shift_p,
+                     code_helper widened_code, bool shift_p,
                      unsigned int max_nops,
                      vect_unpromoted_value *unprom, tree *common_type,
                      enum optab_subtype *subtype = NULL)
 {
   /* Check for an integer operation with the right code.  */
-  gassign *assign = dyn_cast <gassign *> (stmt_info->stmt);
-  if (!assign)
+  gimple* stmt = stmt_info->stmt;
+  if (!(is_gimple_assign (stmt) || is_gimple_call (stmt)))
     return 0;
 
-  tree_code rhs_code = gimple_assign_rhs_code (assign);
-  if (rhs_code != code && rhs_code != widened_code)
+  code_helper rhs_code;
+  if (is_gimple_assign (stmt))
+    {
+      rhs_code = gimple_assign_rhs_code (stmt);
+      if (rhs_code.safe_as_tree_code () != code
+         && rhs_code.get_rep () != widened_code.get_rep ())
+       return 0;
+    }
+  else if (is_gimple_call (stmt))
+    {
+      rhs_code = gimple_call_combined_fn (stmt);
+      if (rhs_code.get_rep () != widened_code.get_rep ())
+       return 0;
+    }
+  else
     return 0;
 
-  tree type = TREE_TYPE (gimple_assign_lhs (assign));
+  tree lhs = gimple_get_lhs (stmt);
+  tree type = TREE_TYPE (lhs);
   if (!INTEGRAL_TYPE_P (type))
     return 0;
 
@@ -588,7 +602,7 @@ vect_widened_op_tree (vec_info *vinfo, stmt_vec_info 
stmt_info, tree_code code,
     {
       vect_unpromoted_value *this_unprom = &unprom[next_op];
       unsigned int nops = 1;
-      tree op = gimple_op (assign, i + 1);
+      tree op = gimple_arg (stmt, i);
       if (i == 1 && TREE_CODE (op) == INTEGER_CST)
        {
          /* We already have a common type from earlier operands.
@@ -1342,8 +1356,9 @@ vect_recog_sad_pattern (vec_info *vinfo,
   /* FORNOW.  Can continue analyzing the def-use chain when this stmt in a phi
      inside the loop (in case we are analyzing an outer-loop).  */
   vect_unpromoted_value unprom[2];
-  if (!vect_widened_op_tree (vinfo, diff_stmt_vinfo, MINUS_EXPR, 
WIDEN_MINUS_EXPR,
-                            false, 2, unprom, &half_type))
+  if (!vect_widened_op_tree (vinfo, diff_stmt_vinfo, MINUS_EXPR,
+                            CFN_VEC_WIDEN_MINUS, false, 2, unprom,
+                            &half_type))
     return NULL;
 
   vect_pattern_detected ("vect_recog_sad_pattern", last_stmt);
@@ -2696,9 +2711,10 @@ vect_recog_average_pattern (vec_info *vinfo,
   internal_fn ifn = IFN_AVG_FLOOR;
   vect_unpromoted_value unprom[3];
   tree new_type;
+  enum optab_subtype subtype;
   unsigned int nops = vect_widened_op_tree (vinfo, plus_stmt_info, PLUS_EXPR,
-                                           WIDEN_PLUS_EXPR, false, 3,
-                                           unprom, &new_type);
+                                           CFN_VEC_WIDEN_PLUS, false, 3,
+                                           unprom, &new_type, &subtype);
   if (nops == 0)
     return NULL;
   if (nops == 3)
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 
f4806073f48d4dedea3ac9bd855792b152d78919..38f4680d45ab80e8f86327327c13667d96bc5bea
 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -5032,9 +5032,7 @@ vectorizable_conversion (vec_info *vinfo,
   else
     return false;
 
-  bool widen_arith = (code == WIDEN_PLUS_EXPR
-                || code == WIDEN_MINUS_EXPR
-                || code == WIDEN_MULT_EXPR
+  bool widen_arith = (code == WIDEN_MULT_EXPR
                 || code == WIDEN_LSHIFT_EXPR
                 || code == IFN_VEC_WIDEN_PLUS
                 || code == IFN_VEC_WIDEN_MINUS);
@@ -5086,8 +5084,6 @@ vectorizable_conversion (vec_info *vinfo,
     {
       gcc_assert (code == WIDEN_MULT_EXPR
                  || code == WIDEN_LSHIFT_EXPR
-                 || code == WIDEN_PLUS_EXPR
-                 || code == WIDEN_MINUS_EXPR
                  || code == IFN_VEC_WIDEN_PLUS
                  || code == IFN_VEC_WIDEN_MINUS);
 
@@ -12211,7 +12207,7 @@ supportable_widening_operation (vec_info *vinfo,
   class loop *vect_loop = NULL;
   machine_mode vec_mode;
   enum insn_code icode1, icode2;
-  optab optab1, optab2;
+  optab optab1 = unknown_optab, optab2 = unknown_optab;
   tree vectype = vectype_in;
   tree wide_vectype = vectype_out;
   code_helper c1 = MAX_TREE_CODES, c2 = MAX_TREE_CODES;
@@ -12305,16 +12301,6 @@ supportable_widening_operation (vec_info *vinfo,
       c2 = VEC_WIDEN_LSHIFT_HI_EXPR;
       break;
 
-    case WIDEN_PLUS_EXPR:
-      c1 = VEC_WIDEN_PLUS_LO_EXPR;
-      c2 = VEC_WIDEN_PLUS_HI_EXPR;
-      break;
-
-    case WIDEN_MINUS_EXPR:
-      c1 = VEC_WIDEN_MINUS_LO_EXPR;
-      c2 = VEC_WIDEN_MINUS_HI_EXPR;
-      break;
-
     CASE_CONVERT:
       c1 = VEC_UNPACK_LO_EXPR;
       c2 = VEC_UNPACK_HI_EXPR;
diff --git a/gcc/tree.def b/gcc/tree.def
index 
ee02754354f015a16737c7e879d89c3e3be0d5aa..a58e608a90078818a7ade9d1173ac7ec84c48c7a
 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1374,15 +1374,16 @@ DEFTREECODE (DOT_PROD_EXPR, "dot_prod_expr", 
tcc_expression, 3)
 DEFTREECODE (WIDEN_SUM_EXPR, "widen_sum_expr", tcc_binary, 2)
 
 /* Widening sad (sum of absolute differences).
-   The first two arguments are of type t1 which should be integer.
-   The third argument and the result are of type t2, such that t2 is at least
-   twice the size of t1.  Like DOT_PROD_EXPR, SAD_EXPR (arg1,arg2,arg3) is
+   The first two arguments are of type t1 which should be a vector of integers.
+   The third argument and the result are of type t2, such that the size of
+   the elements of t2 is at least twice the size of the elements of t1.
+   Like DOT_PROD_EXPR, SAD_EXPR (arg1,arg2,arg3) is
    equivalent to:
-       tmp = WIDEN_MINUS_EXPR (arg1, arg2)
+       tmp = IFN_VEC_WIDEN_MINUS_EXPR (arg1, arg2)
        tmp2 = ABS_EXPR (tmp)
        arg3 = PLUS_EXPR (tmp2, arg3)
   or:
-       tmp = WIDEN_MINUS_EXPR (arg1, arg2)
+       tmp = IFN_VEC_WIDEN_MINUS_EXPR (arg1, arg2)
        tmp2 = ABS_EXPR (tmp)
        arg3 = WIDEN_SUM_EXPR (tmp2, arg3)
  */
@@ -1421,8 +1422,6 @@ DEFTREECODE (WIDEN_MULT_MINUS_EXPR, 
"widen_mult_minus_expr", tcc_expression, 3)
    the first argument from type t1 to type t2, and then shifting it
    by the second argument.  */
 DEFTREECODE (WIDEN_LSHIFT_EXPR, "widen_lshift_expr", tcc_binary, 2)
-DEFTREECODE (WIDEN_PLUS_EXPR, "widen_plus_expr", tcc_binary, 2)
-DEFTREECODE (WIDEN_MINUS_EXPR, "widen_minus_expr", tcc_binary, 2)
 
 /* Widening vector multiplication.
    The two operands are vectors with N elements of size S. Multiplying the
@@ -1487,10 +1486,6 @@ DEFTREECODE (VEC_PACK_FLOAT_EXPR, "vec_pack_float_expr", 
tcc_binary, 2)
  */
 DEFTREECODE (VEC_WIDEN_LSHIFT_HI_EXPR, "widen_lshift_hi_expr", tcc_binary, 2)
 DEFTREECODE (VEC_WIDEN_LSHIFT_LO_EXPR, "widen_lshift_lo_expr", tcc_binary, 2)
-DEFTREECODE (VEC_WIDEN_PLUS_HI_EXPR, "widen_plus_hi_expr", tcc_binary, 2)
-DEFTREECODE (VEC_WIDEN_PLUS_LO_EXPR, "widen_plus_lo_expr", tcc_binary, 2)
-DEFTREECODE (VEC_WIDEN_MINUS_HI_EXPR, "widen_minus_hi_expr", tcc_binary, 2)
-DEFTREECODE (VEC_WIDEN_MINUS_LO_EXPR, "widen_minus_lo_expr", tcc_binary, 2)
 
 /* PREDICT_EXPR.  Specify hint for branch prediction.  The
    PREDICT_EXPR_PREDICTOR specify predictor and PREDICT_EXPR_OUTCOME the

Reply via email to