On 08/31/2018 03:10 PM, Jan Hubicka wrote:
>>
>> 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)

Hello.

Thanks for review.

> 
> Please add/update documentation of set_even_probabilities.

Done.

>>  {
>>    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.

According to my Cauldron 2017 slides, it looks that majority of switch 
statements have
# of cases equal to # of BB. That means a shared edge in between multiple cases
is not so common :) But yes, it's not ideal.

>> 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?

Eventually not needed.

> 
>>  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, fixed as:

              if (prob.initialized_p ()
                  && (prob < pivot_prob || ! (*npp)->m_right))
                break;

Hope it's fine. I'm going to install the patch.

Martin

> 
> OK with those changes.
> Honza
> 

Reply via email to