So it turns out creating a forwarder block in some cases causes a regression.
The rtl ifcvt does like the case were the middle bb does not have a single 
predecessor.
So in some cases ifcvt does not happen any more. So the way to fix this
is not to create a forwarder block for those edges which causes out of ssa to
create a middle bb which has the constant or the copy in it.

I tried to do a simple ifcvt on the gimple level with the last phiopt but
that introduces regressions as then `v += cmp - 43;` is not optimized
and produces an extra subtraction. This is the best workaround I could come up
with until that is fixed. I filed PR 123116 for that issue.

Bootstrapped and tested on x86_64-linux-gnu.

gcc/ChangeLog:

        * tree-cfg.cc (ifconvertable_edge): New function.
        (make_forwarders_with_degenerate_phis): Add skip_ifcvtable argument,
        check ifconvertable_edge if skip_ifcvtable is true.
        * tree-cfg.h (make_forwarders_with_degenerate_phis): New argument
        with default of false.
        * tree-cfgcleanup.cc (execute_cleanup_cfg_post_optimizing): Update
        argument to make_forwarders_with_degenerate_phis.

Signed-off-by: Andrew Pinski <[email protected]>
---
 gcc/tree-cfg.cc        | 73 +++++++++++++++++++++++++++++++++++++++++-
 gcc/tree-cfg.h         |  2 +-
 gcc/tree-cfgcleanup.cc |  2 +-
 3 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index 8ee208b3032..ec3be8125f0 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -10195,13 +10195,77 @@ sort_phi_args (const void *a_, const void *b_)
     return 0;
 }
 
+/* Returns true if edge E comes from a possible ifconvertable branch.
+   That is:
+   ```
+   BB0:
+   if (a) goto BB1; else goto BB2;
+   BB1:
+   BB2:
+   ```
+   Returns true for the edge from BB0->BB2 or BB1->BB2.
+   This function assumes we only have one middle block.
+   And the middle block is empty. */
+
+static bool
+ifconvertable_edge (edge e)
+{
+  basic_block bb2 = e->dest;
+  basic_block bb0 = e->src;
+  basic_block bb1 = nullptr, rbb1;
+  if (e->src == e->dest)
+    return false;
+  if (EDGE_COUNT (bb0->succs) > 2)
+    return false;
+  if (single_succ_p (bb0))
+    {
+      if (!single_pred_p (bb0))
+       return false;
+      /* The middle block can only be empty,
+        otherwise the phis will be
+        different anyways. */
+      if (!empty_block_p (bb0))
+       return false;
+      bb1 = bb0;
+      bb0 = single_pred (bb0);
+      if (EDGE_COUNT (bb0->succs) != 2)
+       return false;
+    }
+
+  /* If convertables are only for conditionals. */
+  if (!is_a<gcond*>(*gsi_last_nondebug_bb (bb0)))
+    return false;
+
+  /* Find the other basic block.  */
+  if (EDGE_SUCC (bb0, 0)->dest == bb2)
+    rbb1 = EDGE_SUCC (bb0, 1)->dest;
+  else if (EDGE_SUCC (bb0, 1)->dest == bb2)
+    rbb1 = EDGE_SUCC (bb0, 0)->dest;
+  else
+    return false;
+
+  /* If we already know bb1, then just test it. */
+  if (bb1)
+    return rbb1 == bb1;
+
+  if (!single_succ_p (rbb1) || !single_pred_p (rbb1))
+    return false;
+  /* The middle block can only be empty,
+     otherwise the phis will be
+     different anyways. */
+  if (!empty_block_p (rbb1))
+    return false;
+
+  return single_succ (rbb1) == bb2;
+}
+
 /* Look for a non-virtual PHIs and make a forwarder block when all PHIs
    have the same argument on a set of edges.  This is to not consider
    control dependences of individual edges for same values but only for
    the common set.  Returns true if changed the CFG.  */
 
 bool
-make_forwarders_with_degenerate_phis (function *fn)
+make_forwarders_with_degenerate_phis (function *fn, bool skip_ifcvtable)
 {
   bool didsomething = false;
 
@@ -10245,6 +10309,13 @@ make_forwarders_with_degenerate_phis (function *fn)
              && loop_exit_edge_p (e->src->loop_father, e))
            continue;
 
+         /* Skip ifconvertable edges when asked as we want the
+            copy/constant on that edge still when going out of ssa.
+            FIXME: phiopt should produce COND_EXPR but there
+            are regressions with that.  */
+         if (skip_ifcvtable && ifconvertable_edge (e))
+           continue;
+
          tree arg = gimple_phi_arg_def (phi, i);
          if (!CONSTANT_CLASS_P (arg) && TREE_CODE (arg) != SSA_NAME)
            need_resort = true;
diff --git a/gcc/tree-cfg.h b/gcc/tree-cfg.h
index 2e01762f04b..56d40406d1f 100644
--- a/gcc/tree-cfg.h
+++ b/gcc/tree-cfg.h
@@ -114,7 +114,7 @@ extern edge gimple_switch_edge (function *, gswitch *, 
unsigned);
 extern edge gimple_switch_default_edge (function *, gswitch *);
 extern bool cond_only_block_p (basic_block);
 extern void copy_phi_arg_into_existing_phi (edge, edge, bool = false);
-extern bool make_forwarders_with_degenerate_phis (function *);
+extern bool make_forwarders_with_degenerate_phis (function *, bool = false);
 
 /* Return true if the LHS of a call should be removed.  */
 
diff --git a/gcc/tree-cfgcleanup.cc b/gcc/tree-cfgcleanup.cc
index 2521e6d09fd..8060e90be79 100644
--- a/gcc/tree-cfgcleanup.cc
+++ b/gcc/tree-cfgcleanup.cc
@@ -1418,7 +1418,7 @@ execute_cleanup_cfg_post_optimizing (void)
   /* When optimizing undo the merging of forwarder blocks
      that phis for better out of ssa expansion.  */
   if (optimize)
-    make_forwarders_with_degenerate_phis (cfun);
+    make_forwarders_with_degenerate_phis (cfun, true);
 
   /* Make sure todo does not have cleanup cfg as we don't want
      remove the forwarder blocks we just created. cleanup cfg
-- 
2.43.0

Reply via email to