On Thu, 4 Jun 2026, Jakub Jelinek wrote:
> Hi!
>
> I've noticed that we emit terrible code for the following testcase (all
> functions). We copy the _BitInt values from memory to a temporary and
> perform the bit query or bswap/bitreverse operations on the temporary when
> it could be done on the value in memory directly, and for bswap/bitreverse
> we can also merge it with a following store to (non-bitfield) memory.
> The only exception is that we need some temporary for the
> x = __builtin_{bswap,bitreverse}g (x);
> cases, when they load from the same memory as they store to. Because the
> implementation loops over all the limbs and swaps them, we need one
> temporary in that case. This is similar to e.g. multiplication/division.
> The bit-query ifns return a small integer scalar, so they don't need any
> merging with a store.
>
> The following patch (obviously on top of the __builtin_{bswap,bitreverse}g
> patch) implements this.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK.
Thanks,
Richard.
> 2026-06-04 Jakub Jelinek <[email protected]>
>
> * gimple-lower-bitint.cc (bitint_large_huge::lower_bswap_bitreverse):
> Add OBJ argument, use it instead of m_vars[part] if non-NULL. Don't
> gsi_remove stmt.
> (bitint_large_huge::lower_call): Adjust caller.
> (bitint_large_huge::lower_stmt): Handle store of
> IFN_BSWAP/IFN_BITREVERSE result.
> (stmt_needs_operand_addr): Return true also for
> IFN_BSWAP/IFN_BITREVERSE.
> (build_bitint_stmt_ssa_conflicts): Formatting fix.
> (gimple_lower_bitint): Allow merging IFN_BSWAP/IFN_BITREVERSE with
> subsequent non-bitfield store. Allow merging load of
> IFN_{CLZ,CTZ,CLRSB,FFS,PARITY,POPCOUNT,BSWAP,BITREVERSE} argument
> with the call.
>
> * gcc.dg/bitint-137.c: New test.
>
> --- gcc/gimple-lower-bitint.cc.jj 2026-06-03 11:49:04.879785702 +0200
> +++ gcc/gimple-lower-bitint.cc 2026-06-03 18:19:10.133853299 +0200
> @@ -474,7 +474,7 @@ struct bitint_large_huge
> void lower_cplxpart_stmt (tree, gimple *);
> void lower_complexexpr_stmt (gimple *);
> void lower_bit_query (gimple *);
> - void lower_bswap_bitreverse (gimple *);
> + void lower_bswap_bitreverse (tree, gimple *);
> void lower_call (tree, gimple *);
> void lower_asm (gimple *);
> void lower_stmt (gimple *);
> @@ -6110,7 +6110,7 @@ bitint_large_huge::lower_bit_query (gimp
> /* Lower a .{BSWAP,BITREVERSE} call with one large/huge _BitInt argument. */
>
> void
> -bitint_large_huge::lower_bswap_bitreverse (gimple *stmt)
> +bitint_large_huge::lower_bswap_bitreverse (tree obj, gimple *stmt)
> {
> tree arg = gimple_call_arg (stmt, 0);
> tree lhs = gimple_call_lhs (stmt);
> @@ -6126,9 +6126,12 @@ bitint_large_huge::lower_bswap_bitrevers
> gcc_assert (BITINT_TYPE_P (type));
> bitint_prec_kind kind = bitint_precision_kind (type);
> gcc_assert (kind >= bitint_prec_large);
> - int part = var_to_partition (m_map, lhs);
> - gcc_assert (m_vars[part] != NULL_TREE);
> - tree obj = m_vars[part];
> + if (!obj)
> + {
> + int part = var_to_partition (m_map, lhs);
> + gcc_assert (m_vars[part] != NULL_TREE);
> + obj = m_vars[part];
> + }
> enum internal_fn ifn = gimple_call_internal_fn (stmt);
> enum built_in_function bcode = END_BUILTINS;
> switch (limb_prec)
> @@ -6279,7 +6282,6 @@ bitint_large_huge::lower_bswap_bitrevers
> g = gimple_build_assign (l, build_zero_cst (m_limb_type));
> insert_before (g);
> }
> - gsi_remove (&gsi, true);
> }
>
> /* Lower a call statement with one or more large/huge _BitInt
> @@ -6313,7 +6315,7 @@ bitint_large_huge::lower_call (tree obj,
> return;
> case IFN_BSWAP:
> case IFN_BITREVERSE:
> - lower_bswap_bitreverse (stmt);
> + lower_bswap_bitreverse (obj, stmt);
> return;
> default:
> break;
> @@ -6626,6 +6628,17 @@ bitint_large_huge::lower_stmt (gimple *s
> lower_call (lhs, g);
> goto handled;
> }
> + else if (is_gimple_call (g) && gimple_call_internal_p (g))
> + switch (gimple_call_internal_fn (g))
> + {
> + case IFN_BSWAP:
> + case IFN_BITREVERSE:
> + lower_call (lhs, g);
> + goto handled;
> + default:
> + break;
> + }
> +
> m_loc = gimple_location (stmt);
> }
> }
> @@ -6780,9 +6793,20 @@ stmt_needs_operand_addr (gimple *stmt)
> default:
> break;
> }
> - else if (gimple_call_internal_p (stmt, IFN_MUL_OVERFLOW)
> - || gimple_call_internal_p (stmt, IFN_UBSAN_CHECK_MUL))
> - return true;
> + else if (is_gimple_call (stmt) && gimple_call_internal_p (stmt))
> + switch (gimple_call_internal_fn (stmt))
> + {
> + case IFN_MUL_OVERFLOW:
> + case IFN_UBSAN_CHECK_MUL:
> + return true;
> + /* These two actually don't take address, but reshuffle
> + all bytes or bits, so need similar treatment. */
> + case IFN_BSWAP:
> + case IFN_BITREVERSE:
> + return true;
> + default:
> + break;
> + }
> return false;
> }
>
> @@ -6995,8 +7019,7 @@ build_bitint_stmt_ssa_conflicts (gimple
> }
> }
> }
> - else if (is_gimple_call (stmt)
> - && gimple_call_internal_p (stmt))
> + else if (is_gimple_call (stmt) && gimple_call_internal_p (stmt))
> switch (gimple_call_internal_fn (stmt))
> {
> case IFN_ADD_OVERFLOW:
> @@ -7811,6 +7834,27 @@ gimple_lower_bitint (void)
> default:
> break;
> }
> + else if (is_gimple_call (SSA_NAME_DEF_STMT (s))
> + && gimple_call_internal_p (SSA_NAME_DEF_STMT (s)))
> + switch (gimple_call_internal_fn (SSA_NAME_DEF_STMT (s)))
> + {
> + case IFN_BSWAP:
> + case IFN_BITREVERSE:
> + if (gimple_store_p (use_stmt)
> + && is_gimple_assign (use_stmt)
> + && !gimple_has_volatile_ops (use_stmt)
> + && !stmt_ends_bb_p (use_stmt))
> + {
> + tree lhs = gimple_assign_lhs (use_stmt);
> + if (TREE_CODE (lhs) == COMPONENT_REF
> + && DECL_BIT_FIELD_TYPE (TREE_OPERAND (lhs, 1)))
> + break;
> + continue;
> + }
> + break;
> + default:
> + break;
> + }
> }
>
> /* Also ignore uninitialized uses. */
> @@ -7866,6 +7910,22 @@ gimple_lower_bitint (void)
> gimple *use_stmt = USE_STMT (use_p);
> if (is_gimple_debug (use_stmt))
> continue;
> + if (is_gimple_call (use_stmt)
> + && gimple_call_internal_p (use_stmt))
> + switch (gimple_call_internal_fn (use_stmt))
> + {
> + case IFN_CLZ:
> + case IFN_CTZ:
> + case IFN_CLRSB:
> + case IFN_FFS:
> + case IFN_PARITY:
> + case IFN_POPCOUNT:
> + case IFN_BSWAP:
> + case IFN_BITREVERSE:
> + continue;
> + default:
> + break;
> + }
> if (gimple_code (use_stmt) == GIMPLE_PHI
> || is_gimple_call (use_stmt)
> || gimple_code (use_stmt) == GIMPLE_ASM
> --- gcc/testsuite/gcc.dg/bitint-137.c.jj 2026-06-04 09:13:38.434003292
> +0200
> +++ gcc/testsuite/gcc.dg/bitint-137.c 2026-06-04 09:13:32.304084847 +0200
> @@ -0,0 +1,66 @@
> +/* { dg-do compile { target bitint575 } } */
> +/* { dg-options "-O2" } */
> +
> +unsigned _BitInt(575) x, y;
> +signed _BitInt(575) z;
> +unsigned _BitInt(568) v, w;
> +
> +int
> +foo ()
> +{
> + return __builtin_clzg (x, 575);
> +}
> +
> +int
> +bar ()
> +{
> + return __builtin_ctzg (x, 575);
> +}
> +
> +int
> +baz ()
> +{
> + return __builtin_clrsbg (z);
> +}
> +
> +int
> +qux ()
> +{
> + return __builtin_ffsg (z);
> +}
> +
> +int
> +corge ()
> +{
> + return __builtin_parityg (x);
> +}
> +
> +int
> +garply ()
> +{
> + return __builtin_popcountg (x);
> +}
> +
> +void
> +fred ()
> +{
> + v = __builtin_bswapg (v);
> +}
> +
> +void
> +grault ()
> +{
> + w = __builtin_bswapg (v);
> +}
> +
> +void
> +waldo ()
> +{
> + x = __builtin_bitreverseg (x);
> +}
> +
> +void
> +xyzzy ()
> +{
> + y = __builtin_bitreverseg (x);
> +}
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)