This patch tweaks arm.md to ensure that it is AOP-safe, i.e. can recognize
bfi and rev16 instructions when expressed in RTL using either PLUS, IOR or
XOR.  The new test cases confirm this behaviour.  The tree-level optimizers
currently canonicalize expressions to use IOR_EXPR, but the changes below
ensure things will continue to work should this change, and also catch
alternate representations (potentially) discovered by combine.

Updating aarch_rev16_p is a pre-requisite to making aarch64 AOP-safe,
which in turn is a pre-requisite to fixing PR tree-optimization/108477.

The new rev16_{3,4}.c testcases are clones of rev16_{1,2}.c checking
that ^ and + produce the same output/results as |.

This patch has been tested on arm-linux-gnueabihf with make bootstrap
and make -k check with no new failures.  OK for mainline?


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

gcc/ChangeLog
        * config/arm/aarch-common.cc (aarch_rev16-p): Allow the rev16
        pattern to be expressed as either IOR, XOR or PLUS.
        * config/arm/arm/arm.cc (arm_bfi_1_p): Allow the bfi pattern
        to be expressed as either IOR, XOR or PLUS.
        (arm_rtx_costs_internal) <case XOR>: Treat like IOR, so that
        bfi and rev16 instructions are correctly costed when expressed
        using XOR.
        * config/arm/arm.md (*bfi): Use new ANYORPLUS code iterator.
        (*bfi_alt1): Likewise.
        (*bfi_alt2): Likewise.
        (*bfi_alt3): Likewise.
        (arm_rev16si2_alt1): Use new ANYORPLUS code iterator.  Use
        aop_pref_ior prefix so that gen_insn constructs the IOR form.
        (*arm_rev6si2_alt2): Use new ANYORPLUS code iterator.
        * config/arm/iterators.md (ANYORPLUS): New code iterator.
        (aop_pref_ior): New code attribute to produce "*" for XOR/PLUS.

gcc/testsuite/ChangeLog
        * gcc.target/arm/bfi.c: New test case.
        * gcc.target/arm/rev16_3.c: Likewise.
        * gcc.target/arm/rev16_4.c: Likewise.


Thanks in advance,
Roger
--

diff --git a/gcc/config/arm/aarch-common.cc b/gcc/config/arm/aarch-common.cc
index 197952ee7df..6d52328daea 100644
--- a/gcc/config/arm/aarch-common.cc
+++ b/gcc/config/arm/aarch-common.cc
@@ -183,7 +183,9 @@ aarch_rev16_p (rtx x)
   rtx left_sub_rtx, right_sub_rtx;
   bool is_rev = false;
 
-  if (GET_CODE (x) != IOR)
+  if (GET_CODE (x) != IOR
+      && GET_CODE (x) != XOR
+      && GET_CODE (x) != PLUS)
     return false;
 
   left_sub_rtx = XEXP (x, 0);
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 33b64995442..84adb395a32 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -10570,7 +10570,9 @@ arm_bfi_1_p (rtx op0, rtx op1, rtx *sub0, rtx *sub1)
 static bool
 arm_bfi_p (rtx x, rtx *sub0, rtx *sub1)
 {
-  if (GET_CODE (x) != IOR)
+  if (GET_CODE (x) != IOR
+      && GET_CODE (x) != XOR
+      && GET_CODE (x) != PLUS)
     return false;
   if (GET_CODE (XEXP (x, 0)) != AND
       || GET_CODE (XEXP (x, 1)) != AND)
@@ -11337,6 +11339,7 @@ arm_rtx_costs_internal (rtx x, enum rtx_code code, enum 
rtx_code outer_code,
       *cost = LIBCALL_COST (2);
       return false;
     case IOR:
+    case XOR:
       {
        rtx sub0, sub1;
        if (mode == SImode && arm_arch6 && aarch_rev16_p (x))
@@ -11359,7 +11362,7 @@ arm_rtx_costs_internal (rtx x, enum rtx_code code, enum 
rtx_code outer_code,
       }
 
       /* Fall through.  */
-    case AND: case XOR:
+    case AND:
       if (mode == SImode)
        {
          enum rtx_code subcode = GET_CODE (XEXP (x, 0));
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index febff17df0b..b2a7efd45cb 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -3517,7 +3517,8 @@
 
 (define_insn "*bfi"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
-       (ior:SI (and:SI (match_operand:SI 1 "s_register_operand" "0")
+       (ANYORPLUS:SI
+               (and:SI (match_operand:SI 1 "s_register_operand" "0")
                        (match_operand 2 "const_int_operand" "Dj"))
                (and:SI (ashift:SI
                         (match_operand:SI 3 "s_register_operand" "r")
@@ -3537,7 +3538,8 @@
 
 (define_insn "*bfi_alt1"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
-       (ior:SI (and:SI (ashift:SI
+       (ANYORPLUS:SI
+               (and:SI (ashift:SI
                         (match_operand:SI 3 "s_register_operand" "r")
                         (match_operand 4 "const_int_operand" "i"))
                        (match_operand 5 "const_int_operand" "i"))
@@ -3557,7 +3559,8 @@
 
 (define_insn "*bfi_alt2"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
-       (ior:SI (and:SI (match_operand:SI 1 "s_register_operand" "0")
+       (ANYORPLUS:SI
+               (and:SI (match_operand:SI 1 "s_register_operand" "0")
                        (match_operand 2 "const_int_operand" "i"))
                (and:SI (match_operand:SI 3 "s_register_operand" "r")
                        (match_operand 4 "const_int_operand" "i"))))]
@@ -3572,7 +3575,8 @@
 
 (define_insn "*bfi_alt3"
   [(set (match_operand:SI 0 "s_register_operand" "=r")
-       (ior:SI (and:SI (match_operand:SI 3 "s_register_operand" "r")
+       (ANYORPLUS:SI
+               (and:SI (match_operand:SI 3 "s_register_operand" "r")
                        (match_operand 4 "const_int_operand" "i"))
                (and:SI (match_operand:SI 1 "s_register_operand" "0")
                        (match_operand 2 "const_int_operand" "i"))))]
@@ -12587,9 +12591,10 @@
 ;; operations within an IOR/AND RTX, therefore we have two patterns matching
 ;; each valid permutation.
 
-(define_insn "arm_rev16si2_alt1"
+(define_insn "<aop_pref_ior>arm_rev16si2_alt1"
   [(set (match_operand:SI 0 "register_operand" "=l,l,r")
-        (ior:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" 
"l,l,r")
+        (ANYORPLUS:SI
+               (and:SI (ashift:SI (match_operand:SI 1 "register_operand" 
"l,l,r")
                                    (const_int 8))
                         (match_operand:SI 3 "const_int_operand" "n,n,n"))
                 (and:SI (lshiftrt:SI (match_dup 1)
@@ -12610,7 +12615,8 @@
 
 (define_insn "*arm_rev16si2_alt2"
   [(set (match_operand:SI 0 "register_operand" "=l,l,r")
-        (ior:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" 
"l,l,r")
+        (ANYORPLUS:SI
+               (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" 
"l,l,r")
                                      (const_int 8))
                         (match_operand:SI 2 "const_int_operand" "n,n,n"))
                 (and:SI (ashift:SI (match_dup 1)
diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md
index c8fd31b52c5..a137e016621 100644
--- a/gcc/config/arm/iterators.md
+++ b/gcc/config/arm/iterators.md
@@ -367,6 +367,8 @@
 
 (define_code_iterator LOGICAL [and ior xor])
 
+(define_code_iterator ANYORPLUS [ior xor plus])
+
 ;; Operations on two halves of a quadword vector.
 (define_code_iterator VQH_OPS [plus smin smax umin umax])
 
@@ -3044,3 +3046,6 @@
                               (VADCQ_M_S "VADCQ_M_S_carry")
                               (VSBCQ_M_U "VSBCQ_M_U_carry")
                               (VSBCQ_M_S "VSBCQ_M_S_carry")])
+
+;; Prefix to generate IOR form of ANYORPLUS
+(define_code_attr aop_pref_ior [(ior "") (xor "*") (plus "*")])
diff --git a/gcc/testsuite/gcc.target/arm/bfi.c 
b/gcc/testsuite/gcc.target/arm/bfi.c
new file mode 100644
index 00000000000..e9156d9b7b8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/bfi.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=armv6t2" } */
+
+int foo(int x, int y)
+{
+  return (x&~0x38) + ((y&7) << 3);
+}
+
+int bar(int x, int y)
+{
+  return (x&~0x38) | ((y&7) << 3);
+}
+
+int baz(int x, int y)
+{
+  return (x&~0x38) ^ ((y&7) << 3);
+}
+
+/* { dg-final { scan-assembler-times "\tbfi\t" 3 } } */
diff --git a/gcc/testsuite/gcc.target/arm/rev16_3.c 
b/gcc/testsuite/gcc.target/arm/rev16_3.c
new file mode 100644
index 00000000000..37075a921bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/rev16_3.c
@@ -0,0 +1,55 @@
+/* { dg-options "-O2" } */
+/* { dg-do run } */
+
+extern void abort (void);
+
+typedef unsigned int __u32;
+
+__u32
+__rev16_32_xor_alt (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0xff00ff00UL) >> 8)
+         ^ (((__u32)(x) & (__u32)0x00ff00ffUL) << 8);
+}
+
+__u32
+__rev16_32_xor (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0x00ff00ffUL) << 8)
+         ^ (((__u32)(x) & (__u32)0xff00ff00UL) >> 8);
+}
+
+__u32
+__rev16_32_plus_alt (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0xff00ff00UL) >> 8)
+         + (((__u32)(x) & (__u32)0x00ff00ffUL) << 8);
+}
+
+__u32
+__rev16_32_plus (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0x00ff00ffUL) << 8)
+         + (((__u32)(x) & (__u32)0xff00ff00UL) >> 8);
+}
+
+int
+main (void)
+{
+  volatile __u32 in32 = 0x12345678;
+  volatile __u32 expected32 = 0x34127856;
+
+  if (__rev16_32_xor (in32) != expected32)
+    abort ();
+
+  if (__rev16_32_xor_alt (in32) != expected32)
+    abort ();
+
+  if (__rev16_32_plus (in32) != expected32)
+    abort ();
+
+  if (__rev16_32_plus_alt (in32) != expected32)
+    abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/arm/rev16_4.c 
b/gcc/testsuite/gcc.target/arm/rev16_4.c
new file mode 100644
index 00000000000..7df7a1d1cad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/rev16_4.c
@@ -0,0 +1,34 @@
+/* { dg-options "-O2" } */
+/* { dg-do compile } */
+
+typedef unsigned int __u32;
+
+__u32
+__rev16_32_xor_alt (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0xff00ff00UL) >> 8)
+         ^ (((__u32)(x) & (__u32)0x00ff00ffUL) << 8);
+}
+
+__u32
+__rev16_32_xor (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0x00ff00ffUL) << 8)
+         ^ (((__u32)(x) & (__u32)0xff00ff00UL) >> 8);
+}
+
+__u32
+__rev16_32_plus_alt (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0xff00ff00UL) >> 8)
+         + (((__u32)(x) & (__u32)0x00ff00ffUL) << 8);
+}
+
+__u32
+__rev16_32_plus (__u32 x)
+{
+  return (((__u32)(x) & (__u32)0x00ff00ffUL) << 8)
+         + (((__u32)(x) & (__u32)0xff00ff00UL) >> 8);
+}
+
+/* { dg-final { scan-assembler-times {rev16\tr[0-9]+, r[0-9]+} 4 { xfail 
arm_thumb1_ok } } } */

Reply via email to