When combine forms an invalid MEM address (plus X C), find_split_point
splits out X, or gives up if X is a register; neither helps when C is
not a valid offset either.  Split out C instead in that case, so that
I3 can keep X, for example a scaled index, in the address.  A valid
offset stays where it is, since splitting it out needs one more
register.  I2DEST receives the constant, so try_combine now checks that
it has the mode of the address.

On arm, ((volatile unsigned int *) 0x20a04000)[i] = v becomes a constant
load and str v, [rC, ri, lsl #2] instead of lsl, add, add and str; on
aarch64 a register plus a mov/movk constant is no longer added first.
A Raspberry Pi firmware (arm1176, -O2 -flto) shrinks by 96 bytes.

Bootstrapped and regression tested on x86_64-pc-linux-gnu, and regression
tested on arm-none-eabi and aarch64-linux-gnu.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * combine.cc (address_constant_mode_mismatch_p): New function.
        (try_combine): Do not split out an address constant into an I2DEST
        of another mode.
        (find_split_point): Split out a MEM address constant that is not a
        valid offset.

gcc/testsuite/ChangeLog:

        * gcc.dg/rtl/aarch64/split-const-offset-1.c: New test.
        * gcc.dg/torture/split-const-offset-1.c: New test.
        * gcc.target/aarch64/split-const-offset-1.c: New test.
        * gcc.target/arm/split-const-offset-1.c: New test.
        * gcc.target/arm/split-const-offset-2.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
OK for trunk?

 gcc/combine.cc                                | 27 +++++++
 .../gcc.dg/rtl/aarch64/split-const-offset-1.c | 71 +++++++++++++++++++
 .../gcc.dg/torture/split-const-offset-1.c     | 51 +++++++++++++
 .../gcc.target/aarch64/split-const-offset-1.c | 28 ++++++++
 .../gcc.target/arm/split-const-offset-1.c     | 31 ++++++++
 .../gcc.target/arm/split-const-offset-2.c     | 20 ++++++
 6 files changed, 228 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/rtl/aarch64/split-const-offset-1.c
 create mode 100644 gcc/testsuite/gcc.dg/torture/split-const-offset-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/split-const-offset-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/split-const-offset-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/split-const-offset-2.c

diff --git a/gcc/combine.cc b/gcc/combine.cc
index 11427f5a920..255f32581a4 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -2508,6 +2508,21 @@ count_auto_inc (rtx, rtx, rtx, rtx, rtx, void *arg)
   return 0;
 }
 
+/* Return true if LOC is the constant term of a MEM address in X and MODE
+   is not the mode of that address.  */
+
+static bool
+address_constant_mode_mismatch_p (rtx x, rtx *loc, machine_mode mode)
+{
+  subrtx_var_iterator::array_type array;
+  FOR_EACH_SUBRTX_VAR (iter, array, x, NONCONST)
+    if (MEM_P (*iter)
+       && GET_CODE (XEXP (*iter, 0)) == PLUS
+       && &XEXP (XEXP (*iter, 0), 1) == loc)
+      return GET_MODE (XEXP (*iter, 0)) != mode;
+  return false;
+}
+
 /* Try to combine the insns I0, I1 and I2 into I3.
    Here I0, I1 and I2 appear earlier than I3.
    I0 and I1 can be zero; then we combine just I2 into I3, or I1 and I2 into
@@ -3738,6 +3753,8 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, 
rtx_insn *i0,
              || GET_MODE (*split) == VOIDmode
              || can_change_dest_mode (i2dest, added_sets_2,
                                       GET_MODE (*split)))
+         && !address_constant_mode_mismatch_p (newpat, split,
+                                               GET_MODE (i2dest))
          && (next_nonnote_nondebug_insn (i2) == i3
              || !modified_between_p (*split, i2, i3))
          /* We can't overwrite I2DEST if its value is still used by
@@ -5026,6 +5043,16 @@ find_split_point (rtx *loc, rtx_insn *insn, bool set_src)
              XEXP (XEXP (XEXP (x, 0), 0), 1) = tem;
            }
 
+         /* If the constant is not a valid offset, compute it separately.
+            Test with a pseudo: a virtual REG may accept any offset.  */
+         machine_mode address_mode = GET_MODE (XEXP (x, 0));
+         rtx base = alloca_raw_REG (address_mode, LAST_VIRTUAL_REGISTER + 1);
+         rtx addr = alloca_rtx_fmt_ee (PLUS, address_mode, base,
+                                       XEXP (XEXP (x, 0), 1));
+         if (!memory_address_addr_space_p (GET_MODE (x), addr,
+                                           MEM_ADDR_SPACE (x)))
+           return &XEXP (XEXP (x, 0), 1);
+
          /* If that didn't work, perhaps the first operand is complex and
             needs to be computed separately, so make a split point there.
             This will occur on machines that just support REG + CONST
diff --git a/gcc/testsuite/gcc.dg/rtl/aarch64/split-const-offset-1.c 
b/gcc/testsuite/gcc.dg/rtl/aarch64/split-const-offset-1.c
new file mode 100644
index 00000000000..0fa4d011dfd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/aarch64/split-const-offset-1.c
@@ -0,0 +1,71 @@
+/* The address constant must not be split out into the narrower I2DEST.  */
+/* { dg-do compile { target aarch64*-*-* } } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2 -mlittle-endian" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/*
+** f1:
+**     mov     (x[0-9]+), -2147483648
+**     str     w[0-9]+, \[x[0-9]+, \1\]
+**     ret
+*/
+void __RTL (startwith ("combine")) f1 (char *p, int z)
+{
+(function "f1"
+  (param "p"
+    (DECL_RTL (reg/v:DI <0> [ p ]))
+    (DECL_RTL_INCOMING (reg:DI x0 [ p ])))
+  (param "z"
+    (DECL_RTL (reg/v:SI <1> [ z ]))
+    (DECL_RTL_INCOMING (reg:SI x1 [ z ])))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 5 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 6 (set (reg:DI <2>) (const_int -2147483648)))
+      (cinsn 7 (set (reg:SI <3>) (and:SI (reg:SI <1>) (subreg:SI (reg:DI <2>) 
4)))
+        (expr_list:REG_DEAD (reg:SI <1>)))
+      (cinsn 8 (set (mem:SI (plus:DI (reg:DI <0>) (reg:DI <2>)) [1 S4 A32])
+                    (reg:SI <3>))
+        (expr_list:REG_DEAD (reg:DI <0>)
+          (expr_list:REG_DEAD (reg:DI <2>)
+            (expr_list:REG_DEAD (reg:SI <3>)))))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
+
+/*
+** f2:
+**     mov     (x[0-9]+), 4294967296
+**     str     w[0-9]+, \[x[0-9]+, \1\]
+**     ret
+*/
+void __RTL (startwith ("combine")) f2 (char *p, int z)
+{
+(function "f2"
+  (param "p"
+    (DECL_RTL (reg/v:DI <0> [ p ]))
+    (DECL_RTL_INCOMING (reg:DI x0 [ p ])))
+  (param "z"
+    (DECL_RTL (reg/v:SI <1> [ z ]))
+    (DECL_RTL_INCOMING (reg:SI x1 [ z ])))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 5 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 6 (set (reg:DI <2>) (const_int 4294967296)))
+      (cinsn 7 (set (reg:SI <3>) (udiv:SI (reg:SI <1>) (subreg:SI (reg:DI <2>) 
4)))
+        (expr_list:REG_DEAD (reg:SI <1>)))
+      (cinsn 8 (set (mem:SI (plus:DI (reg:DI <0>) (reg:DI <2>)) [1 S4 A32])
+                    (reg:SI <3>))
+        (expr_list:REG_DEAD (reg:DI <0>)
+          (expr_list:REG_DEAD (reg:DI <2>)
+            (expr_list:REG_DEAD (reg:SI <3>)))))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+)
+}
diff --git a/gcc/testsuite/gcc.dg/torture/split-const-offset-1.c 
b/gcc/testsuite/gcc.dg/torture/split-const-offset-1.c
new file mode 100644
index 00000000000..5e0663e5cc7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/split-const-offset-1.c
@@ -0,0 +1,51 @@
+/* { dg-do run { target mmap } } */
+
+#include <sys/mman.h>
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#define BASE 0x20000000UL
+#define W ((volatile unsigned int *) (BASE + 0xa04000))
+
+__attribute__ ((noipa)) void
+st (unsigned int i, unsigned int v)
+{
+  W[i >> 1] = v;
+}
+
+__attribute__ ((noipa)) unsigned int
+ld (unsigned int i)
+{
+  return W[i];
+}
+
+__attribute__ ((noipa)) void
+stp (unsigned char *p, unsigned char v)
+{
+  p[0x1234567] = v;
+}
+
+__attribute__ ((noipa)) unsigned char
+ldp (unsigned char *p)
+{
+  return p[0x1234567];
+}
+
+int
+main ()
+{
+  unsigned char *p = mmap ((void *) BASE, 0x2000000, PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (p != (unsigned char *) BASE)
+    return 0;
+  for (unsigned int i = 0; i < 64; i++)
+    st (i, i * 3);
+  for (unsigned int i = 0; i < 32; i++)
+    if (ld (i) != (2 * i + 1) * 3)
+      __builtin_abort ();
+  stp (p, 0x5a);
+  if (ldp (p) != 0x5a || p[0x1234567] != 0x5a || p[0x1234566] != 0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/split-const-offset-1.c 
b/gcc/testsuite/gcc.target/aarch64/split-const-offset-1.c
new file mode 100644
index 00000000000..a6cdb95dfc4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/split-const-offset-1.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/*
+** st:
+**     ...
+**     strb    w1, \[x0, x[0-9]+\]
+**     ret
+*/
+void
+st (char *p, char x)
+{
+  p[0x123456780] = x;
+}
+
+/*
+** ld:
+**     ...
+**     ldr     w0, \[x0, x[0-9]+\]
+**     ret
+*/
+int
+ld (int *p)
+{
+  return p[0x12345678];
+}
diff --git a/gcc/testsuite/gcc.target/arm/split-const-offset-1.c 
b/gcc/testsuite/gcc.target/arm/split-const-offset-1.c
new file mode 100644
index 00000000000..7c8f73ae997
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/split-const-offset-1.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm32 } */
+/* { dg-options "-O2" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#define W ((volatile unsigned int *) 0x20a04000)
+
+/*
+** st:
+**     ...
+**     str     r1, \[r[0-9]+, r[0-9]+, lsl #2\]
+**     bx      lr
+*/
+void
+st (unsigned int i, unsigned int v)
+{
+  W[i >> 1] = v;
+}
+
+/*
+** ld:
+**     ...
+**     ldr     r0, \[r[0-9]+, r[0-9]+, lsl #2\]
+**     bx      lr
+*/
+unsigned int
+ld (unsigned int i)
+{
+  return W[i];
+}
diff --git a/gcc/testsuite/gcc.target/arm/split-const-offset-2.c 
b/gcc/testsuite/gcc.target/arm/split-const-offset-2.c
new file mode 100644
index 00000000000..1bbbdf292e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/split-const-offset-2.c
@@ -0,0 +1,20 @@
+/* A valid offset stays in the address.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target arm32 } */
+/* { dg-options "-O2" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+struct s { char pad[0x300]; unsigned int t[256]; };
+
+/*
+** st:
+**     ...
+**     str     r1, \[r[0-9]+, #1024\]
+**     bx      lr
+*/
+void
+st (unsigned char *q, unsigned int v)
+{
+  ((volatile struct s *) 0x100)->t[*q] = v;
+}
-- 
2.55.0

Reply via email to