rs6000: Refactor altivec_build_resolved_builtin

Message ID a76ba493-290a-fa69-fa27-53226abbc781@linux.ibm.com
State New
Headers
Series rs6000: Refactor altivec_build_resolved_builtin |

Commit Message

Li, Pan2 via Gcc-patches Dec. 15, 2021, 2:21 p.m. UTC
  Hi!

While replacing the built-in machinery, we agreed to defer some necessary
refactoring of the overload processing.  This patch cleans it up considerably.

I've put in one FIXME for an additional level of cleanup that should be done
independently.  The various helper functions (resolve_VEC_*) can be simplified
if we move the argument processing in altivec_resolve_overloaded_builtin
earlier.  But this requires making nontrivial changes to those functions that
will need careful review.  Let's do that in a later patch.

Bootstrapped and tested on powerpc64le-linux-gnu with no regressions.  Is this
okay for trunk?

Thanks!
Bill


2021-12-09  Bill Schmidt  <wschmidt@linux.ibm.com>

gcc/
	* config/rs6000/rs6000-c.c (resolution): New enum.
	(resolve_VEC_MUL): New function.
	(resolve_VEC_CMPNE): Likewise.
	(resolve_VEC_ADDE_SUBE): Likewise.
	(resolve_VEC_ADDEC_SUBEC): Likewise.
	(resolve_VEC_SPLATS): Likewise.
	(resolve_VEC_EXTRACT): Likewise.
	(resolve_VEC_INSERT): Likewise.
	(resolve_VEC_STEP): Likewise.
	(find_instance): Likewise.
	(altivec_resolve_overloaded_builtin): Many cleanups:  Call factored-out
	functions.  Move variable declarations closer to uses.  Add commentary.
	Remove unnecessary levels of braces.  Avoid use of gotos.  Change
	misleading variable names.  Use switches over if-else-if chains.
---
 gcc/config/rs6000/rs6000-c.c | 1835 +++++++++++++++++++---------------
 1 file changed, 1004 insertions(+), 831 deletions(-)
  

Comments

Segher Boessenkool Dec. 15, 2021, 6:16 p.m. UTC | #1
Hi!

On Wed, Dec 15, 2021 at 08:21:24AM -0600, Bill Schmidt wrote:
> While replacing the built-in machinery, we agreed to defer some necessary
> refactoring of the overload processing.  This patch cleans it up considerably.
> 
> I've put in one FIXME for an additional level of cleanup that should be done
> independently.  The various helper functions (resolve_VEC_*) can be simplified
> if we move the argument processing in altivec_resolve_overloaded_builtin
> earlier.  But this requires making nontrivial changes to those functions that
> will need careful review.  Let's do that in a later patch.

All these names should be lower case.  If you promise to do that in the
aforementioned cleanup (and it happens before GCC 12), then okay for
trunk.  Thanks!

Very minor stuff below.

> 	* config/rs6000/rs6000-c.c (resolution): New enum.
> 	(resolve_VEC_MUL): New function.
> 	(resolve_VEC_CMPNE): Likewise.
> 	(resolve_VEC_ADDE_SUBE): Likewise.
> 	(resolve_VEC_ADDEC_SUBEC): Likewise.
> 	(resolve_VEC_SPLATS): Likewise.
> 	(resolve_VEC_EXTRACT): Likewise.
> 	(resolve_VEC_INSERT): Likewise.
> 	(resolve_VEC_STEP): Likewise.
> 	(find_instance): Likewise.
> 	(altivec_resolve_overloaded_builtin): Many cleanups:  Call factored-out

No two spaces after colon, no capital after colon.  You probably want
a full stop though?

> 	functions.  Move variable declarations closer to uses.  Add commentary.
> 	Remove unnecessary levels of braces.  Avoid use of gotos.  Change
> 	misleading variable names.  Use switches over if-else-if chains.

> +	  /* Note:  vec_nand also works but opt changes vec_nand's
> +	     to vec_nor's anyway.  */

Maybe there should be a vec_not?  There is one at the RTL level (called
one_cmpl<mode>2).

> +	    decl= rs6000_builtin_decls[RS6000_OVLD_VEC_NOR];

Missing space btw.

> -   support Altivec's overloaded builtins.  FIXME: This code needs
> -   to be brutally factored.  */

Yay :-)

>    /* Return immediately if this isn't an overload.  */
> +  rs6000_gen_builtins fcode
> +    = (rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
> +
>    if (fcode <= RS6000_OVLD_NONE)
>      return NULL_TREE;

The new code should be before this comment?  I don't see how the comment
makes much sense like this.

> +  /* Some overloads require special handling.  */
> +  /* FIXME: Could we simplify the helper functions if we gathered arguments
> +     and types into arrays first?  */

A lot of the argument checking can be handled more generically.  If
there are many exceptions to that it will not be useful, but a bit of
it would make sense.

If you do that (and maybe similar things as well) this array question
will swivel.

> -      if (TREE_CODE (arg0_type) != VECTOR_TYPE)
> -	goto bad;
> -      if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
> -	goto bad;

Yay, all gotos should go :-)

Thanks again,


Segher
  
Li, Pan2 via Gcc-patches Dec. 15, 2021, 9:45 p.m. UTC | #2
Hi!

On 12/15/21 12:16 PM, Segher Boessenkool wrote:
>> +	  /* Note:  vec_nand also works but opt changes vec_nand's
>> +	     to vec_nor's anyway.  */
> Maybe there should be a vec_not?  There is one at the RTL level (called
> one_cmpl<mode>2).

As I recall, we have an issue open for this already... but nobody's grabbed it yet.

Thanks for the review!

(I'll change all those VEC_* things to lower-case.)

Bill
  

Patch

diff --git a/gcc/config/rs6000/rs6000-c.c b/gcc/config/rs6000/rs6000-c.c
index e0ebdeed548..45f485aab44 100644
--- a/gcc/config/rs6000/rs6000-c.c
+++ b/gcc/config/rs6000/rs6000-c.c
@@ -928,28 +928,847 @@  altivec_build_resolved_builtin (tree *args, int n, tree fntype, tree ret_type,
   return fold_convert (ret_type, call);
 }
 
+/* Enumeration of possible results from attempted overload resolution.
+   This is used by special-case helper functions to tell their caller
+   whether they succeeded and what still needs to be done.
+
+	unresolved = Still needs processing
+	  resolved = Resolved (but may be an error_mark_node)
+      resolved_bad = An error that needs handling by the caller.  */
+
+enum resolution { unresolved, resolved, resolved_bad };
+
+/* Resolve an overloaded vec_mul call and return a tree expression for the
+   resolved call if successful.  NARGS is the number of arguments to the call.
+   ARGLIST contains the arguments.  RES must be set to indicate the status of
+   the resolution attempt.  LOC contains statement location information.  */
+
+static tree
+resolve_VEC_MUL (resolution *res, vec<tree, va_gc> *arglist, unsigned nargs,
+		 location_t loc)
+{
+  /* vec_mul needs to be special cased because there are no instructions for it
+     for the {un}signed char, {un}signed short, and {un}signed int types.  */
+  if (nargs != 2)
+    {
+      error ("builtin %qs only accepts 2 arguments", "vec_mul");
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
+
+  /* Both arguments must be vectors and the types must be compatible.  */
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE
+      || !lang_hooks.types_compatible_p (arg0_type, arg1_type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  switch (TYPE_MODE (TREE_TYPE (arg0_type)))
+    {
+    case E_QImode:
+    case E_HImode:
+    case E_SImode:
+    case E_DImode:
+    case E_TImode:
+      /* For scalar types just use a multiply expression.  */
+      *res = resolved;
+      return fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (arg0), arg0,
+			      fold_convert (TREE_TYPE (arg0), arg1));
+    case E_SFmode:
+      {
+	/* For floats use the xvmulsp instruction directly.  */
+	*res = resolved;
+	tree call = rs6000_builtin_decls[RS6000_BIF_XVMULSP];
+	return build_call_expr (call, 2, arg0, arg1);
+      }
+    case E_DFmode:
+      {
+	/* For doubles use the xvmuldp instruction directly.  */
+	*res = resolved;
+	tree call = rs6000_builtin_decls[RS6000_BIF_XVMULDP];
+	return build_call_expr (call, 2, arg0, arg1);
+      }
+    /* Other types are errors.  */
+    default:
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+}
+
+/* Resolve an overloaded vec_cmpne call and return a tree expression for the
+   resolved call if successful.  NARGS is the number of arguments to the call.
+   ARGLIST contains the arguments.  RES must be set to indicate the status of
+   the resolution attempt.  LOC contains statement location information.  */
+
+static tree
+resolve_VEC_CMPNE (resolution *res, vec<tree, va_gc> *arglist, unsigned nargs,
+		   location_t loc)
+{
+  /* vec_cmpne needs to be special cased because there are no instructions
+     for it (prior to power 9).  */
+  if (nargs != 2)
+    {
+      error ("builtin %qs only accepts 2 arguments", "vec_cmpne");
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
+
+  /* Both arguments must be vectors and the types must be compatible.  */
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE
+      || !lang_hooks.types_compatible_p (arg0_type, arg1_type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  machine_mode arg0_elt_mode = TYPE_MODE (TREE_TYPE (arg0_type));
+
+  /* Power9 instructions provide the most efficient implementation of
+     ALTIVEC_BUILTIN_VEC_CMPNE if the mode is not DImode or TImode
+     or SFmode or DFmode.  */
+  if (!TARGET_P9_VECTOR
+      || arg0_elt_mode == DImode
+      || arg0_elt_mode == TImode
+      || arg0_elt_mode == SFmode
+      || arg0_elt_mode == DFmode)
+    {
+      switch (arg0_elt_mode)
+	{
+	  /* vec_cmpneq (va, vb) == vec_nor (vec_cmpeq (va, vb),
+					     vec_cmpeq (va, vb)).  */
+	  /* Note:  vec_nand also works but opt changes vec_nand's
+	     to vec_nor's anyway.  */
+	case E_QImode:
+	case E_HImode:
+	case E_SImode:
+	case E_DImode:
+	case E_TImode:
+	case E_SFmode:
+	case E_DFmode:
+	  {
+	    /* call = vec_cmpeq (va, vb)
+	       result = vec_nor (call, call).  */
+	    vec<tree, va_gc> *params = make_tree_vector ();
+	    vec_safe_push (params, arg0);
+	    vec_safe_push (params, arg1);
+	    tree decl = rs6000_builtin_decls[RS6000_OVLD_VEC_CMPEQ];
+	    tree call = altivec_resolve_overloaded_builtin (loc, decl, params);
+	    /* Use save_expr to ensure that operands used more than once
+	       that may have side effects (like calls) are only evaluated
+	       once.  */
+	    call = save_expr (call);
+	    params = make_tree_vector ();
+	    vec_safe_push (params, call);
+	    vec_safe_push (params, call);
+	    decl= rs6000_builtin_decls[RS6000_OVLD_VEC_NOR];
+	    *res = resolved;
+	    return altivec_resolve_overloaded_builtin (loc, decl, params);
+	  }
+	  /* Other types are errors.  */
+	default:
+	  *res = resolved_bad;
+	  return error_mark_node;
+	}
+    }
+
+  /* Otherwise this call is unresolved, and altivec_resolve_overloaded_builtin
+     will later process the Power9 alternative.  */
+  *res = unresolved;
+  return error_mark_node;
+}
+
+/* Resolve an overloaded vec_adde or vec_sube call and return a tree
+   expression for the resolved call if successful.  NARGS is the number of
+   arguments to the call.  ARGLIST contains the arguments.  RES must be set
+   to indicate the status of the resolution attempt.  LOC contains statement
+   location information.  */
+
+static tree
+resolve_VEC_ADDE_SUBE (resolution *res, rs6000_gen_builtins fcode,
+		       vec<tree, va_gc> *arglist, unsigned nargs,
+		       location_t loc)
+{
+  /* vec_adde needs to be special cased because there is no instruction
+     for the {un}signed int version.  */
+  if (nargs != 3)
+    {
+      const char *name;
+      name = fcode == RS6000_OVLD_VEC_ADDE ? "vec_adde" : "vec_sube";
+      error ("builtin %qs only accepts 3 arguments", name);
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
+  tree arg2 = (*arglist)[2];
+  tree arg2_type = TREE_TYPE (arg2);
+
+  /* All 3 arguments must be vectors of (signed or unsigned) (int or
+     __int128) and the types must be compatible.  */
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE
+      || !lang_hooks.types_compatible_p (arg0_type, arg1_type)
+      || !lang_hooks.types_compatible_p (arg1_type, arg2_type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  switch (TYPE_MODE (TREE_TYPE (arg0_type)))
+    {
+      /* For {un}signed ints,
+	 vec_adde (va, vb, carryv) == vec_add (vec_add (va, vb),
+					       vec_and (carryv, 1)).
+	 vec_sube (va, vb, carryv) == vec_sub (vec_sub (va, vb),
+					       vec_and (carryv, 1)).  */
+    case E_SImode:
+      {
+	vec<tree, va_gc> *params = make_tree_vector ();
+	vec_safe_push (params, arg0);
+	vec_safe_push (params, arg1);
+
+	tree add_sub_builtin;
+	if (fcode == RS6000_OVLD_VEC_ADDE)
+	  add_sub_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADD];
+	else
+	  add_sub_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUB];
+
+	tree call = altivec_resolve_overloaded_builtin (loc, add_sub_builtin,
+							params);
+	tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
+	tree ones_vector = build_vector_from_val (arg0_type, const1);
+	tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
+					 arg2, ones_vector);
+	params = make_tree_vector ();
+	vec_safe_push (params, call);
+	vec_safe_push (params, and_expr);
+	*res = resolved;
+	return altivec_resolve_overloaded_builtin (loc, add_sub_builtin,
+						   params);
+      }
+      /* For {un}signed __int128s use the vaddeuqm/vsubeuqm instruction
+	 directly using the standard machinery.  */
+    case E_TImode:
+      *res = unresolved;
+      break;
+
+      /* Types other than {un}signed int and {un}signed __int128
+	 are errors.  */
+    default:
+      *res = resolved_bad;
+    }
+
+  return error_mark_node;
+}
+
+/* Resolve an overloaded vec_addec or vec_subec call and return a tree
+   expression for the resolved call if successful.  NARGS is the number of
+   arguments to the call.  ARGLIST contains the arguments.  RES must be set
+   to indicate the status of the resolution attempt.  LOC contains statement
+   location information.  */
+
+static tree
+resolve_VEC_ADDEC_SUBEC (resolution *res, rs6000_gen_builtins fcode,
+			 vec<tree, va_gc> *arglist, unsigned nargs,
+			 location_t loc)
+{
+  /* vec_addec and vec_subec needs to be special cased because there is
+     no instruction for the (un)signed int version.  */
+  if (nargs != 3)
+    {
+      const char *name;
+      name = fcode == RS6000_OVLD_VEC_ADDEC ? "vec_addec" : "vec_subec";
+      error ("builtin %qs only accepts 3 arguments", name);
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
+  tree arg2 = (*arglist)[2];
+  tree arg2_type = TREE_TYPE (arg2);
+
+  /* All 3 arguments must be vectors of (signed or unsigned) (int or
+     __int128) and the types must be compatible.  */
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE
+      || !lang_hooks.types_compatible_p (arg0_type, arg1_type)
+      || !lang_hooks.types_compatible_p (arg1_type, arg2_type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  switch (TYPE_MODE (TREE_TYPE (arg0_type)))
+    {
+      /* For {un}signed ints,
+	   vec_addec (va, vb, carryv) ==
+	     vec_or (vec_addc (va, vb),
+		     vec_addc (vec_add (va, vb),
+			       vec_and (carryv, 0x1))).  */
+    case E_SImode:
+      {
+	/* Use save_expr to ensure that operands used more than once that may
+	   have side effects (like calls) are only evaluated once.  */
+	arg0 = save_expr (arg0);
+	arg1 = save_expr (arg1);
+	vec<tree, va_gc> *params = make_tree_vector ();
+	vec_safe_push (params, arg0);
+	vec_safe_push (params, arg1);
+
+	tree as_c_builtin;
+	if (fcode == RS6000_OVLD_VEC_ADDEC)
+	  as_c_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADDC];
+	else
+	  as_c_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUBC];
+
+	tree call1 = altivec_resolve_overloaded_builtin (loc, as_c_builtin,
+							 params);
+	params = make_tree_vector ();
+	vec_safe_push (params, arg0);
+	vec_safe_push (params, arg1);
+
+	tree as_builtin;
+	if (fcode == RS6000_OVLD_VEC_ADDEC)
+	  as_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADD];
+	else
+	  as_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUB];
+
+	tree call2 = altivec_resolve_overloaded_builtin (loc, as_builtin,
+							 params);
+	tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
+	tree ones_vector = build_vector_from_val (arg0_type, const1);
+	tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
+					 arg2, ones_vector);
+	params = make_tree_vector ();
+	vec_safe_push (params, call2);
+	vec_safe_push (params, and_expr);
+	call2 = altivec_resolve_overloaded_builtin (loc, as_c_builtin, params);
+	params = make_tree_vector ();
+	vec_safe_push (params, call1);
+	vec_safe_push (params, call2);
+	tree or_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_OR];
+	*res = resolved;
+	return altivec_resolve_overloaded_builtin (loc, or_builtin, params);
+      }
+      /* For {un}signed __int128s use the vaddecuq/vsubbecuq
+	 instructions.  This occurs through normal processing.  */
+    case E_TImode:
+      *res = unresolved;
+      break;
+
+      /* Types other than {un}signed int and {un}signed __int128
+	 are errors.  */
+    default:
+      *res = resolved_bad;
+    }
+
+  return error_mark_node;
+}
+
+/* Resolve an overloaded vec_splats or vec_promote call and return a tree
+   expression for the resolved call if successful.  NARGS is the number of
+   arguments to the call.  ARGLIST contains the arguments.  RES must be set
+   to indicate the status of the resolution attempt.  */
+
+static tree
+resolve_VEC_SPLATS (resolution *res, rs6000_gen_builtins fcode,
+		    vec<tree, va_gc> *arglist, unsigned nargs)
+{
+  const char *name;
+  name = fcode == RS6000_OVLD_VEC_SPLATS ? "vec_splats" : "vec_promote";
+
+  if (fcode == RS6000_OVLD_VEC_SPLATS && nargs != 1)
+    {
+      error ("builtin %qs only accepts 1 argument", name);
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  if (fcode == RS6000_OVLD_VEC_PROMOTE && nargs != 2)
+    {
+      error ("builtin %qs only accepts 2 arguments", name);
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  /* Ignore promote's element argument.  */
+  if (fcode == RS6000_OVLD_VEC_PROMOTE
+      && !INTEGRAL_TYPE_P (TREE_TYPE ((*arglist)[1])))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  tree arg = (*arglist)[0];
+  tree type = TREE_TYPE (arg);
+
+  if (!SCALAR_FLOAT_TYPE_P (type) && !INTEGRAL_TYPE_P (type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  bool unsigned_p = TYPE_UNSIGNED (type);
+  int size;
+
+  switch (TYPE_MODE (type))
+    {
+    case E_TImode:
+      type = unsigned_p ? unsigned_V1TI_type_node : V1TI_type_node;
+      size = 1;
+      break;
+    case E_DImode:
+      type = unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node;
+      size = 2;
+      break;
+    case E_SImode:
+      type = unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node;
+      size = 4;
+      break;
+    case E_HImode:
+      type = unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node;
+      size = 8;
+      break;
+    case E_QImode:
+      type = unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node;
+      size = 16;
+      break;
+    case E_SFmode:
+      type = V4SF_type_node;
+      size = 4;
+      break;
+    case E_DFmode:
+      type = V2DF_type_node;
+      size = 2;
+      break;
+    default:
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  arg = save_expr (fold_convert (TREE_TYPE (type), arg));
+  vec<constructor_elt, va_gc> *vec;
+  vec_alloc (vec, size);
+
+  for (int i = 0; i < size; i++)
+    {
+      constructor_elt elt = {NULL_TREE, arg};
+      vec->quick_push (elt);
+    }
+
+  *res = resolved;
+  return build_constructor (type, vec);
+}
+
+/* Resolve an overloaded vec_extract call and return a tree expression for
+   the resolved call if successful.  NARGS is the number of arguments to
+   the call.  ARGLIST contains the arguments.  RES must be set to indicate
+   the status of the resolution attempt.  LOC contains statement location
+   information.  */
+
+static tree
+resolve_VEC_EXTRACT (resolution *res, vec<tree, va_gc> *arglist,
+		     unsigned nargs, location_t loc)
+{
+  if (nargs != 2)
+    {
+      error ("builtin %qs only accepts 2 arguments", "vec_extract");
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg1 = (*arglist)[0];
+  tree arg1_type = TREE_TYPE (arg1);
+  tree arg2 = (*arglist)[1];
+
+  if (TREE_CODE (arg1_type) != VECTOR_TYPE
+      || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  /* See if we can optimize vec_extract with the current VSX instruction
+     set.  */
+  machine_mode mode = TYPE_MODE (arg1_type);
+  tree arg1_inner_type;
+
+  if (VECTOR_MEM_VSX_P (mode))
+    {
+      tree call = NULL_TREE;
+      int nunits = GET_MODE_NUNITS (mode);
+      arg2 = fold_for_warn (arg2);
+
+      /* If the second argument is an integer constant, generate
+	 the built-in code if we can.  We need 64-bit and direct
+	 move to extract the small integer vectors.  */
+      if (TREE_CODE (arg2) == INTEGER_CST)
+	{
+	  wide_int selector = wi::to_wide (arg2);
+	  selector = wi::umod_trunc (selector, nunits);
+	  arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
+	  switch (mode)
+	    {
+	    case E_V1TImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V1TI];
+	      break;
+
+	    case E_V2DFmode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DF];
+	      break;
+
+	    case E_V2DImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DI];
+	      break;
+
+	    case E_V4SFmode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SF];
+	      break;
+
+	    case E_V4SImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SI];
+	      break;
+
+	    case E_V8HImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
+	      break;
+
+	    case E_V16QImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+
+      /* If the second argument is variable, we can optimize it if we are
+	 generating 64-bit code on a machine with direct move.  */
+      else if (TREE_CODE (arg2) != INTEGER_CST && TARGET_DIRECT_MOVE_64BIT)
+	{
+	  switch (mode)
+	    {
+	    case E_V2DFmode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DF];
+	      break;
+
+	    case E_V2DImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DI];
+	      break;
+
+	    case E_V4SFmode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SF];
+	      break;
+
+	    case E_V4SImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SI];
+	      break;
+
+	    case E_V8HImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
+	      break;
+
+	    case E_V16QImode:
+	      call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+
+      if (call)
+	{
+	  tree result = build_call_expr (call, 2, arg1, arg2);
+	  /* Coerce the result to vector element type.  May be no-op.  */
+	  arg1_inner_type = TREE_TYPE (arg1_type);
+	  result = fold_convert (arg1_inner_type, result);
+	  *res = resolved;
+	  return result;
+	}
+    }
+
+  /* Build *(((arg1_inner_type*) & (vector type){arg1}) + arg2). */
+  arg1_inner_type = TREE_TYPE (arg1_type);
+  tree subp = build_int_cst (TREE_TYPE (arg2),
+			     TYPE_VECTOR_SUBPARTS (arg1_type) - 1);
+  arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2, subp, 0);
+
+  tree decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
+  DECL_EXTERNAL (decl) = 0;
+  TREE_PUBLIC (decl) = 0;
+  DECL_CONTEXT (decl) = current_function_decl;
+  TREE_USED (decl) = 1;
+  TREE_TYPE (decl) = arg1_type;
+  TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
+
+  tree stmt;
+  if (c_dialect_cxx ())
+    {
+      stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1, NULL_TREE, NULL_TREE);
+      SET_EXPR_LOCATION (stmt, loc);
+    }
+  else
+    {
+      DECL_INITIAL (decl) = arg1;
+      stmt = build1 (DECL_EXPR, arg1_type, decl);
+      TREE_ADDRESSABLE (decl) = 1;
+      SET_EXPR_LOCATION (stmt, loc);
+      stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
+    }
+
+  tree innerptrtype = build_pointer_type (arg1_inner_type);
+  stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
+  stmt = convert (innerptrtype, stmt);
+  stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
+  stmt = build_indirect_ref (loc, stmt, RO_NULL);
+
+  /* PR83660: We mark this as having side effects so that downstream in
+     fold_build_cleanup_point_expr () it will get a CLEANUP_POINT_EXPR.  If it
+     does not we can run into an ICE later in gimplify_cleanup_point_expr ().
+     Potentially this causes missed optimization because there actually is no
+     side effect.  */
+  if (c_dialect_cxx ())
+    TREE_SIDE_EFFECTS (stmt) = 1;
+
+  *res = resolved;
+  return stmt;
+}
+
+/* Resolve an overloaded vec_insert call and return a tree expression for
+   the resolved call if successful.  NARGS is the number of arguments to
+   the call.  ARGLIST contains the arguments.  RES must be set to indicate
+   the status of the resolution attempt.  LOC contains statement location
+   information.  */
+
+static tree
+resolve_VEC_INSERT (resolution *res, vec<tree, va_gc> *arglist,
+		    unsigned nargs, location_t loc)
+{
+  if (nargs != 3)
+    {
+      error ("builtin %qs only accepts 3 arguments", "vec_insert");
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
+  tree arg2 = fold_for_warn ((*arglist)[2]);
+
+  if (TREE_CODE (arg1_type) != VECTOR_TYPE
+      || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  /* If we can use the VSX xxpermdi instruction, use that for insert.  */
+  machine_mode mode = TYPE_MODE (arg1_type);
+
+  if ((mode == V2DFmode || mode == V2DImode)
+      && VECTOR_UNIT_VSX_P (mode)
+      && TREE_CODE (arg2) == INTEGER_CST)
+    {
+      wide_int selector = wi::to_wide (arg2);
+      selector = wi::umod_trunc (selector, 2);
+      arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
+
+      tree call = NULL_TREE;
+      if (mode == V2DFmode)
+	call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V2DF];
+      else if (mode == V2DImode)
+	call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V2DI];
+
+      /* Note, __builtin_vec_insert_<xxx> has vector and scalar types
+	 reversed.  */
+      if (call)
+	{
+	  *res = resolved;
+	  return build_call_expr (call, 3, arg1, arg0, arg2);
+	}
+    }
+
+  else if (mode == V1TImode
+	   && VECTOR_UNIT_VSX_P (mode)
+	   && TREE_CODE (arg2) == INTEGER_CST)
+    {
+      tree call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V1TI];
+      wide_int selector = wi::zero(32);
+      arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
+
+      /* Note, __builtin_vec_insert_<xxx> has vector and scalar types
+	 reversed.  */
+      *res = resolved;
+      return build_call_expr (call, 3, arg1, arg0, arg2);
+    }
+
+  /* Build *(((arg1_inner_type*) & (vector type){arg1}) + arg2) = arg0 with
+     VIEW_CONVERT_EXPR.  i.e.:
+       D.3192 = v1;
+       _1 = n & 3;
+       VIEW_CONVERT_EXPR<int[4]>(D.3192)[_1] = i;
+       v1 = D.3192;
+       D.3194 = v1;  */
+  if (TYPE_VECTOR_SUBPARTS (arg1_type) == 1)
+    arg2 = build_int_cst (TREE_TYPE (arg2), 0);
+  else
+    {
+      tree c = build_int_cst (TREE_TYPE (arg2),
+			      TYPE_VECTOR_SUBPARTS (arg1_type) - 1);
+      arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2, c, 0);
+    }
+
+  tree decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
+  DECL_EXTERNAL (decl) = 0;
+  TREE_PUBLIC (decl) = 0;
+  DECL_CONTEXT (decl) = current_function_decl;
+  TREE_USED (decl) = 1;
+  TREE_TYPE (decl) = arg1_type;
+  TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
+  TREE_ADDRESSABLE (decl) = 1;
+
+  tree stmt;
+  if (c_dialect_cxx ())
+    {
+      stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1, NULL_TREE, NULL_TREE);
+      SET_EXPR_LOCATION (stmt, loc);
+    }
+  else
+    {
+      DECL_INITIAL (decl) = arg1;
+      stmt = build1 (DECL_EXPR, arg1_type, decl);
+      SET_EXPR_LOCATION (stmt, loc);
+      stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
+    }
+
+  if (TARGET_VSX)
+    {
+      stmt = build_array_ref (loc, stmt, arg2);
+      stmt = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg0), stmt,
+			  convert (TREE_TYPE (stmt), arg0));
+      stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
+    }
+  else
+    {
+      tree arg1_inner_type = TREE_TYPE (arg1_type);
+      tree innerptrtype = build_pointer_type (arg1_inner_type);
+      stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
+      stmt = convert (innerptrtype, stmt);
+      stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
+      stmt = build_indirect_ref (loc, stmt, RO_NULL);
+      stmt = build2 (MODIFY_EXPR, TREE_TYPE (stmt), stmt,
+		     convert (TREE_TYPE (stmt), arg0));
+      stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
+    }
+
+  *res = resolved;
+  return stmt;
+}
+
+/* Resolve an overloaded vec_step call and return a tree expression for
+   the resolved call if successful.  NARGS is the number of arguments to
+   the call.  ARGLIST contains the arguments.  RES must be set to indicate
+   the status of the resolution attempt.  */
+
+static tree
+resolve_VEC_STEP (resolution *res, vec<tree, va_gc> *arglist, unsigned nargs)
+{
+  if (nargs != 1)
+    {
+      error ("builtin %qs only accepts 1 argument", "vec_step");
+      *res = resolved;
+      return error_mark_node;
+    }
+
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE)
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
+
+  *res = resolved;
+  return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (arg0_type));
+}
+
+/* Look for a matching instance in a chain of instances.  INSTANCE points to
+   the chain of instances; INSTANCE_CODE is the code identifying the specific
+   built-in being searched for; FCODE is the overloaded function code; TYPES
+   contains an array of two types that must match the types of the instance's
+   parameters; and ARGS contains an array of two arguments to be passed to
+   the instance.  If found, resolve the built-in and return it, unless the
+   built-in is not supported in context.  In that case, set
+   UNSUPPORTED_BUILTIN to true.  If we don't match, return error_mark_node
+   and leave UNSUPPORTED_BUILTIN alone.  */
+
+tree
+find_instance (bool *unsupported_builtin, ovlddata **instance,
+	       rs6000_gen_builtins instance_code,
+	       rs6000_gen_builtins fcode,
+	       tree *types, tree *args)
+{
+  while (*instance && (*instance)->bifid != instance_code)
+    *instance = (*instance)->next;
+
+  ovlddata *inst = *instance;
+  gcc_assert (inst != NULL);
+  tree fntype = rs6000_builtin_info[inst->bifid].fntype;
+  tree parmtype0 = TREE_VALUE (TYPE_ARG_TYPES (fntype));
+  tree parmtype1 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
+
+  if (rs6000_builtin_type_compatible (types[0], parmtype0)
+      && rs6000_builtin_type_compatible (types[1], parmtype1))
+    {
+      if (rs6000_builtin_decl (inst->bifid, false) != error_mark_node
+	  && rs6000_builtin_is_supported (inst->bifid))
+	{
+	  tree ret_type = TREE_TYPE (inst->fntype);
+	  return altivec_build_resolved_builtin (args, 2, fntype, ret_type,
+						 inst->bifid, fcode);
+	}
+      else
+	*unsupported_builtin = true;
+    }
+
+  return error_mark_node;
+}
+
 /* Implementation of the resolve_overloaded_builtin target hook, to
-   support Altivec's overloaded builtins.  FIXME: This code needs
-   to be brutally factored.  */
+   support Altivec's overloaded builtins.  */
 
 tree
 altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 				    void *passed_arglist)
 {
-  vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
-  unsigned int nargs = vec_safe_length (arglist);
-  enum rs6000_gen_builtins fcode
-    = (enum rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
-  tree fnargs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-  tree types[MAX_OVLD_ARGS];
-  tree args[MAX_OVLD_ARGS];
-
   /* Return immediately if this isn't an overload.  */
+  rs6000_gen_builtins fcode
+    = (rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
+
   if (fcode <= RS6000_OVLD_NONE)
     return NULL_TREE;
 
-  unsigned int adj_fcode = fcode - RS6000_OVLD_NONE;
-
   if (TARGET_DEBUG_BUILTIN)
     fprintf (stderr, "altivec_resolve_overloaded_builtin, code = %4d, %s\n",
 	     (int) fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl)));
@@ -964,674 +1783,84 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	     "%<vec_lvsr%> is deprecated for little endian; use "
 	     "assignment for unaligned loads and stores");
 
-  if (fcode == RS6000_OVLD_VEC_MUL)
+  /* Some overloads require special handling.  */
+  /* FIXME: Could we simplify the helper functions if we gathered arguments
+     and types into arrays first?  */
+  tree returned_expr = NULL;
+  resolution res = unresolved;
+  vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
+  unsigned int nargs = vec_safe_length (arglist);
+
+  switch (fcode)
     {
-      /* vec_mul needs to be special cased because there are no instructions
-	 for it for the {un}signed char, {un}signed short, and {un}signed int
-	 types.  */
-      if (nargs != 2)
-	{
-	  error ("builtin %qs only accepts 2 arguments", "vec_mul");
-	  return error_mark_node;
-	}
+    case RS6000_OVLD_VEC_MUL:
+      returned_expr = resolve_VEC_MUL (&res, arglist, nargs, loc);
+      break;
 
-      tree arg0 = (*arglist)[0];
-      tree arg0_type = TREE_TYPE (arg0);
-      tree arg1 = (*arglist)[1];
-      tree arg1_type = TREE_TYPE (arg1);
+    case RS6000_OVLD_VEC_CMPNE:
+      returned_expr = resolve_VEC_CMPNE (&res, arglist, nargs, loc);
+      break;
 
-      /* Both arguments must be vectors and the types must be compatible.  */
-      if (TREE_CODE (arg0_type) != VECTOR_TYPE)
-	goto bad;
-      if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
-	goto bad;
+    case RS6000_OVLD_VEC_ADDE:
+    case RS6000_OVLD_VEC_SUBE:
+      returned_expr = resolve_VEC_ADDE_SUBE (&res, fcode, arglist, nargs, loc);
+      break;
 
-      switch (TYPE_MODE (TREE_TYPE (arg0_type)))
-	{
-	  case E_QImode:
-	  case E_HImode:
-	  case E_SImode:
-	  case E_DImode:
-	  case E_TImode:
-	    {
-	      /* For scalar types just use a multiply expression.  */
-	      return fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (arg0), arg0,
-				      fold_convert (TREE_TYPE (arg0), arg1));
-	    }
-	  case E_SFmode:
-	    {
-	      /* For floats use the xvmulsp instruction directly.  */
-	      tree call = rs6000_builtin_decls[RS6000_BIF_XVMULSP];
-	      return build_call_expr (call, 2, arg0, arg1);
-	    }
-	  case E_DFmode:
-	    {
-	      /* For doubles use the xvmuldp instruction directly.  */
-	      tree call = rs6000_builtin_decls[RS6000_BIF_XVMULDP];
-	      return build_call_expr (call, 2, arg0, arg1);
-	    }
-	  /* Other types are errors.  */
-	  default:
-	    goto bad;
-	}
+    case RS6000_OVLD_VEC_ADDEC:
+    case RS6000_OVLD_VEC_SUBEC:
+      returned_expr = resolve_VEC_ADDEC_SUBEC (&res, fcode, arglist, nargs,
+					       loc);
+      break;
+
+    case RS6000_OVLD_VEC_SPLATS:
+    case RS6000_OVLD_VEC_PROMOTE:
+      returned_expr = resolve_VEC_SPLATS (&res, fcode, arglist, nargs);
+      break;
+
+    case RS6000_OVLD_VEC_EXTRACT:
+      returned_expr = resolve_VEC_EXTRACT (&res, arglist, nargs, loc);
+      break;
+
+    case RS6000_OVLD_VEC_INSERT:
+      returned_expr = resolve_VEC_INSERT (&res, arglist, nargs, loc);
+      break;
+
+    case RS6000_OVLD_VEC_STEP:
+      returned_expr = resolve_VEC_STEP (&res, arglist, nargs);
+      break;
+
+    default:
+      ;
     }
 
-  if (fcode == RS6000_OVLD_VEC_CMPNE)
+  if (res == resolved)
+    return returned_expr;
+
+  /* "Regular" built-in functions and overloaded functions share a namespace
+     for some arrays, like rs6000_builtin_decls.  But rs6000_overload_info
+     only has information for the overloaded functions, so we need an
+     adjusted index for that.  */
+  unsigned int adj_fcode = fcode - RS6000_OVLD_NONE;
+
+  if (res == resolved_bad)
     {
-      /* vec_cmpne needs to be special cased because there are no instructions
-	 for it (prior to power 9).  */
-      if (nargs != 2)
-	{
-	  error ("builtin %qs only accepts 2 arguments", "vec_cmpne");
-	  return error_mark_node;
-	}
-
-      tree arg0 = (*arglist)[0];
-      tree arg0_type = TREE_TYPE (arg0);
-      tree arg1 = (*arglist)[1];
-      tree arg1_type = TREE_TYPE (arg1);
-
-      /* Both arguments must be vectors and the types must be compatible.  */
-      if (TREE_CODE (arg0_type) != VECTOR_TYPE)
-	goto bad;
-      if (!lang_hooks.types_compatible_p (arg0_type, arg1_type))
-	goto bad;
-
-      /* Power9 instructions provide the most efficient implementation of
-	 ALTIVEC_BUILTIN_VEC_CMPNE if the mode is not DImode or TImode
-	 or SFmode or DFmode.  */
-      if (!TARGET_P9_VECTOR
-	  || (TYPE_MODE (TREE_TYPE (arg0_type)) == DImode)
-	  || (TYPE_MODE (TREE_TYPE (arg0_type)) == TImode)
-	  || (TYPE_MODE (TREE_TYPE (arg0_type)) == SFmode)
-	  || (TYPE_MODE (TREE_TYPE (arg0_type)) == DFmode))
-	{
-	  switch (TYPE_MODE (TREE_TYPE (arg0_type)))
-	    {
-	      /* vec_cmpneq (va, vb) == vec_nor (vec_cmpeq (va, vb),
-		 vec_cmpeq (va, vb)).  */
-	      /* Note:  vec_nand also works but opt changes vec_nand's
-		 to vec_nor's anyway.  */
-	    case E_QImode:
-	    case E_HImode:
-	    case E_SImode:
-	    case E_DImode:
-	    case E_TImode:
-	    case E_SFmode:
-	    case E_DFmode:
-	      {
-		/* call = vec_cmpeq (va, vb)
-		   result = vec_nor (call, call).  */
-		vec<tree, va_gc> *params = make_tree_vector ();
-		vec_safe_push (params, arg0);
-		vec_safe_push (params, arg1);
-		tree call = altivec_resolve_overloaded_builtin
-		  (loc, rs6000_builtin_decls[RS6000_OVLD_VEC_CMPEQ],
-		   params);
-		/* Use save_expr to ensure that operands used more than once
-		   that may have side effects (like calls) are only evaluated
-		   once.  */
-		call = save_expr (call);
-		params = make_tree_vector ();
-		vec_safe_push (params, call);
-		vec_safe_push (params, call);
-		return altivec_resolve_overloaded_builtin
-		  (loc, rs6000_builtin_decls[RS6000_OVLD_VEC_NOR], params);
-	      }
-	      /* Other types are errors.  */
-	    default:
-	      goto bad;
-	    }
-	}
-      /* else, fall through and process the Power9 alternative below */
-    }
-
-  if (fcode == RS6000_OVLD_VEC_ADDE || fcode == RS6000_OVLD_VEC_SUBE)
-    {
-      /* vec_adde needs to be special cased because there is no instruction
-	  for the {un}signed int version.  */
-      if (nargs != 3)
-	{
-	  const char *name;
-	  name = fcode == RS6000_OVLD_VEC_ADDE ? "vec_adde" : "vec_sube";
-	  error ("builtin %qs only accepts 3 arguments", name);
-	  return error_mark_node;
-	}
-
-      tree arg0 = (*arglist)[0];
-      tree arg0_type = TREE_TYPE (arg0);
-      tree arg1 = (*arglist)[1];
-      tree arg1_type = TREE_TYPE (arg1);
-      tree arg2 = (*arglist)[2];
-      tree arg2_type = TREE_TYPE (arg2);
-
-      /* All 3 arguments must be vectors of (signed or unsigned) (int or
-	 __int128) and the types must be compatible.  */
-      if (TREE_CODE (arg0_type) != VECTOR_TYPE)
-	goto bad;
-      if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
-	  || !lang_hooks.types_compatible_p (arg1_type, arg2_type))
-	goto bad;
-
-      switch (TYPE_MODE (TREE_TYPE (arg0_type)))
-	{
-	  /* For {un}signed ints,
-	     vec_adde (va, vb, carryv) == vec_add (vec_add (va, vb),
-						   vec_and (carryv, 1)).
-	     vec_sube (va, vb, carryv) == vec_sub (vec_sub (va, vb),
-						   vec_and (carryv, 1)).  */
-	  case E_SImode:
-	    {
-	      tree add_sub_builtin;
-
-	      vec<tree, va_gc> *params = make_tree_vector ();
-	      vec_safe_push (params, arg0);
-	      vec_safe_push (params, arg1);
-
-	      if (fcode == RS6000_OVLD_VEC_ADDE)
-		add_sub_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADD];
-	      else
-		add_sub_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUB];
-
-	      tree call
-		= altivec_resolve_overloaded_builtin (loc, add_sub_builtin,
-						      params);
-	      tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
-	      tree ones_vector = build_vector_from_val (arg0_type, const1);
-	      tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
-					       arg2, ones_vector);
-	      params = make_tree_vector ();
-	      vec_safe_push (params, call);
-	      vec_safe_push (params, and_expr);
-	      return altivec_resolve_overloaded_builtin (loc, add_sub_builtin,
-							 params);
-	    }
-	  /* For {un}signed __int128s use the vaddeuqm/vsubeuqm instruction
-	     directly.  */
-	  case E_TImode:
-	    break;
-
-	  /* Types other than {un}signed int and {un}signed __int128
-		are errors.  */
-	  default:
-	    goto bad;
-	}
-    }
-
-  if (fcode == RS6000_OVLD_VEC_ADDEC || fcode == RS6000_OVLD_VEC_SUBEC)
-    {
-      /* vec_addec and vec_subec needs to be special cased because there is
-	 no instruction for the {un}signed int version.  */
-      if (nargs != 3)
-	{
-	  const char *name;
-	  name = fcode == RS6000_OVLD_VEC_ADDEC ? "vec_addec" : "vec_subec";
-	  error ("builtin %qs only accepts 3 arguments", name);
-	  return error_mark_node;
-	}
-
-      tree arg0 = (*arglist)[0];
-      tree arg0_type = TREE_TYPE (arg0);
-      tree arg1 = (*arglist)[1];
-      tree arg1_type = TREE_TYPE (arg1);
-      tree arg2 = (*arglist)[2];
-      tree arg2_type = TREE_TYPE (arg2);
-
-      /* All 3 arguments must be vectors of (signed or unsigned) (int or
-	 __int128) and the types must be compatible.  */
-      if (TREE_CODE (arg0_type) != VECTOR_TYPE)
-	goto bad;
-      if (!lang_hooks.types_compatible_p (arg0_type, arg1_type)
-	  || !lang_hooks.types_compatible_p (arg1_type, arg2_type))
-	goto bad;
-
-      switch (TYPE_MODE (TREE_TYPE (arg0_type)))
-	{
-	  /* For {un}signed ints,
-	      vec_addec (va, vb, carryv) ==
-				vec_or (vec_addc (va, vb),
-					vec_addc (vec_add (va, vb),
-						  vec_and (carryv, 0x1))).  */
-	  case E_SImode:
-	    {
-	    /* Use save_expr to ensure that operands used more than once
-		that may have side effects (like calls) are only evaluated
-		once.  */
-	    tree as_builtin;
-	    tree as_c_builtin;
-
-	    arg0 = save_expr (arg0);
-	    arg1 = save_expr (arg1);
-	    vec<tree, va_gc> *params = make_tree_vector ();
-	    vec_safe_push (params, arg0);
-	    vec_safe_push (params, arg1);
-
-	    if (fcode == RS6000_OVLD_VEC_ADDEC)
-	      as_c_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADDC];
-	    else
-	      as_c_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUBC];
-
-	    tree call1 = altivec_resolve_overloaded_builtin (loc, as_c_builtin,
-							     params);
-	    params = make_tree_vector ();
-	    vec_safe_push (params, arg0);
-	    vec_safe_push (params, arg1);
-
-	    if (fcode == RS6000_OVLD_VEC_ADDEC)
-	      as_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_ADD];
-	    else
-	      as_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_SUB];
-
-	    tree call2 = altivec_resolve_overloaded_builtin (loc, as_builtin,
-							     params);
-	    tree const1 = build_int_cstu (TREE_TYPE (arg0_type), 1);
-	    tree ones_vector = build_vector_from_val (arg0_type, const1);
-	    tree and_expr = fold_build2_loc (loc, BIT_AND_EXPR, arg0_type,
-					     arg2, ones_vector);
-	    params = make_tree_vector ();
-	    vec_safe_push (params, call2);
-	    vec_safe_push (params, and_expr);
-	    call2 = altivec_resolve_overloaded_builtin (loc, as_c_builtin,
-							params);
-	    params = make_tree_vector ();
-	    vec_safe_push (params, call1);
-	    vec_safe_push (params, call2);
-	    tree or_builtin = rs6000_builtin_decls[RS6000_OVLD_VEC_OR];
-	    return altivec_resolve_overloaded_builtin (loc, or_builtin,
-						       params);
-	    }
-	  /* For {un}signed __int128s use the vaddecuq/vsubbecuq
-	     instructions.  This occurs through normal processing.  */
-	  case E_TImode:
-	    break;
-
-	  /* Types other than {un}signed int and {un}signed __int128
-		are errors.  */
-	  default:
-	    goto bad;
-	}
-    }
-
-  /* For now treat vec_splats and vec_promote as the same.  */
-  if (fcode == RS6000_OVLD_VEC_SPLATS || fcode == RS6000_OVLD_VEC_PROMOTE)
-    {
-      tree type, arg;
-      int size;
-      int i;
-      bool unsigned_p;
-      vec<constructor_elt, va_gc> *vec;
-      const char *name;
-      name = fcode == RS6000_OVLD_VEC_SPLATS ? "vec_splats" : "vec_promote";
-
-      if (fcode == RS6000_OVLD_VEC_SPLATS && nargs != 1)
-	{
-	  error ("builtin %qs only accepts 1 argument", name);
-	  return error_mark_node;
-	}
-      if (fcode == RS6000_OVLD_VEC_PROMOTE && nargs != 2)
-	{
-	  error ("builtin %qs only accepts 2 arguments", name);
-	  return error_mark_node;
-	}
-      /* Ignore promote's element argument.  */
-      if (fcode == RS6000_OVLD_VEC_PROMOTE
-	  && !INTEGRAL_TYPE_P (TREE_TYPE ((*arglist)[1])))
-	goto bad;
-
-      arg = (*arglist)[0];
-      type = TREE_TYPE (arg);
-      if (!SCALAR_FLOAT_TYPE_P (type)
-	  && !INTEGRAL_TYPE_P (type))
-	goto bad;
-      unsigned_p = TYPE_UNSIGNED (type);
-      switch (TYPE_MODE (type))
-	{
-	  case E_TImode:
-	    type = unsigned_p ? unsigned_V1TI_type_node : V1TI_type_node;
-	    size = 1;
-	    break;
-	  case E_DImode:
-	    type = unsigned_p ? unsigned_V2DI_type_node : V2DI_type_node;
-	    size = 2;
-	    break;
-	  case E_SImode:
-	    type = unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node;
-	    size = 4;
-	    break;
-	  case E_HImode:
-	    type = unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node;
-	    size = 8;
-	    break;
-	  case E_QImode:
-	    type = unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node;
-	    size = 16;
-	    break;
-	  case E_SFmode:
-	    type = V4SF_type_node;
-	    size = 4;
-	    break;
-	  case E_DFmode:
-	    type = V2DF_type_node;
-	    size = 2;
-	    break;
-	  default:
-	    goto bad;
-	}
-      arg = save_expr (fold_convert (TREE_TYPE (type), arg));
-      vec_alloc (vec, size);
-      for (i = 0; i < size; i++)
-	{
-	  constructor_elt elt = {NULL_TREE, arg};
-	  vec->quick_push (elt);
-	}
-      return build_constructor (type, vec);
-    }
-
-  /* For now use pointer tricks to do the extraction, unless we are on VSX
-     extracting a double from a constant offset.  */
-  if (fcode == RS6000_OVLD_VEC_EXTRACT)
-    {
-      tree arg1;
-      tree arg1_type;
-      tree arg2;
-      tree arg1_inner_type;
-      tree decl, stmt;
-      tree innerptrtype;
-      machine_mode mode;
-
-      /* No second argument. */
-      if (nargs != 2)
-	{
-	  error ("builtin %qs only accepts 2 arguments", "vec_extract");
-	  return error_mark_node;
-	}
-
-      arg2 = (*arglist)[1];
-      arg1 = (*arglist)[0];
-      arg1_type = TREE_TYPE (arg1);
-
-      if (TREE_CODE (arg1_type) != VECTOR_TYPE)
-	goto bad;
-      if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
-	goto bad;
-
-      /* See if we can optimize vec_extracts with the current VSX instruction
-	 set.  */
-      mode = TYPE_MODE (arg1_type);
-      if (VECTOR_MEM_VSX_P (mode))
-
-	{
-	  tree call = NULL_TREE;
-	  int nunits = GET_MODE_NUNITS (mode);
-
-	  arg2 = fold_for_warn (arg2);
-
-	  /* If the second argument is an integer constant, generate
-	     the built-in code if we can.  We need 64-bit and direct
-	     move to extract the small integer vectors.  */
-	  if (TREE_CODE (arg2) == INTEGER_CST)
-	    {
-	      wide_int selector = wi::to_wide (arg2);
-	      selector = wi::umod_trunc (selector, nunits);
-	      arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
-	      switch (mode)
-		{
-		default:
-		  break;
-
-		case E_V1TImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V1TI];
-		  break;
-
-		case E_V2DFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DF];
-		  break;
-
-		case E_V2DImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DI];
-		  break;
-
-		case E_V4SFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SF];
-		  break;
-
-		case E_V4SImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SI];
-		  break;
-
-		case E_V8HImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
-		  break;
-
-		case E_V16QImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
-		  break;
-		}
-	    }
-
-	  /* If the second argument is variable, we can optimize it if we are
-	     generating 64-bit code on a machine with direct move.  */
-	  else if (TREE_CODE (arg2) != INTEGER_CST && TARGET_DIRECT_MOVE_64BIT)
-	    {
-	      switch (mode)
-		{
-		default:
-		  break;
-
-		case E_V2DFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DF];
-		  break;
-
-		case E_V2DImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DI];
-		  break;
-
-		case E_V4SFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SF];
-		  break;
-
-		case E_V4SImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SI];
-		  break;
-
-		case E_V8HImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
-		  break;
-
-		case E_V16QImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
-		  break;
-		}
-	    }
-
-	  if (call)
-	    {
-	      tree result = build_call_expr (call, 2, arg1, arg2);
-	      /* Coerce the result to vector element type.  May be no-op.  */
-	      arg1_inner_type = TREE_TYPE (arg1_type);
-	      result = fold_convert (arg1_inner_type, result);
-	      return result;
-	    }
-	}
-
-      /* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2). */
-      arg1_inner_type = TREE_TYPE (arg1_type);
-      tree subp = build_int_cst (TREE_TYPE (arg2),
-				 TYPE_VECTOR_SUBPARTS (arg1_type) - 1);
-      arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2, subp, 0);
-      decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
-      DECL_EXTERNAL (decl) = 0;
-      TREE_PUBLIC (decl) = 0;
-      DECL_CONTEXT (decl) = current_function_decl;
-      TREE_USED (decl) = 1;
-      TREE_TYPE (decl) = arg1_type;
-      TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
-      if (c_dialect_cxx ())
-	{
-	  stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
-			 NULL_TREE, NULL_TREE);
-	  SET_EXPR_LOCATION (stmt, loc);
-	}
-      else
-	{
-	  DECL_INITIAL (decl) = arg1;
-	  stmt = build1 (DECL_EXPR, arg1_type, decl);
-	  TREE_ADDRESSABLE (decl) = 1;
-	  SET_EXPR_LOCATION (stmt, loc);
-	  stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
-	}
-
-      innerptrtype = build_pointer_type (arg1_inner_type);
-
-      stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
-      stmt = convert (innerptrtype, stmt);
-      stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
-      stmt = build_indirect_ref (loc, stmt, RO_NULL);
-
-      /* PR83660: We mark this as having side effects so that
-	 downstream in fold_build_cleanup_point_expr () it will get a
-	 CLEANUP_POINT_EXPR.  If it does not we can run into an ICE
-	 later in gimplify_cleanup_point_expr ().  Potentially this
-	 causes missed optimization because there actually is no side
-	 effect.  */
-      if (c_dialect_cxx ())
-	TREE_SIDE_EFFECTS (stmt) = 1;
-
-      return stmt;
-    }
-
-  /* For now use pointer tricks to do the insertion, unless we are on VSX
-     inserting a double to a constant offset.  */
-  if (fcode == RS6000_OVLD_VEC_INSERT)
-    {
-      tree arg0;
-      tree arg1;
-      tree arg2;
-      tree arg1_type;
-      tree decl, stmt;
-      machine_mode mode;
-
-      /* No second or third arguments. */
-      if (nargs != 3)
-	{
-	  error ("builtin %qs only accepts 3 arguments", "vec_insert");
-	  return error_mark_node;
-	}
-
-      arg0 = (*arglist)[0];
-      arg1 = (*arglist)[1];
-      arg1_type = TREE_TYPE (arg1);
-      arg2 = fold_for_warn ((*arglist)[2]);
-
-      if (TREE_CODE (arg1_type) != VECTOR_TYPE)
-	goto bad;
-      if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
-	goto bad;
-
-      /* If we can use the VSX xxpermdi instruction, use that for insert.  */
-      mode = TYPE_MODE (arg1_type);
-      if ((mode == V2DFmode || mode == V2DImode) && VECTOR_UNIT_VSX_P (mode)
-	  && TREE_CODE (arg2) == INTEGER_CST)
-	{
-	  wide_int selector = wi::to_wide (arg2);
-	  selector = wi::umod_trunc (selector, 2);
-	  tree call = NULL_TREE;
-
-	  arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
-	  if (mode == V2DFmode)
-	    call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V2DF];
-	  else if (mode == V2DImode)
-	    call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V2DI];
-
-	  /* Note, __builtin_vec_insert_<xxx> has vector and scalar types
-	     reversed.  */
-	  if (call)
-	    return build_call_expr (call, 3, arg1, arg0, arg2);
-	}
-      else if (mode == V1TImode && VECTOR_UNIT_VSX_P (mode)
-	       && TREE_CODE (arg2) == INTEGER_CST)
-	{
-	  tree call = rs6000_builtin_decls[RS6000_BIF_VEC_SET_V1TI];
-	  wide_int selector = wi::zero(32);
-
-	  arg2 = wide_int_to_tree (TREE_TYPE (arg2), selector);
-	  /* Note, __builtin_vec_insert_<xxx> has vector and scalar types
-	     reversed.  */
-	  return build_call_expr (call, 3, arg1, arg0, arg2);
-	}
-
-      /* Build *(((arg1_inner_type*)&(vector type){arg1})+arg2) = arg0 with
-	 VIEW_CONVERT_EXPR.  i.e.:
-	 D.3192 = v1;
-	 _1 = n & 3;
-	 VIEW_CONVERT_EXPR<int[4]>(D.3192)[_1] = i;
-	 v1 = D.3192;
-	 D.3194 = v1;  */
-      if (TYPE_VECTOR_SUBPARTS (arg1_type) == 1)
-	arg2 = build_int_cst (TREE_TYPE (arg2), 0);
-      else
-	arg2 = build_binary_op (loc, BIT_AND_EXPR, arg2,
-				build_int_cst (TREE_TYPE (arg2),
-					       TYPE_VECTOR_SUBPARTS (arg1_type)
-					       - 1), 0);
-      decl = build_decl (loc, VAR_DECL, NULL_TREE, arg1_type);
-      DECL_EXTERNAL (decl) = 0;
-      TREE_PUBLIC (decl) = 0;
-      DECL_CONTEXT (decl) = current_function_decl;
-      TREE_USED (decl) = 1;
-      TREE_TYPE (decl) = arg1_type;
-      TREE_READONLY (decl) = TYPE_READONLY (arg1_type);
-      TREE_ADDRESSABLE (decl) = 1;
-      if (c_dialect_cxx ())
-	{
-	  stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
-			 NULL_TREE, NULL_TREE);
-	  SET_EXPR_LOCATION (stmt, loc);
-	}
-      else
-	{
-	  DECL_INITIAL (decl) = arg1;
-	  stmt = build1 (DECL_EXPR, arg1_type, decl);
-	  SET_EXPR_LOCATION (stmt, loc);
-	  stmt = build1 (COMPOUND_LITERAL_EXPR, arg1_type, stmt);
-	}
-
-      if (TARGET_VSX)
-	{
-	  stmt = build_array_ref (loc, stmt, arg2);
-	  stmt = fold_build2 (MODIFY_EXPR, TREE_TYPE (arg0), stmt,
-			      convert (TREE_TYPE (stmt), arg0));
-	  stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
-	}
-      else
-	{
-	  tree arg1_inner_type;
-	  tree innerptrtype;
-	  arg1_inner_type = TREE_TYPE (arg1_type);
-	  innerptrtype = build_pointer_type (arg1_inner_type);
-
-	  stmt = build_unary_op (loc, ADDR_EXPR, stmt, 0);
-	  stmt = convert (innerptrtype, stmt);
-	  stmt = build_binary_op (loc, PLUS_EXPR, stmt, arg2, 1);
-	  stmt = build_indirect_ref (loc, stmt, RO_NULL);
-	  stmt = build2 (MODIFY_EXPR, TREE_TYPE (stmt), stmt,
-			 convert (TREE_TYPE (stmt), arg0));
-	  stmt = build2 (COMPOUND_EXPR, arg1_type, stmt, decl);
-	}
-      return stmt;
+      const char *name = rs6000_overload_info[adj_fcode].ovld_name;
+      error ("invalid parameter combination for AltiVec intrinsic %qs", name);
+      return error_mark_node;
     }
 
+  /* Gather the arguments and their types into arrays for easier handling.  */
+  tree fnargs = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  tree types[MAX_OVLD_ARGS];
+  tree args[MAX_OVLD_ARGS];
   unsigned int n;
+
   for (n = 0;
        !VOID_TYPE_P (TREE_VALUE (fnargs)) && n < nargs;
        fnargs = TREE_CHAIN (fnargs), n++)
     {
       tree decl_type = TREE_VALUE (fnargs);
       tree arg = (*arglist)[n];
-      tree type;
 
       if (arg == error_mark_node)
 	return error_mark_node;
@@ -1640,10 +1869,10 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	abort ();
 
       arg = default_conversion (arg);
+      tree type = TREE_TYPE (arg);
 
       /* The C++ front-end converts float * to const void * using
 	 NOP_EXPR<const void *> (NOP_EXPR<void *> (x)).  */
-      type = TREE_TYPE (arg);
       if (POINTER_TYPE_P (type)
 	  && TREE_CODE (arg) == NOP_EXPR
 	  && lang_hooks.types_compatible_p (TREE_TYPE (arg),
@@ -1672,15 +1901,13 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 
       /* For RS6000_OVLD_VEC_LXVL, convert any const * to its non constant
 	 equivalent to simplify the overload matching below.  */
-      if (fcode == RS6000_OVLD_VEC_LXVL)
+      if (fcode == RS6000_OVLD_VEC_LXVL
+	  && POINTER_TYPE_P (type)
+	  && TYPE_READONLY (TREE_TYPE (type)))
 	{
-	  if (POINTER_TYPE_P (type)
-	      && TYPE_READONLY (TREE_TYPE (type)))
-	    {
-	      type = build_qualified_type (TREE_TYPE (type), 0);
-	      type = build_pointer_type (type);
-	      arg = fold_convert (type, arg);
-	    }
+	  type = build_qualified_type (TREE_TYPE (type), 0);
+	  type = build_pointer_type (type);
+	  arg = fold_convert (type, arg);
 	}
 
       args[n] = arg;
@@ -1692,85 +1919,54 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
   if (!VOID_TYPE_P (TREE_VALUE (fnargs)) || n < nargs)
     return NULL;
 
-  if (fcode == RS6000_OVLD_VEC_STEP)
+  bool unsupported_builtin = false;
+  rs6000_gen_builtins instance_code;
+  bool supported = false;
+  ovlddata *instance = rs6000_overload_info[adj_fcode].first_instance;
+  gcc_assert (instance != NULL);
+
+  /* Functions with no arguments can have only one overloaded instance.  */
+  gcc_assert (nargs > 0 || !instance->next);
+
+  /* Standard overload processing involves determining whether an instance
+     exists that is type-compatible with the overloaded function call.  In
+     a couple of cases, we need to do some extra processing to disambiguate
+     between multiple compatible instances.  */
+  switch (fcode)
     {
-      if (TREE_CODE (types[0]) != VECTOR_TYPE)
-	goto bad;
-
-      return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (types[0]));
-    }
-
-  {
-    bool unsupported_builtin = false;
-    enum rs6000_gen_builtins overloaded_code;
-    bool supported = false;
-    ovlddata *instance = rs6000_overload_info[adj_fcode].first_instance;
-    gcc_assert (instance != NULL);
-
-    /* Need to special case __builtin_cmpb because the overloaded forms
-       of this function take (unsigned int, unsigned int) or (unsigned
-       long long int, unsigned long long int).  Since C conventions
-       allow the respective argument types to be implicitly coerced into
-       each other, the default handling does not provide adequate
-       discrimination between the desired forms of the function.  */
-    if (fcode == RS6000_OVLD_SCAL_CMPB)
+      /* Need to special case __builtin_cmpb because the overloaded forms
+	 of this function take (unsigned int, unsigned int) or (unsigned
+	 long long int, unsigned long long int).  Since C conventions
+	 allow the respective argument types to be implicitly coerced into
+	 each other, the default handling does not provide adequate
+	 discrimination between the desired forms of the function.  */
+    case RS6000_OVLD_SCAL_CMPB:
       {
 	machine_mode arg1_mode = TYPE_MODE (types[0]);
 	machine_mode arg2_mode = TYPE_MODE (types[1]);
 
-	if (nargs != 2)
-	  {
-	    error ("builtin %qs only accepts 2 arguments", "__builtin_cmpb");
-	    return error_mark_node;
-	  }
-
 	/* If any supplied arguments are wider than 32 bits, resolve to
 	   64-bit variant of built-in function.  */
 	if (GET_MODE_PRECISION (arg1_mode) > 32
 	    || GET_MODE_PRECISION (arg2_mode) > 32)
 	  /* Assure all argument and result types are compatible with
 	     the built-in function represented by RS6000_BIF_CMPB.  */
-	  overloaded_code = RS6000_BIF_CMPB;
+	  instance_code = RS6000_BIF_CMPB;
 	else
 	  /* Assure all argument and result types are compatible with
 	     the built-in function represented by RS6000_BIF_CMPB_32.  */
-	  overloaded_code = RS6000_BIF_CMPB_32;
+	  instance_code = RS6000_BIF_CMPB_32;
 
-	while (instance && instance->bifid != overloaded_code)
-	  instance = instance->next;
-
-	gcc_assert (instance != NULL);
-	tree fntype = rs6000_builtin_info[instance->bifid].fntype;
-	tree parmtype0 = TREE_VALUE (TYPE_ARG_TYPES (fntype));
-	tree parmtype1 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
-
-	if (rs6000_builtin_type_compatible (types[0], parmtype0)
-	    && rs6000_builtin_type_compatible (types[1], parmtype1))
-	  {
-	    if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
-		&& rs6000_builtin_is_supported (instance->bifid))
-	      {
-		tree ret_type = TREE_TYPE (instance->fntype);
-		return altivec_build_resolved_builtin (args, n, fntype,
-						       ret_type,
-						       instance->bifid,
-						       fcode);
-	      }
-	    else
-	      unsupported_builtin = true;
-	  }
+	tree call = find_instance (&unsupported_builtin, &instance,
+				   instance_code, fcode, types, args);
+	if (call != error_mark_node)
+	  return call;
+	break;
       }
-    else if (fcode == RS6000_OVLD_VEC_VSIE)
+    case RS6000_OVLD_VEC_VSIE:
       {
 	machine_mode arg1_mode = TYPE_MODE (types[0]);
 
-	if (nargs != 2)
-	  {
-	    error ("builtin %qs only accepts 2 arguments",
-		   "scalar_insert_exp");
-	    return error_mark_node;
-	  }
-
 	/* If supplied first argument is wider than 64 bits, resolve to
 	   128-bit variant of built-in function.  */
 	if (GET_MODE_PRECISION (arg1_mode) > 64)
@@ -1779,9 +1975,9 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	       that expects __ieee128 argument.  Otherwise, expect
 	       __int128 argument.  */
 	    if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
-	      overloaded_code = RS6000_BIF_VSIEQPF;
+	      instance_code = RS6000_BIF_VSIEQPF;
 	    else
-	      overloaded_code = RS6000_BIF_VSIEQP;
+	      instance_code = RS6000_BIF_VSIEQP;
 	  }
 	else
 	  {
@@ -1789,109 +1985,86 @@  altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
 	       that expects double argument.  Otherwise, expect
 	       long long int argument.  */
 	    if (GET_MODE_CLASS (arg1_mode) == MODE_FLOAT)
-	      overloaded_code = RS6000_BIF_VSIEDPF;
+	      instance_code = RS6000_BIF_VSIEDPF;
 	    else
-	      overloaded_code = RS6000_BIF_VSIEDP;
+	      instance_code = RS6000_BIF_VSIEDP;
 	  }
 
-	while (instance && instance->bifid != overloaded_code)
-	  instance = instance->next;
+	tree call = find_instance (&unsupported_builtin, &instance,
+				   instance_code, fcode, types, args);
+	if (call != error_mark_node)
+	  return call;
+	break;
+      }
+    default:
+      /* Standard overload processing.  Look for an instance with compatible
+	 parameter types.  If it is supported in the current context, resolve
+	 the overloaded call to that instance.  */
+      for (; instance != NULL; instance = instance->next)
+	{
+	  bool mismatch = false;
+	  tree nextparm = TYPE_ARG_TYPES (instance->fntype);
 
-	gcc_assert (instance != NULL);
-	tree fntype = rs6000_builtin_info[instance->bifid].fntype;
-	tree parmtype0 = TREE_VALUE (TYPE_ARG_TYPES (fntype));
-	tree parmtype1 = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
+	  for (unsigned int arg_i = 0;
+	       arg_i < nargs && nextparm != NULL;
+	       arg_i++)
+	    {
+	      tree parmtype = TREE_VALUE (nextparm);
+	      if (!rs6000_builtin_type_compatible (types[arg_i], parmtype))
+		{
+		  mismatch = true;
+		  break;
+		}
+	      nextparm = TREE_CHAIN (nextparm);
+	    }
 
-	if (rs6000_builtin_type_compatible (types[0], parmtype0)
-	    && rs6000_builtin_type_compatible (types[1], parmtype1))
-	  {
-	    if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
-		&& rs6000_builtin_is_supported (instance->bifid))
-	      {
-		tree ret_type = TREE_TYPE (instance->fntype);
-		return altivec_build_resolved_builtin (args, n, fntype,
-						       ret_type,
-						       instance->bifid,
-						       fcode);
-	      }
-	    else
+	  if (mismatch)
+	    continue;
+
+	  supported = rs6000_builtin_is_supported (instance->bifid);
+	  if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
+	      && supported)
+	    {
+	      tree fntype = rs6000_builtin_info[instance->bifid].fntype;
+	      tree ret_type = TREE_TYPE (instance->fntype);
+	      return altivec_build_resolved_builtin (args, nargs, fntype,
+						     ret_type, instance->bifid,
+						     fcode);
+	    }
+	  else
+	    {
 	      unsupported_builtin = true;
-	  }
-      }
-    else
-      {
-	/* Functions with no arguments can have only one overloaded
-	   instance.  */
-	gcc_assert (n > 0 || !instance->next);
+	      break;
+	    }
+	}
+    }
 
-	for (; instance != NULL; instance = instance->next)
-	  {
-	    bool mismatch = false;
-	    tree nextparm = TYPE_ARG_TYPES (instance->fntype);
+  if (unsupported_builtin)
+    {
+      const char *name = rs6000_overload_info[adj_fcode].ovld_name;
+      if (!supported)
+	{
+	  /* Indicate that the instantiation of the overloaded builtin
+	     name is not available with the target flags in effect.  */
+	  rs6000_gen_builtins fcode = (rs6000_gen_builtins) instance->bifid;
+	  rs6000_invalid_builtin (fcode);
+	  /* Provide clarity of the relationship between the overload
+	     and the instantiation.  */
+	  const char *internal_name
+	    = rs6000_builtin_info[instance->bifid].bifname;
+	  rich_location richloc (line_table, input_location);
+	  inform (&richloc,
+		  "overloaded builtin %qs is implemented by builtin %qs",
+		  name, internal_name);
+	}
+      else
+	error ("%qs is not supported in this compiler configuration", name);
 
-	    for (unsigned int arg_i = 0;
-		 arg_i < nargs && nextparm != NULL;
-		 arg_i++)
-	      {
-		tree parmtype = TREE_VALUE (nextparm);
-		if (!rs6000_builtin_type_compatible (types[arg_i], parmtype))
-		  {
-		    mismatch = true;
-		    break;
-		  }
-		nextparm = TREE_CHAIN (nextparm);
-	      }
+      return error_mark_node;
+    }
 
-	    if (mismatch)
-	      continue;
-
-	    supported = rs6000_builtin_is_supported (instance->bifid);
-	    if (rs6000_builtin_decl (instance->bifid, false) != error_mark_node
-		&& supported)
-	      {
-		tree fntype = rs6000_builtin_info[instance->bifid].fntype;
-		tree ret_type = TREE_TYPE (instance->fntype);
-		return altivec_build_resolved_builtin (args, n, fntype,
-						       ret_type,
-						       instance->bifid,
-						       fcode);
-	      }
-	    else
-	      {
-		unsupported_builtin = true;
-		break;
-	      }
-	  }
-      }
-
-    if (unsupported_builtin)
-      {
-	const char *name = rs6000_overload_info[adj_fcode].ovld_name;
-	if (!supported)
-	  {
-	    /* Indicate that the instantiation of the overloaded builtin
-	       name is not available with the target flags in effect.  */
-	    rs6000_gen_builtins fcode = (rs6000_gen_builtins) instance->bifid;
-	    rs6000_invalid_builtin (fcode);
-	    /* Provide clarity of the relationship between the overload
-	       and the instantiation.  */
-	    const char *internal_name
-	      = rs6000_builtin_info[instance->bifid].bifname;
-	    rich_location richloc (line_table, input_location);
-	    inform (&richloc,
-		    "overloaded builtin %qs is implemented by builtin %qs",
-		    name, internal_name);
-	  }
-	else
-	  error ("%qs is not supported in this compiler configuration", name);
-
-	return error_mark_node;
-      }
-  }
- bad:
-  {
-    const char *name = rs6000_overload_info[adj_fcode].ovld_name;
-    error ("invalid parameter combination for AltiVec intrinsic %qs", name);
-    return error_mark_node;
-  }
+  /* If we fall through to here, there were no compatible instances.  */
+  const char *name = rs6000_overload_info[adj_fcode].ovld_name;
+  error ("invalid parameter combination for AltiVec intrinsic %qs", name);
+  return error_mark_node;
 }