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?

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

Reply via email to