252 lines
8.3 KiB
Diff
252 lines
8.3 KiB
Diff
|
|
From 77398954ce517aa011b7a254c7aa2858521b2093 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Richard Biener <rguenther@suse.de>
|
||
|
|
Date: Mon, 15 Nov 2021 15:19:36 +0100
|
||
|
|
Subject: [PATCH 18/35] [Backport] tree-optimization/102880 - make PHI-OPT
|
||
|
|
recognize more CFGs
|
||
|
|
|
||
|
|
Reference: https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=f98f373dd822b35c52356b753d528924e9f89678
|
||
|
|
|
||
|
|
This allows extra edges into the middle BB for the PHI-OPT
|
||
|
|
transforms using replace_phi_edge_with_variable that do not
|
||
|
|
end up moving stmts from that middle BB. This avoids regressing
|
||
|
|
gcc.dg/tree-ssa/ssa-hoist-4.c with the actual fix for PR102880
|
||
|
|
where CFG cleanup has the choice to remove two forwarders and
|
||
|
|
picks "the wrong" leading to
|
||
|
|
|
||
|
|
if (a > b) /
|
||
|
|
/\ /
|
||
|
|
/ <BB>
|
||
|
|
/ |
|
||
|
|
# PHI <a, b>
|
||
|
|
|
||
|
|
rather than
|
||
|
|
|
||
|
|
if (a > b) |
|
||
|
|
/\ |
|
||
|
|
<BB> \ |
|
||
|
|
/ \ |
|
||
|
|
# PHI <a, b, b>
|
||
|
|
|
||
|
|
but it's relatively straight-forward to support extra edges
|
||
|
|
into the middle-BB in paths ending in replace_phi_edge_with_variable
|
||
|
|
and that do not require moving stmts. That's because we really
|
||
|
|
only want to remove the edge from the condition to the middle BB.
|
||
|
|
Of course actually doing that means updating dominators in non-trival
|
||
|
|
ways which is why I kept the original code for the single edge
|
||
|
|
case and simply defer to CFG cleanup by adjusting the condition for
|
||
|
|
the complicated case.
|
||
|
|
|
||
|
|
The testcase needs to be a GIMPLE one since it's quite unreliable
|
||
|
|
to produce the desired CFG.
|
||
|
|
|
||
|
|
2021-11-15 Richard Biener <rguenther@suse.de>
|
||
|
|
|
||
|
|
PR tree-optimization/102880
|
||
|
|
* tree-ssa-phiopt.c (tree_ssa_phiopt_worker): Push
|
||
|
|
single_pred (bb1) condition to places that really need it.
|
||
|
|
(match_simplify_replacement): Likewise.
|
||
|
|
(value_replacement): Likewise.
|
||
|
|
(replace_phi_edge_with_variable): Deal with extra edges
|
||
|
|
into the middle BB.
|
||
|
|
|
||
|
|
* gcc.dg/tree-ssa/phi-opt-26.c: New testcase.
|
||
|
|
---
|
||
|
|
gcc/testsuite/gcc.dg/tree-ssa/phi-opt-26.c | 31 +++++++++
|
||
|
|
gcc/tree-ssa-phiopt.c | 73 +++++++++++++---------
|
||
|
|
2 files changed, 75 insertions(+), 29 deletions(-)
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-26.c
|
||
|
|
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-26.c b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-26.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..21aa66e38
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-26.c
|
||
|
|
@@ -0,0 +1,31 @@
|
||
|
|
+/* { dg-do compile } */
|
||
|
|
+/* { dg-options "-O -fgimple -fdump-tree-phiopt1" } */
|
||
|
|
+
|
||
|
|
+int __GIMPLE (ssa,startwith("phiopt"))
|
||
|
|
+foo (int a, int b, int flag)
|
||
|
|
+{
|
||
|
|
+ int res;
|
||
|
|
+
|
||
|
|
+ __BB(2):
|
||
|
|
+ if (flag_2(D) != 0)
|
||
|
|
+ goto __BB6;
|
||
|
|
+ else
|
||
|
|
+ goto __BB4;
|
||
|
|
+
|
||
|
|
+ __BB(4):
|
||
|
|
+ if (a_3(D) > b_4(D))
|
||
|
|
+ goto __BB7;
|
||
|
|
+ else
|
||
|
|
+ goto __BB6;
|
||
|
|
+
|
||
|
|
+ __BB(6):
|
||
|
|
+ goto __BB7;
|
||
|
|
+
|
||
|
|
+ __BB(7):
|
||
|
|
+ res_1 = __PHI (__BB4: a_3(D), __BB6: b_4(D));
|
||
|
|
+ return res_1;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/* We should be able to detect MAX despite the extra edge into
|
||
|
|
+ the middle BB. */
|
||
|
|
+/* { dg-final { scan-tree-dump "MAX" "phiopt1" } } */
|
||
|
|
diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c
|
||
|
|
index 21ac08145..079d29e74 100644
|
||
|
|
--- a/gcc/tree-ssa-phiopt.c
|
||
|
|
+++ b/gcc/tree-ssa-phiopt.c
|
||
|
|
@@ -219,7 +219,6 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
|
||
|
|
|
||
|
|
/* If either bb1's succ or bb2 or bb2's succ is non NULL. */
|
||
|
|
if (EDGE_COUNT (bb1->succs) == 0
|
||
|
|
- || bb2 == NULL
|
||
|
|
|| EDGE_COUNT (bb2->succs) == 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
@@ -279,14 +278,14 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
|
||
|
|
|| (e1->flags & EDGE_FALLTHRU) == 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
- /* Also make sure that bb1 only have one predecessor and that it
|
||
|
|
- is bb. */
|
||
|
|
- if (!single_pred_p (bb1)
|
||
|
|
- || single_pred (bb1) != bb)
|
||
|
|
- continue;
|
||
|
|
-
|
||
|
|
if (do_store_elim)
|
||
|
|
{
|
||
|
|
+ /* Also make sure that bb1 only have one predecessor and that it
|
||
|
|
+ is bb. */
|
||
|
|
+ if (!single_pred_p (bb1)
|
||
|
|
+ || single_pred (bb1) != bb)
|
||
|
|
+ continue;
|
||
|
|
+
|
||
|
|
/* bb1 is the middle block, bb2 the join block, bb the split block,
|
||
|
|
e1 the fallthrough edge from bb1 to bb2. We can't do the
|
||
|
|
optimization if the join block has more than two predecessors. */
|
||
|
|
@@ -331,10 +330,11 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
|
||
|
|
node. */
|
||
|
|
gcc_assert (arg0 != NULL_TREE && arg1 != NULL_TREE);
|
||
|
|
|
||
|
|
- gphi *newphi = factor_out_conditional_conversion (e1, e2, phi,
|
||
|
|
- arg0, arg1,
|
||
|
|
- cond_stmt);
|
||
|
|
- if (newphi != NULL)
|
||
|
|
+ gphi *newphi;
|
||
|
|
+ if (single_pred_p (bb1)
|
||
|
|
+ && (newphi = factor_out_conditional_conversion (e1, e2, phi,
|
||
|
|
+ arg0, arg1,
|
||
|
|
+ cond_stmt)))
|
||
|
|
{
|
||
|
|
phi = newphi;
|
||
|
|
/* factor_out_conditional_conversion may create a new PHI in
|
||
|
|
@@ -355,12 +355,14 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p)
|
||
|
|
else if (abs_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
|
||
|
|
cfgchanged = true;
|
||
|
|
else if (!early_p
|
||
|
|
+ && single_pred_p (bb1)
|
||
|
|
&& cond_removal_in_builtin_zero_pattern (bb, bb1, e1, e2,
|
||
|
|
phi, arg0, arg1))
|
||
|
|
cfgchanged = true;
|
||
|
|
else if (minmax_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
|
||
|
|
cfgchanged = true;
|
||
|
|
- else if (spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
|
||
|
|
+ else if (single_pred_p (bb1)
|
||
|
|
+ && spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1))
|
||
|
|
cfgchanged = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@@ -391,35 +393,41 @@ replace_phi_edge_with_variable (basic_block cond_block,
|
||
|
|
edge e, gphi *phi, tree new_tree)
|
||
|
|
{
|
||
|
|
basic_block bb = gimple_bb (phi);
|
||
|
|
- basic_block block_to_remove;
|
||
|
|
gimple_stmt_iterator gsi;
|
||
|
|
|
||
|
|
/* Change the PHI argument to new. */
|
||
|
|
SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), new_tree);
|
||
|
|
|
||
|
|
/* Remove the empty basic block. */
|
||
|
|
+ edge edge_to_remove;
|
||
|
|
if (EDGE_SUCC (cond_block, 0)->dest == bb)
|
||
|
|
+ edge_to_remove = EDGE_SUCC (cond_block, 1);
|
||
|
|
+ else
|
||
|
|
+ edge_to_remove = EDGE_SUCC (cond_block, 0);
|
||
|
|
+ if (EDGE_COUNT (edge_to_remove->dest->preds) == 1)
|
||
|
|
{
|
||
|
|
- EDGE_SUCC (cond_block, 0)->flags |= EDGE_FALLTHRU;
|
||
|
|
- EDGE_SUCC (cond_block, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
|
||
|
|
- EDGE_SUCC (cond_block, 0)->probability = profile_probability::always ();
|
||
|
|
-
|
||
|
|
- block_to_remove = EDGE_SUCC (cond_block, 1)->dest;
|
||
|
|
+ e->flags |= EDGE_FALLTHRU;
|
||
|
|
+ e->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
|
||
|
|
+ e->probability = profile_probability::always ();
|
||
|
|
+ delete_basic_block (edge_to_remove->dest);
|
||
|
|
+
|
||
|
|
+ /* Eliminate the COND_EXPR at the end of COND_BLOCK. */
|
||
|
|
+ gsi = gsi_last_bb (cond_block);
|
||
|
|
+ gsi_remove (&gsi, true);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
- EDGE_SUCC (cond_block, 1)->flags |= EDGE_FALLTHRU;
|
||
|
|
- EDGE_SUCC (cond_block, 1)->flags
|
||
|
|
- &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
|
||
|
|
- EDGE_SUCC (cond_block, 1)->probability = profile_probability::always ();
|
||
|
|
-
|
||
|
|
- block_to_remove = EDGE_SUCC (cond_block, 0)->dest;
|
||
|
|
+ /* If there are other edges into the middle block make
|
||
|
|
+ CFG cleanup deal with the edge removal to avoid
|
||
|
|
+ updating dominators here in a non-trivial way. */
|
||
|
|
+ gcond *cond = as_a <gcond *> (last_stmt (cond_block));
|
||
|
|
+ if (edge_to_remove->flags & EDGE_TRUE_VALUE)
|
||
|
|
+ gimple_cond_make_false (cond);
|
||
|
|
+ else
|
||
|
|
+ gimple_cond_make_true (cond);
|
||
|
|
}
|
||
|
|
- delete_basic_block (block_to_remove);
|
||
|
|
|
||
|
|
- /* Eliminate the COND_EXPR at the end of COND_BLOCK. */
|
||
|
|
- gsi = gsi_last_bb (cond_block);
|
||
|
|
- gsi_remove (&gsi, true);
|
||
|
|
+ statistics_counter_event (cfun, "Replace PHI with variable", 1);
|
||
|
|
|
||
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
|
|
fprintf (dump_file,
|
||
|
|
@@ -846,6 +854,9 @@ match_simplify_replacement (basic_block cond_bb, basic_block middle_bb,
|
||
|
|
allow it and move it once the transformation is done. */
|
||
|
|
if (!empty_block_p (middle_bb))
|
||
|
|
{
|
||
|
|
+ if (!single_pred_p (middle_bb))
|
||
|
|
+ return false;
|
||
|
|
+
|
||
|
|
stmt_to_move = last_and_only_stmt (middle_bb);
|
||
|
|
if (!stmt_to_move)
|
||
|
|
return false;
|
||
|
|
@@ -1225,6 +1236,11 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
+ if (!single_pred_p (middle_bb))
|
||
|
|
+ return 0;
|
||
|
|
+ statistics_counter_event (cfun, "Replace PHI with "
|
||
|
|
+ "variable/value_replacement", 1);
|
||
|
|
+
|
||
|
|
/* Replace the PHI arguments with arg. */
|
||
|
|
SET_PHI_ARG_DEF (phi, e0->dest_idx, arg);
|
||
|
|
SET_PHI_ARG_DEF (phi, e1->dest_idx, arg);
|
||
|
|
@@ -1239,7 +1255,6 @@ value_replacement (basic_block cond_bb, basic_block middle_bb,
|
||
|
|
}
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
-
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Now optimize (x != 0) ? x + y : y to just x + y. */
|
||
|
|
--
|
||
|
|
2.27.0.windows.1
|
||
|
|
|