I was reviewing the generated code in libgcc for the H8 to see if there were any obvious redundant test/compares.  Sure enough I found one in the first file I looked at ;-)

So after IRA we have something like this:

(insn 43 112 44 4 (set (subreg:SI (reg:DI 40) 0)
        (and:SI (subreg:SI (reg:DI 38) 0)
            (subreg:SI (reg:DI 39) 0))) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 246 {*logicalsi3}
     (nil))
(insn 44 43 47 4 (set (subreg:SI (reg:DI 40) 4)
        (and:SI (subreg:SI (reg:DI 38) 4)
            (subreg:SI (reg:DI 39) 4))) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 246 {*logicalsi3}
     (expr_list:REG_DEAD (reg:DI 39)
        (expr_list:REG_DEAD (reg:DI 38)
            (nil))))
(jump_insn 47 44 83 4 (set (pc)
        (if_then_else (ge (subreg:SI (reg:DI 40) 0)
                (const_int 0 [0]))
            (label_ref 55)
            (pc))) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 281 {*branch}
     (expr_list:REG_DEAD (reg:DI 40)
        (int_list:REG_BR_PROB 1073204964 (nil)))

Which is just a DImode AND and a conditional branch based on the state of the most significant bit.  On the H8 we don't expose the condition codes until split2.  After splitting we have:

(insn 228 227 229 4 (parallel [
            (set (reg:SI 0 r0)
                (and:SI (reg:SI 0 r0)
                    (reg:SI 2 r2)))
            (clobber (reg:CC 12 cc))
        ]) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 263 {*andsi3}
     (nil))
(insn 229 228 230 4 (parallel [
            (set (reg:SI 1 r1)
                (mem/c:SI (plus:SI (reg/f:SI 7 sp)
                        (const_int 12 [0xc])) [1 %sfp+-12 S4 A32]))
            (clobber (reg:CC 12 cc))
        ]) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 20 {*movsi_clobber_flags}
     (nil))
(insn 230 229 231 4 (parallel [
            (set (reg:SI 1 r1)
                (and:SI (reg:SI 1 r1)
                    (reg:SI 3 r3 [orig:39+4 ] [39])))
            (clobber (reg:CC 12 cc))
        ]) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 263 {*andsi3}
     (nil))
(insn 231 230 232 4 (set (reg:CC 12 cc)
        (compare:CC (reg:SI 0 r0)
            (const_int 0 [0]))) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 131 {cmpsi}
     (nil))
(jump_insn 232 231 83 4 (set (pc)
        (if_then_else (ge (reg:CC 12 cc)
                (const_int 0 [0]))
            (label_ref 55)
            (pc))) "/home/jlaw/test/gcc/libgcc/libgcc2.c":247:7 283 {*branch_1}

So far so good.  An astute observer would probably note at this time that insn 228 is a good candidate to eliminate the compare at insn 231, except that insns 229 and 230 clobber the condition codes.

Things stay effectively like this through compare elimination which fails to trigger because the condition codes are clobbered between insn 228 and insn 231.

*But* it turns out that insn 229 and insn 230 are both dead. They'll be detected as such during peephole2, but that's too late to help compare elimination (and in case you're wondering the DImode use in insn 47 keeps insn 44 from being deleted as dead before reload).

Naturally once peep2 has deleted the dead code it becomes obvious there's a redundant test/compare.

The fix here is trivial -- just ask the DF machinery to do a fast DCE from compare-elimination.  That kills the two problematical insns and then allows compare-elimination to detect that the compare is not necessary.  This shows up numerous times throughout libgcc and newlib.  Each time we're able to eliminate a compare like this we save 6 bytes of instruction space and 3 cycles.

*** orig.s      2021-06-13 20:21:55.014435803 -0400
--- new.s       2021-06-13 20:21:34.329494744 -0400
*************** ___absvdi2:
*** 43,50 ****
        not.l   er2
        mov.l   @(8,er7),er0
        and.l   er2,er0
!       cmp.l   #0,er0
!       bge     .L2
        sub.l   er0,er0
        mov.l   er0,@(16,er7)
        sub.l   er1,er1
--- 43,49 ----
        not.l   er2
        mov.l   @(8,er7),er0
        and.l   er2,er0
!       bpl     .L2
        sub.l   er0,er0
        mov.l   er0,@(16,er7)
        sub.l   er1,er1


I've bootstrapped and regression tested this on x86_64, though I doubt it makes any difference there.  BUt I'd bet it would help other targets that don't expose double-word operations and have a condition code that is clobbered by most instructions.


OK for the trunk?

Jeff
Minor improvement to compare elimination

gcc/
        * compare-elim.c (try_eliminate_compare): Run DCE to clean things
        before eliminating comparisons.

        
diff --git a/gcc/compare-elim.c b/gcc/compare-elim.c
index 85085cd6973..607eadc3d96 100644
--- a/gcc/compare-elim.c
+++ b/gcc/compare-elim.c
@@ -906,6 +906,7 @@ try_eliminate_compare (struct comparison *cmp)
 static unsigned int
 execute_compare_elim_after_reload (void)
 {
+  df_set_flags (DF_LR_RUN_DCE);
   df_analyze ();
 
   gcc_checking_assert (!all_compares.exists ());

Reply via email to