Hi!
The following testcase is miscompiled for 2 reasons.
One missed optimization is that fold_stmt_1 doesn't seem to attempt to
use fold_const_call for internal functions into whose argument a constant
is propagated. For normal builtins, I think fold_stmt_1 -> gimple_fold_call
-> gimple_fold_builtin -> fold_call_stmt -> fold_builtin_n ->
fold_builtin_{1,2,3} -> fold_const_call optimizes it, but for internal
fns we just have selected set of optimizations we do on them but
fold_const_call isn't among those. That would be certainly nice to get
fixed, but this patch doesn't do that.
The other problem is forgotten flag. Because of the above missed
optimization we end up with .BITREVERSE (1) call before bitintlower, where
that 1 is unsigned _BitInt(129). If there is a partial limb (like in
this case 1 bit), we emit handling of the result partial limb in straight
line code (but that actually reads some bits from the least significant limb
of the function argument), and then have a loop to handle the remaining
limbs. We use there handle_operand with an index which is not constant
in the loop (obviously) and using this read any limbs in the argument in
reverse order, including the partial limb.
Now, handle_operand has an optimization, for most of the lowerings we have
a guarantee that the partial limb is accessed only in straight line code with
a constant index and e.g. for constants reading the partial limb in that
case simply folds into the constant value of that partial limb. So, we
optimize and emit into .rodata only fewer limbs (all but the partial one)
unless it is one of the even more optimized ones (where we treat 0, all ones
or values fitting into fewer limbs by always using a constant or by
extending at runtime). The exception to this has been the LSHIFT_EXPR
lowering and we have the m_var_msb flag to tell we need all the bits
including the partial limb. Given the way .BSWAP/.BITREVERSE expansion
works, we need that flag for those too.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2026-10-01 Jakub Jelinek <[email protected]>
PR middle-end/127669
* gimple-lower-bitint.cc (bitint_large_huge::lower_bswap_bitreverse):
Set m_var_msb to true.
* gcc.dg/torture/bitint-110.c: New test.
--- a/gcc/gimple-lower-bitint.cc 2026-09-18 11:03:21.676039449 +0200
+++ b/gcc/gimple-lower-bitint.cc 2026-09-30 14:29:15.870162767 +0200
@@ -6172,6 +6172,9 @@ bitint_large_huge::lower_bswap_bitrevers
unsigned prec = TYPE_PRECISION (type);
tree p = build_int_cst (sizetype,
prec / limb_prec - (prec % limb_prec == 0));
+ /* For .BSWAP/.BITREVERSE, we can use handle_operand with non-INTEGER_CST
+ idx even to access the most significant partial limb. */
+ m_var_msb = true;
/* For IFN .BSWAP or .BITREVERSE and
FN corresponding __builtin_bswapN or __builtin_bitreverseN where N
is limb_prec, lower
--- a/gcc/testsuite/gcc.dg/torture/bitint-110.c 2026-09-30 14:29:49.463717815
+0200
+++ b/gcc/testsuite/gcc.dg/torture/bitint-110.c 2026-09-30 14:31:29.115397910
+0200
@@ -0,0 +1,30 @@
+/* PR middle-end/127669 */
+/* { dg-do run { target bitint } } */
+
+#if __BITINT_MAXWIDTH__ >= 129
+typedef unsigned _BitInt(129) T;
+
+[[gnu::noipa]] static T
+foo (T v)
+{
+ return v;
+}
+
+static T
+bar ()
+{
+ return 1;
+}
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 129
+ T x = __builtin_bitreverseg ((T) 1);
+ T y = __builtin_bitreverseg (bar ());
+ T z = __builtin_bitreverseg (foo (1));
+ if (x != y || x != z)
+ __builtin_abort ();
+#endif
+}
Jakub