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

commit r17-4464-ge03a2c4527648bd5bc21a452a1e44d51ec3c2f89
Author: Dominic P <[email protected]>
Date:   Sat Sep 19 10:46:18 2026 -0600

    [PATCH] postreload: recover the branch condition through the cc register
    
    reload_cse_regs_1 records an implicit set on the edges of a conditional
    branch that tests an equality, so cselib knows the compared register holds
    the compared value in the block the edge enters (PR107455).  It reads the
    condition out of the jump pattern, so it works only where the jump carries
    the comparison: riscv and mips branches, aarch64 cbz, Thumb-1 cbz
    (gcc.target/riscv/pr107455-1.c and -2.c).  Where the comparison is a
    separate insn the jump tests a MODE_CC register and no implicit set is made.
    
    Use get_condition, which finds the comparison through the cc register, and
    take the edge polarity from the returned code.  get_condition reverses the
    condition only for a LABEL_REF else arm, so a jump with a return in one arm
    (frv and ia64 *return_false) would give the other edge's condition; reject
    jumps whose target is not a label.
    
    Record the equivalence only where the block sets the register to that value,
    the redundancy the implicit set exists to remove.  That is a profitability
    choice, not a correctness condition: every equivalence is true, but
    unguarded it also reaches reload_cse_simplify_set, which at equal cost
    prefers a register to a constant (postreload.cc:452); with a register known
    to hold the constant that rewrites literal operands, and the lost fusions
    and cross-jumps cost more than the removed sets save: a bcmrpi_defconfig
    kernel at -O2 for arm1176 grows 3128 bytes of .text unguarded and shrinks
    224 guarded.  Of the 783 kernel objects the unguarded change alters, 42
    still change with the guard.  Teaching that tie-break to ignore a value
    known only through an implicit set would remove the guard, but it changes
    the rule for every target, so it belongs in a separate patch.
    
    On Arm the re-materialised value in a shared early-exit block goes away and
    the exit becomes a conditional return:
    
            subs    r3, r0, #0              subs    r3, r0, #0
            bne     .L14                    bxeq    lr
            mov     r0, #0
            bx      lr
    
    The core_list_find loop of gcc.target/riscv/pr107455-2.c gets the same
    treatment on arm1176.
    
    Bootstrapped and regression tested on x86_64-pc-linux-gnu, where dg.exp and
    tree-ssa.exp are identical with and without the patch.  Cross tested
    like-for-like, the patched cc1 against one relinked from the unpatched
    source in the same tree: arm-none-eabi with arm.exp, simd.exp, mve.exp,
    advsimd-intrinsics.exp, acle.exp and cmse.exp on the default board and with
    -mthumb -mfloat-abi=hard -mtune=cortex-a9 -march=armv7-a
    -mfpu=neon-fp-armv8; aarch64-linux-gnu under qemu with aarch64.exp,
    vect.exp and tree-ssa.exp.  Only the new tests change.  
gcc.c-torture/execute
    under qemu on arm1176 with and without the patch: 1564 tests, same results.
    
    Assisted-by: Claude Opus 5 (Anthropic)
    
    gcc/ChangeLog:
    
            * postreload.cc (reload_cse_regs_1): Find the condition of the
            predecessor's conditional branch with get_condition, so a comparison
            held in the condition-code register is seen.  Reject a jump whose
            target is not a label.  Record the implicit set only where the block
            sets the register to that value.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/aarch64/postreload-implicit-set-1.c: New test.
            * gcc.target/arm/postreload-implicit-set-1.c: New test.
            * gcc.target/arm/postreload-implicit-set-2.c: New test.
            * gcc.target/i386/postreload-implicit-set-1.c: New test.
    
    Signed-off-by: Dominic P <[email protected]>

Diff:
---
 gcc/postreload.cc                                  | 44 +++++++++++++----
 .../gcc.target/aarch64/postreload-implicit-set-1.c | 36 ++++++++++++++
 .../gcc.target/arm/postreload-implicit-set-1.c     | 57 ++++++++++++++++++++++
 .../gcc.target/arm/postreload-implicit-set-2.c     | 34 +++++++++++++
 .../gcc.target/i386/postreload-implicit-set-1.c    | 27 ++++++++++
 5 files changed, 188 insertions(+), 10 deletions(-)

diff --git a/gcc/postreload.cc b/gcc/postreload.cc
index 576456c79d56..475b58896298 100644
--- a/gcc/postreload.cc
+++ b/gcc/postreload.cc
@@ -249,18 +249,22 @@ reload_cse_regs_1 (void)
              if (!condjump || ! any_condjump_p (condjump))
                break;
 
+             /* get_condition reverses the condition only when the else arm
+                is a LABEL_REF, so a jump that returns in one arm would give
+                the condition of the other edge.  */
+             if (!JUMP_LABEL (condjump) || !LABEL_P (JUMP_LABEL (condjump)))
+               break;
+
              /* This predecessor ends with a possible equivalence
                 producing conditional branch.  Extract the condition
                 and try to use it to create an equivalence.  */
-             rtx pat = pc_set (condjump);
-             rtx i_t_e = SET_SRC (pat);
-             gcc_assert (GET_CODE (i_t_e) == IF_THEN_ELSE);
-             rtx cond = XEXP (i_t_e, 0);
-
-             if ((((e->flags & EDGE_FALLTHRU) != 0)
-                  == (XEXP (i_t_e, 1) == pc_rtx))
-                 ? GET_CODE (cond) == EQ
-                 : GET_CODE (cond) == NE)
+             rtx cond = get_condition (condjump, NULL, false, true);
+             if (!cond)
+               break;
+
+             if ((e->flags & EDGE_FALLTHRU) != 0
+                 ? GET_CODE (cond) == NE
+                 : GET_CODE (cond) == EQ)
                {
                  /* If this is the first time through record
                     the source and destination.  */
@@ -300,7 +304,27 @@ reload_cse_regs_1 (void)
             It will be entered into the cselib tables before
             we process the first real insn in this block.  */
          if (dest && ei_end_p (ei))
-           implicit_set = make_insn_raw (gen_rtx_SET (dest, src));
+           {
+             rtx_insn *i;
+             bool useful = false;
+             FOR_BB_INSNS (bb, i)
+               {
+                 if (!NONDEBUG_INSN_P (i))
+                   continue;
+                 rtx s = single_set (i);
+                 if (s
+                     && rtx_equal_p (SET_DEST (s), dest)
+                     && rtx_equal_p (SET_SRC (s), src))
+                   {
+                     useful = true;
+                     break;
+                   }
+                 if (reg_set_p (dest, i))
+                   break;
+               }
+             if (useful)
+               implicit_set = make_insn_raw (gen_rtx_SET (dest, src));
+           }
        }
 
       FOR_BB_INSNS (bb, insn)
diff --git a/gcc/testsuite/gcc.target/aarch64/postreload-implicit-set-1.c 
b/gcc/testsuite/gcc.target/aarch64/postreload-implicit-set-1.c
new file mode 100644
index 000000000000..6faf6f7ba5eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/postreload-implicit-set-1.c
@@ -0,0 +1,36 @@
+/* As gcc.target/i386/postreload-implicit-set-1.c.  The zero case is already
+   handled without this, because cbz carries the comparison in the branch;
+   this covers the cmp form.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+int g (int);
+int h (int);
+
+/*
+** seven:
+**     cmp     w0, 7
+**     beq     \.L[0-9]+
+**     stp     x29, x30, \[sp, -16\]!
+**     mov     x29, sp
+**     bl      g
+**     cmp     w0, 7
+**     beq     \.L[0-9]+
+**     ldp     x29, x30, \[sp\], 16
+**     b       h
+**     ldp     x29, x30, \[sp\], 16
+**     ret
+**     ret
+*/
+int
+seven (int x)
+{
+  if (x == 7)
+    return 7;
+  x = g (x);
+  if (x == 7)
+    return 7;
+  return h (x);
+}
diff --git a/gcc/testsuite/gcc.target/arm/postreload-implicit-set-1.c 
b/gcc/testsuite/gcc.target/arm/postreload-implicit-set-1.c
new file mode 100644
index 000000000000..d38d2dd7c7b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/postreload-implicit-set-1.c
@@ -0,0 +1,57 @@
+/* The equality a conditional branch tests holds on one of its edges; reload
+   CSE must see it through the condition-code register, so that re-creating
+   the same value in the target block is dropped and the exit can become a
+   conditional return.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -marm" } */
+/* { dg-require-effective-target arm_arch_v6_arm_ok } */
+/* { dg-add-options arm_arch_v6 } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+int g (int);
+int h (int);
+
+/*
+** zero:
+**     subs    r3, r0, #0
+**     bxeq    lr
+**     push    {r4, lr}
+**     bl      g
+**     cmp     r0, #0
+**     popeq   {r4, pc}
+**     pop     {r4, lr}
+**     b       h
+*/
+int
+zero (int x)
+{
+  if (x == 0)
+    return 0;
+  x = g (x);
+  if (x == 0)
+    return 0;
+  return h (x);
+}
+
+/*
+** nonzero:
+**     cmp     r0, #7
+**     bxeq    lr
+**     push    {r4, lr}
+**     bl      g
+**     cmp     r0, #7
+**     popeq   {r4, pc}
+**     pop     {r4, lr}
+**     b       h
+*/
+int
+nonzero (int x)
+{
+  if (x == 7)
+    return 7;
+  x = g (x);
+  if (x == 7)
+    return 7;
+  return h (x);
+}
diff --git a/gcc/testsuite/gcc.target/arm/postreload-implicit-set-2.c 
b/gcc/testsuite/gcc.target/arm/postreload-implicit-set-2.c
new file mode 100644
index 000000000000..75bbc6f18085
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/postreload-implicit-set-2.c
@@ -0,0 +1,34 @@
+/* As postreload-implicit-set-1.c, in Thumb-2 state.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mthumb" } */
+/* { dg-require-effective-target arm_arch_v7a_ok } */
+/* { dg-add-options arm_arch_v7a } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+int g (int);
+int h (int);
+
+/*
+** nonzero:
+**     cmp     r0, #7
+**     beq     \.L[0-9]+
+**     push    {r3, lr}
+**     bl      g
+**     cmp     r0, #7
+**     beq     \.L[0-9]+
+**     pop     {r3, lr}
+**     b       h
+**     pop     {r3, pc}
+**     bx      lr
+*/
+int
+nonzero (int x)
+{
+  if (x == 7)
+    return 7;
+  x = g (x);
+  if (x == 7)
+    return 7;
+  return h (x);
+}
diff --git a/gcc/testsuite/gcc.target/i386/postreload-implicit-set-1.c 
b/gcc/testsuite/gcc.target/i386/postreload-implicit-set-1.c
new file mode 100644
index 000000000000..f5714aedef0b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/postreload-implicit-set-1.c
@@ -0,0 +1,27 @@
+/* The equality a conditional branch tests holds on one of its edges; reload
+   CSE must see it through the flags register, so that re-creating the same
+   value in the target block goes away.  */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+void f1 (double);
+void f2 (int);
+
+/*
+** foo:
+**     testl   %edi, %edi
+**     je      \.L[0-9]+
+**     jmp     f1
+**     jmp     f2
+*/
+void
+foo (int type, double xx)
+{
+  if (type)
+    f1 (xx);
+  else
+    f2 (type);
+}

Reply via email to