]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
combine: Discard REG_UNUSED note in i2 when register is also referenced in i3 [PR118739]
authorUros Bizjak <ubizjak@gmail.com>
Wed, 12 Feb 2025 10:19:57 +0000 (11:19 +0100)
committerUros Bizjak <ubizjak@gmail.com>
Mon, 3 Mar 2025 15:57:03 +0000 (16:57 +0100)
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 used by I3, then this register
     now dies here, so we must put a REG_DEAD note here unless there
     is one already.  */
  else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))
   && ! (REG_P (XEXP (note, 0))
 ? find_regno_note (i3, REG_DEAD,
    REGNO (XEXP (note, 0)))
 : find_reg_note (i3, REG_DEAD, XEXP (note, 0))))
    {
      PUT_REG_NOTE_KIND (note, REG_DEAD);
      place = i3;
    }
--cut here--

Flags register is used in I3, but there already is a REG_DEAD note in I3.
The above condition doesn't trigger and continues in the "else" part where
REG_DEAD note is put to I2.  The proposed solution corrects the above
logic to trigger every time the register is referenced in I3, avoiding the
"else" part.

PR rtl-optimization/118739

gcc/ChangeLog:

* combine.cc (distribute_notes) <case REG_UNUSED>: Correct the
logic when the register is used by I3.

gcc/testsuite/ChangeLog:

* gcc.target/i386/pr118739.c: New test.

gcc/combine.cc
gcc/testsuite/gcc.target/i386/pr118739.c [new file with mode: 0644]

index 3beeb514b81746404831db00df5e1cd0cfe7a91f..1b2bd34748ec1dac697420f702e5730fbc667fab 100644 (file)
@@ -14523,14 +14523,15 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2,
          /* Otherwise, if this register is used by I3, then this register
             now dies here, so we must put a REG_DEAD note here unless there
             is one already.  */
-         else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))
-                  && ! (REG_P (XEXP (note, 0))
-                        ? find_regno_note (i3, REG_DEAD,
-                                           REGNO (XEXP (note, 0)))
-                        : find_reg_note (i3, REG_DEAD, XEXP (note, 0))))
+         else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
            {
-             PUT_REG_NOTE_KIND (note, REG_DEAD);
-             place = i3;
+             if (! (REG_P (XEXP (note, 0))
+                    ? find_regno_note (i3, REG_DEAD, REGNO (XEXP (note, 0)))
+                    : find_reg_note (i3, REG_DEAD, XEXP (note, 0))))
+               {
+                 PUT_REG_NOTE_KIND (note, REG_DEAD);
+                 place = i3;
+               }
            }
 
          /* A SET or CLOBBER of the REG_UNUSED reg has been removed,
diff --git a/gcc/testsuite/gcc.target/i386/pr118739.c b/gcc/testsuite/gcc.target/i386/pr118739.c
new file mode 100644 (file)
index 0000000..89bed54
--- /dev/null
@@ -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;
+}