https://gcc.gnu.org/g:4a97237ac39074b1083df4490d0fa6815c2ed595

commit r17-845-g4a97237ac39074b1083df4490d0fa6815c2ed595
Author: Philipp Tomsich <[email protected]>
Date:   Fri Mar 20 17:14:15 2026 +0100

    ext-dce: narrow sign-extending loads to zero-extending when upper bits are 
dead
    
    The ext-dce pass tracks bit-level liveness and can replace sign extensions
    with zero extensions when the upper bits are dead.  However,
    ext_dce_try_optimize_extension bails out when the inner operand is MEM
    rather than REG, missing the opportunity to narrow sign-extending loads
    (e.g. lh -> lhu on RISC-V, ldrsh -> ldrh on AArch64).
    
    Add handling for SIGN_EXTEND of MEM: when the liveness analysis has
    already determined the sign bits are dead, replace the sign-extending
    load with a zero-extending load via validate_change, which ensures the
    target has a matching instruction pattern.
    
    gcc/ChangeLog:
    
            * ext-dce.cc (ext_dce_try_optimize_extension): Handle
            SIGN_EXTEND of MEM by replacing with ZERO_EXTEND of MEM
            when upper bits are dead.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/aarch64/ext-dce-1.c: New test.
            * gcc.target/riscv/ext-dce-3.c: New test.
            * gcc.target/riscv/ext-dce-4.c: New test.
    
    Co-authored-by: Konstantinos Eleftheriou <[email protected]>

Diff:
---
 gcc/ext-dce.cc                               | 46 ++++++++++++++++++++++
 gcc/testsuite/gcc.target/aarch64/ext-dce-1.c | 58 ++++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/ext-dce-3.c   | 33 ++++++++++++++++
 gcc/testsuite/gcc.target/riscv/ext-dce-4.c   | 58 ++++++++++++++++++++++++++++
 4 files changed, 195 insertions(+)

diff --git a/gcc/ext-dce.cc b/gcc/ext-dce.cc
index 86457d31af52..929c8530d17c 100644
--- a/gcc/ext-dce.cc
+++ b/gcc/ext-dce.cc
@@ -487,6 +487,52 @@ ext_dce_try_optimize_extension (rtx_insn *insn, rtx set)
   rtx src = SET_SRC (set);
   rtx inner = XEXP (src, 0);
 
+  /* For sign-extending loads from memory, try to replace with a
+     zero-extending load when the upper bits are dead.  E.g. on RISC-V
+     this turns lh+zext.h into just lhu.  */
+  if (MEM_P (inner) && GET_CODE (src) == SIGN_EXTEND)
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, "Processing insn:\n");
+         dump_insn_slim (dump_file, insn);
+         fprintf (dump_file, "Trying to narrow sign_extend to zero_extend:\n");
+         print_rtl_single (dump_file, SET_SRC (set));
+       }
+
+      if (!dbg_cnt (::ext_dce))
+       {
+         if (dump_file)
+           fprintf (dump_file, "Rejected due to debug counter.\n");
+         return;
+       }
+
+      rtx new_pattern = gen_rtx_ZERO_EXTEND (GET_MODE (src), inner);
+      int ok = validate_change (insn, &SET_SRC (set), new_pattern, false);
+
+      rtx x = SET_DEST (set);
+      while (SUBREG_P (x) || GET_CODE (x) == ZERO_EXTRACT)
+       x = XEXP (x, 0);
+
+      gcc_assert (REG_P (x));
+      if (ok)
+       {
+         bitmap_set_bit (changed_pseudos, REGNO (x));
+         remove_reg_equal_equiv_notes (insn, false);
+       }
+
+      if (dump_file)
+       {
+         if (ok)
+           fprintf (dump_file, "Successfully transformed to:\n");
+         else
+           fprintf (dump_file, "Failed transformation to:\n");
+         print_rtl_single (dump_file, new_pattern);
+         fprintf (dump_file, "\n");
+       }
+      return;
+    }
+
   /* Avoid (subreg (mem)) and other constructs which may be valid RTL, but
      not useful for this optimization.  */
   if (!(REG_P (inner) || (SUBREG_P (inner) && REG_P (SUBREG_REG (inner)))))
diff --git a/gcc/testsuite/gcc.target/aarch64/ext-dce-1.c 
b/gcc/testsuite/gcc.target/aarch64/ext-dce-1.c
new file mode 100644
index 000000000000..0c6fb0005af0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ext-dce-1.c
@@ -0,0 +1,58 @@
+/* Verify that ext-dce narrows sign-extending loads to zero-extending loads
+   when the upper bits are dead.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** test_half:
+**     ...
+**     ldrh    .*
+**     ...
+*/
+/* Positive: halfword load-modify-store — sign bits are dead.  */
+void
+test_half (signed short *p)
+{
+  *p = (*p & 0xff00) | (0x00ff & (*p >> 8));
+}
+
+/*
+** test_byte:
+**     ...
+**     ldrb    .*
+**     ...
+*/
+/* Positive: byte load-modify-store — sign bits are dead.  */
+void
+test_byte (signed char *p)
+{
+  *p = (*p & 0xf0) | (0x0f & (*p >> 4));
+}
+
+/*
+** test_half_sign_needed:
+**     ...
+**     ldrsb   .*
+**     ...
+*/
+/* Negative: arithmetic right shift needs the sign extension.  */
+int
+test_half_sign_needed (signed short *p)
+{
+  return *p >> 8;
+}
+
+/*
+** test_half_compare:
+**     ...
+**     ldrsh   .*
+**     ...
+*/
+/* Negative: sign-dependent comparison.  */
+int
+test_half_compare (signed short *p)
+{
+  return *p < 0;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/ext-dce-3.c 
b/gcc/testsuite/gcc.target/riscv/ext-dce-3.c
new file mode 100644
index 000000000000..e62655ae286c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/ext-dce-3.c
@@ -0,0 +1,33 @@
+/* Verify ext-dce for word loads on RV64: lw sign-extends to 64 bits,
+   so when the upper 32 bits are dead lw should be narrowed to lwu.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target rv64 } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** test_word:
+**     ...
+**     lwu     .*
+**     ...
+*/
+/* Positive: word load-modify-store — upper 32 bits are dead.  */
+void
+test_word (signed int *p)
+{
+  *p = (*p & 0xffff0000) | (0x0000ffff & ((unsigned int)*p >> 16));
+}
+
+/*
+** test_word_sign_needed:
+**     ...
+**     lw      .*
+**     ...
+*/
+/* Negative: return value is long — sign extension is needed.  */
+long
+test_word_sign_needed (signed int *p)
+{
+  return *p;
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/ext-dce-4.c 
b/gcc/testsuite/gcc.target/riscv/ext-dce-4.c
new file mode 100644
index 000000000000..21f2d9a13ebb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/ext-dce-4.c
@@ -0,0 +1,58 @@
+/* Verify that ext-dce narrows sign-extending loads to zero-extending loads
+   when the upper bits are dead.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** test_half:
+**     ...
+**     lhu     .*
+**     ...
+*/
+/* Positive: halfword load-modify-store — sign bits are dead.  */
+void
+test_half (signed short *p)
+{
+  *p = (*p & 0xff00) | (0x00ff & (*p >> 8));
+}
+
+/*
+** test_byte:
+**     ...
+**     lbu     .*
+**     ...
+*/
+/* Positive: byte load-modify-store — sign bits are dead.  */
+void
+test_byte (signed char *p)
+{
+  *p = (*p & 0xf0) | (0x0f & (*p >> 4));
+}
+
+/*
+** test_half_sign_needed:
+**     ...
+**     lh      .*
+**     ...
+*/
+/* Negative: arithmetic right shift needs the sign extension.  */
+int
+test_half_sign_needed (signed short *p)
+{
+  return *p >> 8;
+}
+
+/*
+** test_half_compare:
+**     ...
+**     lh      .*
+**     ...
+*/
+/* Negative: sign-dependent comparison.  */
+int
+test_half_compare (signed short *p)
+{
+  return *p < 0;
+}
+

Reply via email to