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 } } } */