> 
> 2018-08-23  Martin Liska  <mli...@suse.cz>
> 
>   PR middle-end/59521
>       * predict.c (set_even_probabilities): Add likely_edges
>         argument and handle cases where we have precisely one
>         likely edge.
>       (combine_predictions_for_bb): Catch also likely_edges.
>       (tree_predict_by_opcode): Handle gswitch statements.
>       * tree-cfg.c (find_taken_edge_switch_expr): Remove
>         const quantifier.
>       (find_case_label_for_value): Likewise.
>       * tree-cfg.h (find_case_label_for_value): New declaration.
>       (find_taken_edge_switch_expr): Likewise.
>       * tree-switch-conversion.c (switch_decision_tree::balance_case_nodes):
>         Find pivot in decision tree based on probabily, not by number of
>         nodes.
> 
> gcc/testsuite/ChangeLog:
> 
> 2018-08-23  Martin Liska  <mli...@suse.cz>
> 
>   PR middle-end/59521
>       * c-c++-common/pr59521-1.c: New test.
>       * c-c++-common/pr59521-2.c: New test.
>       * gcc.dg/tree-prof/pr59521-3.c: New test.
> ---
>  gcc/predict.c                              | 96 +++++++++++++++++-----
>  gcc/testsuite/c-c++-common/pr59521-1.c     | 15 ++++
>  gcc/testsuite/c-c++-common/pr59521-2.c     | 15 ++++
>  gcc/testsuite/gcc.dg/tree-prof/pr59521-3.c | 34 ++++++++
>  gcc/tree-cfg.c                             | 10 +--
>  gcc/tree-cfg.h                             |  2 +
>  gcc/tree-switch-conversion.c               | 45 +++++-----
>  7 files changed, 168 insertions(+), 49 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/pr59521-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/pr59521-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/pr59521-3.c
> 
> diff --git a/gcc/predict.c b/gcc/predict.c
> index 8c8e79153fc..db1fe737cb4 100644
> --- a/gcc/predict.c
> +++ b/gcc/predict.c
> @@ -831,7 +831,8 @@ 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)

Please add/update documentation of set_even_probabilities.
>  {
>    unsigned nedges = 0, unlikely_count = 0;
>    edge e = NULL;
> @@ -843,7 +844,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 ();
> @@ -852,26 +853,54 @@ set_even_probabilities (basic_block bb,
>        }
>  
>    /* Make the distribution even if all edges are unlikely.  */
> +  unsigned likely_count = likely_edges ? likely_edges->elements () : 0;
>    if (unlikely_count == nedges)
>      {
>        unlikely_edges = NULL;
>        unlikely_count = 0;
>      }
>  
> -  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 ();
> +  /* If we have one likely edge, then use its probability and distribute
> +     remaining probabilities as even.  */
> +  if (likely_count == 1)
> +    {
> +      FOR_EACH_EDGE (e, ei, bb->succs)
> +     if (e->probability.initialized_p ())
> +       ;
> +     else if (!unlikely_executed_edge_p (e))
> +       {
> +         edge_prediction *prediction = *likely_edges->begin ();
> +         int p = prediction->ep_probability;
> +         profile_probability prob
> +           = profile_probability::from_reg_br_prob_base (p);
> +         profile_probability remainder = prob.invert ();
> +
> +         if (prediction->ep_edge == e)
> +           e->probability = prob;
> +         else
> +           e->probability = remainder.apply_scale (1, nedges - 1);
> +       }
>       else
> -       e->probability = all.apply_scale (1, c).guessed ();
> -      }
> -    else
> -      e->probability = profile_probability::never ();
> +       e->probability = profile_probability::never ();
> +    }
> +  else
> +    {
> +      /* Make all unlikely edges unlikely and the rest will have even
> +      probability.  */
> +      unsigned scale = 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, scale);
> +       }
> +     else
> +       e->probability = profile_probability::never ();
> +    }
>  }
>  
>  /* Add REG_BR_PROB note to JUMP with PROB.  */
> @@ -1175,6 +1204,7 @@ combine_predictions_for_bb (basic_block bb, bool 
> dry_run)
>    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
> @@ -1182,11 +1212,16 @@ combine_predictions_for_bb (basic_block bb, bool 
> dry_run)
>        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);

Of course we are losing info here if there are multiple cases with same target 
basic block...
It may be possible to keep the information by adding histogram to the stmt.
> diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
> index b021fb0f97b..738e0e7fe7f 100644
> --- a/gcc/tree-cfg.c
> +++ b/gcc/tree-cfg.c
> @@ -171,8 +171,6 @@ static bool gimple_can_merge_blocks_p (basic_block, 
> basic_block);
>  static void remove_bb (basic_block);
>  static edge find_taken_edge_computed_goto (basic_block, tree);
>  static edge find_taken_edge_cond_expr (const gcond *, tree);
> -static edge find_taken_edge_switch_expr (const gswitch *, tree);
> -static tree find_case_label_for_value (const gswitch *, tree);

Why do you remove const?

>  static void lower_phi_internal_fn ();
>  
>  void
> @@ -2436,8 +2434,8 @@ find_taken_edge_cond_expr (const gcond *cond_stmt, tree 
> val)
>     If VAL is NULL_TREE, then the current value of SWITCH_STMT's index
>     is used.  */
>  
> -static edge
> -find_taken_edge_switch_expr (const gswitch *switch_stmt, tree val)
> +edge
> +find_taken_edge_switch_expr (gswitch *switch_stmt, tree val)
>  {
>    basic_block dest_bb;
>    edge e;
> @@ -2466,8 +2464,8 @@ find_taken_edge_switch_expr (const gswitch 
> *switch_stmt, tree 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 (const gswitch *switch_stmt, tree 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);
> diff --git a/gcc/tree-cfg.h b/gcc/tree-cfg.h
> index 349a9543168..10ec50e95fd 100644
> --- a/gcc/tree-cfg.h
> +++ b/gcc/tree-cfg.h
> @@ -102,6 +102,8 @@ extern tree gimplify_build2 (gimple_stmt_iterator *, enum 
> tree_code,
>  extern tree gimplify_build1 (gimple_stmt_iterator *, enum tree_code,
>                            tree, tree);
>  extern void extract_true_false_edges_from_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, tree val);
>  extern unsigned int execute_fixup_cfg (void);
>  extern unsigned int split_critical_edges (void);
>  extern basic_block insert_cond_bb (basic_block, gimple *, gimple *,
> diff --git a/gcc/tree-switch-conversion.c b/gcc/tree-switch-conversion.c
> index a31ff94b895..5e4876474f9 100644
> --- a/gcc/tree-switch-conversion.c
> +++ b/gcc/tree-switch-conversion.c
> @@ -1921,6 +1921,7 @@ switch_decision_tree::balance_case_nodes 
> (case_tree_node **head,
>        int ranges = 0;
>        case_tree_node **npp;
>        case_tree_node *left;
> +      profile_probability prob = profile_probability::never ();
>  
>        /* Count the number of entries on branch.  Also count the ranges.  */
>  
> @@ -1930,6 +1931,7 @@ switch_decision_tree::balance_case_nodes 
> (case_tree_node **head,
>           ranges++;
>  
>         i++;
> +       prob += np->m_c->m_prob;
>         np = np->m_right;
>       }
>  
> @@ -1938,39 +1940,34 @@ switch_decision_tree::balance_case_nodes 
> (case_tree_node **head,
>         /* Split this list if it is long enough for that to help.  */
>         npp = head;
>         left = *npp;
> +       profile_probability pivot_prob = prob.apply_scale (1, 2);
>  
> -       /* If there are just three nodes, split at the middle one.  */
> -       if (i == 3)
> -         npp = &(*npp)->m_right;
> -       else
> +       /* Find the place in the list that bisects the list's total cost,
> +          where ranges count as 2.  */
> +       while (1)
>           {
> -           /* Find the place in the list that bisects the list's total cost,
> -              where ranges count as 2.
> -              Here I gets half the total cost.  */
> -           i = (i + ranges + 1) / 2;
> -           while (1)
> -             {
> -               /* Skip nodes while their cost does not reach that amount.  */
> -               if (!tree_int_cst_equal ((*npp)->m_c->get_low (),
> -                                        (*npp)->m_c->get_high ()))
> -                 i--;
> -               i--;
> -               if (i <= 0)
> -                 break;
> -               npp = &(*npp)->m_right;
> -             }
> +           /* Skip nodes while their probability does not reach
> +              that amount.  */
> +           prob -= (*npp)->m_c->m_prob;
> +           if (prob <= pivot_prob || ! (*npp)->m_right)
> +             break;
> +           npp = &(*npp)->m_right;

You probably want to verify that probability is initialized and pivot_prob is 
not
same as prob. Otherwise the decisison will also degenerate when all probailities
are zero or such.

OK with those changes.
Honza

Reply via email to