]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-cfgcleanup: Don't remove forwarder blocks (with phis) with phis that have abnorm...
authorAndrew Pinski <andrew.pinski@oss.qualcomm.com>
Wed, 20 May 2026 20:20:15 +0000 (13:20 -0700)
committerAndrew Pinski <andrew.pinski@oss.qualcomm.com>
Tue, 26 May 2026 20:00:07 +0000 (13:00 -0700)
This was a latent bug in the checks for removing of a forwarder block which has a phi.

Take:
      <bb 15> [local count: 182536112]:
      # arr1$0_32 = PHI <arr1$0_36(ab)(11)>

      <bb 14> [local count: 206998870]:
      # arr1$0_43(ab) = PHI <arr1$0_25(ab)(13), 9(15)>
      # sj12_44(ab) = PHI <sj12_31(ab)(13), arr1$0_32(15)>
      f8 ();

Here bb 15 predecessor is a normal edge.
So when merging the forwarder bb 15 into bb14 we end up with:
      <bb 12> [local count: 206998870]:
      # arr1$0_42(ab) = PHI <arr1$0_24(ab)(11), 9(9)>
      # sj12_43(ab) = PHI <sj12_30(ab)(11), arr1$0_35(ab)(9)>
      f8 ();
and now there is an overlap of live range of arr1$0_35 and arr1$0_42.
So we need to reject the case where we have phis and the phi arguments that
use abnormal uses.

Changes since v1:
* v2: Look at phi arguments of the forwarder block rather than the dest bb
      having an abnormal edge out.
* v3: Fix bb_phis_references_abnormal_uses to use the gimple_phi_num_args to
      search over the phi arguments. Also fix the commit message which was wrong.

Bootstrapped and tested on x86_64-linux-gnu.

PR tree-optimization/125396

gcc/ChangeLog:

* tree-cfgcleanup.cc (bb_phis_references_abnormal_uses): New function.
(maybe_remove_forwarder_block): Check to make sure the
forwarder block does not have a phi that references ssa name that has
abnormal uses.

gcc/testsuite/ChangeLog:

* gcc.dg/torture/pr125396-1.c: New test.

Signed-off-by: Andrew Pinski <andrew.pinski@oss.qualcomm.com>
gcc/testsuite/gcc.dg/torture/pr125396-1.c [new file with mode: 0644]
gcc/tree-cfgcleanup.cc

diff --git a/gcc/testsuite/gcc.dg/torture/pr125396-1.c b/gcc/testsuite/gcc.dg/torture/pr125396-1.c
new file mode 100644 (file)
index 0000000..b0557d2
--- /dev/null
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* PR tree-optimization/125396 */
+
+
+char g7;
+int g13, g26;
+int g() __attribute__((returns_twice));
+void f8()
+{
+    int arr1[2];
+    int c2, c4;
+    char v6;
+    int sj12;
+    int sj14;
+lbl_br1:
+    if (g13) goto lbl_br3;
+    if (c4)
+    {
+        c4 = 0;
+        goto lbl_br6;
+    }
+    switch (v6)
+    case 1:
+        c4 = 1;
+    sj14 = g();
+    g13 = sj12;
+lbl_br3:
+    arr1[0] = 0;
+    sj12 = g();
+    c2 = g7;
+    g26 = 5;
+    if (c2) __builtin_unreachable();
+lbl_br6:
+    sj12 = arr1[0];
+    arr1[0] = 9;
+    if (g26) f8();
+    goto lbl_br1;
+}
index 85b9a3cedaa243dc249b5cdfb6c145c68d0e3d3d..0f47f13591c1b8595bc6e4f5f2e5c8c5f90a529f 100644 (file)
@@ -464,6 +464,28 @@ move_debug_stmts_from_forwarder (basic_block src,
     }
 }
 
+/* Returns true if a phi of the basic block BB has an
+   use of a ssa name that is used in an abnormal edge.  */
+
+static bool
+bb_phis_references_abnormal_uses (basic_block bb)
+{
+  auto phis = phi_nodes (bb);
+  gimple_stmt_iterator i;
+  for (i = gsi_start (phis); !gsi_end_p (i); gsi_next (&i))
+    {
+      gphi *phi = as_a<gphi*>(*i);
+      for (unsigned indx = 0; indx < gimple_phi_num_args (phi); indx++)
+       {
+         tree value = gimple_phi_arg_def (phi, indx);
+         if (TREE_CODE (value) == SSA_NAME
+             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (value))
+           return true;
+       }
+    }
+  return false;
+}
+
 /* Return true if basic block BB does nothing except pass control
    flow to another block and that we can safely insert a label at
    the start of the successor block and was removed.
@@ -550,6 +572,11 @@ maybe_remove_forwarder_block (basic_block bb, bool can_split = false)
   if (has_phi && gimple_seq_empty_p (phi_nodes (dest)))
     return false;
 
+  /* When we have phi, make sure the phi does not have any uses of
+     names used in abnormal phis.  */
+  if (has_phi && bb_phis_references_abnormal_uses (bb))
+    return false;
+
   /* Now walk through the statements backward.  We can ignore labels,
      anything else means this is not a forwarder block.  */
   for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))