Hi all,

This is a ping for https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01275.html . I've fixed attachment type, hopefully it's easier to read now.

This patch adds support for __builtin_expect in switch statements at
tree level (RTL part would be reviewed/commited separately).  It's an
update of https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01016.html ,
rebased and retested.

Ok for trunk?

-Y
2017-07-21  Yury Gribov  <tetra2...@gmail.com>
            Martin Liska  <mar...@gcc.gnu.org>

        PR middle-end/59521

gcc/
        * predict.c (set_even_probabilities): Handle case of a single
        likely edge.
        (combine_predictions_for_bb): Ditto.
        (tree_predict_by_opcode): Handle switch statements.
        * stmt.c (balance_case_nodes): Select pivot value based on
        probabilities.

gcc/testsuite/
        * gcc.dg/predict-15.c: New test.

diff -rupN gcc/gcc/predict.c gcc-59521/gcc/predict.c
--- gcc/gcc/predict.c   2017-07-18 22:21:16.000000000 +0200
+++ gcc-59521/gcc/predict.c     2017-07-19 08:26:57.000000000 +0200
@@ -815,9 +815,12 @@ unlikely_executed_bb_p (basic_block bb)
 
 static void
 set_even_probabilities (basic_block bb,
-                       hash_set<edge> *unlikely_edges = NULL)
+                       hash_set<edge> *unlikely_edges = NULL,
+                       hash_set<edge_prediction *> *likely_edges = NULL)
 {
   unsigned nedges = 0, unlikely_count = 0;
+  unsigned likely_count
+    = likely_edges ? likely_edges->elements () : 0;
   edge e = NULL;
   edge_iterator ei;
   profile_probability all = profile_probability::always ();
@@ -827,7 +830,7 @@ set_even_probabilities (basic_block bb,
       all -= e->probability;
     else if (!unlikely_executed_edge_p (e))
       {
-        nedges ++;
+        nedges++;
         if (unlikely_edges != NULL && unlikely_edges->contains (e))
          {
            all -= profile_probability::very_unlikely ();
@@ -844,18 +847,44 @@ set_even_probabilities (basic_block bb,
 
   unsigned c = nedges - unlikely_count;
 
-  FOR_EACH_EDGE (e, ei, bb->succs)
-    if (e->probability.initialized_p ())
-      ;
-    else if (!unlikely_executed_edge_p (e))
-      {
-       if (unlikely_edges != NULL && unlikely_edges->contains (e))
-         e->probability = profile_probability::very_unlikely ();
-       else
-         e->probability = all.apply_scale (1, c).guessed ();
-      }
-    else
-      e->probability = profile_probability::never ();
+  /* If we have one likely edge, then use its probability and
+     distribute remaining probabilities as even.  */
+  if (likely_count == 1)
+    {
+      edge_prediction *prediction = *likely_edges->begin ();
+      int p = prediction->ep_probability;
+      profile_probability likely_prob
+       = all.apply_scale (p, REG_BR_PROB_BASE).guessed ();
+      profile_probability remainder = all - likely_prob;
+
+      FOR_EACH_EDGE (e, ei, bb->succs)
+       if (e->probability.initialized_p ())
+         ;
+       else if (!unlikely_executed_edge_p (e))
+         {
+           if (prediction->ep_edge == e)
+             e->probability = likely_prob;
+           else
+             e->probability = remainder.apply_scale (1, nedges - 1);
+         }
+       else
+         e->probability = profile_probability::never ();
+    }
+  else
+    {
+      FOR_EACH_EDGE (e, ei, bb->succs)
+      if (e->probability.initialized_p ())
+       ;
+      else if (!unlikely_executed_edge_p (e))
+       {
+         if (unlikely_edges != NULL && unlikely_edges->contains (e))
+           e->probability = profile_probability::very_unlikely ();
+         else
+           e->probability = all.apply_scale (1, c).guessed ();
+       }
+      else
+       e->probability = profile_probability::never ();
+    }
 }
 
 /* Add REG_BR_PROB note to JUMP with PROB.  */
@@ -1151,6 +1180,7 @@ combine_predictions_for_bb (basic_block 
   if (nedges != 2)
     {
       hash_set<edge> unlikely_edges (4);
+      hash_set<edge_prediction *> likely_edges (4);
 
       /* Identify all edges that have a probability close to very unlikely.
         Doing the approach for very unlikely doesn't worth for doing as
@@ -1158,16 +1188,31 @@ combine_predictions_for_bb (basic_block 
       edge_prediction **preds = bb_predictions->get (bb);
       if (preds)
        for (pred = *preds; pred; pred = pred->ep_next)
-         if (pred->ep_probability <= PROB_VERY_UNLIKELY)
-           unlikely_edges.add (pred->ep_edge);
+         {
+           if (pred->ep_probability <= PROB_VERY_UNLIKELY)
+             unlikely_edges.add (pred->ep_edge);
+           if (pred->ep_probability >= PROB_VERY_LIKELY
+               || pred->ep_predictor == PRED_BUILTIN_EXPECT)
+             likely_edges.add (pred);
+         }
 
       if (!dry_run)
-       set_even_probabilities (bb, &unlikely_edges);
+       set_even_probabilities (bb, &unlikely_edges, &likely_edges);
       clear_bb_predictions (bb);
       if (dump_file)
        {
          fprintf (dump_file, "Predictions for bb %i\n", bb->index);
-         if (unlikely_edges.elements () == 0)
+         if (likely_edges.elements () == 1)
+           {
+             fprintf (dump_file,
+                      "1 edge in bb %i predicted as likely, %i as unlikely\n",
+                      bb->index, nedges - 1);
+             FOR_EACH_EDGE (e, ei, bb->succs)
+               if (!unlikely_executed_edge_p (e))
+                 dump_prediction (dump_file, PRED_COMBINED,
+                  e->probability.to_reg_br_prob_base (), bb, REASON_NONE, e);
+           }
+         else if (unlikely_edges.elements () == 0)
            fprintf (dump_file,
                     "%i edges in bb %i predicted to even probabilities\n",
                     nedges, bb->index);
@@ -2449,7 +2494,30 @@ tree_predict_by_opcode (basic_block bb)
   edge_iterator ei;
   enum br_predictor predictor;
 
-  if (!stmt || gimple_code (stmt) != GIMPLE_COND)
+  if (!stmt)
+    return;
+
+  if (gswitch *sw = dyn_cast <gswitch *> (stmt))
+    {
+      tree index = gimple_switch_index (sw);
+      tree val = expr_expected_value (index, auto_bitmap (),
+                                     &predictor);
+      if (val && TREE_CODE (val) == INTEGER_CST)
+       {
+         edge e = find_taken_edge_switch_expr (sw, bb, val);
+         if (predictor == PRED_BUILTIN_EXPECT)
+           {
+             int percent = PARAM_VALUE (BUILTIN_EXPECT_PROBABILITY);
+             gcc_assert (percent >= 0 && percent <= 100);
+             predict_edge (e, PRED_BUILTIN_EXPECT,
+                           HITRATE (percent));
+           }
+         else
+           predict_edge_def (e, predictor, TAKEN);
+       }
+    }
+
+  if (gimple_code (stmt) != GIMPLE_COND)
     return;
   FOR_EACH_EDGE (then_edge, ei, bb->succs)
     if (then_edge->flags & EDGE_TRUE_VALUE)
diff -rupN gcc/gcc/testsuite/gcc.dg/predict-15.c 
gcc-59521/gcc/testsuite/gcc.dg/predict-15.c
--- gcc/gcc/testsuite/gcc.dg/predict-15.c       1970-01-01 01:00:00.000000000 
+0100
+++ gcc-59521/gcc/testsuite/gcc.dg/predict-15.c 2017-07-19 08:33:40.000000000 
+0200
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-profile_estimate" } */
+
+extern int puts (const char *);
+
+void
+f(int ch) {
+  switch (__builtin_expect(ch, 333)) {
+    case 3: puts("a"); break;
+    case 42: puts("e"); break;
+    case 333: puts("i"); break;
+  } 
+}
+
+/* { dg-final { scan-tree-dump "1 edge in bb 2 predicted as likely, 3 as 
unlikely" "profile_estimate"} } */
+/* { dg-final { scan-tree-dump "switch (.*) .* case 333: <L\[0-9\]*> .90" 
"profile_estimate"} } */
diff -rupN gcc/gcc/tree-cfg.c gcc-59521/gcc/tree-cfg.c
--- gcc/gcc/tree-cfg.c  2017-07-18 22:21:20.000000000 +0200
+++ gcc-59521/gcc/tree-cfg.c    2017-07-19 06:36:39.000000000 +0200
@@ -170,8 +170,6 @@ static bool gimple_can_merge_blocks_p (b
 static void remove_bb (basic_block);
 static edge find_taken_edge_computed_goto (basic_block, tree);
 static edge find_taken_edge_cond_expr (basic_block, tree);
-static edge find_taken_edge_switch_expr (gswitch *, basic_block, tree);
-static tree find_case_label_for_value (gswitch *, tree);
 static void lower_phi_internal_fn ();
 
 void
@@ -2311,73 +2309,6 @@ find_taken_edge_cond_expr (basic_block b
   return (integer_zerop (val) ? false_edge : true_edge);
 }
 
-/* Given an INTEGER_CST VAL and the entry block BB to a SWITCH_EXPR
-   statement, determine which edge will be taken out of the block.  Return
-   NULL if any edge may be taken.  */
-
-static edge
-find_taken_edge_switch_expr (gswitch *switch_stmt, basic_block bb,
-                            tree val)
-{
-  basic_block dest_bb;
-  edge e;
-  tree taken_case;
-
-  if (gimple_switch_num_labels (switch_stmt) == 1)
-    taken_case = gimple_switch_default_label (switch_stmt);
-  else if (! val || TREE_CODE (val) != INTEGER_CST)
-    return NULL;
-  else
-    taken_case = find_case_label_for_value (switch_stmt, val);
-  dest_bb = label_to_block (CASE_LABEL (taken_case));
-
-  e = find_edge (bb, dest_bb);
-  gcc_assert (e);
-  return e;
-}
-
-
-/* Return the CASE_LABEL_EXPR that SWITCH_STMT will take for VAL.
-   We can make optimal use here of the fact that the case labels are
-   sorted: We can do a binary search for a case matching VAL.  */
-
-static tree
-find_case_label_for_value (gswitch *switch_stmt, tree val)
-{
-  size_t low, high, n = gimple_switch_num_labels (switch_stmt);
-  tree default_case = gimple_switch_default_label (switch_stmt);
-
-  for (low = 0, high = n; high - low > 1; )
-    {
-      size_t i = (high + low) / 2;
-      tree t = gimple_switch_label (switch_stmt, i);
-      int cmp;
-
-      /* Cache the result of comparing CASE_LOW and val.  */
-      cmp = tree_int_cst_compare (CASE_LOW (t), val);
-
-      if (cmp > 0)
-       high = i;
-      else
-       low = i;
-
-      if (CASE_HIGH (t) == NULL)
-       {
-         /* A singe-valued case label.  */
-         if (cmp == 0)
-           return t;
-       }
-      else
-       {
-         /* A case range.  We can only handle integer ranges.  */
-         if (cmp <= 0 && tree_int_cst_compare (CASE_HIGH (t), val) >= 0)
-           return t;
-       }
-    }
-
-  return default_case;
-}
-
 
 /* Dump a basic block on stderr.  */
 
@@ -9352,6 +9283,72 @@ gt_pch_nx (edge_def *e, gt_pointer_opera
   op (&(block), cookie);
 }
 
+/* Given an INTEGER_CST VAL and the entry block BB to a SWITCH_EXPR
+   statement, determine which edge will be taken out of the block.  Return
+   NULL if any edge may be taken.  */
+
+edge
+find_taken_edge_switch_expr (gswitch *switch_stmt, basic_block bb,
+                            tree val)
+{
+  basic_block dest_bb;
+  edge e;
+  tree taken_case;
+
+  if (gimple_switch_num_labels (switch_stmt) == 1)
+    taken_case = gimple_switch_default_label (switch_stmt);
+  else if (! val || TREE_CODE (val) != INTEGER_CST)
+    return NULL;
+  else
+    taken_case = find_case_label_for_value (switch_stmt, val);
+  dest_bb = label_to_block (CASE_LABEL (taken_case));
+
+  e = find_edge (bb, dest_bb);
+  gcc_assert (e);
+  return e;
+}
+
+/* Return the CASE_LABEL_EXPR that SWITCH_STMT will take for VAL.
+   We can make optimal use here of the fact that the case labels are
+   sorted: We can do a binary search for a case matching VAL.  */
+
+tree
+find_case_label_for_value (gswitch *switch_stmt, tree val)
+{
+  size_t low, high, n = gimple_switch_num_labels (switch_stmt);
+  tree default_case = gimple_switch_default_label (switch_stmt);
+
+  for (low = 0, high = n; high - low > 1; )
+    {
+      size_t i = (high + low) / 2;
+      tree t = gimple_switch_label (switch_stmt, i);
+      int cmp;
+
+      /* Cache the result of comparing CASE_LOW and val.  */
+      cmp = tree_int_cst_compare (CASE_LOW (t), val);
+
+      if (cmp > 0)
+       high = i;
+      else
+       low = i;
+
+      if (CASE_HIGH (t) == NULL)
+       {
+         /* A singe-valued case label.  */
+         if (cmp == 0)
+           return t;
+       }
+      else
+       {
+         /* A case range.  We can only handle integer ranges.  */
+         if (cmp <= 0 && tree_int_cst_compare (CASE_HIGH (t), val) >= 0)
+           return t;
+       }
+    }
+
+  return default_case;
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff -rupN gcc/gcc/tree-cfg.h gcc-59521/gcc/tree-cfg.h
--- gcc/gcc/tree-cfg.h  2017-07-18 22:21:20.000000000 +0200
+++ gcc-59521/gcc/tree-cfg.h    2017-07-19 06:36:39.000000000 +0200
@@ -109,6 +109,9 @@ extern basic_block insert_cond_bb (basic
 extern bool gimple_find_sub_bbs (gimple_seq, gimple_stmt_iterator *);
 extern bool extract_true_false_controlled_edges (basic_block, basic_block,
                                                 edge *, edge *);
+extern tree find_case_label_for_value (gswitch *switch_stmt, tree val);
+extern edge find_taken_edge_switch_expr (gswitch *switch_stmt,
+                                        basic_block bb, tree val);
 
 /* Return true if the LHS of a call should be removed.  */
 

Reply via email to