https://gcc.gnu.org/g:a7a46d303ca5a4f5b4862bbbfd89a8751eadf69a

commit r17-5787-ga7a46d303ca5a4f5b4862bbbfd89a8751eadf69a
Author: e0001533 <[email protected]>
Date:   Sun Nov 1 15:20:06 2026 -0700

    [PATCH] RISC-V: Keep whole-word tails in block-move group
    
    When a constant-size block copy has a size that is an exact multiple
    of the word size and a copied word is immediately overwritten by a
    later store, the pre-patch expansion defers the trailing whole word
    to move_by_pieces, emitting its accesses after the group's stores.
    RTL DSE then cannot prove that the trailing load does not read the
    overwritten word, so it retains the dead copy pair, costing one dead
    load and one dead store per overwritten word.
    
    Keeping the trailing whole word in the group keeps all loads ahead of
    all stores, which lets DSE remove the dead pair.  The new
    cpymem-dse-1.c test (a copy whose Int_Comp word is immediately
    overwritten; 48 bytes on rv32, 64 on rv64) flips from FAIL to PASS.
    
    Signed-off-by: Ciyan Pan <[email protected]>
    
    gcc/ChangeLog:
    
            * config/riscv/riscv-string.cc (riscv_block_move_straight): Adjust
            generated code to encourage better DSE later.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/cpymem-dse-1.c: New test.

Diff:
---
 gcc/config/riscv/riscv-string.cc              | 17 +++++++++----
 gcc/testsuite/gcc.target/riscv/cpymem-dse-1.c | 36 +++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/gcc/config/riscv/riscv-string.cc b/gcc/config/riscv/riscv-string.cc
index 5ca9f4e1f829..1ce898929549 100644
--- a/gcc/config/riscv/riscv-string.cc
+++ b/gcc/config/riscv/riscv-string.cc
@@ -853,22 +853,29 @@ riscv_block_move_straight (rtx dest, rtx src, unsigned 
HOST_WIDE_INT length,
       if (step <= length)
        {
          int i;
+         /* Keep the trailing chunk in the group when it is a whole word:
+            unlike a ragged tail, move_by_pieces cannot handle it any
+            better (no overlapping access is possible for an aligned
+            word), and keeping all loads ahead of all stores lets RTL DSE
+            eliminate copies of words that later stores overwrite.  */
+         bool group_last = bits == BITS_PER_WORD && length % delta == 0;
+         unsigned HOST_WIDE_INT limit
+           = group_last ? length - delta : length - 2 * delta;
+
          /* Allocate a buffer for the temporary registers.  */
-         regs = XALLOCAVEC (rtx, length / delta - 1);
+         regs = XALLOCAVEC (rtx, limit / delta + 1);
 
          /* Load as many BITS-sized chunks as possible.  Use a normal load
             if the source has enough alignment, otherwise use left/right
             pairs.  */
-         for (offset = 0, i = 0; offset + step <= length;
-              offset += delta, i++)
+         for (offset = 0, i = 0; offset <= limit; offset += delta, i++)
            {
              regs[i] = gen_reg_rtx (mode);
              riscv_emit_move (regs[i], adjust_address (src, mode, offset));
            }
 
          /* Copy the chunks to the destination.  */
-         for (offset = 0, i = 0; offset + step <= length;
-              offset += delta, i++)
+         for (offset = 0, i = 0; offset <= limit; offset += delta, i++)
            riscv_emit_move (adjust_address (dest, mode, offset), regs[i]);
        }
 
diff --git a/gcc/testsuite/gcc.target/riscv/cpymem-dse-1.c 
b/gcc/testsuite/gcc.target/riscv/cpymem-dse-1.c
new file mode 100644
index 000000000000..b7fc42aecd1f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cpymem-dse-1.c
@@ -0,0 +1,36 @@
+/* RTL DSE can only eliminate the copy of a word that a later store
+   overwrites if the copy's loads are not interleaved with its stores.
+   riscv_block_move_straight must therefore keep the trailing whole-word
+   chunk in the group instead of deferring it to move_by_pieces, which
+   emits its accesses after the group's stores.  */
+
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gc -mabi=ilp32d" { target { rv32 } } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d" { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-Os" "-Og" "-Oz" "-flto" } } */
+
+struct Record
+{
+  struct Record *Ptr_Comp;
+  long Discr;
+  long Enum_Comp;
+  long Int_Comp;
+  char Str_Comp[32];
+};
+
+extern struct Record Glob;
+
+void
+Proc (struct Record *p)
+{
+  struct Record *n = p->Ptr_Comp;
+  *n = Glob;
+  n->Int_Comp = 5;
+}
+
+/* The word holding Int_Comp (offset 12 on rv32, 24 on rv64) is
+   immediately overwritten, so the copy of that word (a load from that
+   offset in Glob feeding a store to the same offset in *n) must have
+   been removed.  The store of the constant 5 remains.  */
+/* { dg-final { scan-assembler-not "lw\t\[at\]\[0-9\]+,12\\(" { target rv32 } 
} } */
+/* { dg-final { scan-assembler-not "ld\t\[at\]\[0-9\]+,24\\(" { target rv64 } 
} } */

Reply via email to