From 5959fa8ee9fe04595d70bd271e75959bdc20a9f1 Mon Sep 17 00:00:00 2001
From: Joel Hutton <joel.hutton@arm.com>
Date: Wed, 25 Aug 2021 14:31:15 +0100
Subject: [PATCH 1/3] [vect-patterns] Refactor to allow internal_fn's

Hi all,

This refactor allows widening patterns (such as widen_plus/widen_minus) to be represented as
either internal_fns or tree_codes.

[vect-patterns] Refactor as internal_fn's

Refactor vect-patterns to allow patterns to be internal_fns starting
with widening_plus/minus patterns

gcc/ChangeLog:

	* gimple-match.h (class code_helper): Move code_helper class to more
    visible function.
	* internal-fn.h (internal_fn_name): Add internal_fn range check.
	* tree-vect-patterns.c (vect_recog_widen_op_pattern): Change
    function prototype.
	* tree-vect-stmts.c (vect_gen_widened_results_half): Refactor to use
    code_helper, build internal_fns
	(vect_create_vectorized_promotion_stmts): Refactor to use
    code_helper.
	(vectorizable_conversion): Refactor to use code_helper.
	(supportable_widening_operation): Refactor to use code_helper.
	(supportable_narrowing_operation): Refactor to use code_helper.
	* tree-vectorizer.h (supportable_widening_operation): Refactor to use code_helper.
	(supportable_narrowing_operation): Refactor to use code_helper.
	* tree.h (class code_helper):  Refactor to use code_helper.
---
 gcc/gimple-match.h       |  17 ----
 gcc/internal-fn.h        |   1 +
 gcc/tree-vect-patterns.c |  21 ++--
 gcc/tree-vect-stmts.c    | 206 ++++++++++++++++++++++-----------------
 gcc/tree-vectorizer.h    |  15 +--
 gcc/tree.h               |  26 +++++
 6 files changed, 170 insertions(+), 116 deletions(-)

diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
index 2d4ea476076..40033ac7524 100644
--- a/gcc/gimple-match.h
+++ b/gcc/gimple-match.h
@@ -23,23 +23,6 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_GIMPLE_MATCH_H
 
 
-/* Helper to transparently allow tree codes and builtin function codes
-   exist in one storage entity.  */
-class code_helper
-{
-public:
-  code_helper () {}
-  code_helper (tree_code code) : rep ((int) code) {}
-  code_helper (combined_fn fn) : rep (-(int) fn) {}
-  operator tree_code () const { return (tree_code) rep; }
-  operator combined_fn () const { return (combined_fn) -rep; }
-  bool is_tree_code () const { return rep > 0; }
-  bool is_fn_code () const { return rep < 0; }
-  int get_rep () const { return rep; }
-private:
-  int rep;
-};
-
 /* Represents the condition under which an operation should happen,
    and the value to use otherwise.  The condition applies elementwise
    (as for VEC_COND_EXPR) if the values are vectors.  */
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index 19d0f849a5a..06e540ffd6a 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -108,6 +108,7 @@ extern const char *const internal_fn_name_array[];
 static inline const char *
 internal_fn_name (enum internal_fn fn)
 {
+  gcc_assert ((int) fn <= (int) IFN_NOP);
   return internal_fn_name_array[(int) fn];
 }
 
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c
index 854cbcff390..4a8ea67e62f 100644
--- a/gcc/tree-vect-patterns.c
+++ b/gcc/tree-vect-patterns.c
@@ -1245,7 +1245,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_or_ifn,
 			     bool shift_p, const char *name)
 {
   gimple *last_stmt = last_stmt_info->stmt;
@@ -1288,15 +1288,16 @@ vect_recog_widen_op_pattern (vec_info *vinfo,
       vecctype = get_vectype_for_scalar_type (vinfo, ctype);
     }
 
-  enum tree_code dummy_code;
+  code_helper dummy_c_or_ifn;
   int dummy_int;
   auto_vec<tree> dummy_vec;
   if (!vectype
       || !vecitype
       || !vecctype
-      || !supportable_widening_operation (vinfo, wide_code, last_stmt_info,
+      || !supportable_widening_operation (vinfo, wide_code_or_ifn,
+					  last_stmt_info,
 					  vecitype, vectype,
-					  &dummy_code, &dummy_code,
+					  &dummy_c_or_ifn, &dummy_c_or_ifn,
 					  &dummy_int, &dummy_vec))
     return NULL;
 
@@ -1309,8 +1310,16 @@ 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;
+  if (wide_code_or_ifn.is_tree_code ())
+    pattern_stmt = gimple_build_assign (var, wide_code_or_ifn,
+						oprnd[0], oprnd[1]);
+  else
+    {
+      internal_fn fn = as_internal_fn ((combined_fn) wide_code_or_ifn);
+      pattern_stmt = gimple_build_call_internal (fn, 2, oprnd[0], oprnd[1]);
+      gimple_call_set_lhs (pattern_stmt, var);
+    }
 
   if (vecctype != vecitype)
     pattern_stmt = vect_convert_output (vinfo, last_stmt_info, ctype,
diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c
index 2284ad069e4..d5e1619fabc 100644
--- a/gcc/tree-vect-stmts.c
+++ b/gcc/tree-vect-stmts.c
@@ -4504,7 +4504,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)
@@ -4513,14 +4513,16 @@ 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);
+  if (ch.is_tree_code ())
+    new_stmt = gimple_build_assign (vec_dest, ch, vec_oprnd0, vec_oprnd1);
+  else
+    new_stmt = gimple_build_call_internal (as_internal_fn ((combined_fn) ch),
+					   2, 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;
 }
 
@@ -4597,8 +4599,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;
@@ -4614,10 +4616,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))
@@ -4714,8 +4716,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 code1;
+  code_helper code_or_ifn, code_or_ifn1, code_or_ifn2;
+  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;
@@ -4744,31 +4747,28 @@ 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)
-    return false;
+  if (is_gimple_assign (stmt))
+  {
+    code_or_ifn = gimple_assign_rhs_code (stmt);
+  }
+  else
+    code_or_ifn = gimple_call_combined_fn (stmt);
 
-  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;
 
-  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);
+  bool widen_arith = (code_or_ifn == WIDEN_PLUS_EXPR
+		      || code_or_ifn == WIDEN_MINUS_EXPR
+		      || code_or_ifn == WIDEN_MULT_EXPR
+		      || code_or_ifn == WIDEN_LSHIFT_EXPR);
+  op_type = TREE_CODE_LENGTH ((tree_code) code_or_ifn);
 
   /* 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);
 
@@ -4784,7 +4784,8 @@ vectorizable_conversion (vec_info *vinfo,
     }
 
   rhs_type = TREE_TYPE (op0);
-  if ((code != FIX_TRUNC_EXPR && code != FLOAT_EXPR)
+  if ((code_or_ifn.is_tree_code () && code_or_ifn != FIX_TRUNC_EXPR
+       && code_or_ifn != FLOAT_EXPR)
       && !((INTEGRAL_TYPE_P (lhs_type)
 	    && INTEGRAL_TYPE_P (rhs_type))
 	   || (SCALAR_FLOAT_TYPE_P (lhs_type)
@@ -4806,10 +4807,16 @@ 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_or_ifn == WIDEN_MULT_EXPR
+		  || code_or_ifn == WIDEN_LSHIFT_EXPR
+		  || code_or_ifn == WIDEN_PLUS_EXPR
+		  || code_or_ifn == WIDEN_MINUS_EXPR);
+
+      if (is_gimple_assign (stmt))
+	op1 = gimple_assign_rhs2 (stmt);
+      else
+	op1 = gimple_call_arg (stmt, 1);
 
-      op1 = gimple_assign_rhs2 (stmt);
       tree vectype1_in;
       if (!vect_is_simple_use (vinfo, stmt_info, slp_node, 1,
 			       &op1, &slp_op1, &dt[1], &vectype1_in))
@@ -4889,12 +4896,16 @@ vectorizable_conversion (vec_info *vinfo,
   switch (modifier)
     {
     case NONE:
-      if (code != FIX_TRUNC_EXPR
-	  && code != FLOAT_EXPR
-	  && !CONVERT_EXPR_CODE_P (code))
+      if (code_or_ifn != FIX_TRUNC_EXPR
+	  && code_or_ifn != FLOAT_EXPR
+	  && !CONVERT_EXPR_CODE_P (code_or_ifn))
 	return false;
-      if (supportable_convert_operation (code, vectype_out, vectype_in, &code1))
+      if (supportable_convert_operation (code_or_ifn, vectype_out, vectype_in,
+					 &code1))
+      {
+	code_or_ifn1 = code1;
 	break;
+      }
       /* FALLTHRU */
     unsupported:
       if (dump_enabled_p ())
@@ -4905,16 +4916,17 @@ 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_or_ifn, vectype_out,
+						   vectype_in,
+						   (tree_code*) &code_or_ifn1))
 	    goto unsupported;
 	  gcc_assert (!(multi_step_cvt && op_type == binary_op));
 	  break;
 	}
-      if (supportable_widening_operation (vinfo, code, stmt_info,
-					       vectype_out, vectype_in, &code1,
-					       &code2, &multi_step_cvt,
-					       &interm_types))
+      if (supportable_widening_operation (vinfo, code_or_ifn, stmt_info,
+					       vectype_out, vectype_in,
+					       &code_or_ifn1, &code_or_ifn2,
+					       &multi_step_cvt, &interm_types))
 	{
 	  /* Binary widening operation can only be supported directly by the
 	     architecture.  */
@@ -4922,7 +4934,7 @@ vectorizable_conversion (vec_info *vinfo,
 	  break;
 	}
 
-      if (code != FLOAT_EXPR
+      if (code_or_ifn != FLOAT_EXPR
 	  || GET_MODE_SIZE (lhs_mode) <= GET_MODE_SIZE (rhs_mode))
 	goto unsupported;
 
@@ -4941,14 +4953,16 @@ vectorizable_conversion (vec_info *vinfo,
 
 	  if (GET_MODE_SIZE (rhs_mode) == fltsz)
 	    {
-	      if (!supportable_convert_operation (code, vectype_out,
-						  cvt_type, &codecvt1))
+	      tree_code code;
+	      if (!supportable_convert_operation (code_or_ifn, vectype_out,
+						  cvt_type, &code))
 		goto unsupported;
+	      codecvt1 = code;
 	    }
-	  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_or_ifn,
+						    stmt_info, vectype_out,
+						    cvt_type, &codecvt1,
+						    &codecvt2, &multi_step_cvt,
 						    &interm_types))
 	    continue;
 	  else
@@ -4956,8 +4970,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, &code_or_ifn1,
+					      &code_or_ifn2, &multi_step_cvt,
+					      &interm_types))
 	    {
 	      found_mode = true;
 	      break;
@@ -4979,12 +4994,13 @@ 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_or_ifn, vectype_out,
+					   vectype_in,
+					   &code_or_ifn1, &multi_step_cvt,
 					   &interm_types))
 	break;
 
-      if (code != FIX_TRUNC_EXPR
+      if (code_or_ifn != FIX_TRUNC_EXPR
 	  || GET_MODE_SIZE (lhs_mode) >= GET_MODE_SIZE (rhs_mode))
 	goto unsupported;
 
@@ -4993,11 +5009,11 @@ 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_or_ifn, cvt_type, vectype_in,
+					  (tree_code*) &codecvt1))
 	goto unsupported;
       if (supportable_narrowing_operation (NOP_EXPR, vectype_out, cvt_type,
-					   &code1, &multi_step_cvt,
+					   &code_or_ifn1, &multi_step_cvt,
 					   &interm_types))
 	break;
       goto unsupported;
@@ -5113,8 +5129,9 @@ 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) code_or_ifn1) == unary_op);
+	  gassign *new_stmt = gimple_build_assign (vec_dest, code_or_ifn1,
+						   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);
@@ -5133,9 +5150,9 @@ vectorizable_conversion (vec_info *vinfo,
 	 the vector stmt by a factor VF/nunits.  */
       vect_get_vec_defs (vinfo, stmt_info, slp_node, ncopies * ninputs,
 			 op0, &vec_oprnds0,
-			 code == WIDEN_LSHIFT_EXPR ? NULL_TREE : op1,
+			 code_or_ifn == WIDEN_LSHIFT_EXPR ? NULL_TREE : op1,
 			 &vec_oprnds1);
-      if (code == WIDEN_LSHIFT_EXPR)
+      if (code_or_ifn == WIDEN_LSHIFT_EXPR)
 	{
 	  int oprnds_size = vec_oprnds0.length ();
 	  vec_oprnds1.create (oprnds_size);
@@ -5146,7 +5163,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 = code_or_ifn1, c2 = code_or_ifn2;
 	  if (i == 0 && codecvt2 != ERROR_MARK)
 	    {
 	      c1 = codecvt1;
@@ -5169,7 +5186,7 @@ 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);
 	      vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
@@ -5195,7 +5212,7 @@ 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);
@@ -5206,7 +5223,7 @@ 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, code_or_ifn1);
       break;
     }
   if (!slp_node)
@@ -11721,9 +11738,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_or_ifn,
+				stmt_vec_info stmt_info,
 				tree vectype_out, tree vectype_in,
-                                enum tree_code *code1, enum tree_code *code2,
+				code_helper *code_or_ifn1,
+				code_helper *code_or_ifn2,
                                 int *multi_step_cvt,
                                 vec<tree> *interm_types)
 {
@@ -11734,7 +11753,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;
@@ -11744,7 +11763,7 @@ supportable_widening_operation (vec_info *vinfo,
   if (loop_info)
     vect_loop = LOOP_VINFO_LOOP (loop_info);
 
-  switch (code)
+  switch (code_or_ifn.as_tree_code ())
     {
     case WIDEN_MULT_EXPR:
       /* The result of a vectorized widening operation usually requires
@@ -11773,7 +11792,7 @@ supportable_widening_operation (vec_info *vinfo,
 	 vectorization.  */
       /* TODO: Another case in which order doesn't *really* matter is when we
 	 widen and then contract again, e.g. (short)((int)x * y >> 8).
-	 Normally, pack_trunc performs an even/odd permute, whereas the 
+	 Normally, pack_trunc performs an even/odd permute, whereas the
 	 repack from an even/odd expansion would be an interleave, which
 	 would be significantly simpler for e.g. AVX2.  */
       /* In any case, in order to avoid duplicating the code below, recurse
@@ -11785,20 +11804,22 @@ 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))
-        {
-          /* Elements in a vector with vect_used_by_reduction property cannot
-             be reordered if the use chain with this property does not have the
-             same operation.  One such an example is s += a * b, where elements
-             in a and b cannot be reordered.  Here we check if the vector defined
-             by STMT is only directly used in the reduction statement.  */
+					     vectype_in, code_or_ifn1,
+					     code_or_ifn2, 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 same operation.  One such an example is s += a * b, where
+	     elements in a and b cannot be reordered.  Here we check if the
+	     vector defined by STMT is only directly used in the reduction
+	     statement.  */
 	  tree lhs = gimple_assign_lhs (stmt_info->stmt);
 	  stmt_vec_info use_stmt_info = loop_info->lookup_single_use (lhs);
 	  if (use_stmt_info
 	      && STMT_VINFO_DEF_TYPE (use_stmt_info) == vect_reduction_def)
 	    return true;
-        }
+	}
       c1 = VEC_WIDEN_MULT_LO_EXPR;
       c2 = VEC_WIDEN_MULT_HI_EXPR;
       break;
@@ -11849,6 +11870,17 @@ supportable_widening_operation (vec_info *vinfo,
       c2 = VEC_UNPACK_FIX_TRUNC_HI_EXPR;
       break;
 
+    case MAX_TREE_CODES:
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  switch (code_or_ifn.as_fn_code ())
+    {
+    case CFN_LAST:
+      break;
     default:
       gcc_unreachable ();
     }
@@ -11856,13 +11888,13 @@ supportable_widening_operation (vec_info *vinfo,
   if (BYTES_BIG_ENDIAN && c1 != VEC_WIDEN_MULT_EVEN_EXPR)
     std::swap (c1, c2);
 
-  if (code == FIX_TRUNC_EXPR)
+  if (code_or_ifn == 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);
     }
-  else if (CONVERT_EXPR_CODE_P (code)
+  else if (CONVERT_EXPR_CODE_P ((tree_code) code_or_ifn)
 	   && VECTOR_BOOLEAN_TYPE_P (wide_vectype)
 	   && VECTOR_BOOLEAN_TYPE_P (vectype)
 	   && TYPE_MODE (wide_vectype) == TYPE_MODE (vectype)
@@ -11887,8 +11919,8 @@ supportable_widening_operation (vec_info *vinfo,
        || (icode2 = optab_handler (optab2, vec_mode)) == CODE_FOR_nothing)
     return false;
 
-  *code1 = c1;
-  *code2 = c2;
+  *code_or_ifn1 = c1;
+  *code_or_ifn2 = c2;
 
   if (insn_data[icode1].operand[0].mode == TYPE_MODE (wide_vectype)
       && insn_data[icode2].operand[0].mode == TYPE_MODE (wide_vectype))
@@ -11909,7 +11941,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_or_ifn))
     return false;
 
   /* We assume here that there will not be more than MAX_INTERM_CVT_STEPS
@@ -11976,7 +12008,6 @@ supportable_widening_operation (vec_info *vinfo,
   return false;
 }
 
-
 /* Function supportable_narrowing_operation
 
    Check whether an operation represented by the code CODE is a
@@ -12000,7 +12031,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,
+				 void* _code1, int *multi_step_cvt,
                                  vec<tree> *interm_types)
 {
   machine_mode vec_mode;
@@ -12013,6 +12044,7 @@ supportable_narrowing_operation (enum tree_code code,
   machine_mode intermediate_mode, prev_mode;
   int i;
   bool uns;
+  tree_code * code1 = (tree_code*) _code1;
 
   *multi_step_cvt = 0;
   switch (code)
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index bd6f334d15f..70c06264c11 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -2030,13 +2030,16 @@ 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 *vinfo,
+				code_helper code_or_ifn,
+				stmt_vec_info stmt_info,
+				tree vectype_out, tree vectype_in,
+				code_helper *code_or_ifn1,
+				code_helper *code_or_ifn2,
+				int *multi_step_cvt,
+				vec<tree> *interm_types);
 extern bool supportable_narrowing_operation (enum tree_code, tree, tree,
-					     enum tree_code *, int *,
+					     void *, int *,
 					     vec<tree> *);
 
 extern unsigned record_stmt_cost (stmt_vector_for_cost *, int,
diff --git a/gcc/tree.h b/gcc/tree.h
index f62c00bc870..346565f84ce 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -6546,5 +6546,31 @@ extern unsigned fndecl_dealloc_argno (tree);
    if nonnull, set the second argument to the referenced enclosing
    object or pointer.  Otherwise return null.  */
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
+/* Helper to transparently allow tree codes and builtin function codes
+   exist in one storage entity.  */
+class code_helper
+{
+public:
+  code_helper () {}
+  code_helper (tree_code code) : rep ((int) code) {}
+  code_helper (combined_fn fn) : rep (-(int) fn) {}
+  operator tree_code () const { return is_tree_code () ?
+						       (tree_code) rep :
+						       ERROR_MARK; }
+  operator combined_fn () const { return is_fn_code () ?
+						       (combined_fn) -rep:
+						       CFN_LAST; }
+  bool is_tree_code () const { return rep > 0; }
+  bool is_fn_code () const { return rep < 0; }
+  int get_rep () const { return rep; }
+
+  enum tree_code as_tree_code () const { return is_tree_code () ?
+    (tree_code)* this : MAX_TREE_CODES; }
+  combined_fn as_fn_code () const { return is_fn_code () ? (combined_fn) *this
+    : CFN_LAST;}
+
+private:
+  int rep;
+};
 
 #endif  /* GCC_TREE_H  */
-- 
2.17.1

