When an insn used by combine has multiple SETs, only the non-REG_UNUSED set is used: others will end up dropped on the floor. We have to take note of the dropped REG_UNUSED SETs, clearing their cached values, so that, even if the REGs remain used (e.g. because they were referenced in the used SET_SRC), we will not use properties of the latest value as if they applied to the earlier one.
Regstrapped on x86_64-linux-gnu. Ok to install? for gcc/ChangeLog PR rtl-optimization/80693 * combine.c (distribute_notes): Add IDEST parameter. Reset any REG_UNUSED REGs that are not IDEST, if IDEST is given. Adjust all callers. for gcc/testsuite/ChangeLog PR rtl-optimization/80693 * gcc.dg/pr80693.c: New. --- gcc/combine.c | 80 ++++++++++++++++++++++++++-------------- gcc/testsuite/gcc.dg/pr80693.c | 26 +++++++++++++ 2 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr80693.c diff --git a/gcc/combine.c b/gcc/combine.c index 39ef3c6..6954f92 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -483,7 +483,7 @@ static void reg_dead_at_p_1 (rtx, const_rtx, void *); static int reg_dead_at_p (rtx, rtx_insn *); static void move_deaths (rtx, rtx, int, rtx_insn *, rtx *); static int reg_bitfield_target_p (rtx, rtx); -static void distribute_notes (rtx, rtx_insn *, rtx_insn *, rtx_insn *, rtx, rtx, rtx); +static void distribute_notes (rtx, rtx_insn *, rtx, rtx_insn *, rtx_insn *, rtx, rtx, rtx); static void distribute_links (struct insn_link *); static void mark_used_regs_combine (rtx); static void record_promoted_value (rtx_insn *, rtx); @@ -4170,7 +4170,7 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, remove_note (undobuf.other_insn, note); } - distribute_notes (new_other_notes, undobuf.other_insn, + distribute_notes (new_other_notes, undobuf.other_insn, NULL_RTX, undobuf.other_insn, NULL, NULL_RTX, NULL_RTX, NULL_RTX); } @@ -4424,19 +4424,19 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */ if (i3notes) - distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL, + distribute_notes (i3notes, i3, NULL_RTX, i3, newi2pat ? i2 : NULL, elim_i2, elim_i1, elim_i0); if (i2notes) - distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL, + distribute_notes (i2notes, i2, i2dest, i3, newi2pat ? i2 : NULL, elim_i2, elim_i1, elim_i0); if (i1notes) - distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL, + distribute_notes (i1notes, i1, i1dest, i3, newi2pat ? i2 : NULL, elim_i2, local_elim_i1, local_elim_i0); if (i0notes) - distribute_notes (i0notes, i0, i3, newi2pat ? i2 : NULL, + distribute_notes (i0notes, i0, i0dest, i3, newi2pat ? i2 : NULL, elim_i2, elim_i1, local_elim_i0); if (midnotes) - distribute_notes (midnotes, NULL, i3, newi2pat ? i2 : NULL, + distribute_notes (midnotes, NULL, NULL_RTX, i3, newi2pat ? i2 : NULL, elim_i2, elim_i1, elim_i0); /* Distribute any notes added to I2 or I3 by recog_for_combine. We @@ -4444,12 +4444,12 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, so we always pass it as i3. */ if (newi2pat && new_i2_notes) - distribute_notes (new_i2_notes, i2, i2, NULL, NULL_RTX, NULL_RTX, - NULL_RTX); + distribute_notes (new_i2_notes, i2, NULL_RTX, i2, NULL, + NULL_RTX, NULL_RTX, NULL_RTX); if (new_i3_notes) - distribute_notes (new_i3_notes, i3, i3, NULL, NULL_RTX, NULL_RTX, - NULL_RTX); + distribute_notes (new_i3_notes, i3, NULL_RTX, i3, NULL, + NULL_RTX, NULL_RTX, NULL_RTX); /* If I3DEST was used in I3SRC, it really died in I3. We may need to put a REG_DEAD note for it somewhere. If NEWI2PAT exists and sets @@ -4462,10 +4462,10 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, { rtx new_note = alloc_reg_note (REG_DEAD, i3dest_killed, NULL_RTX); if (newi2pat && reg_set_p (i3dest_killed, newi2pat)) - distribute_notes (new_note, NULL, i2, NULL, elim_i2, + distribute_notes (new_note, NULL, NULL_RTX, i2, NULL, elim_i2, elim_i1, elim_i0); else - distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + distribute_notes (new_note, NULL, NULL_RTX, i3, newi2pat ? i2 : NULL, elim_i2, elim_i1, elim_i0); } @@ -4473,10 +4473,10 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, { rtx new_note = alloc_reg_note (REG_DEAD, i2dest, NULL_RTX); if (newi2pat && reg_set_p (i2dest, newi2pat)) - distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + distribute_notes (new_note, NULL, NULL_RTX, i2, NULL, NULL_RTX, NULL_RTX, NULL_RTX); else - distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + distribute_notes (new_note, NULL, NULL_RTX, i3, newi2pat ? i2 : NULL, NULL_RTX, NULL_RTX, NULL_RTX); } @@ -4484,10 +4484,10 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, { rtx new_note = alloc_reg_note (REG_DEAD, i1dest, NULL_RTX); if (newi2pat && reg_set_p (i1dest, newi2pat)) - distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + distribute_notes (new_note, NULL, NULL_RTX, i2, NULL, NULL_RTX, NULL_RTX, NULL_RTX); else - distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + distribute_notes (new_note, NULL, NULL_RTX, i3, newi2pat ? i2 : NULL, NULL_RTX, NULL_RTX, NULL_RTX); } @@ -4495,10 +4495,10 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0, { rtx new_note = alloc_reg_note (REG_DEAD, i0dest, NULL_RTX); if (newi2pat && reg_set_p (i0dest, newi2pat)) - distribute_notes (new_note, NULL, i2, NULL, NULL_RTX, + distribute_notes (new_note, NULL, NULL_RTX, i2, NULL, NULL_RTX, NULL_RTX, NULL_RTX); else - distribute_notes (new_note, NULL, i3, newi2pat ? i2 : NULL, + distribute_notes (new_note, NULL, NULL_RTX, i3, newi2pat ? i2 : NULL, NULL_RTX, NULL_RTX, NULL_RTX); } @@ -13938,9 +13938,11 @@ reg_bitfield_target_p (rtx x, rtx body) return 0; } -/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them - as appropriate. I3 and I2 are the insns resulting from the combination - insns including FROM (I2 may be zero). +/* Given a chain of REG_NOTES originally from FROM_INSN, try to place + them as appropriate. IDEST is the dest in FROM_INSN used for + substitution (other dests in it are just dropped on the floor). I3 + and I2 are the insns resulting from the combination insns including + FROM (I2 may be zero). ELIM_I2 and ELIM_I1 are either zero or registers that we know will not need REG_DEAD notes because they are being substituted for. This @@ -13950,7 +13952,8 @@ reg_bitfield_target_p (rtx x, rtx body) on the type of note. */ static void -distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, +distribute_notes (rtx notes, rtx_insn *from_insn, rtx idest, + rtx_insn *i3, rtx_insn *i2, rtx elim_i2, rtx elim_i1, rtx elim_i0) { rtx note, next_note; @@ -14087,6 +14090,26 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, PUT_REG_NOTE_KIND (note, REG_DEAD); place = i3; } + + /* If there were any parallel sets in FROM_INSN other than + the one setting IDEST, it must be REG_UNUSED, otherwise + we could not have used FROM_INSN in combine. Since this + combine attempt succeeded, we know this unused SET was + dropped on the floor, because the insn was either deleted + or created from a new pattern that does not use its + SET_DEST. We must forget whatever we knew about the + value that was stored by that SET, since the prior value + may still be present in IDEST's src expression or + elsewhere, and we do not want to use properties of the + dropped value as if they applied to the prior one when + simplifying e.g. subsequent combine attempts. */ + if (idest && XEXP (note, 0) != idest) + { + gcc_assert (REG_P (XEXP (note, 0))); + record_value_for_reg (XEXP (note, 0), NULL, NULL_RTX); + INC_REG_N_SETS (REGNO (XEXP (note, 0)), -1); + } + break; case REG_EQUAL: @@ -14295,7 +14318,8 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, PATTERN (tem_insn) = pc_rtx; REG_NOTES (tem_insn) = NULL; - distribute_notes (old_notes, tem_insn, tem_insn, NULL, + distribute_notes (old_notes, tem_insn, NULL_RTX, + tem_insn, NULL, NULL_RTX, NULL_RTX, NULL_RTX); distribute_links (LOG_LINKS (tem_insn)); @@ -14316,7 +14340,7 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, REG_NOTES (cc0_setter) = NULL; distribute_notes (old_notes, cc0_setter, - cc0_setter, NULL, + NULL_RTX, cc0_setter, NULL, NULL_RTX, NULL_RTX, NULL_RTX); distribute_links (LOG_LINKS (cc0_setter)); @@ -14437,9 +14461,9 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2, rtx new_note = alloc_reg_note (REG_DEAD, piece, NULL_RTX); - distribute_notes (new_note, place, place, - NULL, NULL_RTX, NULL_RTX, - NULL_RTX); + distribute_notes (new_note, place, NULL_RTX, + place, NULL, NULL_RTX, + NULL_RTX, NULL_RTX); } else if (! refers_to_regno_p (i, PATTERN (place)) && ! find_regno_fusage (place, USE, i)) diff --git a/gcc/testsuite/gcc.dg/pr80693.c b/gcc/testsuite/gcc.dg/pr80693.c new file mode 100644 index 0000000..aecddd0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr80693.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-O -fno-tree-coalesce-vars" } */ +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long u64; + +static u64 __attribute__((noinline, noclone)) +foo(u8 u8_0, u16 u16_0, u32 u32_0, u64 u64_0, u16 u16_1) +{ + u16_1 += 0x1051; + u16_1 &= 1; + u8_0 <<= u32_0 & 7; + u16_0 -= !u16_1; + u16_1 >>= ((u16)-u8_0 != 0xff); + return u8_0 + u16_0 + u64_0 + u16_1; +} + +int +main (void) +{ + u64 x = foo(1, 1, 0xffff, 0, 1); + if (x != 0x80) + __builtin_abort(); + return 0; +} -- Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ You must be the change you wish to see in the world. -- Gandhi Be Free! -- http://FSFLA.org/ FSF Latin America board member Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer