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,710 +928,939 @@ altivec_build_resolved_builtin (tree *args, int n, tree fntype, tree ret_type,
   return fold_convert (ret_type, call);
 }
 
-/* Implementation of the resolve_overloaded_builtin target hook, to
-   support Altivec's overloaded builtins.  FIXME: This code needs
-   to be brutally factored.  */
+/* 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.
 
-tree
-altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
-				    void *passed_arglist)
+	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<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];
+  /* 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;
+    }
 
-  /* Return immediately if this isn't an overload.  */
-  if (fcode <= RS6000_OVLD_NONE)
-    return NULL_TREE;
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
+  tree arg1 = (*arglist)[1];
+  tree arg1_type = TREE_TYPE (arg1);
 
-  unsigned int adj_fcode = fcode - RS6000_OVLD_NONE;
+  /* 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;
+    }
 
-  if (TARGET_DEBUG_BUILTIN)
-    fprintf (stderr, "altivec_resolve_overloaded_builtin, code = %4d, %s\n",
-	     (int) fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+  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;
+    }
+}
 
-  /* vec_lvsl and vec_lvsr are deprecated for use with LE element order.  */
-  if (fcode == RS6000_OVLD_VEC_LVSL && !BYTES_BIG_ENDIAN)
-    warning (OPT_Wdeprecated,
-	     "%<vec_lvsl%> is deprecated for little endian; use "
-	     "assignment for unaligned loads and stores");
-  else if (fcode == RS6000_OVLD_VEC_LVSR && !BYTES_BIG_ENDIAN)
-    warning (OPT_Wdeprecated,
-	     "%<vec_lvsr%> is deprecated for little endian; use "
-	     "assignment for unaligned loads and stores");
+/* 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.  */
 
-  if (fcode == RS6000_OVLD_VEC_MUL)
+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)
     {
-      /* 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;
-	}
+      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);
 
-      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;
+    }
 
-      /* 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;
+  machine_mode arg0_elt_mode = TYPE_MODE (TREE_TYPE (arg0_type));
 
-      switch (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)
 	{
-	  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);
-	    }
+	  /* 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:
-	    goto bad;
+	default:
+	  *res = resolved_bad;
+	  return error_mark_node;
 	}
     }
 
-  if (fcode == RS6000_OVLD_VEC_CMPNE)
+  /* 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)
     {
-      /* 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;
-	}
+      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);
-
-      /* 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 */
+  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;
     }
 
-  if (fcode == RS6000_OVLD_VEC_ADDE || fcode == RS6000_OVLD_VEC_SUBE)
+  switch (TYPE_MODE (TREE_TYPE (arg0_type)))
     {
-      /* 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;
-	}
+      /* 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 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;
+	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];
 
-	      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, 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;
 
-	      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;
-	}
+      /* Types other than {un}signed int and {un}signed __int128
+	 are errors.  */
+    default:
+      *res = resolved_bad;
     }
 
-  if (fcode == RS6000_OVLD_VEC_ADDEC || fcode == RS6000_OVLD_VEC_SUBEC)
+  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)
     {
-      /* 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;
-	}
+      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)
-	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;
+  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;
+    }
 
-	    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);
+  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];
 
-	    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 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];
 
-	    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;
-	}
+	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;
     }
 
-  /* 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";
+  return error_mark_node;
+}
 
-      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);
+/* 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;
     }
 
-  /* 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)
+  if (fcode == RS6000_OVLD_VEC_PROMOTE && nargs != 2)
     {
-      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;
-	}
+      error ("builtin %qs only accepts 2 arguments", name);
+      *res = resolved;
+      return error_mark_node;
+    }
 
-      arg2 = (*arglist)[1];
-      arg1 = (*arglist)[0];
-      arg1_type = TREE_TYPE (arg1);
+  /* 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;
+    }
 
-      if (TREE_CODE (arg1_type) != VECTOR_TYPE)
-	goto bad;
-      if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
-	goto bad;
+  tree arg = (*arglist)[0];
+  tree type = TREE_TYPE (arg);
 
-      /* See if we can optimize vec_extracts with the current VSX instruction
-	 set.  */
-      mode = TYPE_MODE (arg1_type);
-      if (VECTOR_MEM_VSX_P (mode))
+  if (!SCALAR_FLOAT_TYPE_P (type) && !INTEGRAL_TYPE_P (type))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
 
-	{
-	  tree call = NULL_TREE;
-	  int nunits = GET_MODE_NUNITS (mode);
+  bool unsigned_p = TYPE_UNSIGNED (type);
+  int size;
 
-	  arg2 = fold_for_warn (arg2);
+  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;
+    }
 
-	  /* 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;
+  arg = save_expr (fold_convert (TREE_TYPE (type), arg));
+  vec<constructor_elt, va_gc> *vec;
+  vec_alloc (vec, size);
 
-		case E_V1TImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V1TI];
-		  break;
+  for (int i = 0; i < size; i++)
+    {
+      constructor_elt elt = {NULL_TREE, arg};
+      vec->quick_push (elt);
+    }
 
-		case E_V2DFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DF];
-		  break;
+  *res = resolved;
+  return build_constructor (type, vec);
+}
 
-		case E_V2DImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V2DI];
-		  break;
+/* 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.  */
 
-		case E_V4SFmode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SF];
-		  break;
+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;
+    }
 
-		case E_V4SImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V4SI];
-		  break;
+  tree arg1 = (*arglist)[0];
+  tree arg1_type = TREE_TYPE (arg1);
+  tree arg2 = (*arglist)[1];
 
-		case E_V8HImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
-		  break;
+  if (TREE_CODE (arg1_type) != VECTOR_TYPE
+      || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
 
-		case E_V16QImode:
-		  if (TARGET_DIRECT_MOVE_64BIT)
-		    call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
-		  break;
-		}
-	    }
+  /* 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 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)
+  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)
 	    {
-	      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_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_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_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_V4SImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		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_V8HImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V8HI];
+	      break;
 
-		case E_V16QImode:
-		  call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
-		  break;
-		}
+	    case E_V16QImode:
+	      if (TARGET_DIRECT_MOVE_64BIT)
+		call = rs6000_builtin_decls[RS6000_BIF_VEC_EXT_V16QI];
+	      break;
+
+	    default:
+	      break;
 	    }
+	}
 
-	  if (call)
+      /* 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)
 	    {
-	      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;
+	    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;
 	    }
 	}
 
-      /* 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 ())
+      if (call)
 	{
-	  stmt = build4 (TARGET_EXPR, arg1_type, decl, arg1,
-			 NULL_TREE, NULL_TREE);
-	  SET_EXPR_LOCATION (stmt, loc);
+	  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;
 	}
-      else
+    }
+
+  /* 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)
 	{
-	  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);
+	  *res = resolved;
+	  return build_call_expr (call, 3, arg1, arg0, arg2);
 	}
+    }
 
-      innerptrtype = build_pointer_type (arg1_inner_type);
+  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);
+    }
 
-      /* 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;
+}
 
-      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.  */
 
-  /* 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)
+static tree
+resolve_VEC_STEP (resolution *res, vec<tree, va_gc> *arglist, unsigned nargs)
+{
+  if (nargs != 1)
     {
-      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;
-	}
+      error ("builtin %qs only accepts 1 argument", "vec_step");
+      *res = resolved;
+      return error_mark_node;
+    }
 
-      arg0 = (*arglist)[0];
-      arg1 = (*arglist)[1];
-      arg1_type = TREE_TYPE (arg1);
-      arg2 = fold_for_warn ((*arglist)[2]);
+  tree arg0 = (*arglist)[0];
+  tree arg0_type = TREE_TYPE (arg0);
 
-      if (TREE_CODE (arg1_type) != VECTOR_TYPE)
-	goto bad;
-      if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
-	goto bad;
+  if (TREE_CODE (arg0_type) != VECTOR_TYPE)
+    {
+      *res = resolved_bad;
+      return error_mark_node;
+    }
 
-      /* 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;
+  *res = resolved;
+  return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (arg0_type));
+}
 
-	  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);
+/* 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.  */
 
-	  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);
-	}
+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;
 
-      /* 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);
-	}
+  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 (TARGET_VSX)
+  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))
 	{
-	  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);
+	  tree ret_type = TREE_TYPE (inst->fntype);
+	  return altivec_build_resolved_builtin (args, 2, fntype, ret_type,
+						 inst->bifid, fcode);
 	}
       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;
+	*unsupported_builtin = true;
+    }
+
+  return error_mark_node;
+}
+
+/* Implementation of the resolve_overloaded_builtin target hook, to
+   support Altivec's overloaded builtins.  */
+
+tree
+altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
+				    void *passed_arglist)
+{
+  /* 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;
+
+  if (TARGET_DEBUG_BUILTIN)
+    fprintf (stderr, "altivec_resolve_overloaded_builtin, code = %4d, %s\n",
+	     (int) fcode, IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+
+  /* vec_lvsl and vec_lvsr are deprecated for use with LE element order.  */
+  if (fcode == RS6000_OVLD_VEC_LVSL && !BYTES_BIG_ENDIAN)
+    warning (OPT_Wdeprecated,
+	     "%<vec_lvsl%> is deprecated for little endian; use "
+	     "assignment for unaligned loads and stores");
+  else if (fcode == RS6000_OVLD_VEC_LVSR && !BYTES_BIG_ENDIAN)
+    warning (OPT_Wdeprecated,
+	     "%<vec_lvsr%> is deprecated for little endian; use "
+	     "assignment for unaligned loads and stores");
+
+  /* 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)
+    {
+    case RS6000_OVLD_VEC_MUL:
+      returned_expr = resolve_VEC_MUL (&res, arglist, nargs, loc);
+      break;
+
+    case RS6000_OVLD_VEC_CMPNE:
+      returned_expr = resolve_VEC_CMPNE (&res, arglist, nargs, loc);
+      break;
+
+    case RS6000_OVLD_VEC_ADDE:
+    case RS6000_OVLD_VEC_SUBE:
+      returned_expr = resolve_VEC_ADDE_SUBE (&res, fcode, arglist, nargs, loc);
+      break;
+
+    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 (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)
+    {
+      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)
-    {
-      if (TREE_CODE (types[0]) != VECTOR_TYPE)
-	goto bad;
+  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);
 
-      return build_int_cst (NULL_TREE, TYPE_VECTOR_SUBPARTS (types[0]));
-    }
+  /* Functions with no arguments can have only one overloaded instance.  */
+  gcc_assert (nargs > 0 || !instance->next);
 
-  {
-    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)
+  /* 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)
+    {
+      /* 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);
-
-	    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 (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);
 
-    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;
+    }
 
-	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;
 }
