Take the following test case: int foo(); int test( int *b ) { (*b)--; if( *b == 0 ) return foo(); return 0; } On x86_64, with "-fomit-frame-pointer -O3 -c", gcc 4.6 compiles this to: 0: 8b 07 mov eax, [rdi] 2: 83 e8 01 sub eax, 0x1 5: 85 c0 test eax, eax 7: 89 07 mov [rdi], eax 9: 74 05 je 10 <test+0x10> b: 31 c0 xor eax, eax d: c3 ret 10: e9 00 00 00 00 jmp 15 <test+0x15> //foo As can be seen, gcc inserts a redundant "test" instruction. This problem is present in all versions of gcc I tested (3.4, 4.3.4, 4.4.1, and 4.6 SVN r160330). According to Alexander Strange on IRC: <astrange> the problem is that subl $1, %eax does (clobber (reg:CC 17 flags)) instead of (set (reg:CCZ 17 flags) (compare:CCZ .... <astrange> which i don't think is fixable in a real way, but i didn't write it
The problem is combine. This: int test2( int *b ) { int b_ = *b; b_--; if( b_ == 0 ) { *b = b_; return foo(); } *b = b_; return 0; } works: _test2: LFB1: movl (%rdi), %eax decl %eax je L7 <- uses decl movl %eax, (%rdi) xorl %eax, %eax ret .align 4,0x90 L7: movl $0, (%rdi) xorl %eax, %eax jmp _foo The original turns (*b)-- into load/dec/store/cmp - combine tries to combine dec/store which fails, but doesn't try dec/cmp.
Still happens with the new combine work (not that I really expected it to change).
Confirmed. Combine does: Trying 7 -> 8: Trying 9 -> 10: But never 7 -> 9 which is needed for this to work. This is for: int foo(); int t; int test( int b ) { (b)--; t = b; if( b == 0 ) return foo(); return 0; } --- CUT -- Where 7, 8, 9, and 10 are: (insn 7 3 8 2 (parallel [ (set (reg/v:SI 60 [ b ]) (plus:SI (reg/v:SI 62 [ b ]) (const_int -1 [0xffffffffffffffff]))) (clobber (reg:CC 17 flags)) ]) t.c:6 253 {*addsi_1} (expr_list:REG_DEAD (reg/v:SI 62 [ b ]) (expr_list:REG_UNUSED (reg:CC 17 flags) (nil)))) (insn 8 7 9 2 (set (mem/c/i:SI (symbol_ref:DI ("t") <var_decl 0x7fedc8e8c140 t>) [2 t+0 S4 A32]) (reg/v:SI 60 [ b ])) t.c:7 64 {*movsi_internal} (nil)) (insn 9 8 10 2 (set (reg:CCZ 17 flags) (compare:CCZ (reg/v:SI 60 [ b ]) (const_int 0 [0]))) t.c:8 2 {*cmpsi_ccno_1} (expr_list:REG_DEAD (reg/v:SI 60 [ b ]) (nil))) (jump_insn 10 9 11 2 (set (pc) (if_then_else (ne (reg:CCZ 17 flags) (const_int 0 [0])) (label_ref 16) (pc))) t.c:8 599 {*jcc_1} (expr_list:REG_DEAD (reg:CCZ 17 flags) (expr_list:REG_BR_PROB (const_int 6102 [0x17d6]) (nil))) -> 16)
Compare-elimination does the right thing and eliminates the redundant test on the trunk. Given the age of the BZ I didn't track it down any further than that.