The combine pass is trying to combine:

Trying 16, 22, 21 -> 23:
   16: r104:QI=flags:CCNO>0
   22: {r120:QI=r104:QI^0x1;clobber flags:CC;}
      REG_UNUSED flags:CC
   21: r119:QI=flags:CCNO<=0
      REG_DEAD flags:CCNO
   23: {r110:QI=r119:QI|r120:QI;clobber flags:CC;}
      REG_DEAD r120:QI
      REG_DEAD r119:QI
      REG_UNUSED flags:CC

and creates the following two insn sequence:

modifying insn i2    22: r104:QI=flags:CCNO>0
      REG_DEAD flags:CC
deferring rescan insn with uid = 22.
modifying insn i3    23: r110:QI=flags:CCNO<=0
      REG_DEAD flags:CC
deferring rescan insn with uid = 23.

where the REG_DEAD note in i2 is not correct, because the flags
register is still referenced in i3.  In try_combine() megafunction, we
have this part:

--cut here--
    /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3.  */
    if (i3notes)
      distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL,
            elim_i2, elim_i1, elim_i0);
    if (i2notes)
      distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL,
            elim_i2, elim_i1, elim_i0);
    if (i1notes)
      distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL,
            elim_i2, local_elim_i1, local_elim_i0);
    if (i0notes)
      distribute_notes (i0notes, i0, i3, newi2pat ? i2 : NULL,
            elim_i2, elim_i1, local_elim_i0);
    if (midnotes)
      distribute_notes (midnotes, NULL, i3, newi2pat ? i2 : NULL,
            elim_i2, elim_i1, elim_i0);
--cut here--

where the compiler distributes REG_UNUSED note from i2:

   22: {r120:QI=r104:QI^0x1;clobber flags:CC;}
      REG_UNUSED flags:CC

via distribute_notes() using the following:

--cut here--
          /* Otherwise, if this register is now referenced in i2
         then the register used to be modified in one of the
         original insns.  If it was i3 (say, in an unused
         parallel), it's now completely gone, so the note can
         be discarded.  But if it was modified in i2, i1 or i0
         and we still reference it in i2, then we're
         referencing the previous value, and since the
         register was modified and REG_UNUSED, we know that
         the previous value is now dead.  So, if we only
         reference the register in i2, we change the note to
         REG_DEAD, to reflect the previous value.  However, if
         we're also setting or clobbering the register as
         scratch, we know (because the register was not
         referenced in i3) that it's unused, just as it was
         unused before, and we place the note in i2.  */
          if (from_insn != i3 && i2 && INSN_P (i2)
          && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
        {
          if (!reg_set_p (XEXP (note, 0), PATTERN (i2)))
            PUT_REG_NOTE_KIND (note, REG_DEAD);
          if (! (REG_P (XEXP (note, 0))
             ? find_regno_note (i2, REG_NOTE_KIND (note),
                        REGNO (XEXP (note, 0)))
             : find_reg_note (i2, REG_NOTE_KIND (note),
                      XEXP (note, 0))))
            place = i2;
        }
--cut here--

However, the flags register is not UNUSED (or DEAD), because it is
used in i3.  The proposed solution is to remove the REG_UNUSED note
from i2 when the register is also mentioned in i3.

Bootstrapped and regression tested on x86_64-linux-gnu {,-m32}.

OK for master and eventual backports?

Uros.
diff --git a/gcc/combine.cc b/gcc/combine.cc
index 3beeb514b81..0589ddbaca7 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -14557,9 +14557,12 @@ distribute_notes (rtx notes, rtx_insn *from_insn, 
rtx_insn *i3, rtx_insn *i2,
                 we're also setting or clobbering the register as
                 scratch, we know (because the register was not
                 referenced in i3) that it's unused, just as it was
-                unused before, and we place the note in i2.  */
+                unused before, and we place the note in i2.  If this
+                register is still referenced in i3, the note can be
+                discarded.  */
              if (from_insn != i3 && i2 && INSN_P (i2)
-                 && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
+                 && reg_referenced_p (XEXP (note, 0), PATTERN (i2))
+                 && !reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
                {
                  if (!reg_set_p (XEXP (note, 0), PATTERN (i2)))
                    PUT_REG_NOTE_KIND (note, REG_DEAD);
diff --git a/gcc/testsuite/gcc.target/i386/pr118739.c 
b/gcc/testsuite/gcc.target/i386/pr118739.c
new file mode 100644
index 00000000000..89bed546363
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr118739.c
@@ -0,0 +1,50 @@
+/* PR rtl-optimization/118739 */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-tree-forwprop -fno-tree-vrp" } */
+
+volatile int a;
+int b, c, d = 1, e, f, g;
+
+int h (void)
+{
+  int i = 1;
+
+ j:
+  for (b = 1; b; b--)
+    {
+      asm ("#");
+
+      g = 0;
+
+      for (; g <= 1; g++)
+       {
+         int k = f = 0;
+
+         for (; f <= 1; f++)
+           k = (1 == i) >= k || ((d = 0) >= a) + k;
+       }
+    }
+
+  for (; i < 3; i++)
+    {
+      if (!c)
+       return g;
+
+      if (e)
+       goto j;
+
+      asm ("#");
+    }
+
+  return 0;
+}
+
+int main()
+{
+  h();
+
+  if (d != 1)
+    __builtin_abort();
+
+  return 0;
+}

Reply via email to