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

Reply via email to