https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127668
Bug ID: 127668
Summary: GCC `__builtin_bitreverseg` ICEs (segfault in the C++
mangler) when applied to a dependent operand in a
template signature
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: bic60176 at gmail dot com
Target Milestone: ---
## Summary
Using `__builtin_bitreverseg` on a dependent operand anywhere in a C++ function
template's *signature* (trailing return type, parameter type, alias template,
template argument, …) makes GCC segfault with an internal compiler error once
the template is instantiated. The C++ front end builds the dependent call with
`CALL_EXPR_FN == NULL_TREE` (an internal-function call), and `write_expression`
in `gcc/cp/mangle.cc` dereferences `CALL_EXPR_FN` unconditionally.
`__builtin_bswapg`, which shares the code path, crashes identically.
Reproducing the Bug
```python
import cc_harness
src = """
template <typename T>
auto f (T t) -> decltype (__builtin_bitreverseg (t)) { return t; }
template unsigned f<unsigned> (unsigned);
"""
r = cc_harness.compile_only(src, "c++")
print(r.compiled, r.crash_kind)
print(r.stderr)
```
Output:
```
False ice
prog.cc: In instantiation of 'decltype (BITREVERSE(t)) f(T) [with T = unsigned
int; ...]':
prog.cc:2:6: internal compiler error: Segmentation fault
0x1d9062b crash_signal(int)
0xf637cc write_expression(tree_node*)
0xf6d5a2 write_type(tree_node*)
0xf70524 write_bare_function_type(tree_node*, int, tree_node*)
0xf7060c write_encoding(tree_node*)
0xf72a0a write_mangled_name(tree_node*, bool)
...
```
Fix
Handle the internal-function form of `CALL_EXPR` in `write_expression`,
mangling
it as an Itanium-ABI vendor extended expression (`u <source-name> <expr>* E`)
rather than falling into the ordinary `cl` path that assumes a callee tree.
```diff
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -4058,6 +4058,20 @@ write_expression (tree expr)
return;
}
+ /* Internal-function calls such as __builtin_bswapg and
+ __builtin_bitreverseg have a null CALL_EXPR_FN; mangle them as a
+ vendor extended expression instead of the ordinary "cl" form. */
+ if (code == CALL_EXPR && CALL_EXPR_FN (expr) == NULL_TREE)
+ {
+ const char *fname = (CALL_EXPR_IFN (expr) == IFN_BSWAP
+ ? "__builtin_bswapg"
+ : "__builtin_bitreverseg");
+ write_char ('u');
+ write_source_name (get_identifier (fname));
+ for (i = 0; i < call_expr_nargs (expr); ++i)
+ write_expression (CALL_EXPR_ARG (expr, i));
+ write_char ('E');
+ return;
+ }
+