https://gcc.gnu.org/g:04f96e7c8ce32b3a198fd8d3e9c5a929d3a713f1

commit 04f96e7c8ce32b3a198fd8d3e9c5a929d3a713f1
Author: Alexandre Oliva <ol...@gnu.org>
Date:   Thu Sep 19 06:43:22 2024 -0300

    adjust probs after modified ifcombine

Diff:
---
 gcc/tree-ssa-ifcombine.cc | 248 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 180 insertions(+), 68 deletions(-)

diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index 79ccc70b2678..4c5f39d9b3e1 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -49,6 +49,28 @@ along with GCC; see the file COPYING3.  If not see
                 false) >= 2)
 #endif
 
+/* Return TRUE iff COND is NULL, or the condition in it is constant.  */
+
+static bool
+constant_condition_p (gcond *cond)
+{
+  if (!cond)
+    return true;
+
+  return (CONSTANT_CLASS_P (gimple_cond_lhs (cond))
+         && CONSTANT_CLASS_P (gimple_cond_rhs (cond)));
+}
+
+/* Return FALSE iff the condition in the COND stmt that ends COND_BB is not
+   constant.  */
+
+static bool
+constant_condition_p (basic_block cond_bb)
+{
+  gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (cond_bb));
+  return constant_condition_p (cond);
+}
+
 /* This pass combines COND_EXPRs to simplify control flow.  It
    currently recognizes bit tests and comparisons in chains that
    represent logical and or logical or of two COND_EXPRs.
@@ -107,12 +129,7 @@ recognize_if_then_else (basic_block cond_bb,
   if (!*else_bb)
     *else_bb = e->dest;
 
-  gcond *cond = safe_dyn_cast <gcond *> (*gsi_last_bb (cond_bb));
-  if (!cond)
-    return false;
-
-  if (CONSTANT_CLASS_P (gimple_cond_lhs (cond))
-      && CONSTANT_CLASS_P (gimple_cond_rhs (cond)))
+  if (constant_condition_p (cond_bb))
     return false;
 
   return true;
@@ -364,14 +381,27 @@ recognize_bits_test (gcond *cond, tree *name, tree *bits, 
bool inv)
 }
 
 
-/* Update profile after code in outer_cond_bb was adjusted so
-   outer_cond_bb has no condition.  */
+/* Update profile after code in either outer_cond_bb or inner_cond_bb was
+   adjusted so that it has no condition.  */
 
 static void
 update_profile_after_ifcombine (basic_block inner_cond_bb,
                                basic_block outer_cond_bb)
 {
-  edge outer_to_inner = find_edge (outer_cond_bb, inner_cond_bb);
+  /* In the following we assume that inner_cond_bb has single predecessor.  */
+  gcc_assert (single_pred_p (inner_cond_bb));
+
+  basic_block outer_to_inner_bb = inner_cond_bb;
+  profile_probability prob = profile_probability::always ();
+  for (basic_block parent = single_pred (outer_to_inner_bb);
+       parent != outer_cond_bb;
+       parent = single_pred (outer_to_inner_bb))
+    {
+      prob *= find_edge (parent, outer_to_inner_bb)->probability;
+      outer_to_inner_bb = parent;
+    }
+
+  edge outer_to_inner = find_edge (outer_cond_bb, outer_to_inner_bb);
   edge outer2 = (EDGE_SUCC (outer_cond_bb, 0) == outer_to_inner
                 ? EDGE_SUCC (outer_cond_bb, 1)
                 : EDGE_SUCC (outer_cond_bb, 0));
@@ -382,29 +412,62 @@ update_profile_after_ifcombine (basic_block inner_cond_bb,
     std::swap (inner_taken, inner_not_taken);
   gcc_assert (inner_taken->dest == outer2->dest);
 
-  /* In the following we assume that inner_cond_bb has single predecessor.  */
-  gcc_assert (single_pred_p (inner_cond_bb));
+  if (constant_condition_p (outer_cond_bb))
+    {
+      gcc_checking_assert (outer_to_inner_bb == inner_cond_bb);
 
-  /* Path outer_cond_bb->(outer2) needs to be merged into path
-     outer_cond_bb->(outer_to_inner)->inner_cond_bb->(inner_taken)
-     and probability of inner_not_taken updated.  */
+      /* Path outer_cond_bb->(outer2) needs to be merged into path
+        outer_cond_bb->(outer_to_inner)->inner_cond_bb->(inner_taken)
+        and probability of inner_not_taken updated.  */
 
-  inner_cond_bb->count = outer_cond_bb->count;
+      inner_cond_bb->count = outer_cond_bb->count;
 
-  /* Handle special case where inner_taken probability is always. In this case
-     we know that the overall outcome will be always as well, but combining
-     probabilities will be conservative because it does not know that
-     outer2->probability is inverse of outer_to_inner->probability.  */
-  if (inner_taken->probability == profile_probability::always ())
-    ;
-  else
-    inner_taken->probability = outer2->probability + 
outer_to_inner->probability
-                              * inner_taken->probability;
-  inner_not_taken->probability = profile_probability::always ()
-                                - inner_taken->probability;
+      /* Handle special case where inner_taken probability is always. In this
+        case we know that the overall outcome will be always as well, but
+        combining probabilities will be conservative because it does not know
+        that outer2->probability is inverse of
+        outer_to_inner->probability.  */
+      if (inner_taken->probability == profile_probability::always ())
+       ;
+      else
+       inner_taken->probability = outer2->probability
+         + outer_to_inner->probability * inner_taken->probability;
+      inner_not_taken->probability = profile_probability::always ()
+       - inner_taken->probability;
 
-  outer_to_inner->probability = profile_probability::always ();
-  outer2->probability = profile_probability::never ();
+      outer_to_inner->probability = profile_probability::always ();
+      outer2->probability = profile_probability::never ();
+    }
+  else if (constant_condition_p (inner_cond_bb))
+    {
+      /* Path inner_cond_bb->(inner_taken) needs to be merged into path
+        outer_cond_bb->(outer2).  We've accumulated the probabilities from
+        outer_cond_bb->(outer)->...->inner_cond_bb in prob, so we have to
+        adjust that by inner_taken, and make inner unconditional.  */
+
+      prob *= inner_taken->probability;
+      outer2->probability += prob;
+      outer_to_inner->probability = profile_probability::always ()
+       - outer2->probability;
+
+      inner_taken->probability = profile_probability::never ();
+      inner_not_taken->probability = profile_probability::always ();
+    }
+  else
+    {
+      /* We've moved part of the inner cond to outer, but we don't know the
+        probabilities for each part, so estimate the effects by moving half of
+        the odds of inner_taken to outer.  */
+
+      inner_taken->probability *= profile_probability::even ();
+      inner_not_taken->probability = profile_probability::always ()
+       - inner_taken->probability;
+      
+      prob *= inner_taken->probability;
+      outer2->probability += prob;
+      outer_to_inner->probability = profile_probability::always ()
+       - outer2->probability;
+    }
 }
 
 /* FIXME: move to a header file.  */
@@ -426,41 +489,86 @@ ifcombine_replace_cond (gcond *inner_cond, bool inner_inv,
                        tree cond, bool must_canon,
                        tree cond2)
 {
-  tree t = cond;
-  bool result_inv = inner_inv;
+  tree ret = cond;
+  bool outer_p = cond2 || (single_pred (gimple_bb (inner_cond))
+                          != gimple_bb (outer_cond));
+  bool result_inv = outer_p ? outer_inv : inner_inv;
 
-  /* ??? Support intervening blocks.  */
-  if (single_pred (gimple_bb (inner_cond)) != gimple_bb (outer_cond))
-    return NULL_TREE;
-
-  /* ??? Use both conditions.  */
-  if (cond2)
-    t = fold_build2 (TRUTH_AND_EXPR, TREE_TYPE (t), cond, cond2);
-
-  /* ??? Insert at outer_cond.  */
   if (result_inv)
-    t = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (t), t);
-  tree ret = t;
+    cond = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (cond), cond);
 
-  if (tree tcanon = canonicalize_cond_expr_cond (t))
-    ret = t = tcanon;
+  if (tree tcanon = canonicalize_cond_expr_cond (cond))
+    cond = tcanon;
   else if (must_canon)
     return NULL_TREE;
-  if (!is_gimple_condexpr_for_cond (t))
+
+  if (outer_p)
     {
-      gimple_stmt_iterator gsi = gsi_for_stmt (inner_cond);
-      t = force_gimple_operand_gsi_1 (&gsi, t, is_gimple_condexpr_for_cond,
-                                     NULL, true, GSI_SAME_STMT);
-    }
-  gimple_cond_set_condition_from_tree (inner_cond, t);
-  update_stmt (inner_cond);
+      /* ???  Check cond for any SSA_NAMEs that need to be pulled ahead of
+        outer_cond.  */
 
-  /* Leave CFG optimization to cfg_cleanup.  */
-  gimple_cond_set_condition_from_tree (outer_cond,
-                                      outer_inv
-                                      ? boolean_false_node
-                                      : boolean_true_node);
-  update_stmt (outer_cond);
+      if (!is_gimple_condexpr_for_cond (cond))
+       {
+         gimple_stmt_iterator gsi = gsi_for_stmt (outer_cond);
+         cond = force_gimple_operand_gsi_1 (&gsi, cond,
+                                            is_gimple_condexpr_for_cond,
+                                            NULL, true, GSI_SAME_STMT);
+       }
+      gimple_cond_set_condition_from_tree (outer_cond, cond);
+      update_stmt (outer_cond);
+
+      /* Leave CFG optimization to cfg_cleanup.  */
+      gimple_cond_set_condition_from_tree (outer_cond,
+                                          outer_inv
+                                          ? boolean_false_node
+                                          : boolean_true_node);
+      update_stmt (outer_cond);
+
+      if (cond2)
+       {
+         ret = fold_build2 (TRUTH_AND_EXPR, TREE_TYPE (ret), ret, cond2);
+
+         if (inner_inv)
+           cond2 = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (cond2), cond2);
+
+         if (tree tcanon = canonicalize_cond_expr_cond (cond2))
+           cond2 = tcanon;
+         if (!is_gimple_condexpr_for_cond (cond2))
+           {
+             gimple_stmt_iterator gsi = gsi_for_stmt (inner_cond);
+             cond2 = force_gimple_operand_gsi_1 (&gsi, cond2,
+                                                 is_gimple_condexpr_for_cond,
+                                                 NULL, true, GSI_SAME_STMT);
+           }
+         gimple_cond_set_condition_from_tree (inner_cond,
+                                              cond2);
+       }
+      else
+       gimple_cond_set_condition_from_tree (inner_cond,
+                                            inner_inv
+                                            ? boolean_false_node
+                                            : boolean_true_node);
+      update_stmt (inner_cond);
+    }
+  else
+    {
+      if (!is_gimple_condexpr_for_cond (cond))
+       {
+         gimple_stmt_iterator gsi = gsi_for_stmt (inner_cond);
+         cond = force_gimple_operand_gsi_1 (&gsi, cond,
+                                            is_gimple_condexpr_for_cond,
+                                            NULL, true, GSI_SAME_STMT);
+       }
+      gimple_cond_set_condition_from_tree (inner_cond, cond);
+      update_stmt (inner_cond);
+
+      /* Leave CFG optimization to cfg_cleanup.  */
+      gimple_cond_set_condition_from_tree (outer_cond,
+                                          outer_inv
+                                          ? boolean_false_node
+                                          : boolean_true_node);
+      update_stmt (outer_cond);
+    }
 
   update_profile_after_ifcombine (gimple_bb (inner_cond),
                                  gimple_bb (outer_cond));
@@ -611,7 +719,7 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool 
inner_inv,
   else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison
           && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison)
     {
-      tree t, ts = NULL_TREE, ts2 = NULL_TREE;
+      tree t, ts = NULL_TREE;
       enum tree_code inner_cond_code = gimple_cond_code (inner_cond);
       enum tree_code outer_cond_code = gimple_cond_code (outer_cond);
 
@@ -635,16 +743,16 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool 
inner_inv,
                                            gimple_cond_lhs (outer_cond),
                                            gimple_cond_rhs (outer_cond),
                                            gimple_bb (outer_cond)))
-         && !(t = ts = (fold_truth_andor_maybe_separate
-                        (UNKNOWN_LOCATION, TRUTH_ANDIF_EXPR,
-                         boolean_type_node,
-                         outer_cond_code,
-                         gimple_cond_lhs (outer_cond),
-                         gimple_cond_rhs (outer_cond),
-                         inner_cond_code,
-                         gimple_cond_lhs (inner_cond),
-                         gimple_cond_rhs (inner_cond),
-                         &ts2))))
+         && !(t = (fold_truth_andor_maybe_separate
+                   (UNKNOWN_LOCATION, TRUTH_ANDIF_EXPR,
+                    boolean_type_node,
+                    outer_cond_code,
+                    gimple_cond_lhs (outer_cond),
+                    gimple_cond_rhs (outer_cond),
+                    inner_cond_code,
+                    gimple_cond_lhs (inner_cond),
+                    gimple_cond_rhs (inner_cond),
+                    &ts))))
        {
          {
          tree t1, t2;
@@ -674,7 +782,7 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool 
inner_inv,
 
       if (!(t = ifcombine_replace_cond (inner_cond, inner_inv,
                                        outer_cond, outer_inv,
-                                       t, false, ts2)))
+                                       t, false, ts)))
        return false;
 
       if (dump_file)
@@ -700,6 +808,8 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, 
basic_block outer_cond_bb,
                         basic_block then_bb, basic_block else_bb,
                         basic_block phi_pred_bb)
 {
+  /* ??? Check the blocks for non-continuous configurations.  */
+
   /* The && form is characterized by a common else_bb with
      the two edges leading to it mergable.  The latter is
      guaranteed by matching PHI arguments in the else_bb and
@@ -800,6 +910,8 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
     {
       basic_block outer_cond_bb = bb = single_pred (bb);
 
+      /* ??? Pass bb down?  */
+
       if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb,
                                   then_bb, else_bb, inner_cond_bb))
        return bb;

Reply via email to