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(-)
@@ -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