]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Cleanup profile updating code in unrolling and splitting
authorHonza <jh@ryzen4.suse.cz>
Fri, 28 Jul 2023 17:39:19 +0000 (19:39 +0200)
committerHonza <jh@ryzen4.suse.cz>
Fri, 28 Jul 2023 17:39:19 +0000 (19:39 +0200)
I have noticed that for all these three cases I need same update of
loop exit probability.  While my earlier patch unified it for unrollers,
this patch makes it more general and also simplifies
tree-ssa-loop-split.cc.

I also refactored the code, since with all the special cases for
corrupted profile it gets relatively long.
I now also handle multiple loop exits in RTL unroller.

Bootstrapped/regtested x86_64-linux, comitted.
gcc/ChangeLog:

* cfgloopmanip.cc (loop_count_in): Break out from ...
(loop_exit_for_scaling): Break out from ...
(update_loop_exit_probability_scale_dom_bbs): Break out from ...;
add more sanity check and debug info.
(scale_loop_profile): ... here.
(create_empty_loop_on_edge): Fix whitespac.
* cfgloopmanip.h (update_loop_exit_probability_scale_dom_bbs): Declare.
* loop-unroll.cc (unroll_loop_constant_iterations): Use
update_loop_exit_probability_scale_dom_bbs.
* tree-ssa-loop-manip.cc (update_exit_probability_after_unrolling): Remove.
(tree_transform_and_unroll_loop): Use
update_loop_exit_probability_scale_dom_bbs.
* tree-ssa-loop-split.cc (split_loop): Use
update_loop_exit_probability_scale_dom_bbs.

gcc/cfgloopmanip.cc
gcc/cfgloopmanip.h
gcc/loop-unroll.cc
gcc/tree-ssa-loop-manip.cc
gcc/tree-ssa-loop-split.cc

index c3d292d0dd4c5b607f5dd1ad0f968841b1cb111e..fe452ecf60f09c1e7b9e0f5293e65333bb911859 100644 (file)
@@ -525,6 +525,184 @@ scale_dominated_blocks_in_loop (class loop *loop, basic_block bb,
       }
 }
 
+/* Compute how many times loop is entered.  */
+
+profile_count
+loop_count_in (class loop *loop)
+{
+  /* Compute number of invocations of the loop.  */
+  profile_count count_in = profile_count::zero ();
+  edge e;
+  edge_iterator ei;
+  bool found_latch = false;
+
+  if (loops_state_satisfies_p (LOOPS_MAY_HAVE_MULTIPLE_LATCHES))
+    FOR_EACH_EDGE (e, ei, loop->header->preds)
+      if (!flow_bb_inside_loop_p (loop, e->src))
+       count_in += e->count ();
+      else
+       found_latch = true;
+  else
+    FOR_EACH_EDGE (e, ei, loop->header->preds)
+      if (e->src != loop->latch)
+       count_in += e->count ();
+      else
+       found_latch = true;
+  gcc_checking_assert (found_latch);
+  return count_in;
+}
+
+/* Return exit that suitable for update when loop iterations
+   changed.  */
+
+static edge
+loop_exit_for_scaling (class loop *loop)
+{
+  edge exit_edge = single_exit (loop);
+  if (!exit_edge)
+    {
+      auto_vec<edge> exits = get_loop_exit_edges  (loop);
+      exit_edge = single_likely_exit (loop, exits);
+    }
+  return exit_edge;
+}
+
+/* Assume that loop's entry count and profile up to a given EXIT_EDGE is
+   consistent. Update exit probability so loop exists with PROFILE_COUNT
+   and rescale profile of basic blocks inside loop dominated by EXIT_EDGE->src.
+
+   This is useful after number of iteraitons of loop has changed.
+   If EXIT_EDGE is NULL, the function will try to identify suitable exit.
+   If DESIRED_COUNT is NULL, loop entry count will be used.
+   In consistent profile usually loop exists as many times as it is entred.
+
+   Return updated exit if successfull and NULL otherwise. */
+
+edge
+update_loop_exit_probability_scale_dom_bbs (class loop *loop,
+                                           edge exit_edge,
+                                           profile_count desired_count)
+{
+  if (!exit_edge)
+    exit_edge = loop_exit_for_scaling (loop);
+  if (!exit_edge)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, ";; Not updating exit probability of loop %i;"
+                  " it has no single exit\n",
+                  loop->num);
+       }
+      return NULL;
+    }
+  /* If exit is inside another loop, adjusting its probability will also
+     adjust number of the inner loop iterations.  Just do noting for now.  */
+  if (!just_once_each_iteration_p (loop, exit_edge->src))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, ";; Not updating exit probability of loop %i;"
+                  " exit is inside inner loop\n",
+                  loop->num);
+       }
+      return NULL;
+    }
+  /* Normal loops exit as many times as they are entered.  */
+  if (!desired_count.initialized_p ())
+    desired_count = loop_count_in (loop);
+  /* See if everything is already perfect.  */
+  if (desired_count == exit_edge->count ())
+    return exit_edge;
+  profile_count old_exit_count = exit_edge->count ();
+  /* See if update is possible.
+     Avoid turning probability to 0 or 1 just trying to reach impossible
+     value.
+
+     Natural profile update after some loop will happily scale header count to
+     be less than count of entering the loop.  This is bad idea and they should
+     special case maybe_flat_loop_profile.
+
+     It may also happen that the source basic block of the exit edge is
+     inside in-loop condition:
+
+         +-> header
+         |    |
+         |   B1
+         |  /  \
+         | |   B2--exit_edge-->
+         |  \  /
+         |   B3
+         +__/
+
+      If B2 count is smaller than desired exit edge count
+      the profile was inconsistent with the newly discovered upper bound.
+      Probablity of edge B1->B2 is too low.  We do not attempt to fix
+      that (as it is hard in general).  */
+  if (desired_count > exit_edge->src->count
+      && exit_edge->src->count.differs_from_p (desired_count))
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, ";; Source bb of loop %i has count ",
+                  loop->num);
+         exit_edge->src->count.dump (dump_file, cfun);
+         fprintf (dump_file,
+                  " which is smaller then desired count of exitting loop ");
+         desired_count.dump (dump_file, cfun);
+         fprintf (dump_file, ". Profile update is impossible.\n");
+       }
+      /* Drop quality of probability since we know it likely
+        bad.  */
+      exit_edge->probability = exit_edge->probability.guessed ();
+      return NULL;
+    }
+  if (!exit_edge->src->count.nonzero_p ())
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, ";; Not updating exit edge probability"
+                  " in loop %i since profile is zero ",
+                  loop->num);
+       }
+      return NULL;
+    }
+  set_edge_probability_and_rescale_others
+    (exit_edge, desired_count.probability_in (exit_edge->src->count));
+  /* Rescale the remaining edge probabilities and see if there is only
+     one.  */
+  edge other_edge = NULL;
+  bool found = false;
+  edge e;
+  edge_iterator ei;
+  FOR_EACH_EDGE (e, ei, exit_edge->src->succs)
+    if (!(e->flags & EDGE_FAKE)
+       && !loop_exit_edge_p (loop, e))
+      {
+       if (found)
+         {
+           other_edge = NULL;
+           break;
+         }
+       other_edge = e;
+       found = true;
+      }
+  /* If there is only loop latch after other edge,
+     update its profile.  This is tiny bit more precise
+     than scaling.  */
+  if (other_edge && other_edge->dest == loop->latch)
+    {
+      if (single_pred_p (loop->latch))
+       loop->latch->count = loop->latch->count
+                            + old_exit_count - exit_edge->count ();
+    }
+  else
+    /* If there are multple blocks, just scale.  */
+    scale_dominated_blocks_in_loop (loop, exit_edge->src,
+                                   exit_edge->src->count - exit_edge->count (),
+                                   exit_edge->src->count - old_exit_count);
+  return exit_edge;
+}
+
 /* Scale profile in LOOP by P.
    If ITERATION_BOUND is not -1, scale even further if loop is predicted
    to iterate too many times.
@@ -571,17 +749,7 @@ scale_loop_profile (class loop *loop, profile_probability p,
   if (iterations <= iteration_bound)
     return;
 
-  /* Compute number of invocations of the loop.  */
-  profile_count count_in = profile_count::zero ();
-  edge e;
-  edge_iterator ei;
-  bool found_latch = false;
-  FOR_EACH_EDGE (e, ei, loop->header->preds)
-    if (e->src != loop->latch)
-      count_in += e->count ();
-    else
-      found_latch = true;
-  gcc_checking_assert (found_latch);
+  profile_count count_in = loop_count_in (loop);
 
   /* Now scale the loop body so header count is
      count_in * (iteration_bound + 1)  */
@@ -596,8 +764,7 @@ scale_loop_profile (class loop *loop, profile_probability p,
               (int)iteration_bound);
     }
   /* Finally attempt to fix exit edge probability.  */
-  auto_vec<edge> exits = get_loop_exit_edges  (loop);
-  edge exit_edge = single_likely_exit (loop, exits);
+  edge exit_edge = loop_exit_for_scaling (loop);
 
   /* In a consistent profile unadjusted_exit_count should be same as count_in,
      however to preserve as much of the original info, avoid recomputing
@@ -606,85 +773,8 @@ scale_loop_profile (class loop *loop, profile_probability p,
   if (exit_edge)
     unadjusted_exit_count = exit_edge->count ();
   scale_loop_frequencies (loop, scale_prob);
-
-  if (exit_edge && exit_edge->src->loop_father != loop)
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       fprintf (dump_file,
-                ";; Loop exit is in inner loop;"
-                " will leave exit probabilities inconsistent\n");
-    }
-  else if (exit_edge)
-    {
-      profile_count old_exit_count = exit_edge->count ();
-      profile_probability new_probability;
-      if (iteration_bound > 0)
-       {
-         /* It may happen that the source basic block of the exit edge is
-            inside in-loop condition:
-
-               +-> header
-               |    |
-               |   B1
-               |  /  \
-               | |   B2--exit_edge-->
-               |  \  /
-               |   B3
-               +__/
-
-             If B2 count is smaller than desired exit edge count
-             the profile was inconsistent with the newly discovered upper bound.
-             Probablity of edge B1->B2 is too low.  We do not attempt to fix
-             that (as it is hard in general) but we want to avoid dropping
-             count of edge B2->B3 to zero may confuse later optimizations.  */
-         if (unadjusted_exit_count.apply_scale (7, 8) > exit_edge->src->count)
-           {
-             if (dump_file && (dump_flags & TDF_DETAILS))
-               fprintf (dump_file,
-                        ";; Source basic block of loop exit count is too small;"
-                        " will leave exit probabilities inconsistent\n");
-             exit_edge->probability = exit_edge->probability.guessed ();
-             return;
-           }
-         new_probability
-           = unadjusted_exit_count.probability_in (exit_edge->src->count);
-       }
-      else
-       new_probability = profile_probability::always ();
-      set_edge_probability_and_rescale_others (exit_edge, new_probability);
-      profile_count new_exit_count = exit_edge->count ();
-
-      /* Rescale the remaining edge probabilities and see if there is only
-        one.  */
-      edge other_edge = NULL;
-      bool found = false;
-      FOR_EACH_EDGE (e, ei, exit_edge->src->succs)
-       if (!(e->flags & EDGE_FAKE)
-           && !loop_exit_edge_p (loop, e))
-         {
-           if (found)
-             {
-               other_edge = NULL;
-               break;
-             }
-           other_edge = e;
-           found = true;
-         }
-      /* If there is only loop latch after other edge,
-        update its profile.  */
-      if (other_edge && other_edge->dest == loop->latch)
-       loop->latch->count -= new_exit_count - old_exit_count;
-      else
-       scale_dominated_blocks_in_loop (loop, exit_edge->src,
-                                       exit_edge->src->count - new_exit_count,
-                                       exit_edge->src->count - old_exit_count);
-    }
-  else if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      fprintf (dump_file,
-              ";; Loop has mulitple exits;"
-              " will leave exit probabilities inconsistent\n");
-    }
+  update_loop_exit_probability_scale_dom_bbs (loop, exit_edge,
+                                             unadjusted_exit_count);
 }
 
 /* Recompute dominance information for basic blocks outside LOOP.  */
@@ -924,7 +1014,7 @@ create_empty_loop_on_edge (edge entry_edge,
    have no successor, which caller is expected to fix somehow.
 
    If this may cause the information about irreducible regions to become
-   invalid, IRRED_INVALIDATED is set to true.  
+   invalid, IRRED_INVALIDATED is set to true.
 
    LOOP_CLOSED_SSA_INVALIDATED, if non-NULL, is a bitmap where we store
    basic blocks that had non-trivial update on their loop_father.*/
index dab7b31c1e77e91e7f1138101c759900768870d5..2dda504082317d6403d5f9444024141349990f33 100644 (file)
@@ -68,6 +68,9 @@ class loop * loop_version (class loop *, void *,
 void adjust_loop_info_after_peeling (class loop *loop, int npeel, bool precise);
 void scale_dominated_blocks_in_loop (class loop *loop, basic_block bb,
                                     profile_count num, profile_count den);
+edge update_loop_exit_probability_scale_dom_bbs
+               (class loop *loop, edge exit_edge = NULL,
+                profile_count desired_count = profile_count::uninitialized ());
 void update_exit_probability_after_unrolling (class loop *loop, edge new_exit);
 
 #endif /* GCC_CFGLOOPMANIP_H */
index bbfa6ccc77004d37b0eb9624dc33f0024c5306a2..1df8e6cd4efc762bbd701122ad41dd8a14730d9d 100644 (file)
@@ -488,6 +488,7 @@ unroll_loop_constant_iterations (class loop *loop)
   struct opt_info *opt_info = NULL;
   bool ok;
   bool flat = maybe_flat_loop_profile (loop);
+  profile_count orig_exit_count = desc->out_edge->count ();
 
   niter = desc->niter;
 
@@ -608,9 +609,10 @@ unroll_loop_constant_iterations (class loop *loop)
     | (flat ? DLTHE_FLAG_FLAT_PROFILE : 0));
   gcc_assert (ok);
 
-  edge new_exit = single_dom_exit (loop);
-  if (new_exit)
-    update_exit_probability_after_unrolling (loop, new_exit);
+  edge exit = update_loop_exit_probability_scale_dom_bbs
+         (loop, desc->out_edge, orig_exit_count);
+  if (exit)
+    update_br_prob_note (exit->src);
 
   if (opt_info)
     {
index e58892e235ca7aea2cf6bdfb624abe4b36ec8801..e7436915e01297e7af2a3bcf1afd01e014de6f32 100644 (file)
@@ -1040,29 +1040,6 @@ determine_exit_conditions (class loop *loop, class tree_niter_desc *desc,
   *exit_bound = bound;
 }
 
-/* Updat NEW_EXIT probability after loop has been unrolled.  */
-
-void
-update_exit_probability_after_unrolling (class loop *loop, edge new_exit)
-{
-  /* gimple_duplicate_loop_body_to_header_edge depending on
-     DLTHE_FLAG_UPDATE_FREQ either keeps original frequency of the loop header
-     or scales it down accordingly.
-     However exit edge probability is kept as original.  Fix it if needed
-     and compensate.  */
-  profile_probability new_prob
-         = loop_preheader_edge
-                 (loop)->count ().probability_in (new_exit->src->count);
-  if (!(new_prob == new_exit->probability))
-    {
-      profile_count old_count = new_exit->src->count - new_exit->count ();
-      set_edge_probability_and_rescale_others (new_exit, new_prob);
-      profile_count new_count = new_exit->src->count - new_exit->count ();
-      scale_dominated_blocks_in_loop (loop, new_exit->src,
-                                     new_count, old_count);
-    }
-}
-
 /* Unroll LOOP FACTOR times.  LOOP is known to have a single exit edge
    whose source block dominates the latch.  DESC describes the number of
    iterations of LOOP.
@@ -1289,7 +1266,12 @@ tree_transform_and_unroll_loop (class loop *loop, unsigned factor,
   update_ssa (TODO_update_ssa);
 
   new_exit = single_dom_exit (loop);
-  update_exit_probability_after_unrolling (loop, new_exit);
+  /* gimple_duplicate_loop_body_to_header_edge depending on
+     DLTHE_FLAG_UPDATE_FREQ either keeps original frequency of the loop header
+     or scales it down accordingly.
+     However exit edge probability is kept as original.  Fix it if needed
+     and compensate.  */
+  update_loop_exit_probability_scale_dom_bbs (loop, new_exit);
   if (!single_loop_p)
     {
       /* Finally create the new counter for number of iterations and add
index 641346cba70260337d7c062677aa2d9a9ab4edba..923e49e716d9e7b96fe703f3bbbad8330cec6abc 100644 (file)
@@ -610,7 +610,6 @@ split_loop (class loop *loop1)
   for (i = 0; i < loop1->num_nodes; i++)
     if ((guard_iv = split_at_bb_p (loop1, bbs[i], &border, &iv, &guard_code)))
       {
-       profile_count entry_count = loop_preheader_edge (loop1)->count ();
        /* Handling opposite steps is not implemented yet.  Neither
           is handling different step sizes.  */
        if ((tree_int_cst_sign_bit (iv.step)
@@ -692,48 +691,8 @@ split_loop (class loop *loop1)
                        = loop1_prob.invert ();
 
        fix_loop_bb_probability (loop1, loop2, true_edge, false_edge);
-
-       /* Fix loop's exit probability after scaling.  */
-
-       if (entry_count.initialized_p () && entry_count.nonzero_p ())
-         {
-           edge exit_to_latch1;
-           if (EDGE_SUCC (exit1->src, 0) == exit1)
-             exit_to_latch1 = EDGE_SUCC (exit1->src, 1);
-           else
-             exit_to_latch1 = EDGE_SUCC (exit1->src, 0);
-           if (exit1->src->count.nonzero_p ())
-             {
-               /* First loop is entered loop1_prob * entry_count times
-                  and it needs to exit the same number of times.  */
-               exit1->probability
-                       = entry_count.apply_probability
-                               (loop1_prob).probability_in (exit1->src->count);
-               exit_to_latch1->probability = exit1->probability.invert ();
-               scale_dominated_blocks_in_loop (loop1, exit1->src,
-                                               exit_to_latch1->count (),
-                                               exit_to_latch1->dest->count);
-             }
-
-           edge exit_to_latch2, exit2 = single_exit (loop2);
-           if (EDGE_SUCC (exit2->src, 0) == exit2)
-             exit_to_latch2 = EDGE_SUCC (exit2->src, 1);
-           else
-             exit_to_latch2 = EDGE_SUCC (exit2->src, 0);
-           if (exit2->src->count.nonzero_p ())
-             {
-               /* Second loop is entered very_likely * entry_count times
-                  and it needs to exit the same number of times.  */
-               exit2->probability
-                       = entry_count.apply_probability
-                               (profile_probability::very_likely ())
-                               .probability_in (exit2->src->count);
-               exit_to_latch2->probability = exit2->probability.invert ();
-               scale_dominated_blocks_in_loop (loop2, exit2->src,
-                                               exit_to_latch2->count (),
-                                               exit_to_latch2->dest->count);
-             }
-         }
+       update_loop_exit_probability_scale_dom_bbs (loop1);
+       update_loop_exit_probability_scale_dom_bbs (loop2);
 
        edge new_e = connect_loops (loop1, loop2);
        connect_loop_phis (loop1, loop2, new_e);