OSDN Git Service

2010-04-08 Tobias Burnus <burnus@net-b.de>
[pf3gnuchains/gcc-fork.git] / gcc / cfglayout.c
index bbdd7a2..5e12057 100644 (file)
@@ -1,11 +1,12 @@
 /* Basic block reordering routines for the GNU compiler.
-   Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   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
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -14,9 +15,8 @@ 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, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -37,6 +37,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "alloc-pool.h"
 #include "flags.h"
 #include "tree-pass.h"
+#include "df.h"
 #include "vecprim.h"
 
 /* Holds the interesting trailing notes for the function.  */
@@ -48,12 +49,11 @@ static void record_effective_endpoints (void);
 static rtx label_for_bb (basic_block);
 static void fixup_reorder_chain (void);
 
-static void set_block_levels (tree, int);
 static void change_scope (rtx, tree, tree);
 
 void verify_insn_chain (void);
 static void fixup_fallthru_exit_predecessor (void);
-static tree insn_scope (rtx);
+static tree insn_scope (const_rtx);
 \f
 rtx
 unlink_insn_chain (rtx first, rtx last)
@@ -99,15 +99,11 @@ skip_insns_after_block (basic_block bb)
          continue;
 
        case NOTE:
-         switch (NOTE_LINE_NUMBER (insn))
+         switch (NOTE_KIND (insn))
            {
            case NOTE_INSN_BLOCK_END:
-             last_insn = insn;
+             gcc_unreachable ();
              continue;
-           case NOTE_INSN_DELETED:
-           case NOTE_INSN_DELETED_LABEL:
-             continue;
-
            default:
              continue;
              break;
@@ -116,9 +112,7 @@ skip_insns_after_block (basic_block bb)
 
        case CODE_LABEL:
          if (NEXT_INSN (insn)
-             && JUMP_P (NEXT_INSN (insn))
-             && (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
-                 || GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC))
+             && JUMP_TABLE_DATA_P (NEXT_INSN (insn)))
            {
              insn = NEXT_INSN (insn);
              last_insn = insn;
@@ -147,9 +141,11 @@ skip_insns_after_block (basic_block bb)
     {
       prev = PREV_INSN (insn);
       if (NOTE_P (insn))
-       switch (NOTE_LINE_NUMBER (insn))
+       switch (NOTE_KIND (insn))
          {
          case NOTE_INSN_BLOCK_END:
+           gcc_unreachable ();
+           break;
          case NOTE_INSN_DELETED:
          case NOTE_INSN_DELETED_LABEL:
            continue;
@@ -192,7 +188,7 @@ record_effective_endpoints (void)
   for (insn = get_insns ();
        insn
        && NOTE_P (insn)
-       && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK;
+       && NOTE_KIND (insn) != NOTE_INSN_BASIC_BLOCK;
        insn = NEXT_INSN (insn))
     continue;
   /* No basic blocks at all?  */
@@ -232,137 +228,180 @@ record_effective_endpoints (void)
    than the following one.  Similarly for the other properties.  */
 static VEC(int,heap) *block_locators_locs;
 static GTY(()) VEC(tree,gc) *block_locators_blocks;
-static VEC(int,heap) *line_locators_locs;
-static VEC(int,heap) *line_locators_lines;
-static VEC(int,heap) *file_locators_locs;
-static GTY(()) varray_type file_locators_files;
+static VEC(int,heap) *locations_locators_locs;
+DEF_VEC_O(location_t);
+DEF_VEC_ALLOC_O(location_t,heap);
+static VEC(location_t,heap) *locations_locators_vals;
 int prologue_locator;
 int epilogue_locator;
 
-/* During the RTL expansion the lexical blocks and line numbers are
-   represented via INSN_NOTEs.  Replace them by representation using
-   INSN_LOCATORs.  */
+/* Hold current location information and last location information, so the
+   datastructures are built lazily only when some instructions in given
+   place are needed.  */
+static location_t curr_location, last_location;
+static tree curr_block, last_block;
+static int curr_rtl_loc = -1;
 
-unsigned int
-insn_locators_initialize (void)
+/* Allocate insn locator datastructure.  */
+void
+insn_locators_alloc (void)
 {
-  tree block = NULL;
-  tree last_block = NULL;
-  rtx insn, next;
-  int loc = 0;
-  int line_number = 0, last_line_number = 0;
-  const char *file_name = NULL, *last_file_name = NULL;
-
   prologue_locator = epilogue_locator = 0;
 
   block_locators_locs = VEC_alloc (int, heap, 32);
   block_locators_blocks = VEC_alloc (tree, gc, 32);
-  line_locators_locs = VEC_alloc (int, heap, 32);
-  line_locators_lines = VEC_alloc (int, heap, 32);
-  file_locators_locs = VEC_alloc (int, heap, 32);
-  VARRAY_CHAR_PTR_INIT (file_locators_files, 32, "file_locators_files");
+  locations_locators_locs = VEC_alloc (int, heap, 32);
+  locations_locators_vals = VEC_alloc (location_t, heap, 32);
+
+  last_location = -1;
+  curr_location = -1;
+  curr_block = NULL;
+  last_block = NULL;
+  curr_rtl_loc = 0;
+}
 
-  for (insn = get_insns (); insn; insn = next)
-    {
-      int active = 0;
+/* At the end of emit stage, clear current location.  */
+void
+insn_locators_finalize (void)
+{
+  if (curr_rtl_loc >= 0)
+    epilogue_locator = curr_insn_locator ();
+  curr_rtl_loc = -1;
+}
 
-      next = NEXT_INSN (insn);
+/* Allocate insn locator datastructure.  */
+void
+insn_locators_free (void)
+{
+  prologue_locator = epilogue_locator = 0;
 
-      if (NOTE_P (insn))
-       {
-         gcc_assert (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_BEG
-                     && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END);
-         if (NOTE_LINE_NUMBER (insn) > 0)
-           {
-             expanded_location xloc;
-             NOTE_EXPANDED_LOCATION (xloc, insn);
-             line_number = xloc.line;
-             file_name = xloc.file;
-             delete_insn (insn);
-           }
-       }
-      else
-       active = (active_insn_p (insn)
-                 && GET_CODE (PATTERN (insn)) != ADDR_VEC
-                 && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC);
+  VEC_free (int, heap, block_locators_locs);
+  VEC_free (tree,gc, block_locators_blocks);
+  VEC_free (int, heap, locations_locators_locs);
+  VEC_free (location_t, heap, locations_locators_vals);
+}
 
-      check_block_change (insn, &block);
 
-      if (active
-         || !next
-         || (!prologue_locator && file_name))
-       {
-         if (last_block != block)
-           {
-             loc++;
-             VEC_safe_push (int, heap, block_locators_locs, loc);
-             VEC_safe_push (tree, gc, block_locators_blocks, block);
-             last_block = block;
-           }
-         if (last_line_number != line_number)
-           {
-             loc++;
-             VEC_safe_push (int, heap, line_locators_locs, loc);
-             VEC_safe_push (int, heap, line_locators_lines, line_number);
-             last_line_number = line_number;
-           }
-         if (last_file_name != file_name)
-           {
-             loc++;
-             VEC_safe_push (int, heap, file_locators_locs, loc);
-             VARRAY_PUSH_CHAR_PTR (file_locators_files, (char *) file_name);
-             last_file_name = file_name;
-           }
-         if (!prologue_locator && file_name)
-           prologue_locator = loc;
-         if (!next)
-           epilogue_locator = loc;
-         if (active)
-           INSN_LOCATOR (insn) = loc;
-       }
+/* Set current location.  */
+void
+set_curr_insn_source_location (location_t location)
+{
+  /* IV opts calls into RTL expansion to compute costs of operations.  At this
+     time locators are not initialized.  */
+  if (curr_rtl_loc == -1)
+    return;
+  curr_location = location;
+}
+
+/* Get current location.  */
+location_t
+get_curr_insn_source_location (void)
+{
+  return curr_location;
+}
+
+/* Set current scope block.  */
+void
+set_curr_insn_block (tree b)
+{
+  /* IV opts calls into RTL expansion to compute costs of operations.  At this
+     time locators are not initialized.  */
+  if (curr_rtl_loc == -1)
+    return;
+  if (b)
+    curr_block = b;
+}
+
+/* Get current scope block.  */
+tree
+get_curr_insn_block (void)
+{
+  return curr_block;
+}
+
+/* Return current insn locator.  */
+int
+curr_insn_locator (void)
+{
+  if (curr_rtl_loc == -1)
+    return 0;
+  if (last_block != curr_block)
+    {
+      curr_rtl_loc++;
+      VEC_safe_push (int, heap, block_locators_locs, curr_rtl_loc);
+      VEC_safe_push (tree, gc, block_locators_blocks, curr_block);
+      last_block = curr_block;
+    }
+  if (last_location != curr_location)
+    {
+      curr_rtl_loc++;
+      VEC_safe_push (int, heap, locations_locators_locs, curr_rtl_loc);
+      VEC_safe_push (location_t, heap, locations_locators_vals, &curr_location);
+      last_location = curr_location;
     }
+  return curr_rtl_loc;
+}
+
+static unsigned int
+into_cfg_layout_mode (void)
+{
+  cfg_layout_initialize (0);
+  return 0;
+}
+
+static unsigned int
+outof_cfg_layout_mode (void)
+{
+  basic_block bb;
 
-  /* Tag the blocks with a depth number so that change_scope can find
-     the common parent easily.  */
-  set_block_levels (DECL_INITIAL (cfun->decl), 0);
+  FOR_EACH_BB (bb)
+    if (bb->next_bb != EXIT_BLOCK_PTR)
+      bb->aux = bb->next_bb;
+
+  cfg_layout_finalize ();
 
-  free_block_changes ();
   return 0;
 }
 
-struct tree_opt_pass pass_insn_locators_initialize =
+struct rtl_opt_pass pass_into_cfg_layout_mode =
 {
-  "locators",                           /* name */
+ {
+  RTL_PASS,
+  "into_cfglayout",                     /* name */
   NULL,                                 /* gate */
-  insn_locators_initialize,             /* execute */
+  into_cfg_layout_mode,                 /* execute */
   NULL,                                 /* sub */
   NULL,                                 /* next */
   0,                                    /* static_pass_number */
-  0,                                    /* tv_id */
+  TV_NONE,                              /* tv_id */
   0,                                    /* properties_required */
-  0,                                    /* properties_provided */
+  PROP_cfglayout,                       /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_dump_func,                       /* todo_flags_finish */
-  0                                     /* letter */
+ }
 };
 
-
-/* For each lexical block, set BLOCK_NUMBER to the depth at which it is
-   found in the block tree.  */
-
-static void
-set_block_levels (tree block, int level)
+struct rtl_opt_pass pass_outof_cfg_layout_mode =
 {
-  while (block)
-    {
-      BLOCK_NUMBER (block) = level;
-      set_block_levels (BLOCK_SUBBLOCKS (block), level + 1);
-      block = BLOCK_CHAIN (block);
-    }
-}
+ {
+  RTL_PASS,
+  "outof_cfglayout",                    /* name */
+  NULL,                                 /* gate */
+  outof_cfg_layout_mode,                /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,                              /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  PROP_cfglayout,                       /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+ }
+};
 \f
-/* Return sope resulting from combination of S1 and S2.  */
+/* Return scope resulting from combination of S1 and S2.  */
 static tree
 choose_inner_scope (tree s1, tree s2)
 {
@@ -419,13 +458,12 @@ change_scope (rtx orig_insn, tree s1, tree s2)
     }
 }
 
-/* Return lexical scope block insn belong to.  */
+/* Return lexical scope block locator belongs to.  */
 static tree
-insn_scope (rtx insn)
+locator_scope (int loc)
 {
   int max = VEC_length (int, block_locators_locs);
   int min = 0;
-  int loc = INSN_LOCATOR (insn);
 
   /* When block_locators_locs was initialized, the pro- and epilogue
      insns didn't exist yet and can therefore not be found this way.
@@ -459,19 +497,24 @@ insn_scope (rtx insn)
   return VEC_index (tree, block_locators_blocks, min);
 }
 
+/* Return lexical scope block insn belongs to.  */
+static tree
+insn_scope (const_rtx insn)
+{
+  return locator_scope (INSN_LOCATOR (insn));
+}
+
 /* Return line number of the statement specified by the locator.  */
-int
-locator_line (int loc)
+location_t
+locator_location (int loc)
 {
-  int max = VEC_length (int, line_locators_locs);
+  int max = VEC_length (int, locations_locators_locs);
   int min = 0;
 
-  if (!max || !loc)
-    return 0;
   while (1)
     {
       int pos = (min + max) / 2;
-      int tmp = VEC_index (int, line_locators_locs, pos);
+      int tmp = VEC_index (int, locations_locators_locs, pos);
 
       if (tmp <= loc && min != pos)
        min = pos;
@@ -483,12 +526,24 @@ locator_line (int loc)
          break;
        }
     }
-  return VEC_index (int, line_locators_lines, min);
+  return *VEC_index (location_t, locations_locators_vals, min);
+}
+
+/* Return source line of the statement that produced this insn.  */
+int
+locator_line (int loc)
+{
+  expanded_location xloc;
+  if (!loc)
+    return 0;
+  else
+    xloc = expand_location (locator_location (loc));
+  return xloc.line;
 }
 
 /* Return line number of the statement that produced this insn.  */
 int
-insn_line (rtx insn)
+insn_line (const_rtx insn)
 {
   return locator_line (INSN_LOCATOR (insn));
 }
@@ -497,36 +552,32 @@ insn_line (rtx insn)
 const char *
 locator_file (int loc)
 {
-  int max = VEC_length (int, file_locators_locs);
-  int min = 0;
-
-  if (!max || !loc)
-    return NULL;
-  while (1)
-    {
-      int pos = (min + max) / 2;
-      int tmp = VEC_index (int, file_locators_locs, pos);
-
-      if (tmp <= loc && min != pos)
-       min = pos;
-      else if (tmp > loc && max != pos)
-       max = pos;
-      else
-       {
-         min = pos;
-         break;
-       }
-    }
-   return VARRAY_CHAR_PTR (file_locators_files, min);
+  expanded_location xloc;
+  if (!loc)
+    return 0;
+  else
+    xloc = expand_location (locator_location (loc));
+  return xloc.file;
 }
 
 /* Return source file of the statement that produced this insn.  */
 const char *
-insn_file (rtx insn)
+insn_file (const_rtx insn)
 {
   return locator_file (INSN_LOCATOR (insn));
 }
 
+/* Return true if LOC1 and LOC2 locators have the same location and scope.  */
+bool
+locator_eq (int loc1, int loc2)
+{
+  if (loc1 == loc2)
+    return true;
+  if (locator_location (loc1) != locator_location (loc2))
+    return false;
+  return locator_scope (loc1) == locator_scope (loc2);
+}
+
 /* Rebuild all the NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes based
    on the scope tree and the newly reordered instructions.  */
 
@@ -544,9 +595,7 @@ reemit_insn_block_notes (void)
       tree this_block;
 
       /* Avoid putting scope notes between jump table and its label.  */
-      if (JUMP_P (insn)
-         && (GET_CODE (PATTERN (insn)) == ADDR_VEC
-             || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+      if (JUMP_TABLE_DATA_P (insn))
        continue;
 
       this_block = insn_scope (insn);
@@ -580,13 +629,83 @@ reemit_insn_block_notes (void)
   reorder_blocks ();
 }
 \f
+
+/* Link the basic blocks in the correct order, compacting the basic
+   block queue while at it.  This also clears the visited flag on
+   all basic blocks.  If STAY_IN_CFGLAYOUT_MODE is false, this function
+   also clears the basic block header and footer fields.
+
+   This function is usually called after a pass (e.g. tracer) finishes
+   some transformations while in cfglayout mode.  The required sequence
+   of the basic blocks is in a linked list along the bb->aux field.
+   This functions re-links the basic block prev_bb and next_bb pointers
+   accordingly, and it compacts and renumbers the blocks.  */
+
+void
+relink_block_chain (bool stay_in_cfglayout_mode)
+{
+  basic_block bb, prev_bb;
+  int index;
+
+  /* Maybe dump the re-ordered sequence.  */
+  if (dump_file)
+    {
+      fprintf (dump_file, "Reordered sequence:\n");
+      for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS;
+          bb;
+          bb = (basic_block) bb->aux, index++)
+       {
+         fprintf (dump_file, " %i ", index);
+         if (get_bb_original (bb))
+           fprintf (dump_file, "duplicate of %i ",
+                    get_bb_original (bb)->index);
+         else if (forwarder_block_p (bb)
+                  && !LABEL_P (BB_HEAD (bb)))
+           fprintf (dump_file, "compensation ");
+         else
+           fprintf (dump_file, "bb %i ", bb->index);
+         fprintf (dump_file, " [%i]\n", bb->frequency);
+       }
+    }
+
+  /* Now reorder the blocks.  */
+  prev_bb = ENTRY_BLOCK_PTR;
+  bb = ENTRY_BLOCK_PTR->next_bb;
+  for (; bb; prev_bb = bb, bb = (basic_block) bb->aux)
+    {
+      bb->prev_bb = prev_bb;
+      prev_bb->next_bb = bb;
+    }
+  prev_bb->next_bb = EXIT_BLOCK_PTR;
+  EXIT_BLOCK_PTR->prev_bb = prev_bb;
+
+  /* Then, clean up the aux and visited fields.  */
+  FOR_ALL_BB (bb)
+    {
+      bb->aux = NULL;
+      bb->il.rtl->visited = 0;
+      if (!stay_in_cfglayout_mode)
+       bb->il.rtl->header = bb->il.rtl->footer = NULL;
+    }
+
+  /* Maybe reset the original copy tables, they are not valid anymore
+     when we renumber the basic blocks in compact_blocks.  If we are
+     are going out of cfglayout mode, don't re-allocate the tables.  */
+  free_original_copy_tables ();
+  if (stay_in_cfglayout_mode)
+    initialize_original_copy_tables ();
+
+  /* Finally, put basic_block_info in the new order.  */
+  compact_blocks ();
+}
+\f
+
 /* Given a reorder chain, rearrange the code to match.  */
 
 static void
 fixup_reorder_chain (void)
 {
-  basic_block bb, prev_bb;
-  int index;
+  basic_block bb;
   rtx insn = NULL;
 
   if (cfg_layout_function_header)
@@ -600,9 +719,7 @@ fixup_reorder_chain (void)
   /* First do the bulk reordering -- rechain the blocks without regard to
      the needed changes to jumps and labels.  */
 
-  for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS;
-       bb != 0;
-       bb = bb->aux, index++)
+  for (bb = ENTRY_BLOCK_PTR->next_bb; bb; bb = (basic_block) bb->aux)
     {
       if (bb->il.rtl->header)
        {
@@ -630,8 +747,6 @@ fixup_reorder_chain (void)
        }
     }
 
-  gcc_assert (index == n_basic_blocks);
-
   NEXT_INSN (insn) = cfg_layout_function_footer;
   if (cfg_layout_function_footer)
     PREV_INSN (cfg_layout_function_footer) = insn;
@@ -643,12 +758,11 @@ fixup_reorder_chain (void)
 #ifdef ENABLE_CHECKING
   verify_insn_chain ();
 #endif
-  delete_dead_jumptables ();
 
   /* Now add jumps and labels as needed to match the blocks new
      outgoing edges.  */
 
-  for (bb = ENTRY_BLOCK_PTR->next_bb; bb ; bb = bb->aux)
+  for (bb = ENTRY_BLOCK_PTR->next_bb; bb ; bb = (basic_block) bb->aux)
     {
       edge e_fall, e_taken, e;
       rtx bb_end_insn;
@@ -673,6 +787,18 @@ fixup_reorder_chain (void)
        {
          if (any_condjump_p (bb_end_insn))
            {
+             /* This might happen if the conditional jump has side
+                effects and could therefore not be optimized away.
+                Make the basic block to end with a barrier in order
+                to prevent rtl_verify_flow_info from complaining.  */
+             if (!e_fall)
+               {
+                 gcc_assert (!onlyjump_p (bb_end_insn)
+                             || returnjump_p (bb_end_insn));
+                 bb->il.rtl->footer = emit_barrier_after (bb_end_insn);
+                 continue;
+               }
+
              /* If the old fallthru is still next, nothing to do.  */
              if (bb->aux == e_fall->dest
                  || e_fall->dest == EXIT_BLOCK_PTR)
@@ -734,6 +860,18 @@ fixup_reorder_chain (void)
                  continue;
                }
            }
+         else if (extract_asm_operands (PATTERN (bb_end_insn)) != NULL)
+           {
+             /* If the old fallthru is still next or if
+                asm goto doesn't have a fallthru (e.g. when followed by
+                __builtin_unreachable ()), nothing to do.  */
+             if (! e_fall
+                 || bb->aux == e_fall->dest
+                 || e_fall->dest == EXIT_BLOCK_PTR)
+               continue;
+
+             /* Otherwise we'll have to use the fallthru fixup below.  */
+           }
          else
            {
              /* Otherwise we have some return, switch or computed
@@ -779,47 +917,11 @@ fixup_reorder_chain (void)
              && JUMP_P (BB_END (bb))
              && !any_condjump_p (BB_END (bb))
              && (EDGE_SUCC (bb, 0)->flags & EDGE_CROSSING))
-           REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
-             (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
+           add_reg_note (BB_END (bb), REG_CROSSING_JUMP, NULL_RTX);
        }
     }
 
-  /* Put basic_block_info in the new order.  */
-
-  if (dump_file)
-    {
-      fprintf (dump_file, "Reordered sequence:\n");
-      for (bb = ENTRY_BLOCK_PTR->next_bb, index = NUM_FIXED_BLOCKS;
-          bb;
-          bb = bb->aux, index++)
-       {
-         fprintf (dump_file, " %i ", index);
-         if (get_bb_original (bb))
-           fprintf (dump_file, "duplicate of %i ",
-                    get_bb_original (bb)->index);
-         else if (forwarder_block_p (bb)
-                  && !LABEL_P (BB_HEAD (bb)))
-           fprintf (dump_file, "compensation ");
-         else
-           fprintf (dump_file, "bb %i ", bb->index);
-         fprintf (dump_file, " [%i]\n", bb->frequency);
-       }
-    }
-
-  prev_bb = ENTRY_BLOCK_PTR;
-  bb = ENTRY_BLOCK_PTR->next_bb;
-  index = NUM_FIXED_BLOCKS;
-
-  for (; bb; prev_bb = bb, bb = bb->aux, index ++)
-    {
-      bb->index = index;
-      SET_BASIC_BLOCK (index, bb);
-
-      bb->prev_bb = prev_bb;
-      prev_bb->next_bb = bb;
-    }
-  prev_bb->next_bb = EXIT_BLOCK_PTR;
-  EXIT_BLOCK_PTR->prev_bb = prev_bb;
+  relink_block_chain (/*stay_in_cfglayout_mode=*/false);
 
   /* Annoying special case - jump around dead jumptables left in the code.  */
   FOR_EACH_BB (bb)
@@ -834,6 +936,52 @@ fixup_reorder_chain (void)
       if (e && !can_fallthru (e->src, e->dest))
        force_nonfallthru (e);
     }
+
+  /* Ensure goto_locus from edges has some instructions with that locus
+     in RTL.  */
+  if (!optimize)
+    FOR_EACH_BB (bb)
+      {
+        edge e;
+        edge_iterator ei;
+
+        FOR_EACH_EDGE (e, ei, bb->succs)
+         if (e->goto_locus && !(e->flags & EDGE_ABNORMAL))
+           {
+             basic_block nb;
+             rtx end;
+
+             insn = BB_END (e->src);
+             end = PREV_INSN (BB_HEAD (e->src));
+             while (insn != end
+                    && (!INSN_P (insn) || INSN_LOCATOR (insn) == 0))
+               insn = PREV_INSN (insn);
+             if (insn != end
+                 && locator_eq (INSN_LOCATOR (insn), (int) e->goto_locus))
+               continue;
+             if (simplejump_p (BB_END (e->src))
+                 && INSN_LOCATOR (BB_END (e->src)) == 0)
+               {
+                 INSN_LOCATOR (BB_END (e->src)) = e->goto_locus;
+                 continue;
+               }
+             if (e->dest != EXIT_BLOCK_PTR)
+               {
+                 insn = BB_HEAD (e->dest);
+                 end = NEXT_INSN (BB_END (e->dest));
+                 while (insn != end && !INSN_P (insn))
+                   insn = NEXT_INSN (insn);
+                 if (insn != end && INSN_LOCATOR (insn)
+                     && locator_eq (INSN_LOCATOR (insn), (int) e->goto_locus))
+                   continue;
+               }
+             nb = split_edge (e);
+             if (!INSN_P (BB_END (nb)))
+               BB_END (nb) = emit_insn_after_noloc (gen_nop (), BB_END (nb),
+                                                    nb);
+             INSN_LOCATOR (BB_END (nb)) = e->goto_locus;
+           }
+      }
 }
 \f
 /* Perform sanity checks on the insn chain.
@@ -898,16 +1046,66 @@ fixup_fallthru_exit_predecessor (void)
        }
 
       while (c->aux != bb)
-       c = c->aux;
+       c = (basic_block) c->aux;
 
       c->aux = bb->aux;
       while (c->aux)
-       c = c->aux;
+       c = (basic_block) c->aux;
 
       c->aux = bb;
       bb->aux = NULL;
     }
 }
+
+/* In case there are more than one fallthru predecessors of exit, force that
+   there is only one.  */
+
+static void
+force_one_exit_fallthru (void)
+{
+  edge e, predecessor = NULL;
+  bool more = false;
+  edge_iterator ei;
+  basic_block forwarder, bb;
+
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+    if (e->flags & EDGE_FALLTHRU)
+      {
+       if (predecessor == NULL)
+         predecessor = e;
+       else
+         {
+           more = true;
+           break;
+         }
+      }
+
+  if (!more)
+    return;
+
+  /* Exit has several fallthru predecessors.  Create a forwarder block for
+     them.  */
+  forwarder = split_edge (predecessor);
+  for (ei = ei_start (EXIT_BLOCK_PTR->preds); (e = ei_safe_edge (ei)); )
+    {
+      if (e->src == forwarder
+         || !(e->flags & EDGE_FALLTHRU))
+       ei_next (&ei);
+      else
+       redirect_edge_and_branch_force (e, forwarder);
+    }
+
+  /* Fix up the chain of blocks -- make FORWARDER immediately precede the
+     exit block.  */
+  FOR_EACH_BB (bb)
+    {
+      if (bb->aux == NULL && bb != forwarder)
+       {
+         bb->aux = forwarder;
+         break;
+       }
+    }
+}
 \f
 /* Return true in case it is possible to duplicate the basic block BB.  */
 
@@ -915,10 +1113,10 @@ fixup_fallthru_exit_predecessor (void)
    only be used through the cfghooks interface, and we do not want to move
    it to cfgrtl.c since it would require also moving quite a lot of related
    code.  */
-extern bool cfg_layout_can_duplicate_bb_p (basic_block);
+extern bool cfg_layout_can_duplicate_bb_p (const_basic_block);
 
 bool
-cfg_layout_can_duplicate_bb_p (basic_block bb)
+cfg_layout_can_duplicate_bb_p (const_basic_block bb)
 {
   /* Do not attempt to duplicate tablejumps, as we need to unshare
      the dispatch table.  This is difficult to do, as the instructions
@@ -946,7 +1144,7 @@ cfg_layout_can_duplicate_bb_p (basic_block bb)
 rtx
 duplicate_insn_chain (rtx from, rtx to)
 {
-  rtx insn, last;
+  rtx insn, last, copy;
 
   /* Avoid updating of boundaries of previous basic block.  The
      note will get removed from insn stream in fixup.  */
@@ -958,6 +1156,7 @@ duplicate_insn_chain (rtx from, rtx to)
     {
       switch (GET_CODE (insn))
        {
+       case DEBUG_INSN:
        case INSN:
        case CALL_INSN:
        case JUMP_INSN:
@@ -967,7 +1166,8 @@ duplicate_insn_chain (rtx from, rtx to)
          if (GET_CODE (PATTERN (insn)) == ADDR_VEC
              || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
            break;
-         emit_copy_of_insn_after (insn, get_last_insn ());
+         copy = emit_copy_of_insn_after (insn, get_last_insn ());
+          maybe_copy_epilogue_insn (insn, copy);
          break;
 
        case CODE_LABEL:
@@ -978,7 +1178,7 @@ duplicate_insn_chain (rtx from, rtx to)
          break;
 
        case NOTE:
-         switch (NOTE_LINE_NUMBER (insn))
+         switch (NOTE_KIND (insn))
            {
              /* In case prologue is empty and function contain label
                 in first BB, we may want to copy the block.  */
@@ -987,28 +1187,19 @@ duplicate_insn_chain (rtx from, rtx to)
            case NOTE_INSN_DELETED:
            case NOTE_INSN_DELETED_LABEL:
              /* No problem to strip these.  */
-           case NOTE_INSN_EPILOGUE_BEG:
-             /* Debug code expect these notes to exist just once.
-                Keep them in the master copy.
-                ??? It probably makes more sense to duplicate them for each
-                epilogue copy.  */
            case NOTE_INSN_FUNCTION_BEG:
              /* There is always just single entry to function.  */
            case NOTE_INSN_BASIC_BLOCK:
              break;
 
+           case NOTE_INSN_EPILOGUE_BEG:
            case NOTE_INSN_SWITCH_TEXT_SECTIONS:
              emit_note_copy (insn);
              break;
 
            default:
-             /* All other notes should have already been eliminated.
-              */
-             gcc_assert (NOTE_LINE_NUMBER (insn) >= 0);
-
-             /* It is possible that no_line_number is set and the note
-                won't be emitted.  */
-             emit_note_copy (insn);
+             /* All other notes should have already been eliminated.  */
+             gcc_unreachable ();
            }
          break;
        default:
@@ -1059,35 +1250,34 @@ cfg_layout_duplicate_bb (basic_block bb)
        new_bb->il.rtl->footer = unlink_insn_chain (insn, get_last_insn ());
     }
 
-  if (bb->il.rtl->global_live_at_start)
-    {
-      new_bb->il.rtl->global_live_at_start = ALLOC_REG_SET (&reg_obstack);
-      new_bb->il.rtl->global_live_at_end = ALLOC_REG_SET (&reg_obstack);
-      COPY_REG_SET (new_bb->il.rtl->global_live_at_start,
-                   bb->il.rtl->global_live_at_start);
-      COPY_REG_SET (new_bb->il.rtl->global_live_at_end,
-                   bb->il.rtl->global_live_at_end);
-    }
-
   return new_bb;
 }
+
 \f
 /* Main entry point to this module - initialize the datastructures for
    CFG layout changes.  It keeps LOOPS up-to-date if not null.
 
-   FLAGS is a set of additional flags to pass to cleanup_cfg().  It should
-   include CLEANUP_UPDATE_LIFE if liveness information must be kept up
-   to date.  */
+   FLAGS is a set of additional flags to pass to cleanup_cfg().  */
 
 void
 cfg_layout_initialize (unsigned int flags)
 {
+  rtx x;
+  basic_block bb;
+
   initialize_original_copy_tables ();
 
   cfg_layout_rtl_register_cfg_hooks ();
 
   record_effective_endpoints ();
 
+  /* Make sure that the targets of non local gotos are marked.  */
+  for (x = nonlocal_goto_handler_labels; x; x = XEXP (x, 1))
+    {
+      bb = BLOCK_FOR_INSN (XEXP (x, 0));
+      bb->flags |= BB_NON_LOCAL_GOTO_TARGET;
+    }
+
   cleanup_cfg (CLEANUP_CFGLAYOUT | flags);
 }
 
@@ -1125,11 +1315,10 @@ break_superblocks (void)
 void
 cfg_layout_finalize (void)
 {
-  basic_block bb;
-
 #ifdef ENABLE_CHECKING
   verify_flow_info ();
 #endif
+  force_one_exit_fallthru ();
   rtl_register_cfg_hooks ();
   if (reload_completed
 #ifdef HAVE_epilogue
@@ -1139,21 +1328,13 @@ cfg_layout_finalize (void)
     fixup_fallthru_exit_predecessor ();
   fixup_reorder_chain ();
 
-#ifdef ENABLE_CHECKING
-  verify_insn_chain ();
-#endif
-  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
-  {
-    bb->il.rtl->header = bb->il.rtl->footer = NULL;
-    bb->aux = NULL;
-    bb->il.rtl->visited = 0;
-  }
+  rebuild_jump_labels (get_insns ());
+  delete_dead_jumptables ();
 
 #ifdef ENABLE_CHECKING
+  verify_insn_chain ();
   verify_flow_info ();
 #endif
-
-  free_original_copy_tables ();
 }
 
 /* Checks whether all N blocks in BBS array can be copied.  */