https://gcc.gnu.org/g:ec79b0779b6eeb9ef3b8a45f46a596cbb75a7844
commit r17-817-gec79b0779b6eeb9ef3b8a45f46a596cbb75a7844 Author: Andrew Pinski <[email protected]> Date: Wed May 20 13:20:15 2026 -0700 tree-cfgcleanup: Don't remove forwarder blocks (with phis) with phis that have abnormal uses [PR125396] 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 <[email protected]> Diff: --- gcc/testsuite/gcc.dg/torture/pr125396-1.c | 38 +++++++++++++++++++++++++++++++ gcc/tree-cfgcleanup.cc | 27 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/gcc/testsuite/gcc.dg/torture/pr125396-1.c b/gcc/testsuite/gcc.dg/torture/pr125396-1.c new file mode 100644 index 000000000000..b0557d2f03f0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr125396-1.c @@ -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; +} diff --git a/gcc/tree-cfgcleanup.cc b/gcc/tree-cfgcleanup.cc index 85b9a3cedaa2..0f47f13591c1 100644 --- a/gcc/tree-cfgcleanup.cc +++ b/gcc/tree-cfgcleanup.cc @@ -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))
