Makefile.in (tracer.o): New.
authorJan Hubicka <jh@suse.cz>
Sat, 1 Jun 2002 21:31:42 +0000 (23:31 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sat, 1 Jun 2002 21:31:42 +0000 (21:31 +0000)
* Makefile.in (tracer.o): New.
* params.def (TRACER_*): New options.
* rtl.h (tracer): Declare.
* timevar.def (TV_TRACER): New.
* toplev.c (dump_file_index): Add DFI_tracer.
(dump_file_info): Add tracer.
(flag_tracer): New.
(lang_indepdenent_options): Add tracer.
(rest_of_compilation): Call tracer.
* tracer.c: New file.
* invoke.texi (-ftracer): Document.
(--param tracer-*): Document.

From-SVN: r54154

gcc/ChangeLog
gcc/Makefile.in
gcc/doc/invoke.texi
gcc/params.def
gcc/rtl.h
gcc/timevar.def
gcc/toplev.c
gcc/tracer.c [new file with mode: 0644]

index e0f3d486384b986099971193481d230b2c60b0ed..2e5018942e995c4eeea755fc01ed5a9286468967 100644 (file)
@@ -1,3 +1,18 @@
+Sat Jun  1 23:29:51 CEST 2002  Jan Hubicka  <jh@suse.cz>
+
+       * Makefile.in (tracer.o): New.
+       * params.def (TRACER_*): New options.
+       * rtl.h (tracer): Declare.
+       * timevar.def (TV_TRACER): New.
+       * toplev.c (dump_file_index): Add DFI_tracer.
+       (dump_file_info): Add tracer.
+       (flag_tracer): New.
+       (lang_indepdenent_options): Add tracer.
+       (rest_of_compilation): Call tracer.
+       * tracer.c: New file.
+       * invoke.texi (-ftracer): Document.
+       (--param tracer-*): Document.
+
 2002-06-01  Daniel Berlin  <dberlin@dberlin.org>
 
        * tree-inline.c (expand_call_inline): Make the statement
index f052d98997ec8a181247ae8691159584b51af27c..69431fcebf0ff163264b013ef07edaff79b1281e 100644 (file)
@@ -732,7 +732,7 @@ OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o       \
  reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o     \
  sbitmap.o sched-deps.o sched-ebb.o sched-rgn.o sched-vis.o sdbout.o   \
  sibcall.o simplify-rtx.o ssa.o ssa-ccp.o ssa-dce.o stmt.o     \
- stor-layout.o stringpool.o timevar.o toplev.o tree.o tree-dump.o      \
+ stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \
  tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \
  $(GGC) $(out_object_file) $(EXTRA_OBJS)
 
@@ -1596,6 +1596,9 @@ predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
 lists.o: lists.c $(CONFIG_H) $(SYSTEM_H) toplev.h $(RTL_H) $(GGC_H)
 bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
    flags.h $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h $(TARGET_H)
+tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
+   $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \
+   $(PARAMS_H) profile.h
 cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
    insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h function.h \
    cfglayout.h
index 1ab9cb39f9850f41858222bc38a22deca9ef0095..0b58d11fa8f413a780ccb642ce6565ba9d41c6d3 100644 (file)
@@ -282,7 +282,7 @@ in the following sections.
 -frerun-cse-after-loop  -frerun-loop-opt @gol
 -fschedule-insns  -fschedule-insns2 @gol
 -fsingle-precision-constant  -fssa -fssa-ccp -fssa-dce @gol
--fstrength-reduce  -fstrict-aliasing  -fthread-jumps  -ftrapv @gol
+-fstrength-reduce  -fstrict-aliasing  -ftracer -fthread-jumps  -ftrapv @gol
 -funroll-all-loops  -funroll-loops  @gol
 --param @var{name}=@var{value}
 -O  -O0  -O1  -O2  -O3  -Os}
@@ -3637,6 +3637,12 @@ those which have no call-preserved registers to use instead.
 For all machines, optimization level 2 and higher enables this flag by
 default.
 
+@item -ftracer
+@opindex ftracer
+Perform tail duplication to enlarge superblock size. This transformation
+simplifies the control flow of the function allowing other optimizations to do
+better job.
+
 @item -funroll-loops
 @opindex funroll-loops
 Unroll loops whose number of iterations can be determined at compile
@@ -3941,6 +3947,39 @@ given basic block needs to have to be considered hot.
 @item hot-bb-frequency-fraction
 Select fraction of the maximal frequency of executions of basic block in
 function given basic block needs to have to be considered hot
+
+@item tracer-dynamic-coverage
+@itemx tracer-dynamic-coverage-feedback
+
+This value is used to limit superblock formation once given percentage of
+executed instructions is covered.  This limits unnecesary code size expansion.
+
+The @option{tracer-dynamic-coverage-feedback} is used only when profile
+feedback is available.  The real profiles (as opposed to statically estimated
+ones) are much less balanced allowing the threshold to be larger value.
+
+@item tracer-max-code-growth
+Stop tail duplication once code growth has reached given percentage.  This is
+rather hokey argument, as most of the duplicates will be elliminated later in
+cross jumping, so it may be set to much higher values than is the desired code
+growth.
+
+@item tracer-min-branch-ratio
+
+Stop reverse growth when the reverse probability of best edge is less than this
+threshold (in percent).
+
+@item tracer-min-branch-ratio
+@itemx tracer-min-branch-ratio-feedback
+
+Stop forward growth if the best edge do have probability lower than this
+threshold.
+
+Similary to @option{tracer-dynamic-coverage} two values are present, one for
+compilation for profile feedback and one for compilation without.  The value
+for compilation with profile feedback needs to be more conservative (higher) in
+order to make tracer effective.
+
 @end table
 @end table
 
index de55ecc5841e5798250f1043bc1538a21f24d225..1b3105556b2c73bb9dcd835c9e68608d7b0865fc 100644 (file)
@@ -153,12 +153,43 @@ DEFPARAM(PARAM_MAX_UNROLLED_INSNS,
 
 DEFPARAM(HOT_BB_COUNT_FRACTION,
         "hot-bb-count-fraction",
-        "Select fraction of the maximal count of repetitions of basic block in program given basic block needs to have to be considered hot",
+        "Select fraction of the maximal count of repetitions of basic block in \
+program given basic block needs to have to be considered hot",
         10000)
 DEFPARAM(HOT_BB_FREQUENCY_FRACTION,
         "hot-bb-frequency-fraction",
-        "Select fraction of the maximal frequency of executions of basic block in function given basic block needs to have to be considered hot",
+        "Select fraction of the maximal frequency of executions of basic \
+block in function given basic block needs to have to be considered hot",
         1000)
+DEFPARAM(TRACER_DYNAMIC_COVERAGE_FEEDBACK,
+        "tracer-dynamic-coverage-feedback",
+        "The percentage of function, weighted by execution frequency, that \
+must be covered by trace formation. Used when profile feedback is available",
+        95)
+DEFPARAM(TRACER_DYNAMIC_COVERAGE,
+        "tracer-dynamic-coverage",
+        "The percentage of function, weighted by execution frequency, that \
+must be covered by trace formation. Used when profile feedback is not available",
+        75)
+DEFPARAM(TRACER_MAX_CODE_GROWTH,
+        "tracer-max-code-growth",
+        "Maximal code growth caused by tail duplication (in percents)",
+        100)
+DEFPARAM(TRACER_MIN_BRANCH_RATIO,
+        "tracer-min-branch-ratio",
+        "Stop reverse growth if the reverse probability of best edge is less \
+than this threshold (in percents)",
+        10)
+DEFPARAM(TRACER_MIN_BRANCH_PROBABILITY_FEEDBACK,
+        "tracer-min-branch-probability-feedback",
+        "Stop forward growth if the probability of best edge is less than \
+this threshold (in percents). Used when profile feedback is available",
+        30)
+DEFPARAM(TRACER_MIN_BRANCH_PROBABILITY,
+        "tracer-min-branch-probability",
+        "Stop forward growth if the probability of best edge is less than \
+this threshold (in percents). Used when profile feedback is not available",
+        50)
 /*
 Local variables:
 mode:c
index f4adc1da6b50e168139cb07068c4ee71bac1a506..81388eb8b74c7eaeae6d292172ac92485b8d0839 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -2281,4 +2281,6 @@ extern void if_convert                    PARAMS ((int));
 /* In predict.c */
 extern void invert_br_probabilities    PARAMS ((rtx));
 extern bool expensive_function_p       PARAMS ((int));
+/* In tracer.c */
+extern void tracer                     PARAMS ((void));
 #endif /* ! GCC_RTL_H */
index 74f8907ae7b58b363e681840ed4b169cec15ce53..49eeedbf0a7e08d3da29b014a21bb07edd15378f 100644 (file)
@@ -58,6 +58,7 @@ DEFTIMEVAR (TV_JUMP                  , "jump")
 DEFTIMEVAR (TV_CSE                   , "CSE")
 DEFTIMEVAR (TV_GCSE                  , "global CSE")
 DEFTIMEVAR (TV_LOOP                  , "loop analysis")
+DEFTIMEVAR (TV_TRACER                , "tracer")
 DEFTIMEVAR (TV_CSE2                  , "CSE 2")
 DEFTIMEVAR (TV_BRANCH_PROB           , "branch prediction")
 DEFTIMEVAR (TV_FLOW                  , "flow analysis")
index a3d0dfd08e0a08360139df0afe064cdfdef29329..b9432f44c357de9e02d3bff9f0129dbfbc978218 100644 (file)
@@ -224,6 +224,7 @@ enum dump_file_index
   DFI_loop,
   DFI_cfg,
   DFI_bp,
+  DFI_tracer,
   DFI_cse2,
   DFI_life,
   DFI_combine,
@@ -271,6 +272,7 @@ static struct dump_file_info dump_file[DFI_MAX] =
   { "loop",    'L', 1, 0, 0 },
   { "cfg",     'f', 1, 0, 0 },
   { "bp",      'b', 1, 0, 0 },
+  { "tracer",  'T', 1, 0, 0 },
   { "cse2",    't', 1, 0, 0 },
   { "life",    'f', 1, 0, 0 }, /* Yes, duplicate enable switch.  */
   { "combine", 'c', 1, 0, 0 },
@@ -866,6 +868,10 @@ int flag_merge_constants = 1;
    one, unconditionally renumber instruction UIDs.  */
 int flag_renumber_insns = 1;
 
+/* Nonzero if we perform superblock formation.  */
+
+int flag_tracer = 0;
+
 /* Values of the -falign-* flags: how much to align labels in code.
    0 means `use default', 1 means `don't align'.
    For each variable, there is an _log variant which is the power
@@ -977,6 +983,8 @@ static const lang_independent_options f_options[] =
    N_("When possible do not generate stack frames") },
   {"optimize-sibling-calls", &flag_optimize_sibling_calls, 1,
    N_("Optimize sibling and tail recursive calls") },
+  {"tracer", &flag_tracer, 1,
+   N_("Perform superblock formation via tail duplication") },
   {"cse-follow-jumps", &flag_cse_follow_jumps, 1,
    N_("When running CSE, follow jumps to their targets") },
   {"cse-skip-blocks", &flag_cse_skip_blocks, 1,
@@ -2958,6 +2966,19 @@ rest_of_compilation (decl)
       close_dump_file (DFI_bp, print_rtl_with_bb, insns);
       timevar_pop (TV_BRANCH_PROB);
     }
+  if (flag_tracer)
+    {
+      timevar_push (TV_TRACER);
+      open_dump_file (DFI_tracer, decl);
+      if (rtl_dump_file)
+       dump_flow_info (rtl_dump_file);
+      cleanup_cfg (CLEANUP_EXPENSIVE);
+      tracer ();
+      cleanup_cfg (CLEANUP_EXPENSIVE);
+      close_dump_file (DFI_tracer, print_rtl_with_bb, get_insns ());
+      timevar_pop (TV_TRACER);
+      reg_scan (get_insns (), max_reg_num (), 0);
+    }
 
   if (optimize > 0)
     {
diff --git a/gcc/tracer.c b/gcc/tracer.c
new file mode 100644 (file)
index 0000000..b7c5a76
--- /dev/null
@@ -0,0 +1,373 @@
+/* The tracer pass for the GNU compiler.
+   Contributed by Jan Hubicka, SuSE Labs.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+/* This pass performs the tail duplication needed for superblock formation.
+   For more information see:
+
+     Design and Analysis of Profile-Based Optimization in Compaq's
+     Compilation Tools for Alpha; Journal of Instruction-Level
+     Parallelism 3 (2000) 1-25
+
+   Unlike Compaq's implementation we don't do the loop peeling as most
+   probably a better job can be done by a special pass and we don't
+   need to worry too much about the code size implications as the tail
+   duplicates are crossjumped again if optimizations are not
+   performed.  */
+
+
+#include "config.h"
+#include "system.h"
+#include "tree.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "cfglayout.h"
+#include "fibheap.h"
+#include "flags.h"
+#include "params.h"
+#include "profile.h"
+
+static int count_insns         PARAMS ((basic_block));
+static bool ignore_bb_p                PARAMS ((basic_block));
+static bool better_p           PARAMS ((edge, edge));
+static int find_trace          PARAMS ((basic_block, basic_block *));
+static void tail_duplicate     PARAMS ((void));
+static void layout_superblocks PARAMS ((void));
+static bool ignore_bb_p                PARAMS ((basic_block));
+
+/* Minimal outgoing edge probability considered for superblock formation.  */
+static int probability_cutoff;
+static int branch_ratio_cutoff;
+
+/* Return true if BB has been seen - it is connected to some trace
+   already.  */
+
+#define seen(bb) (RBI (bb)->visited || RBI (bb)->next)
+
+/* Return true if we should ignore the basic block for purposes of tracing. */
+static bool
+ignore_bb_p (basic_block bb)
+{
+  if (bb->index < 0)
+    return true;
+  if (!maybe_hot_bb_p (bb))
+    return true;
+  return false;
+}
+
+/* Return number of instructions in the block.  */
+
+static int
+count_insns (bb)
+     basic_block bb;
+{
+  rtx insn;
+  int n = 0;
+
+  for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = NEXT_INSN (insn))
+    if (active_insn_p (insn))
+      n++;
+  return n;
+}
+
+/* Return true if E1 is more frequent than E2.  */
+static bool
+better_p (e1, e2)
+     edge e1, e2;
+{
+  if (e1->count != e2->count)
+    return e1->count > e2->count;
+  if (e1->src->frequency * e1->probability !=
+      e2->src->frequency * e2->probability)
+    return (e1->src->frequency * e1->probability
+           > e2->src->frequency * e2->probability);
+  /* This is needed to avoid changes in the decision after
+     CFG is modified.  */
+  if (e1->src != e2->src)
+    return e1->src->index > e2->src->index;
+  return e1->dest->index > e2->dest->index;
+}
+
+/* Return most frequent successor of basic block BB.  */
+
+static edge
+find_best_successor (basic_block bb)
+{
+  edge e;
+  edge best = NULL;
+
+  for (e = bb->succ; e; e = e->succ_next)
+    if (!best || better_p (e, best))
+      best = e;
+  if (!best || ignore_bb_p (best->dest))
+    return NULL;
+  if (best->probability <= probability_cutoff)
+    return NULL;
+  return best;
+}
+
+/* Return most frequent predecessor of basic block BB.  */
+
+static edge
+find_best_predecessor (basic_block bb)
+{
+  edge e;
+  edge best = NULL;
+
+  for (e = bb->pred; e; e = e->pred_next)
+    if (!best || better_p (e, best))
+      best = e;
+  if (!best || ignore_bb_p (best->src))
+    return NULL;
+  if (EDGE_FREQUENCY (best) * REG_BR_PROB_BASE
+      < bb->frequency * branch_ratio_cutoff)
+    return NULL;
+  return best;
+}
+
+/* Find the trace using bb and record it in the TRACE array.
+   Return number of basic blocks recorded.  */
+
+static int
+find_trace (bb, trace)
+     basic_block bb;
+     basic_block *trace;
+{
+  int i = 0;
+  edge e;
+
+  if (rtl_dump_file)
+    fprintf (rtl_dump_file, "Trace seed %i [%i]", bb->index, bb->frequency);
+
+  while ((e = find_best_predecessor (bb)) != NULL)
+    {
+      basic_block bb2 = e->src;
+      if (seen (bb2) || (e->flags & (EDGE_DFS_BACK | EDGE_COMPLEX))
+         || find_best_successor (bb2) != e)
+       break;
+      if (rtl_dump_file)
+       fprintf (rtl_dump_file, ",%i [%i]", bb->index, bb->frequency);
+      bb = bb2;
+    }
+  if (rtl_dump_file)
+    fprintf (rtl_dump_file, " forward %i [%i]", bb->index, bb->frequency);
+  trace[i++] = bb;
+
+  /* Follow the trace in forward direction.  */
+  while ((e = find_best_successor (bb)) != NULL)
+    {
+      bb = e->dest;
+      if (seen (bb) || (e->flags & (EDGE_DFS_BACK | EDGE_COMPLEX))
+         || find_best_predecessor (bb) != e)
+       break;
+      if (rtl_dump_file)
+       fprintf (rtl_dump_file, ",%i [%i]", bb->index, bb->frequency);
+      trace[i++] = bb;
+    }
+  if (rtl_dump_file)
+    fprintf (rtl_dump_file, "\n");
+  return i;
+}
+
+/* Look for basic blocks in frequency order, construct traces and tail duplicate
+   if profitable.  */
+
+static void
+tail_duplicate ()
+{
+  fibnode_t *blocks = xcalloc (last_basic_block, sizeof (fibnode_t));
+  basic_block *trace = xmalloc (sizeof (basic_block) * n_basic_blocks);
+  int *counts = xmalloc (sizeof (int) * last_basic_block);
+  int ninsns = 0, nduplicated = 0;
+  gcov_type weighted_insns = 0, traced_insns = 0;
+  fibheap_t heap = fibheap_new ();
+  gcov_type cover_insns;
+  int max_dup_insns;
+  basic_block bb;
+
+  if (profile_info.count_profiles_merged && flag_branch_probabilities)
+    probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY_FEEDBACK);
+  else
+    probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY);
+  probability_cutoff = REG_BR_PROB_BASE / 100 * probability_cutoff;
+
+  branch_ratio_cutoff =
+    (REG_BR_PROB_BASE / 100 * PARAM_VALUE (TRACER_MIN_BRANCH_RATIO));
+
+  FOR_EACH_BB (bb)
+    {
+      int n = count_insns (bb);
+      if (!ignore_bb_p (bb))
+       blocks[bb->index] = fibheap_insert (heap, -bb->frequency,
+                                           bb);
+
+      counts [bb->index] = n;
+      ninsns += n;
+      weighted_insns += n * bb->frequency;
+    }
+
+  if (profile_info.count_profiles_merged && flag_branch_probabilities)
+    cover_insns = PARAM_VALUE (TRACER_DYNAMIC_COVERAGE_FEEDBACK);
+  else
+    cover_insns = PARAM_VALUE (TRACER_DYNAMIC_COVERAGE);
+  cover_insns = (weighted_insns * cover_insns + 50) / 100;
+  max_dup_insns = (ninsns * PARAM_VALUE (TRACER_MAX_CODE_GROWTH) + 50) / 100;
+
+  while (traced_insns < cover_insns && nduplicated < max_dup_insns
+         && !fibheap_empty (heap))
+    {
+      basic_block bb = fibheap_extract_min (heap);
+      int n, pos;
+
+      if (!bb)
+       break;
+
+      blocks[bb->index] = NULL;
+
+      if (ignore_bb_p (bb))
+       continue;
+      if (seen (bb))
+       abort ();
+
+      n = find_trace (bb, trace);
+
+      bb = trace[0];
+      traced_insns += bb->frequency * counts [bb->index];
+      if (blocks[bb->index])
+       {
+         fibheap_delete_node (heap, blocks[bb->index]);
+         blocks[bb->index] = NULL;
+       }
+
+      for (pos = 1; pos < n; pos++)
+       {
+         basic_block bb2 = trace[pos];
+
+         if (blocks[bb2->index])
+           {
+             fibheap_delete_node (heap, blocks[bb2->index]);
+             blocks[bb2->index] = NULL;
+           }
+         traced_insns += bb2->frequency * counts [bb2->index];
+         if (bb2->pred && bb2->pred->pred_next
+             && cfg_layout_can_duplicate_bb_p (bb2))
+           {
+             edge e = bb2->pred;
+             basic_block old = bb2;
+
+             while (e->src != bb)
+               e = e->pred_next;
+             nduplicated += counts [bb2->index];
+             bb2 = cfg_layout_duplicate_bb (bb2, e);
+
+             /* Reconsider the original copy of block we've duplicated.
+                Removing the most common predecesor may make it to be
+                head.  */
+             blocks[old->index] =
+               fibheap_insert (heap, -old->frequency, old);
+
+             if (rtl_dump_file)
+               fprintf (rtl_dump_file, "Duplicated %i as %i [%i]\n",
+                        old->index, bb2->index, bb2->frequency);
+           }
+         RBI (bb)->next = bb2;
+         RBI (bb2)->visited = 1;
+         bb = bb2;
+         /* In case the trace became infrequent, stop duplicating.  */
+         if (ignore_bb_p (bb))
+           break;
+       }
+      if (rtl_dump_file)
+       fprintf (rtl_dump_file, " covered now %.1f\n\n",
+                traced_insns * 100.0 / weighted_insns);
+    }
+  if (rtl_dump_file)
+    fprintf (rtl_dump_file, "Duplicated %i insns (%i%%)\n", nduplicated,
+            nduplicated * 100 / ninsns);
+
+  free (blocks);
+  free (trace);
+  free (counts);
+  fibheap_delete (heap);
+}
+
+/* Connect the superblocks into linear seuqence.  At the moment we attempt to keep
+   the original order as much as possible, but the algorithm may be made smarter
+   later if needed.  BB reordering pass should void most of the benefits of such
+   change though.  */
+
+static void
+layout_superblocks ()
+{
+  basic_block end = ENTRY_BLOCK_PTR->succ->dest;
+  basic_block bb = ENTRY_BLOCK_PTR->succ->dest->next_bb;
+
+  while (bb != EXIT_BLOCK_PTR)
+    {
+      edge e, best = NULL;
+      while (RBI (end)->next)
+       end = RBI (end)->next;
+
+      for (e = end->succ; e; e = e->succ_next)
+       if (e->dest != EXIT_BLOCK_PTR
+           && e->dest != ENTRY_BLOCK_PTR->succ->dest
+           && !RBI (e->dest)->visited
+           && (!best || EDGE_FREQUENCY (e) > EDGE_FREQUENCY (best)))
+         best = e;
+
+      if (best)
+       {
+         RBI (end)->next = best->dest;
+         RBI (best->dest)->visited = 1;
+       }
+      else
+       for (; bb != EXIT_BLOCK_PTR; bb=bb->next_bb)
+         {
+           if (!RBI (bb)->visited)
+             {
+               RBI (end)->next = bb;
+               RBI (bb)->visited = 1;
+               break;
+             }
+         }
+    }
+}
+
+/* Main entry point to this file.  */
+
+void
+tracer ()
+{
+  if (n_basic_blocks <= 1)
+    return;
+  cfg_layout_initialize ();
+  mark_dfs_back_edges ();
+  if (rtl_dump_file)
+    dump_flow_info (rtl_dump_file);
+  tail_duplicate ();
+  layout_superblocks ();
+  if (rtl_dump_file)
+    dump_flow_info (rtl_dump_file);
+  cfg_layout_finalize ();
+  /* Merge basic blocks in duplicated traces.  */
+  cleanup_cfg (CLEANUP_EXPENSIVE);
+}