[v3] rs6000: Fix spurious -flax-vector-conversions for overloaded builtins

Message ID 20260930140153.1124362-1-kishan@linux.ibm.com
State New
Headers
Series [v3] rs6000: Fix spurious -flax-vector-conversions for overloaded builtins |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Kishan Parmar Sept. 30, 2026, 2:01 p.m. UTC
  Hello All,

Changes from v2:
	- Add line "PR target/104930" in commit message.

Changes from v1:
	- Dropped rs6000-c.cc change. Resolver needs no modification
	  since v2 does not have opaque instance entries.
	- Dropped isopaque field on typeinfo and the corresponding
	  complete_vector_type() change; opaque string is now built in
	  a self-contained helper make_opaque_fndecl().
	- Dropped parse_first_ovld_entry() and
	  maybe_generate_opaque_variant(). v2 does not insert synthetic
	  ovlddata entries into the ovlds[] array.
	- Removed the _VOP / __VOP enum entries and the wrong bifids
	  they introduced.

Thank You,
Kishan

Before r12-5752 (gd08236359eb229), the public overload declaration for
overloaded PowerPC built-in functions used opaque_V4SI_type_node for all
vector argument and return types.  The C front end's compatibility check
(vector_types_convertible_p in c-family/c-common.cc) has an early-exit
for TYPE_VECTOR_OPAQUE types, so mis-typed vector arguments were passed
silently at that stage and left for altivec_resolve_overloaded_builtin()
to diagnose properly.

r12-5752 switched the new built-in generator to register each overload's
public declaration using the concrete vector type of the first instance
prototype.  This reactivated the front end's early vector-type
check, causing a spurious "use -flax-vector-conversions to permit
conversions between vectors with differing element types"
note before the overload resolver ran, obscuring the real diagnostic
such as "too few arguments".

Fix this in rs6000-gen-builtins.cc by restoring the opaque prototype for
the public overload declaration whenever the overload has by-value
vector arguments or a by-value vector return type.  A new "vop4si"
entry is added to type_map[], mapping to opaque_V4SI_type_node, and a
new helper make_opaque_fndecl() builds the function-type identifier
string with every by-value vector position replaced by "vop4si".
Vector-pointer arguments (e.g. vsc *) remain as it was, because
front-end's pointer-type check does not emit the 
-flax-vector-conversions note anyway.

2026-09-30  Kishan Parmar  <kishan@linux.ibm.com>

gcc/ChangeLog:
	PR target/104930
	* config/rs6000/rs6000-gen-builtins.cc (type_map): Add "vop4si"
	entry mapping to opaque_V4SI.
	(make_opaque_fndecl): New function.  Build a function-type
	identifier string like construct_fntype_id() but with every
	by-value vector position replaced by "vop4si".
	(proto_has_vector_arg): New function.  Return true if a
	prototype has at least one vector argument.
	(write_init_ovld_table): Introduce reg_fndecl. Use
	make_opaque_fndecl() for the public overload declaration when
	the stanza has vector arguments, otherwise use the first
	instance's concrete fndecl as before.  Update the generated
	comment in the output file.  Free reg_fndecl after use when
	it was malloc'd by make_opaque_fndecl().
	(write_init_file): Add pre-pass loop to call
	make_opaque_fndecl() for every stanza with vector arguments
	before the fntype_rbt inorder walk, so that the opaque type
	strings are declared before they are used.

gcc/testsuite/ChangeLog:
	PR target/104930
	* gcc.target/powerpc/pr104930.c: New test.
---
 gcc/config/rs6000/rs6000-gen-builtins.cc | 179 ++++++++++++++++++++++-
 1 file changed, 171 insertions(+), 8 deletions(-)
  

Patch

diff --git a/gcc/config/rs6000/rs6000-gen-builtins.cc b/gcc/config/rs6000/rs6000-gen-builtins.cc
index 08068ba78fd..76dd33bbb90 100644
--- a/gcc/config/rs6000/rs6000-gen-builtins.cc
+++ b/gcc/config/rs6000/rs6000-gen-builtins.cc
@@ -600,6 +600,7 @@  static typemap type_map[] =
     { "v4sf",		"V4SF" },
     { "v4si",		"V4SI" },
     { "v8hi",		"V8HI" },
+    { "vop4si",		"opaque_V4SI" },
     { "vp8hi",		"pixel_V8HI" },
   };
 
@@ -2772,15 +2773,145 @@  write_init_bif_table (void)
     }
 }
 
+/* Build a function-type identifier string like construct_fntype_id() does,
+   but replace every by-value vector position with "vop4si" (opaque_V4SI).
+   This string is used as the public overload declaration so the C front end
+   does not complain about vector type mismatches before the overload resolver
+   runs (PR target/104930).
+
+   Rules for each position:
+     - by-value vector (isvector=1, ispointer=0): use "vop4si".
+     - vector pointer (isvector=1, ispointer=1): As it was, e.g. "pv16qi".
+       Pointer arguments never trigger the -flax-vector-conversions note anyway.
+     - everything else: same as construct_fntype_id(), no change.  */
+
+static char *
+make_opaque_fndecl (prototype *protoptr)
+{
+  /* Use the same size estimate as construct_fntype_id.  "vop4si" is 6 chars,
+     same worst-case length as "bv16qi", within the 9-char per-type budget.  */
+  int len = protoptr->nargs ? (protoptr->nargs + 1) * 9 + 6 : 17;
+  char *buf = (char *) malloc (len);
+  int bufi = 0;
+
+  //  Return type
+  if (protoptr->rettype.ispointer)
+    buf[bufi++] = 'p';
+
+  if (protoptr->rettype.isvoid)
+    buf[bufi++] = 'v';
+  else if (protoptr->rettype.isvector && !protoptr->rettype.ispointer)
+    {
+      memcpy (&buf[bufi], "vop4si", 6);
+      bufi += 6;
+    }
+  else
+    {
+      /* Scalar return, or vector-pointer return (e.g. vsc *): emit
+	 the concrete type, identical to construct_fntype_id().  */
+      if (protoptr->rettype.isunsigned)
+	buf[bufi++] = 'u';
+      if (protoptr->rettype.isvector)
+	complete_vector_type (&protoptr->rettype, buf, &bufi);
+      else
+	complete_base_type (&protoptr->rettype, buf, &bufi);
+    }
+
+  memcpy (&buf[bufi], "_ftype", 6);
+  bufi += 6;
+
+  // Argument types
+  if (!protoptr->nargs)
+    {
+      memcpy (&buf[bufi], "_v", 2);
+      bufi += 2;
+    }
+  else
+    {
+      typelist *argptr = protoptr->args;
+      for (int i = 0; i < protoptr->nargs; i++, argptr = argptr->next)
+	{
+	  assert (argptr);
+	  buf[bufi++] = '_';
+
+	  if (argptr->info.isvector && !argptr->info.ispointer)
+	    {
+	      memcpy (&buf[bufi], "vop4si", 6);
+	      bufi += 6;
+	    }
+	  else
+	    {
+	      /* Non-vector argument, or vector-pointer argument (e.g. vsc *,
+		 const vbc *): emit the concrete type, identical to
+		 construct_fntype_id().  */
+	      if (argptr->info.isconst
+		  && argptr->info.base == BT_INT
+		  && !argptr->info.ispointer)
+		{
+		  buf[bufi++] = 'c';
+		  buf[bufi++] = 'i';
+		  continue;
+		}
+	      if (argptr->info.ispointer)
+		{
+		  if (argptr->info.isvoid)
+		    {
+		      if (argptr->info.isconst)
+			{
+			  memcpy (&buf[bufi], "pcvoid", 6);
+			  bufi += 6;
+			  continue;
+			}
+		      else
+			{
+			  buf[bufi++] = 'p';
+			  buf[bufi++] = 'v';
+			  continue;
+			}
+		    }
+		  else
+		    buf[bufi++] = 'p';
+		}
+	      if (argptr->info.isunsigned)
+		buf[bufi++] = 'u';
+	      if (argptr->info.isvector)
+		complete_vector_type (&argptr->info, buf, &bufi);
+	      else
+		complete_base_type (&argptr->info, buf, &bufi);
+	    }
+	}
+      assert (!argptr);
+    }
+
+  buf[bufi] = '\0';
+
+  rbt_insert (&fntype_rbt, buf);
+
+  return buf;
+}
+
+/* Return true if PROTOPTR has at least one vector argument.  */
+static bool
+proto_has_vector_arg (prototype *protoptr)
+{
+  for (typelist *arg = protoptr->args; arg; arg = arg->next)
+    if (arg->info.isvector)
+      return true;
+  return false;
+}
+
 /* Write code to initialize the overload table.  */
 static void
 write_init_ovld_table (void)
 {
   fprintf (init_file, "  int base = RS6000_OVLD_NONE;\n\n");
   fprintf (init_file,
-	   "  /* The fndecl for an overload is arbitrarily the first one\n"
-	   "     for the overload.  We sort out the real types when\n"
-	   "     processing the overload in the gcc front end.  */\n");
+	   "  /* The fndecl for the public overload declaration is an opaque-\n"
+	   "     typed prototype when the overload has vector arguments.  This\n"
+	   "     avoids a spurious -flax-vector-conversions note in the C/C++\n"
+	   "     front end before altivec_resolve_overloaded_builtin() runs.\n"
+	   "     See PR target/104930.  For overloads without vector arguments\n"
+	   "     the first instance's concrete type is used, as before.  */\n");
 
   for (int i = 0; i <= curr_ovld; i++)
     {
@@ -2794,16 +2925,33 @@  write_init_ovld_table (void)
 	  ovld_stanza *stanza = &ovld_stanzas[ovlds[i].stanza];
 	  fprintf (init_file, "\n");
 
+	  /* If this stanza has vector arguments, register its public
+	     declaration with an opaque prototype so that the front end's
+	     early argument-type check does not fire before the overload
+	     resolver can inspect the actual argument types.  Otherwise
+	     use the first instance's concrete fndecl, as before.
+	     make_opaque_fndecl() returns a malloc'd buffer, on duplicate
+	     the RBT did not take ownership so we must free it after use.  */
+	  char *reg_fndecl;
+	  bool free_reg_fndecl = false;
+	  if (proto_has_vector_arg (&ovlds[i].proto))
+	    {
+	      reg_fndecl = make_opaque_fndecl (&ovlds[i].proto);
+	      free_reg_fndecl = true;
+	    }
+	  else
+	    reg_fndecl = ovlds[i].fndecl;
+
 	  /* Check whether we have a "tf" token in this string, representing
 	     a float128_type_node.  It's possible that float128_type_node is
 	     undefined (occurs for -maltivec -mno-vsx, for example), so we
 	     must guard against that.  */
-	  int tf_found = strstr (ovlds[i].fndecl, "tf") != NULL;
+	  int tf_found = strstr (reg_fndecl, "tf") != NULL;
 
 	  /* Similarly, look for decimal float tokens.  */
-	  int dfp_found = (strstr (ovlds[i].fndecl, "sd") != NULL
-			   || strstr (ovlds[i].fndecl, "dd") != NULL
-			   || strstr (ovlds[i].fndecl, "td") != NULL);
+	  int dfp_found = (strstr (reg_fndecl, "sd") != NULL
+			   || strstr (reg_fndecl, "dd") != NULL
+			   || strstr (reg_fndecl, "td") != NULL);
 
 	  if (tf_found)
 	    {
@@ -2824,7 +2972,7 @@  write_init_ovld_table (void)
 		   stanza->intern_name);
 	  fprintf (init_file,
 		   "                            %s,\n",
-		   ovlds[i].fndecl);
+		   reg_fndecl);
 	  fprintf (init_file,
 		   "                            (int)RS6000_OVLD_%s,"
 		   " BUILT_IN_MD,\n",
@@ -2844,6 +2992,9 @@  write_init_ovld_table (void)
 	  fprintf (init_file,
 		   "    = RS6000_INST_%s;\n\n",
 		   ovlds[i].ovld_id_name);
+
+	  if (free_reg_fndecl)
+	    free (reg_fndecl);
 	}
     }
 }
@@ -2871,6 +3022,18 @@  write_init_file (void)
   write_bif_static_init ();
   write_ovld_static_init ();
 
+  /*   We call make_opaque_fndecl() here first (before the RBT walk below)
+     so that the opaque type strings land in fntype_rbt in time for
+     write_fntype_init() to emit their tree-variable declarations.
+     write_init_ovld_table() calls make_opaque_fndecl() a second time to
+     get the string for fprintf,that second rbt_insert() is a no-op
+     since the string is already in the tree.  Two calls, but the second
+     one is cheap and avoids storing extra pointers in ovld_stanzas.  */
+  for (int i = 0; i <= curr_ovld; i++)
+    if (i == 0 || ovlds[i].stanza != ovlds[i-1].stanza)
+      if (proto_has_vector_arg (&ovlds[i].proto))
+	make_opaque_fndecl (&ovlds[i].proto);
+
   /* The reason to pass pointers to the function instead of accessing
      the rs6000_{{builtin,instance}_info_fntype,overload_info,builtin_decls}
      arrays directly is to decrease size of the already large function and