c++: Fix up mangling of __builtin_{bswap,bitreverse}g calls [PR127668]

Message ID ar35SnS2c_NmTf7M@tucnak
State New
Headers
Series c++: Fix up mangling of __builtin_{bswap,bitreverse}g calls [PR127668] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed

Commit Message

Jakub Jelinek Oct. 1, 2026, 6:10 a.m. UTC
  Hi!

The following testcases ICE during mangling, the dependent
__builtin_{bswap,bitreverse}g calls (where those identifiers
we treat as keywords and don't even have such builtins,
given that those are the first type-generic builtins with the
return type dependent on the argument type(s)) are emitted
as internal fn calls (IFN_BSWAP or IFN_BITREVERSE), and
during mangling we just ICE on any calls to internal functions.

The following patch mangles those 2 calls like we mangle e.g.
template <typename T>
auto foo (T t) -> decltype (__builtin_popcountg (t)) { return t; }

template int foo <unsigned> (unsigned);
- _Z3fooIjEDTcl19__builtin_popcountgfp_EET_ .  In
that case it is a type-generic builtin with a fixed return
type and we trigger the
  /* Mangle a dependent name as the name, not whatever happens to
     be the first function in the overload set.  */
  if (OVL_P (fn)
      && type_dependent_expression_p_push (expr))
    fn = OVL_NAME (fn);
case in the CALL_EXPR write_expression handling, so fn is
then an identifier.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-10-01  Jakub Jelinek  <jakub@redhat.com>

	PR c++/127668
	* mangle.cc (write_expression): Handle mangling of
	calls to IFN_BSWAP and IFN_BITREVERSE.

	* g++.dg/ext/builtin-bswapg-1.C: New test.
	* g++.dg/ext/builtin-bitreverseg-1.C: New test.


	Jakub
  

Comments

Jason Merrill Oct. 3, 2026, 7:49 a.m. UTC | #1
On 10/1/26 8:10 AM, Jakub Jelinek wrote:
> Hi!
> 
> The following testcases ICE during mangling, the dependent
> __builtin_{bswap,bitreverse}g calls (where those identifiers
> we treat as keywords and don't even have such builtins,
> given that those are the first type-generic builtins with the
> return type dependent on the argument type(s)) are emitted
> as internal fn calls (IFN_BSWAP or IFN_BITREVERSE), and
> during mangling we just ICE on any calls to internal functions.
> 
> The following patch mangles those 2 calls like we mangle e.g.
> template <typename T>
> auto foo (T t) -> decltype (__builtin_popcountg (t)) { return t; }
> 
> template int foo <unsigned> (unsigned);
> - _Z3fooIjEDTcl19__builtin_popcountgfp_EET_ .  In
> that case it is a type-generic builtin with a fixed return
> type and we trigger the
>    /* Mangle a dependent name as the name, not whatever happens to
>       be the first function in the overload set.  */
>    if (OVL_P (fn)
>        && type_dependent_expression_p_push (expr))
>      fn = OVL_NAME (fn);
> case in the CALL_EXPR write_expression handling, so fn is
> then an identifier.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Hmm, I'm a bit uncomfortable exposing builtins in mangling; relatedly, 
we reject mangling TRAIT_TYPE.  But OK.

Jason
  

Patch

--- a/gcc/cp/mangle.cc	2026-09-14 10:37:36.867105116 +0200
+++ b/gcc/cp/mangle.cc	2026-09-30 11:30:53.077749289 +0200
@@ -4094,20 +4094,35 @@  write_expression (tree expr)
       switch (code)
 	{
 	case CALL_EXPR:
-	  {
-	    tree fn = CALL_EXPR_FN (expr);
+	  if (tree fn = CALL_EXPR_FN (expr))
+	    {
+	      if (TREE_CODE (fn) == ADDR_EXPR)
+		fn = TREE_OPERAND (fn, 0);
 
-	    if (TREE_CODE (fn) == ADDR_EXPR)
-	      fn = TREE_OPERAND (fn, 0);
+	      /* Mangle a dependent name as the name, not whatever happens to
+		 be the first function in the overload set.  */
+	      if (OVL_P (fn)
+		  && type_dependent_expression_p_push (expr))
+		fn = OVL_NAME (fn);
 
-	    /* Mangle a dependent name as the name, not whatever happens to
-	       be the first function in the overload set.  */
-	    if (OVL_P (fn)
-		&& type_dependent_expression_p_push (expr))
-	      fn = OVL_NAME (fn);
-
-	    write_expression (fn);
-	  }
+	      write_expression (fn);
+	    }
+	  else
+	    switch (CALL_EXPR_IFN (expr))
+	      {
+	      /* Calls to __builtin_bswapg/__builtin_bitreverseg are parsed
+		 as IFN_BSWAP/IFN_BITREVERSE calls because we don't have
+		 those builtins but instead keywords.  Pretend they are
+		 builtin function template.  */
+	      case IFN_BSWAP:
+		write_source_name (get_identifier ("__builtin_bswapg"));
+		break;
+	      case IFN_BITREVERSE:
+		write_source_name (get_identifier ("__builtin_bitreverseg"));
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
 
 	  for (i = 0; i < call_expr_nargs (expr); ++i)
 	    write_expression (CALL_EXPR_ARG (expr, i));
--- a/gcc/testsuite/g++.dg/ext/builtin-bswapg-1.C	2026-09-30 11:42:29.019461158 +0200
+++ b/gcc/testsuite/g++.dg/ext/builtin-bswapg-1.C	2026-09-30 11:47:14.689650324 +0200
@@ -0,0 +1,8 @@ 
+// PR c++/127668
+// { dg-do compile { target c++11 } }
+// { dg-final { scan-assembler "_Z3fooIjEDTcl16__builtin_bswapgfp_EET_" } }
+
+template <typename T>
+auto foo (T t) -> decltype (__builtin_bswapg (t)) { return t; }
+
+template unsigned foo <unsigned> (unsigned);
--- a/gcc/testsuite/g++.dg/ext/builtin-bitreverseg-1.C	2026-09-30 11:42:51.873156288 +0200
+++ b/gcc/testsuite/g++.dg/ext/builtin-bitreverseg-1.C	2026-09-30 11:47:28.209469970 +0200
@@ -0,0 +1,8 @@ 
+// PR c++/127668
+// { dg-do compile { target c++11 } }
+// { dg-final { scan-assembler "_Z3fooIjEDTcl21__builtin_bitreversegfp_EET_" } }
+
+template <typename T>
+auto foo (T t) -> decltype (__builtin_bitreverseg (t)) { return t; }
+
+template unsigned foo <unsigned> (unsigned);