http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59278
Bug ID: 59278 Summary: combine does not replace matched insn Product: gcc Version: 4.9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: rtl-optimization Assignee: unassigned at gcc dot gnu.org Reporter: olegendo at gcc dot gnu.org Target: sh*-*-* The following happens on SH, with the following example function: struct result { int a, b; }; result test2 (int a, int b, int d) { result r; r.a = a != b; r.b = d + b + 1; return r; }; With -O2 -m4 -ml it compiles to: cmp/eq r5,r4 add r5,r6 mov #-1,r0 mov r6,r1 negc r0,r0 rts add #1,r1 ... where it fails to utilize the addc (a + b + T) insn, which is usually formed during combine. The RTL before combine looks as follows: (note 6 0 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) (insn 2 6 3 2 (set (reg/v:SI 166 [ a ]) (reg:SI 4 r4 [ a ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 4 r4 [ a ]) (nil))) (insn 3 2 4 2 (set (reg/v:SI 167 [ b ]) (reg:SI 5 r5 [ b ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 5 r5 [ b ]) (nil))) (insn 4 3 5 2 (set (reg/v:SI 168 [ d ]) (reg:SI 6 r6 [ d ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 6 r6 [ d ]) (nil))) (note 5 4 8 2 NOTE_INSN_FUNCTION_BEG) (insn 8 5 9 2 (set (reg:SI 147 t) (eq:SI (reg/v:SI 166 [ a ]) (reg/v:SI 167 [ b ]))) sh_tmp.cpp:542 17 {cmpeqsi_t} (expr_list:REG_DEAD (reg/v:SI 166 [ a ]) (nil))) (insn 9 8 10 2 (set (reg:SI 171) (const_int -1 [0xffffffffffffffff])) sh_tmp.cpp:542 257 {movsi_ie} (nil)) (insn 10 9 12 2 (parallel [ (set (reg:SI 170 [ D.1945 ]) (xor:SI (reg:SI 147 t) (const_int 1 [0x1]))) (set (reg:SI 147 t) (const_int 1 [0x1])) (use (reg:SI 171)) ]) sh_tmp.cpp:542 401 {movrt_negc} (expr_list:REG_DEAD (reg:SI 171) (expr_list:REG_UNUSED (reg:SI 147 t) (nil)))) (insn 12 10 13 2 (set (reg:SI 172 [ D.1946 ]) (plus:SI (reg/v:SI 168 [ d ]) (reg/v:SI 167 [ b ]))) sh_tmp.cpp:543 75 {*addsi3_compact} (expr_list:REG_DEAD (reg/v:SI 168 [ d ]) (expr_list:REG_DEAD (reg/v:SI 167 [ b ]) (nil)))) (insn 13 12 27 2 (set (reg:SI 173 [ D.1946 ]) (plus:SI (reg:SI 172 [ D.1946 ]) (const_int 1 [0x1]))) sh_tmp.cpp:543 75 {*addsi3_compact} (expr_list:REG_DEAD (reg:SI 172 [ D.1946 ]) (nil))) (insn 27 13 28 2 (set (reg:SI 0 r0) (reg:SI 170 [ D.1945 ])) sh_tmp.cpp:546 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 170 [ D.1945 ]) (nil))) (insn 28 27 22 2 (set (reg:SI 1 r1 [+4 ]) (reg:SI 173 [ D.1946 ])) sh_tmp.cpp:546 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 173 [ D.1946 ]) (nil))) (insn 22 28 0 2 (use (reg/i:DI 0 r0)) sh_tmp.cpp:546 -1 (nil)) During combine, the following is tried: Trying 12 -> 13: Successfully matched this instruction: (set (reg:SI 173 [ D.1946 ]) (plus:SI (plus:SI (reg:SI 6 r6 [ d ]) (reg/v:SI 167 [ b ])) (const_int 1 [0x1]))) Trying 12 -> 13: Successfully matched this instruction: (set (reg:SI 173 [ D.1946 ]) (plus:SI (plus:SI (reg:SI 6 r6 [ d ]) (reg/v:SI 167 [ b ])) (const_int 1 [0x1]))) Trying 28 -> 22: Trying 13, 28 -> 22: starting the processing of deferred insns rescanning insn with uid = 8. rescanning insn with uid = 12. ending the processing of deferred insns There's an 'reg + reg + 1' insn in sh.md, which combine obviously successfully matches: (define_insn_and_split "*addc_r_r_1" [(set (match_operand:SI 0 "arith_reg_dest" "") (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand" "") (match_operand:SI 2 "arith_reg_operand" "")) (const_int 1))) (clobber (reg:SI T_REG))] "TARGET_SH1" "#" "&& 1" [(set (reg:SI T_REG) (const_int 1)) (parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 1) (match_dup 2)) (reg:SI T_REG))) (clobber (reg:SI T_REG))])]) .. but for some reason the matched insn is not used. Modifying the function above a little bit to result test2 (int a, int b, int d) { result r; r.a = a == b; r.b = d + b + 1; return r; }; will re-order the insns before combine, so that it can't combine the plus insns, because the T_REG that holds the comparison result is live across the plus insns. (note 7 0 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) (insn 2 7 3 2 (set (reg/v:SI 166 [ a ]) (reg:SI 4 r4 [ a ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 4 r4 [ a ]) (nil))) (insn 3 2 4 2 (set (reg/v:SI 167 [ b ]) (reg:SI 5 r5 [ b ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 5 r5 [ b ]) (nil))) (insn 4 3 5 2 (set (reg/v:SI 168 [ d ]) (reg:SI 6 r6 [ d ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 6 r6 [ d ]) (nil))) (insn 5 4 6 2 (set (reg/v:SI 169 [ e ]) (reg:SI 7 r7 [ e ])) sh_tmp.cpp:540 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 7 r7 [ e ]) (nil))) (note 6 5 9 2 NOTE_INSN_FUNCTION_BEG) (insn 9 6 12 2 (set (reg:SI 147 t) (eq:SI (reg/v:SI 166 [ a ]) (reg/v:SI 167 [ b ]))) sh_tmp.cpp:542 17 {cmpeqsi_t} (expr_list:REG_DEAD (reg/v:SI 167 [ b ]) (expr_list:REG_DEAD (reg/v:SI 166 [ a ]) (nil)))) (insn 12 9 13 2 (set (reg:SI 172 [ D.1947 ]) (plus:SI (reg/v:SI 168 [ d ]) (reg/v:SI 169 [ e ]))) sh_tmp.cpp:543 75 {*addsi3_compact} (expr_list:REG_DEAD (reg/v:SI 169 [ e ]) (expr_list:REG_DEAD (reg/v:SI 168 [ d ]) (nil)))) (insn 13 12 27 2 (set (reg:SI 173 [ D.1947 ]) (plus:SI (reg:SI 172 [ D.1947 ]) (const_int 1 [0x1]))) sh_tmp.cpp:543 75 {*addsi3_compact} (expr_list:REG_DEAD (reg:SI 172 [ D.1947 ]) (nil))) (insn 27 13 28 2 (set (reg:SI 0 r0) (reg:SI 147 t)) sh_tmp.cpp:545 399 {movt} (expr_list:REG_DEAD (reg:SI 147 t) (nil))) (insn 28 27 22 2 (set (reg:SI 1 r1 [+4 ]) (reg:SI 173 [ D.1947 ])) sh_tmp.cpp:545 257 {movsi_ie} (expr_list:REG_DEAD (reg:SI 173 [ D.1947 ]) (nil))) (insn 22 28 0 2 (use (reg/i:DI 0 r0)) sh_tmp.cpp:545 -1 (nil)) However, in the first case, there is no register overlap and yet the insns don't get combined. I guess this is because of the SH's multiple set negc insn: (insn 10 9 12 2 (parallel [ (set (reg:SI 170 [ D.1945 ]) (xor:SI (reg:SI 147 t) (const_int 1 [0x1]))) (set (reg:SI 147 t) (const_int 1 [0x1])) (use (reg:SI 171)) ]) sh_tmp.cpp:542 401 {movrt_negc} (expr_list:REG_DEAD (reg:SI 171) (expr_list:REG_UNUSED (reg:SI 147 t) (nil)))) which sets the T_REG to a constant '1' but the T_REG is marked as unused and not dead, and combine doesn't understand this and thinks that T_REG can't be clobbered by the following insn (the missed addc insn would clobber the T_REG).