[backport from gcc-4.9/trunk r197002 ] List-Archive: From: Eric Botcazou Subject: Fix combiner bug with 4th insn Date: Sat, 23 Mar 2013 12:04:15 +0100 We ran into a combiner bug on the 4.7 branch for ARM (LE/soft-float/Thumb): FAIL: gcc.c-torture/execute/930718-1.c compilation, -O2 (internal compiler error) It's a bug introduced by the handling of the 4th instruction: (insn 8 68 9 2 (set (reg/v:SI 139 [ foo ]) (const_int 1 [0x1])) 930718-1.c:11:16 714 {*thumb2_movsi_insn} (nil)) (insn 9 8 11 2 (set (zero_extract:SI (reg/v:SI 139 [ foo ]) (const_int 1 [0x1]) (const_int 1 [0x1])) (const_int 0 [0])) 930718-1.c:11:16 84 {insv_zero} (nil)) (insn 14 13 67 2 (set (reg:CC_NOOV 24 cc) (compare:CC_NOOV (zero_extract:SI (reg/v:SI 139 [ foo ]) (const_int 2 [0x2]) (const_int 0 [0])) (const_int 0 [0]))) 930718-1.c:11:16 79 {*zeroextractsi_compare0_scratch} (expr_list:REG_DEAD (reg/v:SI 139 [ foo ]) (nil))) (insn 21 19 22 2 (set (reg:SI 147) (if_then_else:SI (eq:SI (reg:CC_NOOV 24 cc) (const_int 0 [0])) (const_int 2 [0x2]) (const_int 0 [0]))) 930718-1.c:31:30 721 {*thumb2_movsicc_insn} (expr_list:REG_DEAD (reg:CC 24 cc) (nil))) The combiner first thinks that it needs to keep the set in insn 8, only to discover later that the REG_DEAD note in insn 14 kills it, which confuses the code putting back the notes at the end. Fixed thusly. The patch also does a bit of refactoring in the block of code putting back notes at the end. Tested on x86_64-suse-linux, applied on the mainline. gcc/ 2013-03-23 Eric Botcazou * combine.c (try_combine): Adjust comment. Do not add the set of insn #0 if the destination indirectly is set or dies in insn #2. Tidy up code to distribute a new note. --- gcc-4.7.2/gcc/combine.c.~1~ 2012-08-09 17:33:28.000000000 +0200 +++ gcc-4.7.2/gcc/combine.c 2013-03-23 20:31:25.355730217 +0100 @@ -2993,13 +2993,13 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx /* See if the SETs in I1 or I2 need to be kept around in the merged instruction: whenever the value set there is still needed past I3. - For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3. + For the SET in I2, this is easy: we see if I2DEST dies or is set in I3. - For the SET in I1, we have two cases: If I1 and I2 independently - feed into I3, the set in I1 needs to be kept around if I1DEST dies + For the SET in I1, we have two cases: if I1 and I2 independently feed + into I3, the set in I1 needs to be kept around unless I1DEST dies or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set in I1 needs to be kept around unless I1DEST dies or is set in either - I2 or I3. The same consideration applies to I0. */ + I2 or I3. The same considerations apply to I0. */ added_sets_2 = !dead_or_set_p (i3, i2dest); @@ -3011,8 +3011,9 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx if (i0) added_sets_0 = !(dead_or_set_p (i3, i0dest) - || (i0_feeds_i2_n && dead_or_set_p (i2, i0dest)) - || (i0_feeds_i1_n && dead_or_set_p (i1, i0dest))); + || (i0_feeds_i1_n && dead_or_set_p (i1, i0dest)) + || ((i0_feeds_i2_n || (i0_feeds_i1_n && i1_feeds_i2_n)) + && dead_or_set_p (i2, i0dest))); else added_sets_0 = 0; @@ -4305,14 +4306,12 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx if (i3dest_killed) { + rtx new_note = alloc_reg_note (REG_DEAD, i3dest_killed, NULL_RTX); if (newi2pat && reg_set_p (i3dest_killed, newi2pat)) - distribute_notes (alloc_reg_note (REG_DEAD, i3dest_killed, - NULL_RTX), - NULL_RTX, i2, NULL_RTX, elim_i2, elim_i1, elim_i0); + distribute_notes (new_note, NULL_RTX, i2, NULL_RTX, elim_i2, + elim_i1, elim_i0); else - distribute_notes (alloc_reg_note (REG_DEAD, i3dest_killed, - NULL_RTX), - NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, + distribute_notes (new_note, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX, elim_i2, elim_i1, elim_i0); }