This patch is a revision of my previous patch to use add_optab in
the expansion of bitreverse and store_fixed_bitfield which improves
code generation on x86, cris, riscv, parisc, sh and possibly other
targets.
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728199.html

The AVR backend change has been approved by the AVR maintainers,
and a patch committed to avoid issues on AArch64, so all that
remains are the middle-end pieces.

This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures.  Ok for mainline?


2026-09-03  Roger Sayle  <[email protected]>

gcc/ChangeLog
        * expmed.cc (store_fixed_bit_field_1): Use new aop_optab when
        writing a value into a fixed size bitfield of a structure.
        * optabs.cc (expand_binop): If target doesn't provide a suitable
        aop<mode>3 instruction, intelligently use PLUS or IOR instead.
        (expand_binop): For rotations, use aop_optab to select AOP
        implementation instead of hard-coding add_optab.
        (expand_bitreverse): Likwise, Use aop_optab instead of ior_optab
        when composing swapped bits and nibbles (allows use of shift_add).
        * optabs.def (aop_optab): New named optab for any_or_plus.
        * rtl.def (AOP): New RTX code to capture any_or_plus semantics.

        * config/avr/avr.md (aop<mode>3): New define_expand to specify
        that IOR should always be used to implement any_or_plus (AOP).

gcc/testsuite/ChangeLog
        * gcc.target/i386/builtin-bitreverse-1.c: New test case.


Thanks in advance,
Roger
--

diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index 9c70e48e311..e403ba05693 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -1382,8 +1382,9 @@ store_fixed_bit_field_1 (rtx op0, scalar_int_mode mode,
 
   if (! all_zero)
     {
-      temp = expand_binop (mode, ior_optab, temp, value,
-                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
+      /* Use aop_optab if the field has been cleared.  */
+      temp = expand_binop (mode, all_one ? ior_optab : aop_optab,
+                          temp, value, NULL_RTX, 1, OPTAB_LIB_WIDEN);
       temp = force_reg (mode, temp);
     }
 
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index 012c0e9736e..fb9ecef8bcc 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -1632,6 +1632,16 @@ expand_binop (machine_mode mode, optab binoptab, rtx 
op0, rtx op1,
        }
     }
 
+  /* If backend's machine description doesn't specify an any_or_plus
+     (AOP) preference, choose for it.  */
+  if (binoptab == aop_optab)
+    {
+      binoptab = (mode == word_mode || mode == SImode) ? add_optab
+                                                      : ior_optab;
+      return expand_binop (mode, binoptab, op0, op1,
+                          target, unsignedp, methods);
+    }
+
   /* If this is a vector shift by a scalar, see if we can do a vector
      shift by a vector.  If so, broadcast the scalar into a vector.  */
   if (mclass == MODE_VECTOR_INT)
@@ -1975,7 +1985,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, 
rtx op1,
                                     NULL_RTX, unsignedp, next_methods);
 
          if (into_temp1 != 0 && into_temp2 != 0)
-           inter = expand_binop (word_mode, add_optab, into_temp1, into_temp2,
+           inter = expand_binop (word_mode, aop_optab, into_temp1, into_temp2,
                                  into_target, unsignedp, next_methods);
          else
            inter = 0;
@@ -1991,7 +2001,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, 
rtx op1,
                                      NULL_RTX, unsignedp, next_methods);
 
          if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
-           inter = expand_binop (word_mode, add_optab,
+           inter = expand_binop (word_mode, aop_optab,
                                  outof_temp1, outof_temp2,
                                  outof_target, unsignedp, next_methods);
 
@@ -3029,7 +3039,7 @@ expand_bitreverse (scalar_int_mode mode, rtx op0, rtx 
target)
                                NULL_RTX, true, OPTAB_LIB_WIDEN);
       if (lo == NULL_RTX) goto fail;
 
-      x = expand_binop (mode, ior_optab, hi, lo,
+      x = expand_binop (mode, aop_optab, hi, lo,
                        NULL_RTX, true, OPTAB_LIB_WIDEN);
       if (x == NULL_RTX) goto fail;
     }
@@ -3056,7 +3066,7 @@ expand_bitreverse (scalar_int_mode mode, rtx op0, rtx 
target)
                              NULL_RTX, true, OPTAB_LIB_WIDEN);
     if (lo == NULL_RTX) goto fail;
 
-    x = expand_binop (mode, ior_optab, hi, lo,
+    x = expand_binop (mode, aop_optab, hi, lo,
                      NULL_RTX, true, OPTAB_LIB_WIDEN);
     if (x == NULL_RTX) goto fail;
   }
@@ -3084,7 +3094,7 @@ expand_bitreverse (scalar_int_mode mode, rtx op0, rtx 
target)
                              NULL_RTX, true, OPTAB_LIB_WIDEN);
     if (lo == NULL_RTX) goto fail;
 
-    x = expand_binop (mode, ior_optab, hi, lo,
+    x = expand_binop (mode, aop_optab, hi, lo,
                      target, true, OPTAB_LIB_WIDEN);
     if (x == NULL_RTX) goto fail;
   }
diff --git a/gcc/optabs.def b/gcc/optabs.def
index 327e3efaf8a..0868187c544 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -573,3 +573,4 @@ OPTAB_D (mask_len_strided_load_optab, 
"mask_len_strided_load_$a")
 OPTAB_D (mask_len_strided_store_optab, "mask_len_strided_store_$a")
 OPTAB_D (andn_optab, "andn$a3")
 OPTAB_D (iorn_optab, "iorn$a3")
+OPTAB_DC (aop_optab, "aop$a3", AOP)
diff --git a/gcc/rtl.def b/gcc/rtl.def
index ae757929de2..cec11d83207 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -490,6 +490,7 @@ DEF_RTL_EXPR(UMOD, "umod", "ee", RTX_BIN_ARITH)
 DEF_RTL_EXPR(AND, "and", "ee", RTX_COMM_ARITH)
 DEF_RTL_EXPR(IOR, "ior", "ee", RTX_COMM_ARITH)
 DEF_RTL_EXPR(XOR, "xor", "ee", RTX_COMM_ARITH)
+DEF_RTL_EXPR(AOP, "aop", "ee", RTX_COMM_ARITH)
 DEF_RTL_EXPR(NOT, "not", "e", RTX_UNARY)
 
 /* Operand:
diff --git a/gcc/testsuite/gcc.target/i386/builtin-bitreverse-1.c 
b/gcc/testsuite/gcc.target/i386/builtin-bitreverse-1.c
new file mode 100644
index 00000000000..797f9018cd6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/builtin-bitreverse-1.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-gfni" } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bitreverse32 (x);
+}
+
+/* { dg-final { scan-assembler-times "leal" 2 } } */
+/* { dg-final { scan-assembler-not "orl" } } */

Reply via email to