OSDN Git Service

2007-10-15 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-cfg.c
index 342b9d2..05c69b8 100644 (file)
@@ -1,12 +1,13 @@
 /* Control flow functions for trees.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 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)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GCC is distributed in the hope that it will be useful,
@@ -15,9 +16,8 @@ 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, 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"
@@ -43,8 +43,10 @@ Boston, MA 02110-1301, USA.  */
 #include "except.h"
 #include "cfgloop.h"
 #include "cfglayout.h"
-#include "hashtab.h"
 #include "tree-ssa-propagate.h"
+#include "value-prof.h"
+#include "pointer-set.h"
+#include "tree-inline.h"
 
 /* This file contains functions for building the Control Flow Graph (CFG)
    for a function tree.  */
@@ -67,19 +69,7 @@ static const int initial_cfg_capacity = 20;
    more persistent.  The key is getting notification of changes to
    the CFG (particularly edge removal, creation and redirection).  */
 
-struct edge_to_cases_elt
-{
-  /* The edge itself.  Necessary for hashing and equality tests.  */
-  edge e;
-
-  /* The case labels associated with this edge.  We link these up via
-     their TREE_CHAIN field, then we wipe out the TREE_CHAIN fields
-     when we destroy the hash table.  This prevents problems when copying
-     SWITCH_EXPRs.  */
-  tree case_labels;
-};
-
-static htab_t edge_to_cases;
+static struct pointer_map_t *edge_to_cases;
 
 /* CFG statistics.  */
 struct cfg_stats_d
@@ -99,20 +89,19 @@ static void factor_computed_gotos (void);
 
 /* Edges.  */
 static void make_edges (void);
-static void make_ctrl_stmt_edges (basic_block);
-static void make_exit_edges (basic_block);
 static void make_cond_expr_edges (basic_block);
 static void make_switch_expr_edges (basic_block);
 static void make_goto_expr_edges (basic_block);
 static edge tree_redirect_edge_and_branch (edge, basic_block);
 static edge tree_try_redirect_by_replacing_jump (edge, basic_block);
-static void split_critical_edges (void);
+static unsigned int split_critical_edges (void);
 
 /* Various helpers.  */
-static inline bool stmt_starts_bb_p (tree, tree);
+static inline bool stmt_starts_bb_p (const_tree, const_tree);
 static int tree_verify_flow_info (void);
 static void tree_make_forwarder_block (edge);
 static void tree_cfg2vcg (FILE *);
+static inline void change_bb_for_stmt (tree t, basic_block bb);
 
 /* Flowgraph optimization and cleanup.  */
 static void tree_merge_blocks (basic_block, basic_block);
@@ -131,16 +120,17 @@ init_empty_tree_cfg (void)
   profile_status = PROFILE_ABSENT;
   n_basic_blocks = NUM_FIXED_BLOCKS;
   last_basic_block = NUM_FIXED_BLOCKS;
-  VARRAY_BB_INIT (basic_block_info, initial_cfg_capacity, "basic_block_info");
+  basic_block_info = VEC_alloc (basic_block, gc, initial_cfg_capacity);
+  VEC_safe_grow_cleared (basic_block, gc, basic_block_info,
+                        initial_cfg_capacity);
 
   /* Build a mapping of labels to their associated blocks.  */
   label_to_block_map = VEC_alloc (basic_block, gc, initial_cfg_capacity);
-  VEC_safe_grow (basic_block, gc, label_to_block_map, initial_cfg_capacity);
-  memset (VEC_address (basic_block, label_to_block_map),
-         0, sizeof (basic_block) * initial_cfg_capacity);
+  VEC_safe_grow_cleared (basic_block, gc, label_to_block_map,
+                        initial_cfg_capacity);
 
-  BASIC_BLOCK (ENTRY_BLOCK) = ENTRY_BLOCK_PTR;
-  BASIC_BLOCK (EXIT_BLOCK) = EXIT_BLOCK_PTR;
+  SET_BASIC_BLOCK (ENTRY_BLOCK, ENTRY_BLOCK_PTR);
+  SET_BASIC_BLOCK (EXIT_BLOCK, EXIT_BLOCK_PTR);
   ENTRY_BLOCK_PTR->next_bb = EXIT_BLOCK_PTR;
   EXIT_BLOCK_PTR->prev_bb = ENTRY_BLOCK_PTR;
 }
@@ -178,7 +168,8 @@ build_tree_cfg (tree *tp)
     create_empty_bb (ENTRY_BLOCK_PTR);
 
   /* Adjust the size of the array.  */
-  VARRAY_GROW (basic_block_info, n_basic_blocks);
+  if (VEC_length (basic_block, basic_block_info) < (size_t) n_basic_blocks)
+    VEC_safe_grow_cleared (basic_block, gc, basic_block_info, n_basic_blocks);
 
   /* To speed up statement iterator walks, we first purge dead labels.  */
   cleanup_dead_labels ();
@@ -190,17 +181,18 @@ build_tree_cfg (tree *tp)
 
   /* Create the edges of the flowgraph.  */
   make_edges ();
+  cleanup_dead_labels ();
 
   /* Debugging dumps.  */
 
   /* Write the flowgraph to a VCG file.  */
   {
     int local_dump_flags;
-    FILE *dump_file = dump_begin (TDI_vcg, &local_dump_flags);
-    if (dump_file)
+    FILE *vcg_file = dump_begin (TDI_vcg, &local_dump_flags);
+    if (vcg_file)
       {
-       tree_cfg2vcg (dump_file);
-       dump_end (TDI_vcg, dump_file);
+       tree_cfg2vcg (vcg_file);
+       dump_end (TDI_vcg, vcg_file);
       }
   }
 
@@ -213,10 +205,11 @@ build_tree_cfg (tree *tp)
     dump_tree_cfg (dump_file, dump_flags);
 }
 
-static void
+static unsigned int
 execute_build_cfg (void)
 {
   build_tree_cfg (&DECL_SAVED_TREE (current_function_decl));
+  return 0;
 }
 
 struct tree_opt_pass pass_build_cfg =
@@ -232,13 +225,13 @@ struct tree_opt_pass pass_build_cfg =
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_verify_stmts,                   /* todo_flags_finish */
+  TODO_verify_stmts | TODO_cleanup_cfg,        /* todo_flags_finish */
   0                                    /* letter */
 };
 
-/* Search the CFG for any computed gotos.  If found, factor them to a 
+/* Search the CFG for any computed gotos.  If found, factor them to a
    common computed goto site.  Also record the location of that site so
-   that we can un-factor the gotos after we have converted back to 
+   that we can un-factor the gotos after we have converted back to
    normal form.  */
 
 static void
@@ -253,7 +246,7 @@ factor_computed_gotos (void)
   /* We know there are one or more computed gotos in this function.
      Examine the last statement in each basic block to see if the block
      ends with a computed goto.  */
-       
+
   FOR_EACH_BB (bb)
     {
       block_stmt_iterator bsi = bsi_last (bb);
@@ -302,8 +295,8 @@ factor_computed_gotos (void)
            }
 
          /* Copy the original computed goto's destination into VAR.  */
-         assignment = build2 (MODIFY_EXPR, ptr_type_node,
-                              var, GOTO_DESTINATION (last));
+         assignment = build_gimple_modify_stmt (var,
+                                                GOTO_DESTINATION (last));
          bsi_insert_before (&bsi, assignment, BSI_SAME_STMT);
 
          /* And re-vector the computed goto to the new destination.  */
@@ -376,20 +369,21 @@ create_bb (void *h, void *e, basic_block after)
 
   bb->index = last_basic_block;
   bb->flags = BB_NEW;
-  bb->stmt_list = h ? (tree) h : alloc_stmt_list ();
+  bb->il.tree = GGC_CNEW (struct tree_bb_info);
+  set_bb_stmt_list (bb, h ? (tree) h : alloc_stmt_list ());
 
   /* Add the new block to the linked list of blocks.  */
   link_block (bb, after);
 
   /* Grow the basic block array if needed.  */
-  if ((size_t) last_basic_block == VARRAY_SIZE (basic_block_info))
+  if ((size_t) last_basic_block == VEC_length (basic_block, basic_block_info))
     {
       size_t new_size = last_basic_block + (last_basic_block + 3) / 4;
-      VARRAY_GROW (basic_block_info, new_size);
+      VEC_safe_grow_cleared (basic_block, gc, basic_block_info, new_size);
     }
 
   /* Add the newly created block to the array.  */
-  BASIC_BLOCK (last_basic_block) = bb;
+  SET_BASIC_BLOCK (last_basic_block, bb);
 
   n_basic_blocks++;
   last_basic_block++;
@@ -416,10 +410,19 @@ fold_cond_expr_cond (void)
       if (stmt
          && TREE_CODE (stmt) == COND_EXPR)
        {
-         tree cond = fold (COND_EXPR_COND (stmt));
-         if (integer_zerop (cond))
+         tree cond;
+         bool zerop, onep;
+
+         fold_defer_overflow_warnings ();
+         cond = fold (COND_EXPR_COND (stmt));
+         zerop = integer_zerop (cond);
+         onep = integer_onep (cond);
+         fold_undefer_overflow_warnings (zerop || onep,
+                                         stmt,
+                                         WARN_STRICT_OVERFLOW_CONDITIONAL);
+         if (zerop)
            COND_EXPR_COND (stmt) = boolean_false_node;
-         else if (integer_onep (cond))
+         else if (onep)
            COND_EXPR_COND (stmt) = boolean_true_node;
        }
     }
@@ -431,6 +434,7 @@ static void
 make_edges (void)
 {
   basic_block bb;
+  struct omp_region *cur_region = NULL;
 
   /* Create an edge from entry to the first block with executable
      statements in it.  */
@@ -439,136 +443,155 @@ make_edges (void)
   /* Traverse the basic block array placing edges.  */
   FOR_EACH_BB (bb)
     {
-      tree first = first_stmt (bb);
       tree last = last_stmt (bb);
+      bool fallthru;
 
-      if (first)
+      if (last)
        {
-         /* Edges for statements that always alter flow control.  */
-         if (is_ctrl_stmt (last))
-           make_ctrl_stmt_edges (bb);
-
-         /* Edges for statements that sometimes alter flow control.  */
-         if (is_ctrl_altering_stmt (last))
-           make_exit_edges (bb);
-       }
-
-      /* Finally, if no edges were created above, this is a regular
-        basic block that only needs a fallthru edge.  */
-      if (EDGE_COUNT (bb->succs) == 0)
-       make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
-    }
-
-  /* We do not care about fake edges, so remove any that the CFG
-     builder inserted for completeness.  */
-  remove_fake_exit_edges ();
-
-  /* Fold COND_EXPR_COND of each COND_EXPR.  */
-  fold_cond_expr_cond ();
-
-  /* Clean up the graph and warn for unreachable code.  */
-  cleanup_tree_cfg ();
-}
+         enum tree_code code = TREE_CODE (last);
+         switch (code)
+           {
+           case GOTO_EXPR:
+             make_goto_expr_edges (bb);
+             fallthru = false;
+             break;
+           case RETURN_EXPR:
+             make_edge (bb, EXIT_BLOCK_PTR, 0);
+             fallthru = false;
+             break;
+           case COND_EXPR:
+             make_cond_expr_edges (bb);
+             fallthru = false;
+             break;
+           case SWITCH_EXPR:
+             make_switch_expr_edges (bb);
+             fallthru = false;
+             break;
+           case RESX_EXPR:
+             make_eh_edges (last);
+             fallthru = false;
+             break;
 
+           case CALL_EXPR:
+             /* If this function receives a nonlocal goto, then we need to
+                make edges from this call site to all the nonlocal goto
+                handlers.  */
+             if (tree_can_make_abnormal_goto (last))
+               make_abnormal_goto_edges (bb, true);
 
-/* Create edges for control statement at basic block BB.  */
+             /* If this statement has reachable exception handlers, then
+                create abnormal edges to them.  */
+             make_eh_edges (last);
 
-static void
-make_ctrl_stmt_edges (basic_block bb)
-{
-  tree last = last_stmt (bb);
+             /* Some calls are known not to return.  */
+             fallthru = !(call_expr_flags (last) & ECF_NORETURN);
+             break;
 
-  gcc_assert (last);
-  switch (TREE_CODE (last))
-    {
-    case GOTO_EXPR:
-      make_goto_expr_edges (bb);
-      break;
+           case MODIFY_EXPR:
+             gcc_unreachable ();
 
-    case RETURN_EXPR:
-      make_edge (bb, EXIT_BLOCK_PTR, 0);
-      break;
+           case GIMPLE_MODIFY_STMT:
+             if (is_ctrl_altering_stmt (last))
+               {
+                 /* A GIMPLE_MODIFY_STMT may have a CALL_EXPR on its RHS and
+                    the CALL_EXPR may have an abnormal edge.  Search the RHS
+                    for this case and create any required edges.  */
+                 if (tree_can_make_abnormal_goto (last))
+                   make_abnormal_goto_edges (bb, true);  
 
-    case COND_EXPR:
-      make_cond_expr_edges (bb);
-      break;
+                 make_eh_edges (last);
+               }
+             fallthru = true;
+             break;
 
-    case SWITCH_EXPR:
-      make_switch_expr_edges (bb);
-      break;
+           case OMP_PARALLEL:
+           case OMP_FOR:
+           case OMP_SINGLE:
+           case OMP_MASTER:
+           case OMP_ORDERED:
+           case OMP_CRITICAL:
+           case OMP_SECTION:
+             cur_region = new_omp_region (bb, code, cur_region);
+             fallthru = true;
+             break;
 
-    case RESX_EXPR:
-      make_eh_edges (last);
-      /* Yet another NORETURN hack.  */
-      if (EDGE_COUNT (bb->succs) == 0)
-       make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
-      break;
+           case OMP_SECTIONS:
+             cur_region = new_omp_region (bb, code, cur_region);
+             fallthru = true;
+             break;
 
-    default:
-      gcc_unreachable ();
-    }
-}
+           case OMP_SECTIONS_SWITCH:
+             fallthru = false;
+             break;
 
+           case OMP_RETURN:
+             /* In the case of an OMP_SECTION, the edge will go somewhere
+                other than the next block.  This will be created later.  */
+             cur_region->exit = bb;
+             fallthru = cur_region->type != OMP_SECTION;
+             cur_region = cur_region->outer;
+             break;
 
-/* Create exit edges for statements in block BB that alter the flow of
-   control.  Statements that alter the control flow are 'goto', 'return'
-   and calls to non-returning functions.  */
+           case OMP_CONTINUE:
+             cur_region->cont = bb;
+             switch (cur_region->type)
+               {
+               case OMP_FOR:
+                 /* Make the loopback edge.  */
+                 make_edge (bb, single_succ (cur_region->entry), 0);
+             
+                 /* Create an edge from OMP_FOR to exit, which corresponds to
+                    the case that the body of the loop is not executed at
+                    all.  */
+                 make_edge (cur_region->entry, bb->next_bb, 0);
+                 fallthru = true;
+                 break;
+
+               case OMP_SECTIONS:
+                 /* Wire up the edges into and out of the nested sections.  */
+                 {
+                   basic_block switch_bb = single_succ (cur_region->entry);
+
+                   struct omp_region *i;
+                   for (i = cur_region->inner; i ; i = i->next)
+                     {
+                       gcc_assert (i->type == OMP_SECTION);
+                       make_edge (switch_bb, i->entry, 0);
+                       make_edge (i->exit, bb, EDGE_FALLTHRU);
+                     }
+
+                   /* Make the loopback edge to the block with
+                      OMP_SECTIONS_SWITCH.  */
+                   make_edge (bb, switch_bb, 0);
+
+                   /* Make the edge from the switch to exit.  */
+                   make_edge (switch_bb, bb->next_bb, 0);
+                   fallthru = false;
+                 }
+                 break;
 
-static void
-make_exit_edges (basic_block bb)
-{
-  tree last = last_stmt (bb), op;
+               default:
+                 gcc_unreachable ();
+               }
+             break;
 
-  gcc_assert (last);
-  switch (TREE_CODE (last))
-    {
-    case RESX_EXPR:
-      break;
-    case CALL_EXPR:
-      /* If this function receives a nonlocal goto, then we need to
-        make edges from this call site to all the nonlocal goto
-        handlers.  */
-      if (TREE_SIDE_EFFECTS (last)
-         && current_function_has_nonlocal_label)
-       make_goto_expr_edges (bb);
-
-      /* If this statement has reachable exception handlers, then
-        create abnormal edges to them.  */
-      make_eh_edges (last);
-
-      /* Some calls are known not to return.  For such calls we create
-        a fake edge.
-
-        We really need to revamp how we build edges so that it's not
-        such a bloody pain to avoid creating edges for this case since
-        all we do is remove these edges when we're done building the
-        CFG.  */
-      if (call_expr_flags (last) & ECF_NORETURN)
-       {
-         make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
-         return;
+           default:
+             gcc_assert (!stmt_ends_bb_p (last));
+             fallthru = true;
+           }
        }
+      else
+       fallthru = true;
 
-      /* Don't forget the fall-thru edge.  */
-      make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
-      break;
+      if (fallthru)
+       make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+    }
 
-    case MODIFY_EXPR:
-      /* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR
-        may have an abnormal edge.  Search the RHS for this case and
-        create any required edges.  */
-      op = get_call_expr_in (last);
-      if (op && TREE_SIDE_EFFECTS (op)
-         && current_function_has_nonlocal_label)
-       make_goto_expr_edges (bb);
-
-      make_eh_edges (last);
-      make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
-      break;
+  if (root_omp_region)
+    free_omp_regions ();
 
-    default:
-      gcc_unreachable ();
-    }
+  /* Fold COND_EXPR_COND of each COND_EXPR.  */
+  fold_cond_expr_cond ();
 }
 
 
@@ -607,50 +630,34 @@ make_cond_expr_edges (basic_block bb)
       e->goto_locus = EXPR_LOCUS (COND_EXPR_ELSE (entry));
 #endif
     }
-}
 
-/* Hashing routine for EDGE_TO_CASES.  */
-
-static hashval_t
-edge_to_cases_hash (const void *p)
-{
-  edge e = ((struct edge_to_cases_elt *)p)->e;
-
-  /* Hash on the edge itself (which is a pointer).  */
-  return htab_hash_pointer (e);
+  /* We do not need the gotos anymore.  */
+  COND_EXPR_THEN (entry) = NULL_TREE;
+  COND_EXPR_ELSE (entry) = NULL_TREE;
 }
 
-/* Equality routine for EDGE_TO_CASES, edges are unique, so testing
-   for equality is just a pointer comparison.  */
-
-static int
-edge_to_cases_eq (const void *p1, const void *p2)
-{
-  edge e1 = ((struct edge_to_cases_elt *)p1)->e;
-  edge e2 = ((struct edge_to_cases_elt *)p2)->e;
-
-  return e1 == e2;
-}
 
 /* Called for each element in the hash table (P) as we delete the
    edge to cases hash table.
 
-   Clear all the TREE_CHAINs to prevent problems with copying of 
+   Clear all the TREE_CHAINs to prevent problems with copying of
    SWITCH_EXPRs and structure sharing rules, then free the hash table
    element.  */
 
-static void
-edge_to_cases_cleanup (void *p)
+static bool
+edge_to_cases_cleanup (const void *key ATTRIBUTE_UNUSED, void **value,
+                      void *data ATTRIBUTE_UNUSED)
 {
-  struct edge_to_cases_elt *elt = (struct edge_to_cases_elt *) p;
   tree t, next;
 
-  for (t = elt->case_labels; t; t = next)
+  for (t = (tree) *value; t; t = next)
     {
       next = TREE_CHAIN (t);
       TREE_CHAIN (t) = NULL;
     }
-  free (p);
+
+  *value = NULL;
+  return false;
 }
 
 /* Start recording information mapping edges to case labels.  */
@@ -659,11 +666,7 @@ void
 start_recording_case_labels (void)
 {
   gcc_assert (edge_to_cases == NULL);
-
-  edge_to_cases = htab_create (37,
-                              edge_to_cases_hash,
-                              edge_to_cases_eq,
-                              edge_to_cases_cleanup);
+  edge_to_cases = pointer_map_create ();
 }
 
 /* Return nonzero if we are recording information for case labels.  */
@@ -679,46 +682,11 @@ recording_case_labels_p (void)
 void
 end_recording_case_labels (void)
 {
-  htab_delete (edge_to_cases);
+  pointer_map_traverse (edge_to_cases, edge_to_cases_cleanup, NULL);
+  pointer_map_destroy (edge_to_cases);
   edge_to_cases = NULL;
 }
 
-/* Record that CASE_LABEL (a CASE_LABEL_EXPR) references edge E.  */
-
-static void
-record_switch_edge (edge e, tree case_label)
-{
-  struct edge_to_cases_elt *elt;
-  void **slot;
-
-  /* Build a hash table element so we can see if E is already
-     in the table.  */
-  elt = XNEW (struct edge_to_cases_elt);
-  elt->e = e;
-  elt->case_labels = case_label;
-
-  slot = htab_find_slot (edge_to_cases, elt, INSERT);
-
-  if (*slot == NULL)
-    {
-      /* E was not in the hash table.  Install E into the hash table.  */
-      *slot = (void *)elt;
-    }
-  else
-    {
-      /* E was already in the hash table.  Free ELT as we do not need it
-        anymore.  */
-      free (elt);
-
-      /* Get the entry stored in the hash table.  */
-      elt = (struct edge_to_cases_elt *) *slot;
-
-      /* Add it to the chain of CASE_LABEL_EXPRs referencing E.  */
-      TREE_CHAIN (case_label) = elt->case_labels;
-      elt->case_labels = case_label;
-    }
-}
-
 /* If we are inside a {start,end}_recording_cases block, then return
    a chain of CASE_LABEL_EXPRs from T which reference E.
 
@@ -727,7 +695,6 @@ record_switch_edge (edge e, tree case_label)
 static tree
 get_cases_for_edge (edge e, tree t)
 {
-  struct edge_to_cases_elt elt, *elt_p;
   void **slot;
   size_t i, n;
   tree vec;
@@ -736,17 +703,10 @@ get_cases_for_edge (edge e, tree t)
      chains available.  Return NULL so the caller can detect this case.  */
   if (!recording_case_labels_p ())
     return NULL;
-  
-restart:
-  elt.e = e;
-  elt.case_labels = NULL;
-  slot = htab_find_slot (edge_to_cases, &elt, NO_INSERT);
 
+  slot = pointer_map_contains (edge_to_cases, e);
   if (slot)
-    {
-      elt_p = (struct edge_to_cases_elt *)*slot;
-      return elt_p->case_labels;
-    }
+    return (tree) *slot;
 
   /* If we did not find E in the hash table, then this must be the first
      time we have been queried for information about E & T.  Add all the
@@ -756,11 +716,19 @@ restart:
   n = TREE_VEC_LENGTH (vec);
   for (i = 0; i < n; i++)
     {
-      tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
+      tree elt = TREE_VEC_ELT (vec, i);
+      tree lab = CASE_LABEL (elt);
       basic_block label_bb = label_to_block (lab);
-      record_switch_edge (find_edge (e->src, label_bb), TREE_VEC_ELT (vec, i));
+      edge this_edge = find_edge (e->src, label_bb);
+
+      /* Add it to the chain of CASE_LABEL_EXPRs referencing E, or create
+        a new chain.  */
+      slot = pointer_map_insert (edge_to_cases, this_edge);
+      TREE_CHAIN (elt) = (tree) *slot;
+      *slot = elt;
     }
-  goto restart;
+
+  return (tree) *pointer_map_contains (edge_to_cases, e);
 }
 
 /* Create the edges for a SWITCH_EXPR starting at block BB.
@@ -798,7 +766,7 @@ label_to_block_fn (struct function *ifun, tree dest)
      and undefined variable warnings quite right.  */
   if ((errorcount || sorrycount) && uid < 0)
     {
-      block_stmt_iterator bsi = 
+      block_stmt_iterator bsi =
        bsi_start (BASIC_BLOCK (NUM_FIXED_BLOCKS));
       tree stmt;
 
@@ -812,80 +780,60 @@ label_to_block_fn (struct function *ifun, tree dest)
   return VEC_index (basic_block, ifun->cfg->x_label_to_block_map, uid);
 }
 
+/* Create edges for an abnormal goto statement at block BB.  If FOR_CALL
+   is true, the source statement is a CALL_EXPR instead of a GOTO_EXPR.  */
+
+void
+make_abnormal_goto_edges (basic_block bb, bool for_call)
+{
+  basic_block target_bb;
+  block_stmt_iterator bsi;
+
+  FOR_EACH_BB (target_bb)
+    for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      {
+       tree target = bsi_stmt (bsi);
+
+       if (TREE_CODE (target) != LABEL_EXPR)
+         break;
+
+       target = LABEL_EXPR_LABEL (target);
+
+       /* Make an edge to every label block that has been marked as a
+          potential target for a computed goto or a non-local goto.  */
+       if ((FORCED_LABEL (target) && !for_call)
+           || (DECL_NONLOCAL (target) && for_call))
+         {
+           make_edge (bb, target_bb, EDGE_ABNORMAL);
+           break;
+         }
+      }
+}
+
 /* Create edges for a goto statement at block BB.  */
 
 static void
 make_goto_expr_edges (basic_block bb)
 {
-  tree goto_t;
-  basic_block target_bb;
-  int for_call;
   block_stmt_iterator last = bsi_last (bb);
+  tree goto_t = bsi_stmt (last);
 
-  goto_t = bsi_stmt (last);
-
-  /* If the last statement is not a GOTO (i.e., it is a RETURN_EXPR,
-     CALL_EXPR or MODIFY_EXPR), then the edge is an abnormal edge resulting
-     from a nonlocal goto.  */
-  if (TREE_CODE (goto_t) != GOTO_EXPR)
-    for_call = 1;
-  else
+  /* A simple GOTO creates normal edges.  */
+  if (simple_goto_p (goto_t))
     {
       tree dest = GOTO_DESTINATION (goto_t);
-      for_call = 0;
-
-      /* A GOTO to a local label creates normal edges.  */
-      if (simple_goto_p (goto_t))
-       {
-         edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
+      edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
 #ifdef USE_MAPPED_LOCATION
-         e->goto_locus = EXPR_LOCATION (goto_t);
+      e->goto_locus = EXPR_LOCATION (goto_t);
 #else
-         e->goto_locus = EXPR_LOCUS (goto_t);
+      e->goto_locus = EXPR_LOCUS (goto_t);
 #endif
-         bsi_remove (&last, true);
-         return;
-       }
-
-      /* Nothing more to do for nonlocal gotos.  */
-      if (TREE_CODE (dest) == LABEL_DECL)
-       return;
-
-      /* Computed gotos remain.  */
-    }
-
-  /* Look for the block starting with the destination label.  In the
-     case of a computed goto, make an edge to any label block we find
-     in the CFG.  */
-  FOR_EACH_BB (target_bb)
-    {
-      block_stmt_iterator bsi;
-
-      for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi))
-       {
-         tree target = bsi_stmt (bsi);
-
-         if (TREE_CODE (target) != LABEL_EXPR)
-           break;
-
-         if (
-             /* Computed GOTOs.  Make an edge to every label block that has
-                been marked as a potential target for a computed goto.  */
-             (FORCED_LABEL (LABEL_EXPR_LABEL (target)) && for_call == 0)
-             /* Nonlocal GOTO target.  Make an edge to every label block
-                that has been marked as a potential target for a nonlocal
-                goto.  */
-             || (DECL_NONLOCAL (LABEL_EXPR_LABEL (target)) && for_call == 1))
-           {
-             make_edge (bb, target_bb, EDGE_ABNORMAL);
-             break;
-           }
-       }
+      bsi_remove (&last, true);
+      return;
     }
 
-  /* Degenerate case of computed goto with no labels.  */
-  if (!for_call && EDGE_COUNT (bb->succs) == 0)
-    make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
+  /* A computed GOTO creates abnormal edges.  */
+  make_abnormal_goto_edges (bb, false);
 }
 
 
@@ -897,11 +845,19 @@ make_goto_expr_edges (basic_block bb)
    to do early because it allows us to group case labels before creating
    the edges for the CFG, and it speeds up block statement iterators in
    all passes later on.
-   We only run this pass once, running it more than once is probably not
-   profitable.  */
+   We rerun this pass after CFG is created, to get rid of the labels that
+   are no longer referenced.  After then we do not run it any more, since
+   (almost) no new labels should be created.  */
 
 /* A map from basic block index to the leading label of that block.  */
-static tree *label_for_bb;
+static struct label_record
+{
+  /* The label.  */
+  tree label;
+
+  /* True if the label is referenced from somewhere.  */
+  bool used;
+} *label_for_bb;
 
 /* Callback for for_each_eh_region.  Helper for cleanup_dead_labels.  */
 static void
@@ -919,7 +875,8 @@ update_eh_label (struct eh_region *region)
       if (! bb)
        return;
 
-      new_label = label_for_bb[bb->index];
+      new_label = label_for_bb[bb->index].label;
+      label_for_bb[bb->index].used = true;
       set_eh_region_tree_label (region, new_label);
     }
 }
@@ -929,11 +886,17 @@ static tree
 main_block_label (tree label)
 {
   basic_block bb = label_to_block (label);
+  tree main_label = label_for_bb[bb->index].label;
 
   /* label_to_block possibly inserted undefined label into the chain.  */
-  if (!label_for_bb[bb->index])
-    label_for_bb[bb->index] = label;
-  return label_for_bb[bb->index];
+  if (!main_label)
+    {
+      label_for_bb[bb->index].label = label;
+      main_label = label;
+    }
+
+  label_for_bb[bb->index].used = true;
+  return main_label;
 }
 
 /* Cleanup redundant labels.  This is a three-step process:
@@ -945,7 +908,7 @@ void
 cleanup_dead_labels (void)
 {
   basic_block bb;
-  label_for_bb = XCNEWVEC (tree, last_basic_block);
+  label_for_bb = XCNEWVEC (struct label_record, last_basic_block);
 
   /* Find a suitable label for each block.  We use the first user-defined
      label if there is one, or otherwise just the first label we see.  */
@@ -964,19 +927,19 @@ cleanup_dead_labels (void)
 
          /* If we have not yet seen a label for the current block,
             remember this one and see if there are more labels.  */
-         if (! label_for_bb[bb->index])
+         if (!label_for_bb[bb->index].label)
            {
-             label_for_bb[bb->index] = label;
+             label_for_bb[bb->index].label = label;
              continue;
            }
 
          /* If we did see a label for the current block already, but it
             is an artificially created label, replace it if the current
             label is a user defined label.  */
-         if (! DECL_ARTIFICIAL (label)
-             && DECL_ARTIFICIAL (label_for_bb[bb->index]))
+         if (!DECL_ARTIFICIAL (label)
+             && DECL_ARTIFICIAL (label_for_bb[bb->index].label))
            {
-             label_for_bb[bb->index] = label;
+             label_for_bb[bb->index].label = label;
              break;
            }
        }
@@ -999,20 +962,22 @@ cleanup_dead_labels (void)
            true_branch = COND_EXPR_THEN (stmt);
            false_branch = COND_EXPR_ELSE (stmt);
 
-           GOTO_DESTINATION (true_branch)
-             = main_block_label (GOTO_DESTINATION (true_branch));
-           GOTO_DESTINATION (false_branch)
-             = main_block_label (GOTO_DESTINATION (false_branch));
+           if (true_branch)
+             GOTO_DESTINATION (true_branch)
+                     = main_block_label (GOTO_DESTINATION (true_branch));
+           if (false_branch)
+             GOTO_DESTINATION (false_branch)
+                     = main_block_label (GOTO_DESTINATION (false_branch));
 
            break;
          }
-  
+
        case SWITCH_EXPR:
          {
            size_t i;
            tree vec = SWITCH_LABELS (stmt);
            size_t n = TREE_VEC_LENGTH (vec);
-  
+
            /* Replace all destination labels.  */
            for (i = 0; i < n; ++i)
              {
@@ -1041,15 +1006,20 @@ cleanup_dead_labels (void)
   for_each_eh_region (update_eh_label);
 
   /* Finally, purge dead labels.  All user-defined labels and labels that
-     can be the target of non-local gotos are preserved.  */
+     can be the target of non-local gotos and labels which have their
+     address taken are preserved.  */
   FOR_EACH_BB (bb)
     {
       block_stmt_iterator i;
-      tree label_for_this_bb = label_for_bb[bb->index];
+      tree label_for_this_bb = label_for_bb[bb->index].label;
 
-      if (! label_for_this_bb)
+      if (!label_for_this_bb)
        continue;
 
+      /* If the main label of the block is unused, we may still remove it.  */
+      if (!label_for_bb[bb->index].used)
+       label_for_this_bb = NULL;
+
       for (i = bsi_start (bb); !bsi_end_p (i); )
        {
          tree label, stmt = bsi_stmt (i);
@@ -1061,7 +1031,8 @@ cleanup_dead_labels (void)
 
          if (label == label_for_this_bb
              || ! DECL_ARTIFICIAL (label)
-             || DECL_NONLOCAL (label))
+             || DECL_NONLOCAL (label)
+             || FORCED_LABEL (label))
            bsi_next (&i);
          else
            bsi_remove (&i, true);
@@ -1090,7 +1061,7 @@ group_case_labels (void)
          int old_size = TREE_VEC_LENGTH (labels);
          int i, j, new_size = old_size;
          tree default_case = TREE_VEC_ELT (labels, old_size - 1);
-         tree default_label;
+         tree default_label;
 
          /* The default label is always the last case in a switch
             statement after gimplification.  */
@@ -1166,7 +1137,7 @@ group_case_labels (void)
 static bool
 tree_can_merge_blocks_p (basic_block a, basic_block b)
 {
-  tree stmt;
+  const_tree stmt;
   block_stmt_iterator bsi;
   tree phi;
 
@@ -1184,10 +1155,12 @@ tree_can_merge_blocks_p (basic_block a, basic_block b)
 
   if (b == EXIT_BLOCK_PTR)
     return false;
-  
+
   /* If A ends by a statement causing exceptions or something similar, we
      cannot merge the blocks.  */
-  stmt = last_stmt (a);
+  /* This CONST_CAST is okay because last_stmt doesn't modify its
+     argument and the return value is assign to a const_tree.  */
+  stmt = last_stmt (CONST_CAST_BB (a));
   if (stmt && stmt_ends_bb_p (stmt))
     return false;
 
@@ -1197,11 +1170,13 @@ tree_can_merge_blocks_p (basic_block a, basic_block b)
     return false;
 
   /* It must be possible to eliminate all phi nodes in B.  If ssa form
-     is not up-to-date, we cannot eliminate any phis.  */
+     is not up-to-date, we cannot eliminate any phis; however, if only
+     some symbols as whole are marked for renaming, this is not a problem,
+     as phi nodes for those symbols are irrelevant in updating anyway.  */
   phi = phi_nodes (b);
   if (phi)
     {
-      if (need_ssa_update_p ())
+      if (name_mappings_registered_p ())
        return false;
 
       for (; phi; phi = PHI_CHAIN (phi))
@@ -1237,66 +1212,60 @@ replace_uses_by (tree name, tree val)
   use_operand_p use;
   tree stmt;
   edge e;
-  unsigned i;
-  VEC(tree,heap) *stmts = VEC_alloc (tree, heap, 20);
 
-  FOR_EACH_IMM_USE_SAFE (use, imm_iter, name)
+  FOR_EACH_IMM_USE_STMT (stmt, imm_iter, name)
     {
-      stmt = USE_STMT (use);
-      replace_exp (use, val);
+      if (TREE_CODE (stmt) != PHI_NODE)
+       push_stmt_changes (&stmt);
 
-      if (TREE_CODE (stmt) == PHI_NODE)
-       {
-         e = PHI_ARG_EDGE (stmt, PHI_ARG_INDEX_FROM_USE (use));
-         if (e->flags & EDGE_ABNORMAL)
+      FOR_EACH_IMM_USE_ON_STMT (use, imm_iter)
+        {
+         replace_exp (use, val);
+
+         if (TREE_CODE (stmt) == PHI_NODE)
            {
-             /* This can only occur for virtual operands, since
-                for the real ones SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name))
-                would prevent replacement.  */
-             gcc_assert (!is_gimple_reg (name));
-             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (val) = 1;
+             e = PHI_ARG_EDGE (stmt, PHI_ARG_INDEX_FROM_USE (use));
+             if (e->flags & EDGE_ABNORMAL)
+               {
+                 /* This can only occur for virtual operands, since
+                    for the real ones SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name))
+                    would prevent replacement.  */
+                 gcc_assert (!is_gimple_reg (name));
+                 SSA_NAME_OCCURS_IN_ABNORMAL_PHI (val) = 1;
+               }
            }
        }
-      else
-       VEC_safe_push (tree, heap, stmts, stmt);
-    }
-  /* We do not update the statements in the loop above.  Consider
-     x = w * w;
 
-     If we performed the update in the first loop, the statement
-     would be rescanned after first occurrence of w is replaced,
-     the new uses would be placed to the beginning of the list,
-     and we would never process them.  */
-  for (i = 0; VEC_iterate (tree, stmts, i, stmt); i++)
-    {
-      tree rhs;
+      if (TREE_CODE (stmt) != PHI_NODE)
+       {
+         tree rhs;
 
-      fold_stmt_inplace (stmt);
+         fold_stmt_inplace (stmt);
+         if (cfgcleanup_altered_bbs)
+           bitmap_set_bit (cfgcleanup_altered_bbs, bb_for_stmt (stmt)->index);
 
-      rhs = get_rhs (stmt);
-      if (TREE_CODE (rhs) == ADDR_EXPR)
-       recompute_tree_invariant_for_addr_expr (rhs);
+         /* FIXME.  This should go in pop_stmt_changes.  */
+         rhs = get_rhs (stmt);
+         if (TREE_CODE (rhs) == ADDR_EXPR)
+           recompute_tree_invariant_for_addr_expr (rhs);
 
-      /* If the statement could throw and now cannot, we need to prune cfg.  */
-      if (maybe_clean_or_replace_eh_stmt (stmt, stmt))
-       tree_purge_dead_eh_edges (bb_for_stmt (stmt));
+         maybe_clean_or_replace_eh_stmt (stmt, stmt);
 
-      mark_new_vars_to_rename (stmt);
+         pop_stmt_changes (&stmt);
+       }
     }
 
-  VEC_free (tree, heap, stmts);
+  gcc_assert (has_zero_uses (name));
 
   /* Also update the trees stored in loop structures.  */
   if (current_loops)
     {
       struct loop *loop;
+      loop_iterator li;
 
-      for (i = 0; i < current_loops->num; i++)
+      FOR_EACH_LOOP (li, loop, 0)
        {
-         loop = current_loops->parray[i];
-         if (loop)
-           substitute_in_loop_info (loop, name, val);
+         substitute_in_loop_info (loop, name, val);
        }
     }
 }
@@ -1322,9 +1291,10 @@ tree_merge_blocks (basic_block a, basic_block b)
       tree copy;
       bool may_replace_uses = may_propagate_copy (def, use);
 
-      /* In case we have loops to care about, do not propagate arguments of
-        loop closed ssa phi nodes.  */
+      /* In case we maintain loop closed ssa form, do not propagate arguments
+        of loop exit phi nodes.  */
       if (current_loops
+         && loops_state_satisfies_p (LOOP_CLOSED_SSA)
          && is_gimple_reg (def)
          && TREE_CODE (use) == SSA_NAME
          && a->loop_father != b->loop_father)
@@ -1338,15 +1308,16 @@ tree_merge_blocks (basic_block a, basic_block b)
             with ordering of phi nodes.  This is because A is the single
             predecessor of B, therefore results of the phi nodes cannot
             appear as arguments of the phi nodes.  */
-         copy = build2 (MODIFY_EXPR, void_type_node, def, use);
+         copy = build_gimple_modify_stmt (def, use);
          bsi_insert_after (&bsi, copy, BSI_NEW_STMT);
-         SET_PHI_RESULT (phi, NULL_TREE);
          SSA_NAME_DEF_STMT (def) = copy;
+          remove_phi_node (phi, NULL, false);
        }
       else
-       replace_uses_by (def, use);
-
-      remove_phi_node (phi, NULL);
+        {
+          replace_uses_by (def, use);
+          remove_phi_node (phi, NULL, true);
+        }
     }
 
   /* Ensure that B follows A.  */
@@ -1377,15 +1348,41 @@ tree_merge_blocks (basic_block a, basic_block b)
        }
       else
        {
-         set_bb_for_stmt (bsi_stmt (bsi), a);
+         change_bb_for_stmt (bsi_stmt (bsi), a);
          bsi_next (&bsi);
        }
     }
 
   /* Merge the chains.  */
-  last = tsi_last (a->stmt_list);
-  tsi_link_after (&last, b->stmt_list, TSI_NEW_STMT);
-  b->stmt_list = NULL;
+  last = tsi_last (bb_stmt_list (a));
+  tsi_link_after (&last, bb_stmt_list (b), TSI_NEW_STMT);
+  set_bb_stmt_list (b, NULL_TREE);
+
+  if (cfgcleanup_altered_bbs)
+    bitmap_set_bit (cfgcleanup_altered_bbs, a->index);
+}
+
+
+/* Return the one of two successors of BB that is not reachable by a
+   reached by a complex edge, if there is one.  Else, return BB.  We use
+   this in optimizations that use post-dominators for their heuristics,
+   to catch the cases in C++ where function calls are involved.  */
+
+basic_block
+single_noncomplex_succ (basic_block bb)
+{
+  edge e0, e1;
+  if (EDGE_COUNT (bb->succs) != 2)
+    return bb;
+
+  e0 = EDGE_SUCC (bb, 0);
+  e1 = EDGE_SUCC (bb, 1);
+  if (e0->flags & EDGE_COMPLEX)
+    return e1->dest;
+  if (e1->flags & EDGE_COMPLEX)
+    return e0->dest;
+
+  return bb;
 }
 
 
@@ -1544,9 +1541,9 @@ remove_useless_stmts_cond (tree *stmt_p, struct rus_data *data)
       else if (TREE_CODE (cond) == VAR_DECL || TREE_CODE (cond) == PARM_DECL)
        {
          if (else_stmt
-             && TREE_CODE (else_stmt) == MODIFY_EXPR
-             && TREE_OPERAND (else_stmt, 0) == cond
-             && integer_zerop (TREE_OPERAND (else_stmt, 1)))
+             && TREE_CODE (else_stmt) == GIMPLE_MODIFY_STMT
+             && GIMPLE_STMT_OPERAND (else_stmt, 0) == cond
+             && integer_zerop (GIMPLE_STMT_OPERAND (else_stmt, 1)))
            COND_EXPR_ELSE (*stmt_p) = alloc_stmt_list ();
        }
       else if ((TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR)
@@ -1561,9 +1558,9 @@ remove_useless_stmts_cond (tree *stmt_p, struct rus_data *data)
                            : &COND_EXPR_ELSE (*stmt_p));
 
          if (stmt
-             && TREE_CODE (stmt) == MODIFY_EXPR
-             && TREE_OPERAND (stmt, 0) == TREE_OPERAND (cond, 0)
-             && TREE_OPERAND (stmt, 1) == TREE_OPERAND (cond, 1))
+             && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && GIMPLE_STMT_OPERAND (stmt, 0) == TREE_OPERAND (cond, 0)
+             && GIMPLE_STMT_OPERAND (stmt, 1) == TREE_OPERAND (cond, 1))
            *location = alloc_stmt_list ();
        }
     }
@@ -1765,7 +1762,7 @@ remove_useless_stmts_label (tree *stmt_p, struct rus_data *data)
 
 /* If the function is "const" or "pure", then clear TREE_SIDE_EFFECTS on its
    decl.  This allows us to eliminate redundant or useless
-   calls to "const" functions. 
+   calls to "const" functions.
 
    Gimplifier already does the same operation, but we may notice functions
    being const and pure once their calls has been gimplified, so we need
@@ -1856,6 +1853,9 @@ remove_useless_stmts_1 (tree *tp, struct rus_data *data)
       break;
 
     case MODIFY_EXPR:
+      gcc_unreachable ();
+
+    case GIMPLE_MODIFY_STMT:
       data->last_goto = NULL;
       fold_stmt (tp);
       op = get_call_expr_in (t);
@@ -1879,7 +1879,7 @@ remove_useless_stmts_1 (tree *tp, struct rus_data *data)
                tsi_delink (&i);
                continue;
              }
-           
+
            remove_useless_stmts_1 (tsi_stmt_ptr (i), data);
 
            t = tsi_stmt (i);
@@ -1904,7 +1904,7 @@ remove_useless_stmts_1 (tree *tp, struct rus_data *data)
     }
 }
 
-static void
+static unsigned int
 remove_useless_stmts (void)
 {
   struct rus_data data;
@@ -1917,10 +1917,11 @@ remove_useless_stmts (void)
       remove_useless_stmts_1 (&DECL_SAVED_TREE (current_function_decl), &data);
     }
   while (data.repeat);
+  return 0;
 }
 
 
-struct tree_opt_pass pass_remove_useless_stmts = 
+struct tree_opt_pass pass_remove_useless_stmts =
 {
   "useless",                           /* name */
   NULL,                                        /* gate */
@@ -1950,7 +1951,7 @@ remove_phi_nodes_and_edges_for_unreachable_block (basic_block bb)
   while (phi)
     {
       tree next = PHI_CHAIN (phi);
-      remove_phi_node (phi, NULL_TREE);
+      remove_phi_node (phi, NULL_TREE, true);
       phi = next;
     }
 
@@ -1982,78 +1983,72 @@ remove_bb (basic_block bb)
        }
     }
 
-  /* If we remove the header or the latch of a loop, mark the loop for
-     removal by setting its header and latch to NULL.  */
   if (current_loops)
     {
       struct loop *loop = bb->loop_father;
 
+      /* If a loop gets removed, clean up the information associated
+        with it.  */
       if (loop->latch == bb
          || loop->header == bb)
-       {
-         loop->latch = NULL;
-         loop->header = NULL;
-
-         /* Also clean up the information associated with the loop.  Updating
-            it would waste time. More importantly, it may refer to ssa
-            names that were defined in other removed basic block -- these
-            ssa names are now removed and invalid.  */
-         free_numbers_of_iterations_estimates_loop (loop);
-       }
+       free_numbers_of_iterations_estimates_loop (loop);
     }
 
   /* Remove all the instructions in the block.  */
-  for (i = bsi_start (bb); !bsi_end_p (i);)
+  if (bb_stmt_list (bb) != NULL_TREE)
     {
-      tree stmt = bsi_stmt (i);
-      if (TREE_CODE (stmt) == LABEL_EXPR
-          && (FORCED_LABEL (LABEL_EXPR_LABEL (stmt))
-             || DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt))))
+      for (i = bsi_start (bb); !bsi_end_p (i);)
        {
-         basic_block new_bb;
-         block_stmt_iterator new_bsi;
+         tree stmt = bsi_stmt (i);
+         if (TREE_CODE (stmt) == LABEL_EXPR
+             && (FORCED_LABEL (LABEL_EXPR_LABEL (stmt))
+                 || DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt))))
+           {
+             basic_block new_bb;
+             block_stmt_iterator new_bsi;
 
-         /* A non-reachable non-local label may still be referenced.
-            But it no longer needs to carry the extra semantics of
-            non-locality.  */
-         if (DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
+             /* A non-reachable non-local label may still be referenced.
+                But it no longer needs to carry the extra semantics of
+                non-locality.  */
+             if (DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
+               {
+                 DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)) = 0;
+                 FORCED_LABEL (LABEL_EXPR_LABEL (stmt)) = 1;
+               }
+
+             new_bb = bb->prev_bb;
+             new_bsi = bsi_start (new_bb);
+             bsi_remove (&i, false);
+             bsi_insert_before (&new_bsi, stmt, BSI_NEW_STMT);
+           }
+         else
            {
-             DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)) = 0;
-             FORCED_LABEL (LABEL_EXPR_LABEL (stmt)) = 1;
+             /* Release SSA definitions if we are in SSA.  Note that we
+                may be called when not in SSA.  For example,
+                final_cleanup calls this function via
+                cleanup_tree_cfg.  */
+             if (gimple_in_ssa_p (cfun))
+               release_defs (stmt);
+
+             bsi_remove (&i, true);
            }
-                 
-         new_bb = bb->prev_bb;
-         new_bsi = bsi_start (new_bb);
-         bsi_remove (&i, false);
-         bsi_insert_before (&new_bsi, stmt, BSI_NEW_STMT);
-       }
-      else
-        {
-         /* Release SSA definitions if we are in SSA.  Note that we
-            may be called when not in SSA.  For example,
-            final_cleanup calls this function via
-            cleanup_tree_cfg.  */
-         if (in_ssa_p)
-           release_defs (stmt);
-
-         bsi_remove (&i, true);
-       }
 
-      /* Don't warn for removed gotos.  Gotos are often removed due to
-        jump threading, thus resulting in bogus warnings.  Not great,
-        since this way we lose warnings for gotos in the original
-        program that are indeed unreachable.  */
-      if (TREE_CODE (stmt) != GOTO_EXPR && EXPR_HAS_LOCATION (stmt) && !loc)
-       {
+         /* Don't warn for removed gotos.  Gotos are often removed due to
+            jump threading, thus resulting in bogus warnings.  Not great,
+            since this way we lose warnings for gotos in the original
+            program that are indeed unreachable.  */
+         if (TREE_CODE (stmt) != GOTO_EXPR && EXPR_HAS_LOCATION (stmt) && !loc)
+           {
 #ifdef USE_MAPPED_LOCATION
-         if (EXPR_HAS_LOCATION (stmt))
-           loc = EXPR_LOCATION (stmt);
+             if (EXPR_HAS_LOCATION (stmt))
+               loc = EXPR_LOCATION (stmt);
 #else
-         source_locus t;
-         t = EXPR_LOCUS (stmt);
-         if (t && LOCATION_LINE (*t) > 0)
-           loc = t;
+             source_locus t;
+             t = EXPR_LOCUS (stmt);
+             if (t && LOCATION_LINE (*t) > 0)
+               loc = t;
 #endif
+           }
        }
     }
 
@@ -2062,7 +2057,7 @@ remove_bb (basic_block bb)
      loop above, so the last statement we process is the first statement
      in the block.  */
 #ifdef USE_MAPPED_LOCATION
-  if (loc > BUILTINS_LOCATION)
+  if (loc > BUILTINS_LOCATION && LOCATION_LINE (loc) > 0)
     warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc);
 #else
   if (loc)
@@ -2070,6 +2065,7 @@ remove_bb (basic_block bb)
 #endif
 
   remove_phi_nodes_and_edges_for_unreachable_block (bb);
+  bb->il.tree = NULL;
 }
 
 
@@ -2098,7 +2094,18 @@ find_taken_edge (basic_block bb, tree val)
     return find_taken_edge_switch_expr (bb, val);
 
   if (computed_goto_p (stmt))
-    return find_taken_edge_computed_goto (bb, TREE_OPERAND( val, 0));
+    {
+      /* Only optimize if the argument is a label, if the argument is
+        not a label then we can not construct a proper CFG.
+
+         It may be the case that we only need to allow the LABEL_REF to
+         appear inside an ADDR_EXPR, but we also allow the LABEL_REF to
+         appear inside a LABEL_EXPR just to be safe.  */
+      if ((TREE_CODE (val) == ADDR_EXPR || TREE_CODE (val) == LABEL_EXPR)
+         && TREE_CODE (TREE_OPERAND (val, 0)) == LABEL_DECL)
+       return find_taken_edge_computed_goto (bb, TREE_OPERAND (val, 0));
+      return NULL;
+    }
 
   gcc_unreachable ();
 }
@@ -2133,9 +2140,9 @@ find_taken_edge_cond_expr (basic_block bb, tree val)
   edge true_edge, false_edge;
 
   extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
-  
+
   gcc_assert (TREE_CODE (val) == INTEGER_CST);
-  return (zero_p (val) ? false_edge : true_edge);
+  return (integer_zerop (val) ? false_edge : true_edge);
 }
 
 /* Given an INTEGER_CST VAL and the entry block BB to a SWITCH_EXPR
@@ -2213,7 +2220,7 @@ find_case_label_for_value (tree switch_expr, tree val)
 void
 tree_dump_bb (basic_block bb, FILE *outf, int indent)
 {
-  dump_generic_bb (outf, bb, indent, TDF_VOPS);
+  dump_generic_bb (outf, bb, indent, TDF_VOPS|TDF_MEMSYMS);
 }
 
 
@@ -2233,13 +2240,13 @@ debug_tree_bb_n (int n)
 {
   debug_tree_bb (BASIC_BLOCK (n));
   return BASIC_BLOCK (n);
-}       
+}
 
 
 /* Dump the CFG on stderr.
 
    FLAGS are the same used by the tree dumping functions
-   (see TDF_* in tree.h).  */
+   (see TDF_* in tree-pass.h).  */
 
 void
 debug_tree_cfg (int flags)
@@ -2432,7 +2439,7 @@ tree_cfg2vcg (FILE *file)
 /* Return true if T represents a stmt that always transfers control.  */
 
 bool
-is_ctrl_stmt (tree t)
+is_ctrl_stmt (const_tree t)
 {
   return (TREE_CODE (t) == COND_EXPR
          || TREE_CODE (t) == SWITCH_EXPR
@@ -2446,12 +2453,12 @@ is_ctrl_stmt (tree t)
    (e.g., a call to a non-returning function).  */
 
 bool
-is_ctrl_altering_stmt (tree t)
+is_ctrl_altering_stmt (const_tree t)
 {
-  tree call;
+  const_tree call;
 
   gcc_assert (t);
-  call = get_call_expr_in (t);
+  call = get_call_expr_in (CONST_CAST_TREE (t));
   if (call)
     {
       /* A non-pure/const CALL_EXPR alters flow control if the current
@@ -2464,6 +2471,10 @@ is_ctrl_altering_stmt (tree t)
        return true;
     }
 
+  /* OpenMP directives alter control flow.  */
+  if (OMP_DIRECTIVE_P (t))
+    return true;
+
   /* If a statement can throw, it alters control flow.  */
   return tree_can_throw_internal (t);
 }
@@ -2472,20 +2483,38 @@ is_ctrl_altering_stmt (tree t)
 /* Return true if T is a computed goto.  */
 
 bool
-computed_goto_p (tree t)
+computed_goto_p (const_tree t)
 {
   return (TREE_CODE (t) == GOTO_EXPR
          && TREE_CODE (GOTO_DESTINATION (t)) != LABEL_DECL);
 }
 
 
-/* Checks whether EXPR is a simple local goto.  */
+/* Return true if T is a simple local goto.  */
+
+bool
+simple_goto_p (const_tree t)
+{
+  return (TREE_CODE (t) == GOTO_EXPR
+         && TREE_CODE (GOTO_DESTINATION (t)) == LABEL_DECL);
+}
+
+
+/* Return true if T can make an abnormal transfer of control flow.
+   Transfers of control flow associated with EH are excluded.  */
 
 bool
-simple_goto_p (tree expr)
+tree_can_make_abnormal_goto (const_tree t)
 {
-  return (TREE_CODE (expr) == GOTO_EXPR
-         && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL);
+  if (computed_goto_p (t))
+    return true;
+  if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
+    t = GIMPLE_STMT_OPERAND (t, 1);
+  if (TREE_CODE (t) == WITH_SIZE_EXPR)
+    t = TREE_OPERAND (t, 0);
+  if (TREE_CODE (t) == CALL_EXPR)
+    return TREE_SIDE_EFFECTS (t) && current_function_has_nonlocal_label;
+  return false;
 }
 
 
@@ -2496,7 +2525,7 @@ simple_goto_p (tree expr)
    unnecessary basic blocks that only contain a single label.  */
 
 static inline bool
-stmt_starts_bb_p (tree t, tree prev_t)
+stmt_starts_bb_p (const_tree t, const_tree prev_t)
 {
   if (t == NULL_TREE)
     return false;
@@ -2531,102 +2560,32 @@ stmt_starts_bb_p (tree t, tree prev_t)
 /* Return true if T should end a basic block.  */
 
 bool
-stmt_ends_bb_p (tree t)
+stmt_ends_bb_p (const_tree t)
 {
   return is_ctrl_stmt (t) || is_ctrl_altering_stmt (t);
 }
 
-
-/* Add gotos that used to be represented implicitly in the CFG.  */
+/* Remove block annotations and other datastructures.  */
 
 void
-disband_implicit_edges (void)
+delete_tree_cfg_annotations (void)
 {
   basic_block bb;
-  block_stmt_iterator last;
-  edge e;
-  edge_iterator ei;
-  tree stmt, label;
+  block_stmt_iterator bsi;
 
+  /* Remove annotations from every tree in the function.  */
   FOR_EACH_BB (bb)
-    {
-      last = bsi_last (bb);
-      stmt = last_stmt (bb);
-
-      if (stmt && TREE_CODE (stmt) == COND_EXPR)
-       {
-         /* Remove superfluous gotos from COND_EXPR branches.  Moved
-            from cfg_remove_useless_stmts here since it violates the
-            invariants for tree--cfg correspondence and thus fits better
-            here where we do it anyway.  */
-         e = find_edge (bb, bb->next_bb);
-         if (e)
-           {
-             if (e->flags & EDGE_TRUE_VALUE)
-               COND_EXPR_THEN (stmt) = build_empty_stmt ();
-             else if (e->flags & EDGE_FALSE_VALUE)
-               COND_EXPR_ELSE (stmt) = build_empty_stmt ();
-             else
-               gcc_unreachable ();
-             e->flags |= EDGE_FALLTHRU;
-           }
+    for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      {
+       tree stmt = bsi_stmt (bsi);
+       ggc_free (stmt->base.ann);
+       stmt->base.ann = NULL;
+      }
+  label_to_block_map = NULL;
+}
 
-         continue;
-       }
 
-      if (stmt && TREE_CODE (stmt) == RETURN_EXPR)
-       {
-         /* Remove the RETURN_EXPR if we may fall though to the exit
-            instead.  */
-         gcc_assert (single_succ_p (bb));
-         gcc_assert (single_succ (bb) == EXIT_BLOCK_PTR);
-
-         if (bb->next_bb == EXIT_BLOCK_PTR
-             && !TREE_OPERAND (stmt, 0))
-           {
-             bsi_remove (&last, true);
-             single_succ_edge (bb)->flags |= EDGE_FALLTHRU;
-           }
-         continue;
-       }
-
-      /* There can be no fallthru edge if the last statement is a control
-        one.  */
-      if (stmt && is_ctrl_stmt (stmt))
-       continue;
-
-      /* Find a fallthru edge and emit the goto if necessary.  */
-      FOR_EACH_EDGE (e, ei, bb->succs)
-       if (e->flags & EDGE_FALLTHRU)
-         break;
-
-      if (!e || e->dest == bb->next_bb)
-       continue;
-
-      gcc_assert (e->dest != EXIT_BLOCK_PTR);
-      label = tree_block_label (e->dest);
-
-      stmt = build1 (GOTO_EXPR, void_type_node, label);
-#ifdef USE_MAPPED_LOCATION
-      SET_EXPR_LOCATION (stmt, e->goto_locus);
-#else
-      SET_EXPR_LOCUS (stmt, e->goto_locus);
-#endif
-      bsi_insert_after (&last, stmt, BSI_NEW_STMT);
-      e->flags &= ~EDGE_FALLTHRU;
-    }
-}
-
-/* Remove block annotations and other datastructures.  */
-
-void
-delete_tree_cfg_annotations (void)
-{
-  label_to_block_map = NULL;
-}
-
-
-/* Return the first statement in basic block BB.  */
+/* Return the first statement in basic block BB.  */
 
 tree
 first_stmt (basic_block bb)
@@ -2635,7 +2594,6 @@ first_stmt (basic_block bb)
   return !bsi_end_p (i) ? bsi_stmt (i) : NULL_TREE;
 }
 
-
 /* Return the last statement in basic block BB.  */
 
 tree
@@ -2645,17 +2603,6 @@ last_stmt (basic_block bb)
   return !bsi_end_p (b) ? bsi_stmt (b) : NULL_TREE;
 }
 
-
-/* Return a pointer to the last statement in block BB.  */
-
-tree *
-last_stmt_ptr (basic_block bb)
-{
-  block_stmt_iterator last = bsi_last (bb);
-  return !bsi_end_p (last) ? bsi_stmt_ptr (last) : NULL;
-}
-
-
 /* Return the last statement of an otherwise empty block.  Return NULL
    if the block is totally empty, or if it contains more than one
    statement.  */
@@ -2708,7 +2655,7 @@ set_bb_for_stmt (tree t, basic_block bb)
       ann->bb = bb;
 
       /* If the statement is a label, add the label to block-to-labels map
-        so that we can speed up edge creation for GOTO_EXPRs.  */
+        so that we can speed up edge creation for GOTO_EXPRs.  */
       if (TREE_CODE (t) == LABEL_EXPR)
        {
          int uid;
@@ -2721,14 +2668,10 @@ set_bb_for_stmt (tree t, basic_block bb)
              LABEL_DECL_UID (t) = uid = cfun->last_label_uid++;
              if (old_len <= (unsigned) uid)
                {
-                 basic_block *addr;
                  unsigned new_len = 3 * uid / 2;
 
-                 VEC_safe_grow (basic_block, gc, label_to_block_map,
-                                new_len);
-                 addr = VEC_address (basic_block, label_to_block_map);
-                 memset (&addr[old_len],
-                         0, sizeof (basic_block) * (new_len - old_len));
+                 VEC_safe_grow_cleared (basic_block, gc, label_to_block_map,
+                                        new_len);
                }
            }
          else
@@ -2741,6 +2684,20 @@ set_bb_for_stmt (tree t, basic_block bb)
     }
 }
 
+/* Faster version of set_bb_for_stmt that assume that statement is being moved
+   from one basic block to another.  
+   For BB splitting we can run into quadratic case, so performance is quite
+   important and knowing that the tables are big enough, change_bb_for_stmt
+   can inline as leaf function.  */
+static inline void
+change_bb_for_stmt (tree t, basic_block bb)
+{
+  get_stmt_ann (t)->bb = bb;
+  if (TREE_CODE (t) == LABEL_EXPR)
+    VEC_replace (basic_block, label_to_block_map,
+                LABEL_DECL_UID (LABEL_EXPR_LABEL (t)), bb);
+}
+
 /* Finds iterator for STMT.  */
 
 extern block_stmt_iterator
@@ -2759,6 +2716,8 @@ bsi_for_stmt (tree stmt)
 static inline void
 update_modified_stmts (tree t)
 {
+  if (!ssa_operands_active ())
+    return;
   if (TREE_CODE (t) == STATEMENT_LIST)
     {
       tree_stmt_iterator i;
@@ -2800,7 +2759,7 @@ bsi_insert_after (block_stmt_iterator *i, tree t, enum bsi_iterator_update m)
 
 
 /* Remove the statement pointed to by iterator I.  The iterator is updated
-   to the next statement. 
+   to the next statement.
 
    When REMOVE_EH_INFO is true we remove the statement pointed to by
    iterator I from the EH tables.  Otherwise we do not modify the EH
@@ -2818,28 +2777,36 @@ bsi_remove (block_stmt_iterator *i, bool remove_eh_info)
   tsi_delink (&i->tsi);
   mark_stmt_modified (t);
   if (remove_eh_info)
-    remove_stmt_from_eh_region (t);
+    {
+      remove_stmt_from_eh_region (t);
+      gimple_remove_stmt_histograms (cfun, t);
+    }
 }
 
 
 /* Move the statement at FROM so it comes right after the statement at TO.  */
 
-void 
+void
 bsi_move_after (block_stmt_iterator *from, block_stmt_iterator *to)
 {
   tree stmt = bsi_stmt (*from);
   bsi_remove (from, false);
-  bsi_insert_after (to, stmt, BSI_SAME_STMT);
-} 
+  /* We must have BSI_NEW_STMT here, as bsi_move_after is sometimes used to
+     move statements to an empty block.  */
+  bsi_insert_after (to, stmt, BSI_NEW_STMT);
+}
 
 
 /* Move the statement at FROM so it comes right before the statement at TO.  */
 
-void 
+void
 bsi_move_before (block_stmt_iterator *from, block_stmt_iterator *to)
 {
   tree stmt = bsi_stmt (*from);
   bsi_remove (from, false);
+  /* For consistency with bsi_move_after, it might be better to have
+     BSI_NEW_STMT here; however, that breaks several places that expect
+     that TO does not change.  */
   bsi_insert_before (to, stmt, BSI_SAME_STMT);
 }
 
@@ -2850,7 +2817,7 @@ void
 bsi_move_to_bb_end (block_stmt_iterator *from, basic_block bb)
 {
   block_stmt_iterator last = bsi_last (bb);
-  
+
   /* Have to check bsi_end_p because it could be an empty block.  */
   if (!bsi_end_p (last) && is_ctrl_stmt (bsi_stmt (last)))
     bsi_move_before (from, &last);
@@ -2862,7 +2829,6 @@ bsi_move_to_bb_end (block_stmt_iterator *from, basic_block bb)
 /* Replace the contents of the statement pointed to by iterator BSI
    with STMT.  If UPDATE_EH_INFO is true, the exception handling
    information of the original statement is moved to the new statement.  */
-  
 
 void
 bsi_replace (const block_stmt_iterator *bsi, tree stmt, bool update_eh_info)
@@ -2870,6 +2836,8 @@ bsi_replace (const block_stmt_iterator *bsi, tree stmt, bool update_eh_info)
   int eh_region;
   tree orig_stmt = bsi_stmt (*bsi);
 
+  if (stmt == orig_stmt)
+    return;
   SET_EXPR_LOCUS (stmt, EXPR_LOCUS (orig_stmt));
   set_bb_for_stmt (stmt, bsi->bb);
 
@@ -2885,6 +2853,8 @@ bsi_replace (const block_stmt_iterator *bsi, tree stmt, bool update_eh_info)
        }
     }
 
+  gimple_duplicate_stmt_histograms (cfun, stmt, cfun, orig_stmt);
+  gimple_remove_stmt_histograms (cfun, orig_stmt);
   delink_stmt_imm_use (orig_stmt);
   *bsi_stmt_ptr (*bsi) = stmt;
   mark_stmt_modified (stmt);
@@ -2913,7 +2883,7 @@ tree_find_edge_insert_loc (edge e, block_stmt_iterator *bsi,
  restart:
 
   /* If the destination has one predecessor which has no PHI nodes,
-     insert there.  Except for the exit block. 
+     insert there.  Except for the exit block.
 
      The requirement for no PHI nodes could be relaxed.  Basically we
      would have to examine the PHIs to prove that none of them used
@@ -2969,9 +2939,9 @@ tree_find_edge_insert_loc (edge e, block_stmt_iterator *bsi,
          tree op = TREE_OPERAND (tmp, 0);
          if (op && !is_gimple_val (op))
            {
-             gcc_assert (TREE_CODE (op) == MODIFY_EXPR);
+             gcc_assert (TREE_CODE (op) == GIMPLE_MODIFY_STMT);
              bsi_insert_before (bsi, op, BSI_NEW_STMT);
-             TREE_OPERAND (tmp, 0) = TREE_OPERAND (op, 0);
+             TREE_OPERAND (tmp, 0) = GIMPLE_STMT_OPERAND (op, 0);
            }
          bsi_prev (bsi);
          return true;
@@ -3069,7 +3039,7 @@ reinstall_phi_args (edge new_edge, edge old_edge)
 
   if (!PENDING_STMT (old_edge))
     return;
-  
+
   for (var = PENDING_STMT (old_edge), phi = phi_nodes (new_edge->dest);
        var && phi;
        var = TREE_CHAIN (var), phi = PHI_CHAIN (phi))
@@ -3085,7 +3055,7 @@ reinstall_phi_args (edge new_edge, edge old_edge)
   PENDING_STMT (old_edge) = NULL;
 }
 
-/* Returns the basic block after that the new basic block created
+/* Returns the basic block after which the new basic block created
    by splitting edge EDGE_IN should be placed.  Tries to keep the new block
    near its "logical" location.  This is of most help to humans looking
    at debugging dumps.  */
@@ -3107,13 +3077,12 @@ split_edge_bb_loc (edge edge_in)
 static basic_block
 tree_split_edge (edge edge_in)
 {
-  basic_block new_bb, after_bb, dest, src;
+  basic_block new_bb, after_bb, dest;
   edge new_edge, e;
 
   /* Abnormal edges cannot be split.  */
   gcc_assert (!(edge_in->flags & EDGE_ABNORMAL));
 
-  src = edge_in->src;
   dest = edge_in->dest;
 
   after_bb = split_edge_bb_loc (edge_in);
@@ -3126,33 +3095,12 @@ tree_split_edge (edge edge_in)
   new_edge->count = edge_in->count;
 
   e = redirect_edge_and_branch (edge_in, new_bb);
-  gcc_assert (e);
+  gcc_assert (e == edge_in);
   reinstall_phi_args (new_edge, e);
 
   return new_bb;
 }
 
-
-/* Return true when BB has label LABEL in it.  */
-
-static bool
-has_label_p (basic_block bb, tree label)
-{
-  block_stmt_iterator bsi;
-
-  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
-    {
-      tree stmt = bsi_stmt (bsi);
-
-      if (TREE_CODE (stmt) != LABEL_EXPR)
-       return false;
-      if (LABEL_EXPR_LABEL (stmt) == label)
-       return true;
-    }
-  return false;
-}
-
-
 /* Callback for walk_tree, check that all elements with address taken are
    properly noticed as such.  The DATA is an int* that is 1 if TP was seen
    inside a PHI node.  */
@@ -3165,7 +3113,7 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 
   if (TYPE_P (t))
     *walk_subtrees = 0;
-  
+
   /* Check operand N for being valid GIMPLE and give error MSG if not.  */
 #define CHECK_OP(N, MSG) \
   do { if (!is_gimple_val (TREE_OPERAND (t, N)))               \
@@ -3191,7 +3139,10 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
       break;
 
     case MODIFY_EXPR:
-      x = TREE_OPERAND (t, 0);
+      gcc_unreachable ();
+
+    case GIMPLE_MODIFY_STMT:
+      x = GIMPLE_STMT_OPERAND (t, 0);
       if (TREE_CODE (x) == BIT_FIELD_REF
          && is_gimple_reg (TREE_OPERAND (x, 0)))
        {
@@ -3265,9 +3216,9 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 
     case COND_EXPR:
       x = COND_EXPR_COND (t);
-      if (TREE_CODE (TREE_TYPE (x)) != BOOLEAN_TYPE)
+      if (!INTEGRAL_TYPE_P (TREE_TYPE (x)))
        {
-         error ("non-boolean used in condition");
+         error ("non-integral used in condition");
          return x;
        }
       if (!is_gimple_condexpr (x))
@@ -3280,9 +3231,6 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
     case NOP_EXPR:
     case CONVERT_EXPR:
     case FIX_TRUNC_EXPR:
-    case FIX_CEIL_EXPR:
-    case FIX_FLOOR_EXPR:
-    case FIX_ROUND_EXPR:
     case FLOAT_EXPR:
     case NEGATE_EXPR:
     case ABS_EXPR:
@@ -3332,7 +3280,36 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
        }
       *walk_subtrees = 0;
       break;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      /* PLUS_EXPR and MINUS_EXPR don't work on pointers, they should be done using
+        POINTER_PLUS_EXPR. */
+      if (POINTER_TYPE_P (TREE_TYPE (t)))
+       {
+         error ("invalid operand to plus/minus, type is a pointer");
+         return t;
+       }
+      CHECK_OP (0, "invalid operand to binary operator");
+      CHECK_OP (1, "invalid operand to binary operator");
+      break;
 
+    case POINTER_PLUS_EXPR:
+      /* Check to make sure the first operand is a pointer or reference type. */
+      if (!POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0))))
+       {
+         error ("invalid operand to pointer plus, first operand is not a pointer");
+         return t;
+       }
+      /* Check to make sure the second operand is an integer with type of
+        sizetype.  */
+      if (!useless_type_conversion_p (sizetype,
+                                    TREE_TYPE (TREE_OPERAND (t, 1))))
+       {
+         error ("invalid operand to pointer plus, second operand is not an "
+                "integer with type of sizetype.");
+         return t;
+       }
+      /* FALLTHROUGH */
     case LT_EXPR:
     case LE_EXPR:
     case GT_EXPR:
@@ -3347,8 +3324,6 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
     case UNGE_EXPR:
     case UNEQ_EXPR:
     case LTGT_EXPR:
-    case PLUS_EXPR:
-    case MINUS_EXPR:
     case MULT_EXPR:
     case TRUNC_DIV_EXPR:
     case CEIL_DIV_EXPR:
@@ -3373,6 +3348,11 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
       CHECK_OP (1, "invalid operand to binary operator");
       break;
 
+    case CONSTRUCTOR:
+      if (TREE_CONSTANT (t) && TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
+       *walk_subtrees = 0;
+      break;
+
     default:
       break;
     }
@@ -3381,1077 +3361,2705 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 #undef CHECK_OP
 }
 
-
-/* Verify STMT, return true if STMT is not in GIMPLE form.
-   TODO: Implement type checking.  */
+/* Verifies if EXPR is a valid GIMPLE unary expression.  Returns true
+   if there is an error, otherwise false.  */
 
 static bool
-verify_stmt (tree stmt, bool last_in_block)
+verify_gimple_unary_expr (const_tree expr)
 {
-  tree addr;
+  tree op = TREE_OPERAND (expr, 0);
+  tree type = TREE_TYPE (expr);
 
-  if (!is_gimple_stmt (stmt))
-    {
-      error ("is not a valid GIMPLE statement");
-      goto fail;
-    }
-
-  addr = walk_tree (&stmt, verify_expr, NULL, NULL);
-  if (addr)
+  if (!is_gimple_val (op))
     {
-      debug_generic_stmt (addr);
+      error ("invalid operand in unary expression");
       return true;
     }
 
-  /* If the statement is marked as part of an EH region, then it is
-     expected that the statement could throw.  Verify that when we
-     have optimizations that simplify statements such that we prove
-     that they cannot throw, that we update other data structures
-     to match.  */
-  if (lookup_stmt_eh_region (stmt) >= 0)
+  /* For general unary expressions we have the operations type
+     as the effective type the operation is carried out on.  So all
+     we need to require is that the operand is trivially convertible
+     to that type.  */
+  if (!useless_type_conversion_p (type, TREE_TYPE (op)))
     {
-      if (!tree_could_throw_p (stmt))
-       {
-         error ("statement marked for throw, but doesn%'t");
-         goto fail;
-       }
-      if (!last_in_block && tree_can_throw_internal (stmt))
-       {
-         error ("statement marked for throw in middle of block");
-         goto fail;
-       }
+      error ("type mismatch in unary expression");
+      debug_generic_expr (type);
+      debug_generic_expr (TREE_TYPE (op));
+      return true;
     }
 
   return false;
-
- fail:
-  debug_generic_stmt (stmt);
-  return true;
 }
 
-
-/* Return true when the T can be shared.  */
+/* Verifies if EXPR is a valid GIMPLE binary expression.  Returns true
+   if there is an error, otherwise false.  */
 
 static bool
-tree_node_can_be_shared (tree t)
+verify_gimple_binary_expr (const_tree expr)
 {
-  if (IS_TYPE_OR_DECL_P (t)
-      /* We check for constants explicitly since they are not considered
-        gimple invariants if they overflowed.  */
-      || CONSTANT_CLASS_P (t)
-      || is_gimple_min_invariant (t)
-      || TREE_CODE (t) == SSA_NAME
-      || t == error_mark_node)
-    return true;
-
-  if (TREE_CODE (t) == CASE_LABEL_EXPR)
-    return true;
+  tree op0 = TREE_OPERAND (expr, 0);
+  tree op1 = TREE_OPERAND (expr, 1);
+  tree type = TREE_TYPE (expr);
 
-  while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
-         /* We check for constants explicitly since they are not considered
-            gimple invariants if they overflowed.  */
-         && (CONSTANT_CLASS_P (TREE_OPERAND (t, 1))
-             || is_gimple_min_invariant (TREE_OPERAND (t, 1))))
-        || (TREE_CODE (t) == COMPONENT_REF
-            || TREE_CODE (t) == REALPART_EXPR
-            || TREE_CODE (t) == IMAGPART_EXPR))
-    t = TREE_OPERAND (t, 0);
+  if (!is_gimple_val (op0) || !is_gimple_val (op1))
+    {
+      error ("invalid operands in binary expression");
+      return true;
+    }
 
-  if (DECL_P (t))
-    return true;
+  /* For general binary expressions we have the operations type
+     as the effective type the operation is carried out on.  So all
+     we need to require is that both operands are trivially convertible
+     to that type.  */
+  if (!useless_type_conversion_p (type, TREE_TYPE (op0))
+      || !useless_type_conversion_p (type, TREE_TYPE (op1)))
+    {
+      error ("type mismatch in binary expression");
+      debug_generic_stmt (type);
+      debug_generic_stmt (TREE_TYPE (op0));
+      debug_generic_stmt (TREE_TYPE (op1));
+      return true;
+    }
 
   return false;
 }
 
+/* Verify if EXPR is either a GIMPLE ID or a GIMPLE indirect reference.
+   Returns true if there is an error, otherwise false.  */
 
-/* Called via walk_trees.  Verify tree sharing.  */
-
-static tree
-verify_node_sharing (tree * tp, int *walk_subtrees, void *data)
+static bool
+verify_gimple_min_lval (tree expr)
 {
-  htab_t htab = (htab_t) data;
-  void **slot;
+  tree op;
 
-  if (tree_node_can_be_shared (*tp))
+  if (is_gimple_id (expr))
+    return false;
+
+  if (TREE_CODE (expr) != INDIRECT_REF
+      && TREE_CODE (expr) != ALIGN_INDIRECT_REF
+      && TREE_CODE (expr) != MISALIGNED_INDIRECT_REF)
     {
-      *walk_subtrees = false;
-      return NULL;
+      error ("invalid expression for min lvalue");
+      return true;
     }
 
-  slot = htab_find_slot (htab, *tp, INSERT);
-  if (*slot)
-    return (tree) *slot;
-  *slot = *tp;
+  op = TREE_OPERAND (expr, 0);
+  if (!is_gimple_val (op))
+    {
+      error ("invalid operand in indirect reference");
+      debug_generic_stmt (op);
+      return true;
+    }
+  if (!useless_type_conversion_p (TREE_TYPE (expr),
+                                 TREE_TYPE (TREE_TYPE (op))))
+    {
+      error ("type mismatch in indirect reference");
+      debug_generic_stmt (TREE_TYPE (expr));
+      debug_generic_stmt (TREE_TYPE (TREE_TYPE (op)));
+      return true;
+    }
 
-  return NULL;
+  return false;
 }
 
+/* Verify if EXPR is a valid GIMPLE reference expression.  Returns true
+   if there is an error, otherwise false.  */
 
-/* Verify the GIMPLE statement chain.  */
-
-void
-verify_stmts (void)
+static bool
+verify_gimple_reference (tree expr)
 {
-  basic_block bb;
-  block_stmt_iterator bsi;
-  bool err = false;
-  htab_t htab;
-  tree addr;
-
-  timevar_push (TV_TREE_STMT_VERIFY);
-  htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
-
-  FOR_EACH_BB (bb)
+  while (handled_component_p (expr))
     {
-      tree phi;
-      int i;
+      tree op = TREE_OPERAND (expr, 0);
 
-      for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+      if (TREE_CODE (expr) == ARRAY_REF
+         || TREE_CODE (expr) == ARRAY_RANGE_REF)
        {
-         int phi_num_args = PHI_NUM_ARGS (phi);
-
-         if (bb_for_stmt (phi) != bb)
+         if (!is_gimple_val (TREE_OPERAND (expr, 1))
+             || (TREE_OPERAND (expr, 2)
+                 && !is_gimple_val (TREE_OPERAND (expr, 2)))
+             || (TREE_OPERAND (expr, 3)
+                 && !is_gimple_val (TREE_OPERAND (expr, 3))))
            {
-             error ("bb_for_stmt (phi) is set to a wrong basic block");
-             err |= true;
+             error ("invalid operands to array reference");
+             debug_generic_stmt (expr);
+             return true;
            }
+       }
 
-         for (i = 0; i < phi_num_args; i++)
-           {
-             tree t = PHI_ARG_DEF (phi, i);
-             tree addr;
-
-             /* Addressable variables do have SSA_NAMEs but they
-                are not considered gimple values.  */
-             if (TREE_CODE (t) != SSA_NAME
-                 && TREE_CODE (t) != FUNCTION_DECL
-                 && !is_gimple_val (t))
-               {
-                 error ("PHI def is not a GIMPLE value");
-                 debug_generic_stmt (phi);
-                 debug_generic_stmt (t);
-                 err |= true;
-               }
-
-             addr = walk_tree (&t, verify_expr, (void *) 1, NULL);
-             if (addr)
-               {
-                 debug_generic_stmt (addr);
-                 err |= true;
-               }
+      /* Verify if the reference array element types are compatible.  */
+      if (TREE_CODE (expr) == ARRAY_REF
+         && !useless_type_conversion_p (TREE_TYPE (expr),
+                                        TREE_TYPE (TREE_TYPE (op))))
+       {
+         error ("type mismatch in array reference");
+         debug_generic_stmt (TREE_TYPE (expr));
+         debug_generic_stmt (TREE_TYPE (TREE_TYPE (op)));
+         return true;
+       }
+      if (TREE_CODE (expr) == ARRAY_RANGE_REF
+         && !useless_type_conversion_p (TREE_TYPE (TREE_TYPE (expr)),
+                                        TREE_TYPE (TREE_TYPE (op))))
+       {
+         error ("type mismatch in array range reference");
+         debug_generic_stmt (TREE_TYPE (TREE_TYPE (expr)));
+         debug_generic_stmt (TREE_TYPE (TREE_TYPE (op)));
+         return true;
+       }
 
-             addr = walk_tree (&t, verify_node_sharing, htab, NULL);
-             if (addr)
-               {
-                 error ("incorrect sharing of tree nodes");
-                 debug_generic_stmt (phi);
-                 debug_generic_stmt (addr);
-                 err |= true;
-               }
-           }
+      if ((TREE_CODE (expr) == REALPART_EXPR
+          || TREE_CODE (expr) == IMAGPART_EXPR)
+         && !useless_type_conversion_p (TREE_TYPE (expr),
+                                        TREE_TYPE (TREE_TYPE (op))))
+       {
+         error ("type mismatch in real/imagpart reference");
+         debug_generic_stmt (TREE_TYPE (expr));
+         debug_generic_stmt (TREE_TYPE (TREE_TYPE (op)));
+         return true;
        }
 
-      for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
+      if (TREE_CODE (expr) == COMPONENT_REF
+         && !useless_type_conversion_p (TREE_TYPE (expr),
+                                        TREE_TYPE (TREE_OPERAND (expr, 1))))
        {
-         tree stmt = bsi_stmt (bsi);
+         error ("type mismatch in component reference");
+         debug_generic_stmt (TREE_TYPE (expr));
+         debug_generic_stmt (TREE_TYPE (TREE_OPERAND (expr, 1)));
+         return true;
+       }
 
-         if (bb_for_stmt (stmt) != bb)
-           {
-             error ("bb_for_stmt (stmt) is set to a wrong basic block");
-             err |= true;
-           }
+      /* For VIEW_CONVERT_EXPRs which are allowed here, too, there
+        is nothing to verify.  Gross mismatches at most invoke
+        undefined behavior.  */
 
-         bsi_next (&bsi);
-         err |= verify_stmt (stmt, bsi_end_p (bsi));
-         addr = walk_tree (&stmt, verify_node_sharing, htab, NULL);
-         if (addr)
-           {
-             error ("incorrect sharing of tree nodes");
-             debug_generic_stmt (stmt);
-             debug_generic_stmt (addr);
-             err |= true;
-           }
-       }
+      expr = op;
     }
 
-  if (err)
-    internal_error ("verify_stmts failed");
-
-  htab_delete (htab);
-  timevar_pop (TV_TREE_STMT_VERIFY);
+  return verify_gimple_min_lval (expr);
 }
 
+/* Returns true if there is one pointer type in TYPE_POINTER_TO (SRC_OBJ)
+   list of pointer-to types that is trivially convertible to DEST.  */
 
-/* Verifies that the flow information is OK.  */
-
-static int
-tree_verify_flow_info (void)
+static bool
+one_pointer_to_useless_type_conversion_p (tree dest, tree src_obj)
 {
-  int err = 0;
-  basic_block bb;
-  block_stmt_iterator bsi;
-  tree stmt;
+  tree src;
+
+  if (!TYPE_POINTER_TO (src_obj))
+    return true;
+
+  for (src = TYPE_POINTER_TO (src_obj); src; src = TYPE_NEXT_PTR_TO (src))
+    if (useless_type_conversion_p (dest, src))
+      return true;
+
+  return false;
+}
+
+/* Verify the GIMPLE expression EXPR.  Returns true if there is an
+   error, otherwise false.  */
+
+static bool
+verify_gimple_expr (tree expr)
+{
+  tree type = TREE_TYPE (expr);
+
+  if (is_gimple_val (expr))
+    return false;
+
+  /* Special codes we cannot handle via their class.  */
+  switch (TREE_CODE (expr))
+    {
+    case NOP_EXPR:
+    case CONVERT_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in conversion");
+           return true;
+         }
+
+       /* Allow conversions between integral types and between
+          pointer types.  */
+        if ((INTEGRAL_TYPE_P (type) && INTEGRAL_TYPE_P (TREE_TYPE (op)))
+           || (POINTER_TYPE_P (type) && POINTER_TYPE_P (TREE_TYPE (op))))
+         return false;
+
+       /* Allow conversions between integral types and pointers only if
+          there is no sign or zero extension involved.  */
+       if (((POINTER_TYPE_P (type) && INTEGRAL_TYPE_P (TREE_TYPE (op)))
+            || (POINTER_TYPE_P (TREE_TYPE (op)) && INTEGRAL_TYPE_P (type)))
+           && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (op)))
+         return false;
+
+       /* Allow conversion from integer to offset type and vice versa.  */
+       if ((TREE_CODE (type) == OFFSET_TYPE
+            && TREE_CODE (TREE_TYPE (op)) == INTEGER_TYPE)
+           || (TREE_CODE (type) == INTEGER_TYPE
+               && TREE_CODE (TREE_TYPE (op)) == OFFSET_TYPE))
+         return false;
+
+       /* Otherwise assert we are converting between types of the
+          same kind.  */
+       if (TREE_CODE (type) != TREE_CODE (TREE_TYPE (op)))
+         {
+           error ("invalid types in nop conversion");
+           debug_generic_expr (type);
+           debug_generic_expr (TREE_TYPE (op));
+           return true;
+         }
+
+       return false;
+      }
+
+    case FLOAT_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in int to float conversion");
+           return true;
+         }
+       if (!INTEGRAL_TYPE_P (TREE_TYPE (op))
+           || !SCALAR_FLOAT_TYPE_P (type))
+         {
+           error ("invalid types in conversion to floating point");
+           debug_generic_expr (type);
+           debug_generic_expr (TREE_TYPE (op));
+           return true;
+         }
+        return false;
+      }
+
+    case FIX_TRUNC_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in float to int conversion");
+           return true;
+         }
+       if (!INTEGRAL_TYPE_P (type)
+           || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (op)))
+         {
+           error ("invalid types in conversion to integer");
+           debug_generic_expr (type);
+           debug_generic_expr (TREE_TYPE (op));
+           return true;
+         }
+        return false;
+      }
+
+    case COMPLEX_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       if (!is_gimple_val (op0) || !is_gimple_val (op1))
+         {
+           error ("invalid operands in complex expression");
+           return true;
+         }
+       if (!TREE_CODE (type) == COMPLEX_TYPE
+           || !(TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE
+                || SCALAR_FLOAT_TYPE_P (TREE_TYPE (op0)))
+           || !(TREE_CODE (TREE_TYPE (op1)) == INTEGER_TYPE
+                || SCALAR_FLOAT_TYPE_P (TREE_TYPE (op1)))
+           || !useless_type_conversion_p (TREE_TYPE (type),
+                                          TREE_TYPE (op0))
+           || !useless_type_conversion_p (TREE_TYPE (type),
+                                          TREE_TYPE (op1)))
+         {
+           error ("type mismatch in complex expression");
+           debug_generic_stmt (TREE_TYPE (expr));
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           return true;
+         }
+       return false;
+      }
+
+    case CONSTRUCTOR:
+      {
+       /* This is used like COMPLEX_EXPR but for vectors.  */
+       if (TREE_CODE (type) != VECTOR_TYPE)
+         {
+           error ("constructor not allowed for non-vector types");
+           debug_generic_stmt (type);
+           return true;
+         }
+       /* FIXME: verify constructor arguments.  */
+       return false;
+      }
+
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+    case LROTATE_EXPR:
+    case RROTATE_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       if (!is_gimple_val (op0) || !is_gimple_val (op1))
+         {
+           error ("invalid operands in shift expression");
+           return true;
+         }
+       if (!TREE_CODE (TREE_TYPE (op1)) == INTEGER_TYPE
+           || !useless_type_conversion_p (type, TREE_TYPE (op0)))
+         {
+           error ("type mismatch in shift expression");
+           debug_generic_stmt (TREE_TYPE (expr));
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           return true;
+         }
+       return false;
+      }
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       if (POINTER_TYPE_P (type)
+           || POINTER_TYPE_P (TREE_TYPE (op0))
+           || POINTER_TYPE_P (TREE_TYPE (op1)))
+         {
+           error ("invalid (pointer) operands to plus/minus");
+           return true;
+         }
+       /* Continue with generic binary expression handling.  */
+       break;
+      }
+
+    case POINTER_PLUS_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       if (!is_gimple_val (op0) || !is_gimple_val (op1))
+         {
+           error ("invalid operands in pointer plus expression");
+           return true;
+         }
+       if (!POINTER_TYPE_P (TREE_TYPE (op0))
+           || !useless_type_conversion_p (type, TREE_TYPE (op0))
+           || !useless_type_conversion_p (sizetype, TREE_TYPE (op1)))
+         {
+           error ("type mismatch in pointer plus expression");
+           debug_generic_stmt (type);
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           return true;
+         }
+       return false;
+      }
+
+    case COND_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       tree op2 = TREE_OPERAND (expr, 2);
+       if ((!is_gimple_val (op1)
+            && TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE)
+           || (!is_gimple_val (op2)
+               && TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE))
+         {
+           error ("invalid operands in conditional expression");
+           return true;
+         }
+       if (!INTEGRAL_TYPE_P (TREE_TYPE (op0))
+           || (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE
+               && !useless_type_conversion_p (type, TREE_TYPE (op1)))
+           || (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE
+               && !useless_type_conversion_p (type, TREE_TYPE (op2))))
+         {
+           error ("type mismatch in conditional expression");
+           debug_generic_stmt (type);
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           debug_generic_stmt (TREE_TYPE (op2));
+           return true;
+         }
+       return verify_gimple_expr (op0);
+      }
+
+    case ADDR_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_addressable (op))
+         {
+           error ("invalid operand in unary expression");
+           return true;
+         }
+       if (!one_pointer_to_useless_type_conversion_p (type, TREE_TYPE (op))
+           /* FIXME: a longstanding wart, &a == &a[0].  */
+           && (TREE_CODE (TREE_TYPE (op)) != ARRAY_TYPE
+               || !one_pointer_to_useless_type_conversion_p (type,
+                     TREE_TYPE (TREE_TYPE (op)))))
+         {
+           error ("type mismatch in address expression");
+           debug_generic_stmt (TREE_TYPE (expr));
+           debug_generic_stmt (TYPE_POINTER_TO (TREE_TYPE (op)));
+           return true;
+         }
+
+       return verify_gimple_reference (op);
+      }
+
+    case TRUTH_ANDIF_EXPR:
+    case TRUTH_ORIF_EXPR:
+    case TRUTH_AND_EXPR:
+    case TRUTH_OR_EXPR:
+    case TRUTH_XOR_EXPR:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+
+       if (!is_gimple_val (op0) || !is_gimple_val (op1))
+         {
+           error ("invalid operands in truth expression");
+           return true;
+         }
+
+       /* We allow any kind of integral typed argument and result.  */
+       if (!INTEGRAL_TYPE_P (TREE_TYPE (op0))
+           || !INTEGRAL_TYPE_P (TREE_TYPE (op1))
+           || !INTEGRAL_TYPE_P (type))
+         {
+           error ("type mismatch in binary truth expression");
+           debug_generic_stmt (type);
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           return true;
+         }
+
+       return false;
+      }
+
+    case TRUTH_NOT_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in unary not");
+           return true;
+         }
+
+       /* For TRUTH_NOT_EXPR we can have any kind of integral
+          typed arguments and results.  */
+       if (!INTEGRAL_TYPE_P (TREE_TYPE (op))
+           || !INTEGRAL_TYPE_P (type))
+         {
+           error ("type mismatch in not expression");
+           debug_generic_expr (TREE_TYPE (expr));
+           debug_generic_expr (TREE_TYPE (op));
+           return true;
+         }
+
+       return false;
+      }
+
+    case CALL_EXPR:
+      /* FIXME.  The C frontend passes unpromoted arguments in case it
+        didn't see a function declaration before the call.  */
+      return false;
+
+    case OBJ_TYPE_REF:
+      /* FIXME.  */
+      return false;
+
+    default:;
+    }
+
+  /* Generic handling via classes.  */
+  switch (TREE_CODE_CLASS (TREE_CODE (expr)))
+    {
+    case tcc_unary:
+      return verify_gimple_unary_expr (expr);
+
+    case tcc_binary:
+      return verify_gimple_binary_expr (expr);
+
+    case tcc_reference:
+      return verify_gimple_reference (expr);
+
+    case tcc_comparison:
+      {
+       tree op0 = TREE_OPERAND (expr, 0);
+       tree op1 = TREE_OPERAND (expr, 1);
+       if (!is_gimple_val (op0) || !is_gimple_val (op1))
+         {
+           error ("invalid operands in comparison expression");
+           return true;
+         }
+       /* For comparisons we do not have the operations type as the
+          effective type the comparison is carried out in.  Instead
+          we require that either the first operand is trivially
+          convertible into the second, or the other way around.
+          The resulting type of a comparison may be any integral type.
+          Because we special-case pointers to void we allow
+          comparisons of pointers with the same mode as well.  */
+       if ((!useless_type_conversion_p (TREE_TYPE (op0), TREE_TYPE (op1))
+            && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0))
+            && (!POINTER_TYPE_P (TREE_TYPE (op0))
+                || !POINTER_TYPE_P (TREE_TYPE (op1))
+                || TYPE_MODE (TREE_TYPE (op0)) != TYPE_MODE (TREE_TYPE (op1))))
+           || !INTEGRAL_TYPE_P (type))
+         {
+           error ("type mismatch in comparison expression");
+           debug_generic_stmt (TREE_TYPE (expr));
+           debug_generic_stmt (TREE_TYPE (op0));
+           debug_generic_stmt (TREE_TYPE (op1));
+           return true;
+         }
+        break;
+      }
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return false;
+}
+
+/* Verify the GIMPLE assignment statement STMT.  Returns true if there
+   is an error, otherwise false.  */
+
+static bool
+verify_gimple_modify_stmt (const_tree stmt)
+{
+  tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+  tree rhs = GIMPLE_STMT_OPERAND (stmt, 1);
+
+  gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT);
+
+  if (!useless_type_conversion_p (TREE_TYPE (lhs),
+                                 TREE_TYPE (rhs)))
+    {
+      error ("non-trivial conversion at assignment");
+      debug_generic_expr (TREE_TYPE (lhs));
+      debug_generic_expr (TREE_TYPE (rhs));
+      return true;
+    }
+
+  /* Loads/stores from/to a variable are ok.  */
+  if ((is_gimple_val (lhs)
+       && is_gimple_variable (rhs))
+      || (is_gimple_val (rhs)
+         && is_gimple_variable (lhs)))
+    return false;
+
+  /* Aggregate copies are ok.  */
+  if (!is_gimple_reg_type (TREE_TYPE (lhs))
+      && !is_gimple_reg_type (TREE_TYPE (rhs)))
+    return false;
+
+  /* We might get 'loads' from a parameter which is not a gimple value.  */
+  if (TREE_CODE (rhs) == PARM_DECL)
+    return verify_gimple_expr (lhs);
+
+  if (!is_gimple_variable (lhs)
+      && verify_gimple_expr (lhs))
+    return true;
+
+  if (!is_gimple_variable (rhs)
+      && verify_gimple_expr (rhs))
+    return true;
+
+  return false;
+}
+
+/* Verify the GIMPLE statement STMT.  Returns true if there is an
+   error, otherwise false.  */
+
+static bool
+verify_gimple_stmt (tree stmt)
+{
+  if (!is_gimple_stmt (stmt))
+    {
+      error ("is not a valid GIMPLE statement");
+      return true;
+    }
+
+  if (OMP_DIRECTIVE_P (stmt))
+    {
+      /* OpenMP directives are validated by the FE and never operated
+        on by the optimizers.  Furthermore, OMP_FOR may contain
+        non-gimple expressions when the main index variable has had
+        its address taken.  This does not affect the loop itself
+        because the header of an OMP_FOR is merely used to determine
+        how to setup the parallel iteration.  */
+      return false;
+    }
+
+  switch (TREE_CODE (stmt))
+    {
+    case GIMPLE_MODIFY_STMT:
+      return verify_gimple_modify_stmt (stmt);
+
+    case GOTO_EXPR:
+    case LABEL_EXPR:
+      return false;
+
+    case SWITCH_EXPR:
+      if (!is_gimple_val (TREE_OPERAND (stmt, 0)))
+       {
+         error ("invalid operand to switch statement");
+         debug_generic_expr (TREE_OPERAND (stmt, 0));
+       }
+      return false;
+
+    case RETURN_EXPR:
+      {
+       tree op = TREE_OPERAND (stmt, 0);
+
+       if (TREE_CODE (TREE_TYPE (stmt)) != VOID_TYPE)
+         {
+           error ("type error in return expression");
+           return true;
+         }
+
+       if (op == NULL_TREE
+           || TREE_CODE (op) == RESULT_DECL)
+         return false;
+
+       return verify_gimple_modify_stmt (op);
+      }
+
+    case CALL_EXPR:
+    case COND_EXPR:
+      return verify_gimple_expr (stmt);
+
+    case NOP_EXPR:
+    case CHANGE_DYNAMIC_TYPE_EXPR:
+    case ASM_EXPR:
+      return false;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Verify the GIMPLE statements inside the statement list STMTS.
+   Returns true if there were any errors.  */
+
+static bool
+verify_gimple_2 (tree stmts)
+{
+  tree_stmt_iterator tsi;
+  bool err = false;
+
+  for (tsi = tsi_start (stmts); !tsi_end_p (tsi); tsi_next (&tsi))
+    {
+      tree stmt = tsi_stmt (tsi);
+
+      switch (TREE_CODE (stmt))
+       {
+       case BIND_EXPR:
+         err |= verify_gimple_2 (BIND_EXPR_BODY (stmt));
+         break;
+
+       case TRY_CATCH_EXPR:
+       case TRY_FINALLY_EXPR:
+         err |= verify_gimple_2 (TREE_OPERAND (stmt, 0));
+         err |= verify_gimple_2 (TREE_OPERAND (stmt, 1));
+         break;
+
+       case CATCH_EXPR:
+         err |= verify_gimple_2 (CATCH_BODY (stmt));
+         break;
+
+       case EH_FILTER_EXPR:
+         err |= verify_gimple_2 (EH_FILTER_FAILURE (stmt));
+         break;
+
+       default:
+         {
+           bool err2 = verify_gimple_stmt (stmt);
+           if (err2)
+             debug_generic_expr (stmt);
+           err |= err2;
+         }
+       }
+    }
+
+  return err;
+}
+
+
+/* Verify the GIMPLE statements inside the statement list STMTS.  */
+
+void
+verify_gimple_1 (tree stmts)
+{
+  if (verify_gimple_2 (stmts))
+    internal_error ("verify_gimple failed");
+}
+
+/* Verify the GIMPLE statements inside the current function.  */
+
+void
+verify_gimple (void)
+{
+  verify_gimple_1 (BIND_EXPR_BODY (DECL_SAVED_TREE (cfun->decl)));
+}
+
+/* Verify STMT, return true if STMT is not in GIMPLE form.
+   TODO: Implement type checking.  */
+
+static bool
+verify_stmt (tree stmt, bool last_in_block)
+{
+  tree addr;
+
+  if (OMP_DIRECTIVE_P (stmt))
+    {
+      /* OpenMP directives are validated by the FE and never operated
+        on by the optimizers.  Furthermore, OMP_FOR may contain
+        non-gimple expressions when the main index variable has had
+        its address taken.  This does not affect the loop itself
+        because the header of an OMP_FOR is merely used to determine
+        how to setup the parallel iteration.  */
+      return false;
+    }
+
+  if (!is_gimple_stmt (stmt))
+    {
+      error ("is not a valid GIMPLE statement");
+      goto fail;
+    }
+
+  addr = walk_tree (&stmt, verify_expr, NULL, NULL);
+  if (addr)
+    {
+      debug_generic_stmt (addr);
+      return true;
+    }
+
+  /* If the statement is marked as part of an EH region, then it is
+     expected that the statement could throw.  Verify that when we
+     have optimizations that simplify statements such that we prove
+     that they cannot throw, that we update other data structures
+     to match.  */
+  if (lookup_stmt_eh_region (stmt) >= 0)
+    {
+      if (!tree_could_throw_p (stmt))
+       {
+         error ("statement marked for throw, but doesn%'t");
+         goto fail;
+       }
+      if (!last_in_block && tree_can_throw_internal (stmt))
+       {
+         error ("statement marked for throw in middle of block");
+         goto fail;
+       }
+    }
+
+  return false;
+
+ fail:
+  debug_generic_stmt (stmt);
+  return true;
+}
+
+
+/* Return true when the T can be shared.  */
+
+static bool
+tree_node_can_be_shared (tree t)
+{
+  if (IS_TYPE_OR_DECL_P (t)
+      || is_gimple_min_invariant (t)
+      || TREE_CODE (t) == SSA_NAME
+      || t == error_mark_node
+      || TREE_CODE (t) == IDENTIFIER_NODE)
+    return true;
+
+  if (TREE_CODE (t) == CASE_LABEL_EXPR)
+    return true;
+
+  while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
+          && is_gimple_min_invariant (TREE_OPERAND (t, 1)))
+        || TREE_CODE (t) == COMPONENT_REF
+        || TREE_CODE (t) == REALPART_EXPR
+        || TREE_CODE (t) == IMAGPART_EXPR)
+    t = TREE_OPERAND (t, 0);
+
+  if (DECL_P (t))
+    return true;
+
+  return false;
+}
+
+
+/* Called via walk_trees.  Verify tree sharing.  */
+
+static tree
+verify_node_sharing (tree * tp, int *walk_subtrees, void *data)
+{
+  struct pointer_set_t *visited = (struct pointer_set_t *) data;
+
+  if (tree_node_can_be_shared (*tp))
+    {
+      *walk_subtrees = false;
+      return NULL;
+    }
+
+  if (pointer_set_insert (visited, *tp))
+    return *tp;
+
+  return NULL;
+}
+
+
+/* Helper function for verify_gimple_tuples.  */
+
+static tree
+verify_gimple_tuples_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+                        void *data ATTRIBUTE_UNUSED)
+{
+  switch (TREE_CODE (*tp))
+    {
+    case MODIFY_EXPR:
+      error ("unexpected non-tuple");
+      debug_tree (*tp);
+      gcc_unreachable ();
+      return NULL_TREE;
+
+    default:
+      return NULL_TREE;
+    }
+}
+
+/* Verify that there are no trees that should have been converted to
+   gimple tuples.  Return true if T contains a node that should have
+   been converted to a gimple tuple, but hasn't.  */
+
+static bool
+verify_gimple_tuples (tree t)
+{
+  return walk_tree (&t, verify_gimple_tuples_1, NULL, NULL) != NULL;
+}
+
+static bool eh_error_found;
+static int
+verify_eh_throw_stmt_node (void **slot, void *data)
+{
+  struct throw_stmt_node *node = (struct throw_stmt_node *)*slot;
+  struct pointer_set_t *visited = (struct pointer_set_t *) data;
+
+  if (!pointer_set_contains (visited, node->stmt))
+    {
+      error ("Dead STMT in EH table");
+      debug_generic_stmt (node->stmt);
+      eh_error_found = true;
+    }
+  return 0;
+}
+
+/* Verify the GIMPLE statement chain.  */
+
+void
+verify_stmts (void)
+{
+  basic_block bb;
+  block_stmt_iterator bsi;
+  bool err = false;
+  struct pointer_set_t *visited, *visited_stmts;
+  tree addr;
+
+  timevar_push (TV_TREE_STMT_VERIFY);
+  visited = pointer_set_create ();
+  visited_stmts = pointer_set_create ();
+
+  FOR_EACH_BB (bb)
+    {
+      tree phi;
+      int i;
+
+      for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+       {
+         int phi_num_args = PHI_NUM_ARGS (phi);
+
+         pointer_set_insert (visited_stmts, phi);
+         if (bb_for_stmt (phi) != bb)
+           {
+             error ("bb_for_stmt (phi) is set to a wrong basic block");
+             err |= true;
+           }
+
+         for (i = 0; i < phi_num_args; i++)
+           {
+             tree t = PHI_ARG_DEF (phi, i);
+             tree addr;
+
+             if (!t)
+               {
+                 error ("missing PHI def");
+                 debug_generic_stmt (phi);
+                 err |= true;
+                 continue;
+               }
+             /* Addressable variables do have SSA_NAMEs but they
+                are not considered gimple values.  */
+             else if (TREE_CODE (t) != SSA_NAME
+                      && TREE_CODE (t) != FUNCTION_DECL
+                      && !is_gimple_val (t))
+               {
+                 error ("PHI def is not a GIMPLE value");
+                 debug_generic_stmt (phi);
+                 debug_generic_stmt (t);
+                 err |= true;
+               }
+
+             addr = walk_tree (&t, verify_expr, (void *) 1, NULL);
+             if (addr)
+               {
+                 debug_generic_stmt (addr);
+                 err |= true;
+               }
+
+             addr = walk_tree (&t, verify_node_sharing, visited, NULL);
+             if (addr)
+               {
+                 error ("incorrect sharing of tree nodes");
+                 debug_generic_stmt (phi);
+                 debug_generic_stmt (addr);
+                 err |= true;
+               }
+           }
+       }
+
+      for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
+       {
+         tree stmt = bsi_stmt (bsi);
+
+         pointer_set_insert (visited_stmts, stmt);
+         err |= verify_gimple_tuples (stmt);
+
+         if (bb_for_stmt (stmt) != bb)
+           {
+             error ("bb_for_stmt (stmt) is set to a wrong basic block");
+             err |= true;
+           }
+
+         bsi_next (&bsi);
+         err |= verify_stmt (stmt, bsi_end_p (bsi));
+         addr = walk_tree (&stmt, verify_node_sharing, visited, NULL);
+         if (addr)
+           {
+             error ("incorrect sharing of tree nodes");
+             debug_generic_stmt (stmt);
+             debug_generic_stmt (addr);
+             err |= true;
+           }
+       }
+    }
+  eh_error_found = false;
+  if (get_eh_throw_stmt_table (cfun))
+    htab_traverse (get_eh_throw_stmt_table (cfun),
+                  verify_eh_throw_stmt_node,
+                  visited_stmts);
+
+  if (err | eh_error_found)
+    internal_error ("verify_stmts failed");
+
+  pointer_set_destroy (visited);
+  pointer_set_destroy (visited_stmts);
+  verify_histograms ();
+  timevar_pop (TV_TREE_STMT_VERIFY);
+}
+
+
+/* Verifies that the flow information is OK.  */
+
+static int
+tree_verify_flow_info (void)
+{
+  int err = 0;
+  basic_block bb;
+  block_stmt_iterator bsi;
+  tree stmt;
+  edge e;
+  edge_iterator ei;
+
+  if (ENTRY_BLOCK_PTR->il.tree)
+    {
+      error ("ENTRY_BLOCK has IL associated with it");
+      err = 1;
+    }
+
+  if (EXIT_BLOCK_PTR->il.tree)
+    {
+      error ("EXIT_BLOCK has IL associated with it");
+      err = 1;
+    }
+
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+    if (e->flags & EDGE_FALLTHRU)
+      {
+       error ("fallthru to exit from bb %d", e->src->index);
+       err = 1;
+      }
+
+  FOR_EACH_BB (bb)
+    {
+      bool found_ctrl_stmt = false;
+
+      stmt = NULL_TREE;
+
+      /* Skip labels on the start of basic block.  */
+      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+       {
+         tree prev_stmt = stmt;
+
+         stmt = bsi_stmt (bsi);
+
+         if (TREE_CODE (stmt) != LABEL_EXPR)
+           break;
+
+         if (prev_stmt && DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
+           {
+             error ("nonlocal label ");
+             print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
+             fprintf (stderr, " is not first in a sequence of labels in bb %d",
+                      bb->index);
+             err = 1;
+           }
+
+         if (label_to_block (LABEL_EXPR_LABEL (stmt)) != bb)
+           {
+             error ("label ");
+             print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
+             fprintf (stderr, " to block does not match in bb %d",
+                      bb->index);
+             err = 1;
+           }
+
+         if (decl_function_context (LABEL_EXPR_LABEL (stmt))
+             != current_function_decl)
+           {
+             error ("label ");
+             print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
+             fprintf (stderr, " has incorrect context in bb %d",
+                      bb->index);
+             err = 1;
+           }
+       }
+
+      /* Verify that body of basic block BB is free of control flow.  */
+      for (; !bsi_end_p (bsi); bsi_next (&bsi))
+       {
+         tree stmt = bsi_stmt (bsi);
+
+         if (found_ctrl_stmt)
+           {
+             error ("control flow in the middle of basic block %d",
+                    bb->index);
+             err = 1;
+           }
+
+         if (stmt_ends_bb_p (stmt))
+           found_ctrl_stmt = true;
+
+         if (TREE_CODE (stmt) == LABEL_EXPR)
+           {
+             error ("label ");
+             print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
+             fprintf (stderr, " in the middle of basic block %d", bb->index);
+             err = 1;
+           }
+       }
+
+      bsi = bsi_last (bb);
+      if (bsi_end_p (bsi))
+       continue;
+
+      stmt = bsi_stmt (bsi);
+
+      err |= verify_eh_edges (stmt);
+
+      if (is_ctrl_stmt (stmt))
+       {
+         FOR_EACH_EDGE (e, ei, bb->succs)
+           if (e->flags & EDGE_FALLTHRU)
+             {
+               error ("fallthru edge after a control statement in bb %d",
+                      bb->index);
+               err = 1;
+             }
+       }
+
+      if (TREE_CODE (stmt) != COND_EXPR)
+       {
+         /* Verify that there are no edges with EDGE_TRUE/FALSE_FLAG set
+            after anything else but if statement.  */
+         FOR_EACH_EDGE (e, ei, bb->succs)
+           if (e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
+             {
+               error ("true/false edge after a non-COND_EXPR in bb %d",
+                      bb->index);
+               err = 1;
+             }
+       }
+
+      switch (TREE_CODE (stmt))
+       {
+       case COND_EXPR:
+         {
+           edge true_edge;
+           edge false_edge;
+  
+           if (COND_EXPR_THEN (stmt) != NULL_TREE
+               || COND_EXPR_ELSE (stmt) != NULL_TREE)
+             {
+               error ("COND_EXPR with code in branches at the end of bb %d",
+                      bb->index);
+               err = 1;
+             }
+
+           extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
+
+           if (!true_edge || !false_edge
+               || !(true_edge->flags & EDGE_TRUE_VALUE)
+               || !(false_edge->flags & EDGE_FALSE_VALUE)
+               || (true_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
+               || (false_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
+               || EDGE_COUNT (bb->succs) >= 3)
+             {
+               error ("wrong outgoing edge flags at end of bb %d",
+                      bb->index);
+               err = 1;
+             }
+         }
+         break;
+
+       case GOTO_EXPR:
+         if (simple_goto_p (stmt))
+           {
+             error ("explicit goto at end of bb %d", bb->index);
+             err = 1;
+           }
+         else
+           {
+             /* FIXME.  We should double check that the labels in the
+                destination blocks have their address taken.  */
+             FOR_EACH_EDGE (e, ei, bb->succs)
+               if ((e->flags & (EDGE_FALLTHRU | EDGE_TRUE_VALUE
+                                | EDGE_FALSE_VALUE))
+                   || !(e->flags & EDGE_ABNORMAL))
+                 {
+                   error ("wrong outgoing edge flags at end of bb %d",
+                          bb->index);
+                   err = 1;
+                 }
+           }
+         break;
+
+       case RETURN_EXPR:
+         if (!single_succ_p (bb)
+             || (single_succ_edge (bb)->flags
+                 & (EDGE_FALLTHRU | EDGE_ABNORMAL
+                    | EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+           {
+             error ("wrong outgoing edge flags at end of bb %d", bb->index);
+             err = 1;
+           }
+         if (single_succ (bb) != EXIT_BLOCK_PTR)
+           {
+             error ("return edge does not point to exit in bb %d",
+                    bb->index);
+             err = 1;
+           }
+         break;
+
+       case SWITCH_EXPR:
+         {
+           tree prev;
+           edge e;
+           size_t i, n;
+           tree vec;
+
+           vec = SWITCH_LABELS (stmt);
+           n = TREE_VEC_LENGTH (vec);
+
+           /* Mark all the destination basic blocks.  */
+           for (i = 0; i < n; ++i)
+             {
+               tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
+               basic_block label_bb = label_to_block (lab);
+
+               gcc_assert (!label_bb->aux || label_bb->aux == (void *)1);
+               label_bb->aux = (void *)1;
+             }
+
+           /* Verify that the case labels are sorted.  */
+           prev = TREE_VEC_ELT (vec, 0);
+           for (i = 1; i < n - 1; ++i)
+             {
+               tree c = TREE_VEC_ELT (vec, i);
+               if (! CASE_LOW (c))
+                 {
+                   error ("found default case not at end of case vector");
+                   err = 1;
+                   continue;
+                 }
+               if (! tree_int_cst_lt (CASE_LOW (prev), CASE_LOW (c)))
+                 {
+                   error ("case labels not sorted: ");
+                   print_generic_expr (stderr, prev, 0);
+                   fprintf (stderr," is greater than ");
+                   print_generic_expr (stderr, c, 0);
+                   fprintf (stderr," but comes before it.\n");
+                   err = 1;
+                 }
+               prev = c;
+             }
+           if (CASE_LOW (TREE_VEC_ELT (vec, n - 1)))
+             {
+               error ("no default case found at end of case vector");
+               err = 1;
+             }
+
+           FOR_EACH_EDGE (e, ei, bb->succs)
+             {
+               if (!e->dest->aux)
+                 {
+                   error ("extra outgoing edge %d->%d",
+                          bb->index, e->dest->index);
+                   err = 1;
+                 }
+               e->dest->aux = (void *)2;
+               if ((e->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL
+                                | EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+                 {
+                   error ("wrong outgoing edge flags at end of bb %d",
+                          bb->index);
+                   err = 1;
+                 }
+             }
+
+           /* Check that we have all of them.  */
+           for (i = 0; i < n; ++i)
+             {
+               tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
+               basic_block label_bb = label_to_block (lab);
+
+               if (label_bb->aux != (void *)2)
+                 {
+                   error ("missing edge %i->%i",
+                          bb->index, label_bb->index);
+                   err = 1;
+                 }
+             }
+
+           FOR_EACH_EDGE (e, ei, bb->succs)
+             e->dest->aux = (void *)0;
+         }
+
+       default: ;
+       }
+    }
+
+  if (dom_info_state (CDI_DOMINATORS) >= DOM_NO_FAST_QUERY)
+    verify_dominators (CDI_DOMINATORS);
+
+  return err;
+}
+
+
+/* Updates phi nodes after creating a forwarder block joined
+   by edge FALLTHRU.  */
+
+static void
+tree_make_forwarder_block (edge fallthru)
+{
+  edge e;
+  edge_iterator ei;
+  basic_block dummy, bb;
+  tree phi, new_phi, var;
+
+  dummy = fallthru->src;
+  bb = fallthru->dest;
+
+  if (single_pred_p (bb))
+    return;
+
+  /* If we redirected a branch we must create new PHI nodes at the
+     start of BB.  */
+  for (phi = phi_nodes (dummy); phi; phi = PHI_CHAIN (phi))
+    {
+      var = PHI_RESULT (phi);
+      new_phi = create_phi_node (var, bb);
+      SSA_NAME_DEF_STMT (var) = new_phi;
+      SET_PHI_RESULT (phi, make_ssa_name (SSA_NAME_VAR (var), phi));
+      add_phi_arg (new_phi, PHI_RESULT (phi), fallthru);
+    }
+
+  /* Ensure that the PHI node chain is in the same order.  */
+  set_phi_nodes (bb, phi_reverse (phi_nodes (bb)));
+
+  /* Add the arguments we have stored on edges.  */
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (e == fallthru)
+       continue;
+
+      flush_pending_stmts (e);
+    }
+}
+
+
+/* Return a non-special label in the head of basic block BLOCK.
+   Create one if it doesn't exist.  */
+
+tree
+tree_block_label (basic_block bb)
+{
+  block_stmt_iterator i, s = bsi_start (bb);
+  bool first = true;
+  tree label, stmt;
+
+  for (i = s; !bsi_end_p (i); first = false, bsi_next (&i))
+    {
+      stmt = bsi_stmt (i);
+      if (TREE_CODE (stmt) != LABEL_EXPR)
+       break;
+      label = LABEL_EXPR_LABEL (stmt);
+      if (!DECL_NONLOCAL (label))
+       {
+         if (!first)
+           bsi_move_before (&i, &s);
+         return label;
+       }
+    }
+
+  label = create_artificial_label ();
+  stmt = build1 (LABEL_EXPR, void_type_node, label);
+  bsi_insert_before (&s, stmt, BSI_NEW_STMT);
+  return label;
+}
+
+
+/* Attempt to perform edge redirection by replacing a possibly complex
+   jump instruction by a goto or by removing the jump completely.
+   This can apply only if all edges now point to the same block.  The
+   parameters and return values are equivalent to
+   redirect_edge_and_branch.  */
+
+static edge
+tree_try_redirect_by_replacing_jump (edge e, basic_block target)
+{
+  basic_block src = e->src;
+  block_stmt_iterator b;
+  tree stmt;
+
+  /* We can replace or remove a complex jump only when we have exactly
+     two edges.  */
+  if (EDGE_COUNT (src->succs) != 2
+      /* Verify that all targets will be TARGET.  Specifically, the
+        edge that is not E must also go to TARGET.  */
+      || EDGE_SUCC (src, EDGE_SUCC (src, 0) == e)->dest != target)
+    return NULL;
+
+  b = bsi_last (src);
+  if (bsi_end_p (b))
+    return NULL;
+  stmt = bsi_stmt (b);
+
+  if (TREE_CODE (stmt) == COND_EXPR
+      || TREE_CODE (stmt) == SWITCH_EXPR)
+    {
+      bsi_remove (&b, true);
+      e = ssa_redirect_edge (e, target);
+      e->flags = EDGE_FALLTHRU;
+      return e;
+    }
+
+  return NULL;
+}
+
+
+/* Redirect E to DEST.  Return NULL on failure.  Otherwise, return the
+   edge representing the redirected branch.  */
+
+static edge
+tree_redirect_edge_and_branch (edge e, basic_block dest)
+{
+  basic_block bb = e->src;
+  block_stmt_iterator bsi;
+  edge ret;
+  tree stmt;
+
+  if (e->flags & EDGE_ABNORMAL)
+    return NULL;
+
+  if (e->src != ENTRY_BLOCK_PTR
+      && (ret = tree_try_redirect_by_replacing_jump (e, dest)))
+    return ret;
+
+  if (e->dest == dest)
+    return NULL;
+
+  bsi = bsi_last (bb);
+  stmt = bsi_end_p (bsi) ? NULL : bsi_stmt (bsi);
+
+  switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
+    {
+    case COND_EXPR:
+      /* For COND_EXPR, we only need to redirect the edge.  */
+      break;
+
+    case GOTO_EXPR:
+      /* No non-abnormal edges should lead from a non-simple goto, and
+        simple ones should be represented implicitly.  */
+      gcc_unreachable ();
+
+    case SWITCH_EXPR:
+      {
+        tree cases = get_cases_for_edge (e, stmt);
+       tree label = tree_block_label (dest);
+
+       /* If we have a list of cases associated with E, then use it
+          as it's a lot faster than walking the entire case vector.  */
+       if (cases)
+         {
+           edge e2 = find_edge (e->src, dest);
+           tree last, first;
+
+           first = cases;
+           while (cases)
+             {
+               last = cases;
+               CASE_LABEL (cases) = label;
+               cases = TREE_CHAIN (cases);
+             }
+
+           /* If there was already an edge in the CFG, then we need
+              to move all the cases associated with E to E2.  */
+           if (e2)
+             {
+               tree cases2 = get_cases_for_edge (e2, stmt);
+
+               TREE_CHAIN (last) = TREE_CHAIN (cases2);
+               TREE_CHAIN (cases2) = first;
+             }
+         }
+       else
+         {
+           tree vec = SWITCH_LABELS (stmt);
+           size_t i, n = TREE_VEC_LENGTH (vec);
+
+           for (i = 0; i < n; i++)
+             {
+               tree elt = TREE_VEC_ELT (vec, i);
+
+               if (label_to_block (CASE_LABEL (elt)) == e->dest)
+                 CASE_LABEL (elt) = label;
+             }
+         }
+
+       break;
+      }
+
+    case RETURN_EXPR:
+      bsi_remove (&bsi, true);
+      e->flags |= EDGE_FALLTHRU;
+      break;
+
+    case OMP_RETURN:
+    case OMP_CONTINUE:
+    case OMP_SECTIONS_SWITCH:
+    case OMP_FOR:
+      /* The edges from OMP constructs can be simply redirected.  */
+      break;
+
+    default:
+      /* Otherwise it must be a fallthru edge, and we don't need to
+        do anything besides redirecting it.  */
+      gcc_assert (e->flags & EDGE_FALLTHRU);
+      break;
+    }
+
+  /* Update/insert PHI nodes as necessary.  */
+
+  /* Now update the edges in the CFG.  */
+  e = ssa_redirect_edge (e, dest);
+
+  return e;
+}
+
+/* Returns true if it is possible to remove edge E by redirecting
+   it to the destination of the other edge from E->src.  */
+
+static bool
+tree_can_remove_branch_p (const_edge e)
+{
+  if (e->flags & EDGE_ABNORMAL)
+    return false;
+
+  return true;
+}
+
+/* Simple wrapper, as we can always redirect fallthru edges.  */
+
+static basic_block
+tree_redirect_edge_and_branch_force (edge e, basic_block dest)
+{
+  e = tree_redirect_edge_and_branch (e, dest);
+  gcc_assert (e);
+
+  return NULL;
+}
+
+
+/* Splits basic block BB after statement STMT (but at least after the
+   labels).  If STMT is NULL, BB is split just after the labels.  */
+
+static basic_block
+tree_split_block (basic_block bb, void *stmt)
+{
+  block_stmt_iterator bsi;
+  tree_stmt_iterator tsi_tgt;
+  tree act, list;
+  basic_block new_bb;
+  edge e;
+  edge_iterator ei;
+
+  new_bb = create_empty_bb (bb);
+
+  /* Redirect the outgoing edges.  */
+  new_bb->succs = bb->succs;
+  bb->succs = NULL;
+  FOR_EACH_EDGE (e, ei, new_bb->succs)
+    e->src = new_bb;
+
+  if (stmt && TREE_CODE ((tree) stmt) == LABEL_EXPR)
+    stmt = NULL;
+
+  /* Move everything from BSI to the new basic block.  */
+  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+    {
+      act = bsi_stmt (bsi);
+      if (TREE_CODE (act) == LABEL_EXPR)
+       continue;
+
+      if (!stmt)
+       break;
+
+      if (stmt == act)
+       {
+         bsi_next (&bsi);
+         break;
+       }
+    }
+
+  if (bsi_end_p (bsi))
+    return new_bb;
+
+  /* Split the statement list - avoid re-creating new containers as this
+     brings ugly quadratic memory consumption in the inliner.  
+     (We are still quadratic since we need to update stmt BB pointers,
+     sadly.)  */
+  list = tsi_split_statement_list_before (&bsi.tsi);
+  set_bb_stmt_list (new_bb, list);
+  for (tsi_tgt = tsi_start (list);
+       !tsi_end_p (tsi_tgt); tsi_next (&tsi_tgt))
+    change_bb_for_stmt (tsi_stmt (tsi_tgt), new_bb);
+
+  return new_bb;
+}
+
+
+/* Moves basic block BB after block AFTER.  */
+
+static bool
+tree_move_block_after (basic_block bb, basic_block after)
+{
+  if (bb->prev_bb == after)
+    return true;
+
+  unlink_block (bb);
+  link_block (bb, after);
+
+  return true;
+}
+
+
+/* Return true if basic_block can be duplicated.  */
+
+static bool
+tree_can_duplicate_bb_p (const_basic_block bb ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+
+/* Create a duplicate of the basic block BB.  NOTE: This does not
+   preserve SSA form.  */
+
+static basic_block
+tree_duplicate_bb (basic_block bb)
+{
+  basic_block new_bb;
+  block_stmt_iterator bsi, bsi_tgt;
+  tree phi;
+
+  new_bb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
+
+  /* Copy the PHI nodes.  We ignore PHI node arguments here because
+     the incoming edges have not been setup yet.  */
+  for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+    {
+      tree copy = create_phi_node (PHI_RESULT (phi), new_bb);
+      create_new_def_for (PHI_RESULT (copy), copy, PHI_RESULT_PTR (copy));
+    }
+
+  /* Keep the chain of PHI nodes in the same order so that they can be
+     updated by ssa_redirect_edge.  */
+  set_phi_nodes (new_bb, phi_reverse (phi_nodes (new_bb)));
+
+  bsi_tgt = bsi_start (new_bb);
+  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+    {
+      def_operand_p def_p;
+      ssa_op_iter op_iter;
+      tree stmt, copy;
+      int region;
+
+      stmt = bsi_stmt (bsi);
+      if (TREE_CODE (stmt) == LABEL_EXPR)
+       continue;
+
+      /* Create a new copy of STMT and duplicate STMT's virtual
+        operands.  */
+      copy = unshare_expr (stmt);
+      bsi_insert_after (&bsi_tgt, copy, BSI_NEW_STMT);
+      copy_virtual_operands (copy, stmt);
+      region = lookup_stmt_eh_region (stmt);
+      if (region >= 0)
+       add_stmt_to_eh_region (copy, region);
+      gimple_duplicate_stmt_histograms (cfun, copy, cfun, stmt);
+
+      /* Create new names for all the definitions created by COPY and
+        add replacement mappings for each new name.  */
+      FOR_EACH_SSA_DEF_OPERAND (def_p, copy, op_iter, SSA_OP_ALL_DEFS)
+       create_new_def_for (DEF_FROM_PTR (def_p), copy, def_p);
+    }
+
+  return new_bb;
+}
+
+/* Adds phi node arguments for edge E_COPY after basic block duplication.  */
+
+static void
+add_phi_args_after_copy_edge (edge e_copy)
+{
+  basic_block bb, bb_copy = e_copy->src, dest;
   edge e;
   edge_iterator ei;
+  tree phi, phi_copy, phi_next, def;
 
-  if (ENTRY_BLOCK_PTR->stmt_list)
-    {
-      error ("ENTRY_BLOCK has a statement list associated with it");
-      err = 1;
-    }
+  if (!phi_nodes (e_copy->dest))
+    return;
 
-  if (EXIT_BLOCK_PTR->stmt_list)
-    {
-      error ("EXIT_BLOCK has a statement list associated with it");
-      err = 1;
-    }
+  bb = bb_copy->flags & BB_DUPLICATED ? get_bb_original (bb_copy) : bb_copy;
 
-  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
-    if (e->flags & EDGE_FALLTHRU)
-      {
-       error ("fallthru to exit from bb %d", e->src->index);
-       err = 1;
-      }
+  if (e_copy->dest->flags & BB_DUPLICATED)
+    dest = get_bb_original (e_copy->dest);
+  else
+    dest = e_copy->dest;
 
-  FOR_EACH_BB (bb)
+  e = find_edge (bb, dest);
+  if (!e)
     {
-      bool found_ctrl_stmt = false;
+      /* During loop unrolling the target of the latch edge is copied.
+        In this case we are not looking for edge to dest, but to
+        duplicated block whose original was dest.  */
+      FOR_EACH_EDGE (e, ei, bb->succs)
+       {
+         if ((e->dest->flags & BB_DUPLICATED)
+             && get_bb_original (e->dest) == dest)
+           break;
+       }
 
-      stmt = NULL_TREE;
+      gcc_assert (e != NULL);
+    }
 
-      /* Skip labels on the start of basic block.  */
-      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
-       {
-         tree prev_stmt = stmt;
+  for (phi = phi_nodes (e->dest), phi_copy = phi_nodes (e_copy->dest);
+       phi;
+       phi = phi_next, phi_copy = PHI_CHAIN (phi_copy))
+    {
+      phi_next = PHI_CHAIN (phi);
+      def = PHI_ARG_DEF_FROM_EDGE (phi, e);
+      add_phi_arg (phi_copy, def, e_copy);
+    }
+}
 
-         stmt = bsi_stmt (bsi);
 
-         if (TREE_CODE (stmt) != LABEL_EXPR)
-           break;
+/* Basic block BB_COPY was created by code duplication.  Add phi node
+   arguments for edges going out of BB_COPY.  The blocks that were
+   duplicated have BB_DUPLICATED set.  */
 
-         if (prev_stmt && DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
-           {
-             error ("nonlocal label %s is not first "
-                    "in a sequence of labels in bb %d",
-                    IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
-                    bb->index);
-             err = 1;
-           }
+void
+add_phi_args_after_copy_bb (basic_block bb_copy)
+{
+  edge_iterator ei;
+  edge e_copy;
 
-         if (label_to_block (LABEL_EXPR_LABEL (stmt)) != bb)
-           {
-             error ("label %s to block does not match in bb %d",
-                    IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
-                    bb->index);
-             err = 1;
-           }
+  FOR_EACH_EDGE (e_copy, ei, bb_copy->succs)
+    {
+      add_phi_args_after_copy_edge (e_copy);
+    }
+}
 
-         if (decl_function_context (LABEL_EXPR_LABEL (stmt))
-             != current_function_decl)
-           {
-             error ("label %s has incorrect context in bb %d",
-                    IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
-                    bb->index);
-             err = 1;
-           }
-       }
+/* Blocks in REGION_COPY array of length N_REGION were created by
+   duplication of basic blocks.  Add phi node arguments for edges
+   going from these blocks.  If E_COPY is not NULL, also add
+   phi node arguments for its destination.*/
 
-      /* Verify that body of basic block BB is free of control flow.  */
-      for (; !bsi_end_p (bsi); bsi_next (&bsi))
-       {
-         tree stmt = bsi_stmt (bsi);
+void
+add_phi_args_after_copy (basic_block *region_copy, unsigned n_region,
+                        edge e_copy)
+{
+  unsigned i;
 
-         if (found_ctrl_stmt)
-           {
-             error ("control flow in the middle of basic block %d",
-                    bb->index);
-             err = 1;
-           }
+  for (i = 0; i < n_region; i++)
+    region_copy[i]->flags |= BB_DUPLICATED;
 
-         if (stmt_ends_bb_p (stmt))
-           found_ctrl_stmt = true;
+  for (i = 0; i < n_region; i++)
+    add_phi_args_after_copy_bb (region_copy[i]);
+  if (e_copy)
+    add_phi_args_after_copy_edge (e_copy);
 
-         if (TREE_CODE (stmt) == LABEL_EXPR)
-           {
-             error ("label %s in the middle of basic block %d",
-                    IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
-                    bb->index);
-             err = 1;
-           }
-       }
-      bsi = bsi_last (bb);
-      if (bsi_end_p (bsi))
-       continue;
+  for (i = 0; i < n_region; i++)
+    region_copy[i]->flags &= ~BB_DUPLICATED;
+}
 
-      stmt = bsi_stmt (bsi);
+/* Duplicates a REGION (set of N_REGION basic blocks) with just a single
+   important exit edge EXIT.  By important we mean that no SSA name defined
+   inside region is live over the other exit edges of the region.  All entry
+   edges to the region must go to ENTRY->dest.  The edge ENTRY is redirected
+   to the duplicate of the region.  SSA form, dominance and loop information
+   is updated.  The new basic blocks are stored to REGION_COPY in the same
+   order as they had in REGION, provided that REGION_COPY is not NULL.
+   The function returns false if it is unable to copy the region,
+   true otherwise.  */
 
-      err |= verify_eh_edges (stmt);
+bool
+tree_duplicate_sese_region (edge entry, edge exit,
+                           basic_block *region, unsigned n_region,
+                           basic_block *region_copy)
+{
+  unsigned i;
+  bool free_region_copy = false, copying_header = false;
+  struct loop *loop = entry->dest->loop_father;
+  edge exit_copy;
+  VEC (basic_block, heap) *doms;
+  edge redirected;
+  int total_freq = 0, entry_freq = 0;
+  gcov_type total_count = 0, entry_count = 0;
 
-      if (is_ctrl_stmt (stmt))
-       {
-         FOR_EACH_EDGE (e, ei, bb->succs)
-           if (e->flags & EDGE_FALLTHRU)
-             {
-               error ("fallthru edge after a control statement in bb %d",
-                      bb->index);
-               err = 1;
-             }
-       }
+  if (!can_copy_bbs_p (region, n_region))
+    return false;
 
-      switch (TREE_CODE (stmt))
-       {
-       case COND_EXPR:
-         {
-           edge true_edge;
-           edge false_edge;
-           if (TREE_CODE (COND_EXPR_THEN (stmt)) != GOTO_EXPR
-               || TREE_CODE (COND_EXPR_ELSE (stmt)) != GOTO_EXPR)
-             {
-               error ("structured COND_EXPR at the end of bb %d", bb->index);
-               err = 1;
-             }
+  /* Some sanity checking.  Note that we do not check for all possible
+     missuses of the functions.  I.e. if you ask to copy something weird,
+     it will work, but the state of structures probably will not be
+     correct.  */
+  for (i = 0; i < n_region; i++)
+    {
+      /* We do not handle subloops, i.e. all the blocks must belong to the
+        same loop.  */
+      if (region[i]->loop_father != loop)
+       return false;
 
-           extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
+      if (region[i] != entry->dest
+         && region[i] == loop->header)
+       return false;
+    }
 
-           if (!true_edge || !false_edge
-               || !(true_edge->flags & EDGE_TRUE_VALUE)
-               || !(false_edge->flags & EDGE_FALSE_VALUE)
-               || (true_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
-               || (false_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
-               || EDGE_COUNT (bb->succs) >= 3)
-             {
-               error ("wrong outgoing edge flags at end of bb %d",
-                      bb->index);
-               err = 1;
-             }
+  set_loop_copy (loop, loop);
 
-           if (!has_label_p (true_edge->dest,
-                             GOTO_DESTINATION (COND_EXPR_THEN (stmt))))
-             {
-               error ("%<then%> label does not match edge at end of bb %d",
-                      bb->index);
-               err = 1;
-             }
+  /* In case the function is used for loop header copying (which is the primary
+     use), ensure that EXIT and its copy will be new latch and entry edges.  */
+  if (loop->header == entry->dest)
+    {
+      copying_header = true;
+      set_loop_copy (loop, loop_outer (loop));
 
-           if (!has_label_p (false_edge->dest,
-                             GOTO_DESTINATION (COND_EXPR_ELSE (stmt))))
-             {
-               error ("%<else%> label does not match edge at end of bb %d",
-                      bb->index);
-               err = 1;
-             }
-         }
-         break;
+      if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit->src))
+       return false;
 
-       case GOTO_EXPR:
-         if (simple_goto_p (stmt))
-           {
-             error ("explicit goto at end of bb %d", bb->index);
-             err = 1;
-           }
-         else
-           {
-             /* FIXME.  We should double check that the labels in the 
-                destination blocks have their address taken.  */
-             FOR_EACH_EDGE (e, ei, bb->succs)
-               if ((e->flags & (EDGE_FALLTHRU | EDGE_TRUE_VALUE
-                                | EDGE_FALSE_VALUE))
-                   || !(e->flags & EDGE_ABNORMAL))
-                 {
-                   error ("wrong outgoing edge flags at end of bb %d",
-                          bb->index);
-                   err = 1;
-                 }
-           }
-         break;
+      for (i = 0; i < n_region; i++)
+       if (region[i] != exit->src
+           && dominated_by_p (CDI_DOMINATORS, region[i], exit->src))
+         return false;
+    }
 
-       case RETURN_EXPR:
-         if (!single_succ_p (bb)
-             || (single_succ_edge (bb)->flags
-                 & (EDGE_FALLTHRU | EDGE_ABNORMAL
-                    | EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
-           {
-             error ("wrong outgoing edge flags at end of bb %d", bb->index);
-             err = 1;
-           }
-         if (single_succ (bb) != EXIT_BLOCK_PTR)
-           {
-             error ("return edge does not point to exit in bb %d",
-                    bb->index);
-             err = 1;
-           }
-         break;
+  if (!region_copy)
+    {
+      region_copy = XNEWVEC (basic_block, n_region);
+      free_region_copy = true;
+    }
 
-       case SWITCH_EXPR:
-         {
-           tree prev;
-           edge e;
-           size_t i, n;
-           tree vec;
+  gcc_assert (!need_ssa_update_p ());
 
-           vec = SWITCH_LABELS (stmt);
-           n = TREE_VEC_LENGTH (vec);
+  /* Record blocks outside the region that are dominated by something
+     inside.  */
+  doms = NULL;
+  initialize_original_copy_tables ();
 
-           /* Mark all the destination basic blocks.  */
-           for (i = 0; i < n; ++i)
-             {
-               tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
-               basic_block label_bb = label_to_block (lab);
+  doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region);
 
-               gcc_assert (!label_bb->aux || label_bb->aux == (void *)1);
-               label_bb->aux = (void *)1;
-             }
+  if (entry->dest->count)
+    {
+      total_count = entry->dest->count;
+      entry_count = entry->count;
+      /* Fix up corner cases, to avoid division by zero or creation of negative
+        frequencies.  */
+      if (entry_count > total_count)
+       entry_count = total_count;
+    }
+  else
+    {
+      total_freq = entry->dest->frequency;
+      entry_freq = EDGE_FREQUENCY (entry);
+      /* Fix up corner cases, to avoid division by zero or creation of negative
+        frequencies.  */
+      if (total_freq == 0)
+       total_freq = 1;
+      else if (entry_freq > total_freq)
+       entry_freq = total_freq;
+    }
 
-           /* Verify that the case labels are sorted.  */
-           prev = TREE_VEC_ELT (vec, 0);
-           for (i = 1; i < n - 1; ++i)
-             {
-               tree c = TREE_VEC_ELT (vec, i);
-               if (! CASE_LOW (c))
-                 {
-                   error ("found default case not at end of case vector");
-                   err = 1;
-                   continue;
-                 }
-               if (! tree_int_cst_lt (CASE_LOW (prev), CASE_LOW (c)))
-                 {
-                   error ("case labels not sorted:");
-                   print_generic_expr (stderr, prev, 0);
-                   fprintf (stderr," is greater than ");
-                   print_generic_expr (stderr, c, 0);
-                   fprintf (stderr," but comes before it.\n");
-                   err = 1;
-                 }
-               prev = c;
-             }
-           if (CASE_LOW (TREE_VEC_ELT (vec, n - 1)))
-             {
-               error ("no default case found at end of case vector");
-               err = 1;
-             }
+  copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop,
+           split_edge_bb_loc (entry));
+  if (total_count)
+    {
+      scale_bbs_frequencies_gcov_type (region, n_region,
+                                      total_count - entry_count,
+                                      total_count);
+      scale_bbs_frequencies_gcov_type (region_copy, n_region, entry_count,
+                                      total_count);
+    }
+  else
+    {
+      scale_bbs_frequencies_int (region, n_region, total_freq - entry_freq,
+                                total_freq);
+      scale_bbs_frequencies_int (region_copy, n_region, entry_freq, total_freq);
+    }
 
-           FOR_EACH_EDGE (e, ei, bb->succs)
-             {
-               if (!e->dest->aux)
-                 {
-                   error ("extra outgoing edge %d->%d",
-                          bb->index, e->dest->index);
-                   err = 1;
-                 }
-               e->dest->aux = (void *)2;
-               if ((e->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL
-                                | EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
-                 {
-                   error ("wrong outgoing edge flags at end of bb %d",
-                          bb->index);
-                   err = 1;
-                 }
-             }
+  if (copying_header)
+    {
+      loop->header = exit->dest;
+      loop->latch = exit->src;
+    }
 
-           /* Check that we have all of them.  */
-           for (i = 0; i < n; ++i)
-             {
-               tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
-               basic_block label_bb = label_to_block (lab);
+  /* Redirect the entry and add the phi node arguments.  */
+  redirected = redirect_edge_and_branch (entry, get_bb_copy (entry->dest));
+  gcc_assert (redirected != NULL);
+  flush_pending_stmts (entry);
 
-               if (label_bb->aux != (void *)2)
-                 {
-                   error ("missing edge %i->%i",
-                          bb->index, label_bb->index);
-                   err = 1;
-                 }
-             }
+  /* Concerning updating of dominators:  We must recount dominators
+     for entry block and its copy.  Anything that is outside of the
+     region, but was dominated by something inside needs recounting as
+     well.  */
+  set_immediate_dominator (CDI_DOMINATORS, entry->dest, entry->src);
+  VEC_safe_push (basic_block, heap, doms, get_bb_original (entry->dest));
+  iterate_fix_dominators (CDI_DOMINATORS, doms, false);
+  VEC_free (basic_block, heap, doms);
 
-           FOR_EACH_EDGE (e, ei, bb->succs)
-             e->dest->aux = (void *)0;
-         }
+  /* Add the other PHI node arguments.  */
+  add_phi_args_after_copy (region_copy, n_region, NULL);
 
-       default: ;
-       }
-    }
+  /* Update the SSA web.  */
+  update_ssa (TODO_update_ssa);
 
-  if (dom_computed[CDI_DOMINATORS] >= DOM_NO_FAST_QUERY)
-    verify_dominators (CDI_DOMINATORS);
+  if (free_region_copy)
+    free (region_copy);
 
-  return err;
+  free_original_copy_tables ();
+  return true;
 }
 
+/* Duplicates REGION consisting of N_REGION blocks.  The new blocks
+   are stored to REGION_COPY in the same order in that they appear
+   in REGION, if REGION_COPY is not NULL.  ENTRY is the entry to
+   the region, EXIT an exit from it.  The condition guarding EXIT
+   is moved to ENTRY.  Returns true if duplication succeeds, false
+   otherwise.
 
-/* Updates phi nodes after creating a forwarder block joined
-   by edge FALLTHRU.  */
+   For example, 
+   some_code;
+   if (cond)
+     A;
+   else
+     B;
+
+   is transformed to
+
+   if (cond)
+     {
+       some_code;
+       A;
+     }
+   else
+     {
+       some_code;
+       B;
+     }
+*/
 
-static void
-tree_make_forwarder_block (edge fallthru)
+bool
+tree_duplicate_sese_tail (edge entry, edge exit,
+                         basic_block *region, unsigned n_region,
+                         basic_block *region_copy)
 {
-  edge e;
-  edge_iterator ei;
-  basic_block dummy, bb;
-  tree phi, new_phi, var;
+  unsigned i;
+  bool free_region_copy = false;
+  struct loop *loop = exit->dest->loop_father;
+  struct loop *orig_loop = entry->dest->loop_father;
+  basic_block switch_bb, entry_bb, nentry_bb;
+  VEC (basic_block, heap) *doms;
+  int total_freq = 0, exit_freq = 0;
+  gcov_type total_count = 0, exit_count = 0;
+  edge exits[2], nexits[2], e;
+  block_stmt_iterator bsi;
+  tree cond;
+  edge sorig, snew;
 
-  dummy = fallthru->src;
-  bb = fallthru->dest;
+  gcc_assert (EDGE_COUNT (exit->src->succs) == 2);
+  exits[0] = exit;
+  exits[1] = EDGE_SUCC (exit->src, EDGE_SUCC (exit->src, 0) == exit);
 
-  if (single_pred_p (bb))
-    return;
+  if (!can_copy_bbs_p (region, n_region))
+    return false;
 
-  /* If we redirected a branch we must create new phi nodes at the
-     start of BB.  */
-  for (phi = phi_nodes (dummy); phi; phi = PHI_CHAIN (phi))
+  /* Some sanity checking.  Note that we do not check for all possible
+     missuses of the functions.  I.e. if you ask to copy something weird
+     (e.g., in the example, if there is a jump from inside to the middle
+     of some_code, or come_code defines some of the values used in cond)
+     it will work, but the resulting code will not be correct.  */
+  for (i = 0; i < n_region; i++)
     {
-      var = PHI_RESULT (phi);
-      new_phi = create_phi_node (var, bb);
-      SSA_NAME_DEF_STMT (var) = new_phi;
-      SET_PHI_RESULT (phi, make_ssa_name (SSA_NAME_VAR (var), phi));
-      add_phi_arg (new_phi, PHI_RESULT (phi), fallthru);
+      /* We do not handle subloops, i.e. all the blocks must belong to the
+        same loop.  */
+      if (region[i]->loop_father != orig_loop)
+       return false;
+
+      if (region[i] == orig_loop->latch)
+       return false;
     }
 
-  /* Ensure that the PHI node chain is in the same order.  */
-  set_phi_nodes (bb, phi_reverse (phi_nodes (bb)));
+  initialize_original_copy_tables ();
+  set_loop_copy (orig_loop, loop);
 
-  /* Add the arguments we have stored on edges.  */
-  FOR_EACH_EDGE (e, ei, bb->preds)
+  if (!region_copy)
     {
-      if (e == fallthru)
-       continue;
-
-      flush_pending_stmts (e);
+      region_copy = XNEWVEC (basic_block, n_region);
+      free_region_copy = true;
     }
-}
 
+  gcc_assert (!need_ssa_update_p ());
 
-/* Return a non-special label in the head of basic block BLOCK.
-   Create one if it doesn't exist.  */
+  /* Record blocks outside the region that are dominated by something
+     inside.  */
+  doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region);
 
-tree
-tree_block_label (basic_block bb)
-{
-  block_stmt_iterator i, s = bsi_start (bb);
-  bool first = true;
-  tree label, stmt;
+  if (exit->src->count)
+    {
+      total_count = exit->src->count;
+      exit_count = exit->count;
+      /* Fix up corner cases, to avoid division by zero or creation of negative
+        frequencies.  */
+      if (exit_count > total_count)
+       exit_count = total_count;
+    }
+  else
+    {
+      total_freq = exit->src->frequency;
+      exit_freq = EDGE_FREQUENCY (exit);
+      /* Fix up corner cases, to avoid division by zero or creation of negative
+        frequencies.  */
+      if (total_freq == 0)
+       total_freq = 1;
+      if (exit_freq > total_freq)
+       exit_freq = total_freq;
+    }
 
-  for (i = s; !bsi_end_p (i); first = false, bsi_next (&i))
+  copy_bbs (region, n_region, region_copy, exits, 2, nexits, orig_loop,
+           split_edge_bb_loc (exit));
+  if (total_count)
     {
-      stmt = bsi_stmt (i);
-      if (TREE_CODE (stmt) != LABEL_EXPR)
-       break;
-      label = LABEL_EXPR_LABEL (stmt);
-      if (!DECL_NONLOCAL (label))
-       {
-         if (!first)
-           bsi_move_before (&i, &s);
-         return label;
-       }
+      scale_bbs_frequencies_gcov_type (region, n_region,
+                                      total_count - exit_count,
+                                      total_count);
+      scale_bbs_frequencies_gcov_type (region_copy, n_region, exit_count,
+                                      total_count);
+    }
+  else
+    {
+      scale_bbs_frequencies_int (region, n_region, total_freq - exit_freq,
+                                total_freq);
+      scale_bbs_frequencies_int (region_copy, n_region, exit_freq, total_freq);
     }
 
-  label = create_artificial_label ();
-  stmt = build1 (LABEL_EXPR, void_type_node, label);
-  bsi_insert_before (&s, stmt, BSI_NEW_STMT);
-  return label;
-}
+  /* Create the switch block, and put the exit condition to it.  */
+  entry_bb = entry->dest;
+  nentry_bb = get_bb_copy (entry_bb);
+  if (!last_stmt (entry->src)
+      || !stmt_ends_bb_p (last_stmt (entry->src)))
+    switch_bb = entry->src;
+  else
+    switch_bb = split_edge (entry);
+  set_immediate_dominator (CDI_DOMINATORS, nentry_bb, switch_bb);
 
+  bsi = bsi_last (switch_bb);
+  cond = last_stmt (exit->src);
+  gcc_assert (TREE_CODE (cond) == COND_EXPR);
+  bsi_insert_after (&bsi, unshare_expr (cond), BSI_NEW_STMT);
 
-/* Attempt to perform edge redirection by replacing a possibly complex
-   jump instruction by a goto or by removing the jump completely.
-   This can apply only if all edges now point to the same block.  The
-   parameters and return values are equivalent to
-   redirect_edge_and_branch.  */
+  sorig = single_succ_edge (switch_bb);
+  sorig->flags = exits[1]->flags;
+  snew = make_edge (switch_bb, nentry_bb, exits[0]->flags);
 
-static edge
-tree_try_redirect_by_replacing_jump (edge e, basic_block target)
-{
-  basic_block src = e->src;
-  block_stmt_iterator b;
-  tree stmt;
+  /* Register the new edge from SWITCH_BB in loop exit lists.  */
+  rescan_loop_exit (snew, true, false);
 
-  /* We can replace or remove a complex jump only when we have exactly
-     two edges.  */
-  if (EDGE_COUNT (src->succs) != 2
-      /* Verify that all targets will be TARGET.  Specifically, the
-        edge that is not E must also go to TARGET.  */
-      || EDGE_SUCC (src, EDGE_SUCC (src, 0) == e)->dest != target)
-    return NULL;
+  /* Add the PHI node arguments.  */
+  add_phi_args_after_copy (region_copy, n_region, snew);
 
-  b = bsi_last (src);
-  if (bsi_end_p (b))
-    return NULL;
-  stmt = bsi_stmt (b);
+  /* Get rid of now superfluous conditions and associated edges (and phi node
+     arguments).  */
+  e = redirect_edge_and_branch (exits[0], exits[1]->dest);
+  PENDING_STMT (e) = NULL_TREE;
+  e = redirect_edge_and_branch (nexits[1], nexits[0]->dest);
+  PENDING_STMT (e) = NULL_TREE;
 
-  if (TREE_CODE (stmt) == COND_EXPR
-      || TREE_CODE (stmt) == SWITCH_EXPR)
-    {
-      bsi_remove (&b, true);
-      e = ssa_redirect_edge (e, target);
-      e->flags = EDGE_FALLTHRU;
-      return e;
-    }
+  /* Anything that is outside of the region, but was dominated by something
+     inside needs to update dominance info.  */
+  iterate_fix_dominators (CDI_DOMINATORS, doms, false);
+  VEC_free (basic_block, heap, doms);
+
+  /* Update the SSA web.  */
+  update_ssa (TODO_update_ssa);
+
+  if (free_region_copy)
+    free (region_copy);
 
-  return NULL;
+  free_original_copy_tables ();
+  return true;
 }
 
+/*
+DEF_VEC_P(basic_block);
+DEF_VEC_ALLOC_P(basic_block,heap);
+*/
 
-/* Redirect E to DEST.  Return NULL on failure.  Otherwise, return the
-   edge representing the redirected branch.  */
+/* Add all the blocks dominated by ENTRY to the array BBS_P.  Stop
+   adding blocks when the dominator traversal reaches EXIT.  This
+   function silently assumes that ENTRY strictly dominates EXIT.  */
 
-static edge
-tree_redirect_edge_and_branch (edge e, basic_block dest)
+static void
+gather_blocks_in_sese_region (basic_block entry, basic_block exit,
+                             VEC(basic_block,heap) **bbs_p)
 {
-  basic_block bb = e->src;
-  block_stmt_iterator bsi;
-  edge ret;
-  tree label, stmt;
+  basic_block son;
 
-  if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
-    return NULL;
+  for (son = first_dom_son (CDI_DOMINATORS, entry);
+       son;
+       son = next_dom_son (CDI_DOMINATORS, son))
+    {
+      VEC_safe_push (basic_block, heap, *bbs_p, son);
+      if (son != exit)
+       gather_blocks_in_sese_region (son, exit, bbs_p);
+    }
+}
 
-  if (e->src != ENTRY_BLOCK_PTR 
-      && (ret = tree_try_redirect_by_replacing_jump (e, dest)))
-    return ret;
+/* Replaces *TP with a duplicate (belonging to function TO_CONTEXT).
+   The duplicates are recorded in VARS_MAP.  */
 
-  if (e->dest == dest)
-    return NULL;
+static void
+replace_by_duplicate_decl (tree *tp, struct pointer_map_t *vars_map,
+                          tree to_context)
+{
+  tree t = *tp, new_t;
+  struct function *f = DECL_STRUCT_FUNCTION (to_context);
+  void **loc;
 
-  label = tree_block_label (dest);
+  if (DECL_CONTEXT (t) == to_context)
+    return;
 
-  bsi = bsi_last (bb);
-  stmt = bsi_end_p (bsi) ? NULL : bsi_stmt (bsi);
+  loc = pointer_map_contains (vars_map, t);
 
-  switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
+  if (!loc)
     {
-    case COND_EXPR:
-      stmt = (e->flags & EDGE_TRUE_VALUE
-             ? COND_EXPR_THEN (stmt)
-             : COND_EXPR_ELSE (stmt));
-      GOTO_DESTINATION (stmt) = label;
-      break;
+      loc = pointer_map_insert (vars_map, t);
 
-    case GOTO_EXPR:
-      /* No non-abnormal edges should lead from a non-simple goto, and
-        simple ones should be represented implicitly.  */
-      gcc_unreachable ();
+      if (SSA_VAR_P (t))
+       {
+         new_t = copy_var_decl (t, DECL_NAME (t), TREE_TYPE (t));
+         f->unexpanded_var_list
+                 = tree_cons (NULL_TREE, new_t, f->unexpanded_var_list);
+       }
+      else
+       {
+         gcc_assert (TREE_CODE (t) == CONST_DECL);
+         new_t = copy_node (t);
+       }
+      DECL_CONTEXT (new_t) = to_context;
 
-    case SWITCH_EXPR:
-      {
-        tree cases = get_cases_for_edge (e, stmt);
+      *loc = new_t;
+    }
+  else
+    new_t = *loc;
 
-       /* If we have a list of cases associated with E, then use it
-          as it's a lot faster than walking the entire case vector.  */
-       if (cases)
-         {
-           edge e2 = find_edge (e->src, dest);
-           tree last, first;
+  *tp = new_t;
+}
 
-           first = cases;
-           while (cases)
-             {
-               last = cases;
-               CASE_LABEL (cases) = label;
-               cases = TREE_CHAIN (cases);
-             }
+/* Creates an ssa name in TO_CONTEXT equivalent to NAME.
+   VARS_MAP maps old ssa names and var_decls to the new ones.  */
 
-           /* If there was already an edge in the CFG, then we need
-              to move all the cases associated with E to E2.  */
-           if (e2)
-             {
-               tree cases2 = get_cases_for_edge (e2, stmt);
+static tree
+replace_ssa_name (tree name, struct pointer_map_t *vars_map,
+                 tree to_context)
+{
+  void **loc;
+  tree new_name, decl = SSA_NAME_VAR (name);
 
-               TREE_CHAIN (last) = TREE_CHAIN (cases2);
-               TREE_CHAIN (cases2) = first;
-             }
-         }
-       else
-         {
-           tree vec = SWITCH_LABELS (stmt);
-           size_t i, n = TREE_VEC_LENGTH (vec);
+  gcc_assert (is_gimple_reg (name));
 
-           for (i = 0; i < n; i++)
-             {
-               tree elt = TREE_VEC_ELT (vec, i);
+  loc = pointer_map_contains (vars_map, name);
 
-               if (label_to_block (CASE_LABEL (elt)) == e->dest)
-                 CASE_LABEL (elt) = label;
-             }
-         }
+  if (!loc)
+    {
+      replace_by_duplicate_decl (&decl, vars_map, to_context);
 
-       break;
-      }
+      push_cfun (DECL_STRUCT_FUNCTION (to_context));
+      if (gimple_in_ssa_p (cfun))
+       add_referenced_var (decl);
 
-    case RETURN_EXPR:
-      bsi_remove (&bsi, true);
-      e->flags |= EDGE_FALLTHRU;
-      break;
+      new_name = make_ssa_name (decl, SSA_NAME_DEF_STMT (name));
+      if (SSA_NAME_IS_DEFAULT_DEF (name))
+       set_default_def (decl, new_name);
+      pop_cfun ();
 
-    default:
-      /* Otherwise it must be a fallthru edge, and we don't need to
-        do anything besides redirecting it.  */
-      gcc_assert (e->flags & EDGE_FALLTHRU);
-      break;
+      loc = pointer_map_insert (vars_map, name);
+      *loc = new_name;
     }
+  else
+    new_name = *loc;
 
-  /* Update/insert PHI nodes as necessary.  */
-
-  /* Now update the edges in the CFG.  */
-  e = ssa_redirect_edge (e, dest);
-
-  return e;
+  return new_name;
 }
 
-
-/* Simple wrapper, as we can always redirect fallthru edges.  */
-
-static basic_block
-tree_redirect_edge_and_branch_force (edge e, basic_block dest)
+struct move_stmt_d
 {
-  e = tree_redirect_edge_and_branch (e, dest);
-  gcc_assert (e);
-
-  return NULL;
-}
-
+  tree block;
+  tree from_context;
+  tree to_context;
+  struct pointer_map_t *vars_map;
+  htab_t new_label_map;
+  bool remap_decls_p;
+};
 
-/* Splits basic block BB after statement STMT (but at least after the
-   labels).  If STMT is NULL, BB is split just after the labels.  */
+/* Helper for move_block_to_fn.  Set TREE_BLOCK in every expression
+   contained in *TP and change the DECL_CONTEXT of every local
+   variable referenced in *TP.  */
 
-static basic_block
-tree_split_block (basic_block bb, void *stmt)
+static tree
+move_stmt_r (tree *tp, int *walk_subtrees, void *data)
 {
-  block_stmt_iterator bsi, bsi_tgt;
-  tree act;
-  basic_block new_bb;
-  edge e;
-  edge_iterator ei;
+  struct move_stmt_d *p = (struct move_stmt_d *) data;
+  tree t = *tp;
 
-  new_bb = create_empty_bb (bb);
+  if (p->block
+      && (EXPR_P (t) || GIMPLE_STMT_P (t)))
+    TREE_BLOCK (t) = p->block;
 
-  /* Redirect the outgoing edges.  */
-  new_bb->succs = bb->succs;
-  bb->succs = NULL;
-  FOR_EACH_EDGE (e, ei, new_bb->succs)
-    e->src = new_bb;
+  if (OMP_DIRECTIVE_P (t)
+      && TREE_CODE (t) != OMP_RETURN
+      && TREE_CODE (t) != OMP_CONTINUE)
+    {
+      /* Do not remap variables inside OMP directives.  Variables
+        referenced in clauses and directive header belong to the
+        parent function and should not be moved into the child
+        function.  */
+      bool save_remap_decls_p = p->remap_decls_p;
+      p->remap_decls_p = false;
+      *walk_subtrees = 0;
 
-  if (stmt && TREE_CODE ((tree) stmt) == LABEL_EXPR)
-    stmt = NULL;
+      walk_tree (&OMP_BODY (t), move_stmt_r, p, NULL);
 
-  /* Move everything from BSI to the new basic block.  */
-  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      p->remap_decls_p = save_remap_decls_p;
+    }
+  else if (DECL_P (t) || TREE_CODE (t) == SSA_NAME)
     {
-      act = bsi_stmt (bsi);
-      if (TREE_CODE (act) == LABEL_EXPR)
-       continue;
-
-      if (!stmt)
-       break;
+      if (TREE_CODE (t) == SSA_NAME)
+       *tp = replace_ssa_name (t, p->vars_map, p->to_context);
+      else if (TREE_CODE (t) == LABEL_DECL)
+       {
+         if (p->new_label_map)
+           {
+             struct tree_map in, *out;
+             in.base.from = t;
+             out = htab_find_with_hash (p->new_label_map, &in, DECL_UID (t));
+             if (out)
+               *tp = t = out->to;
+           }
 
-      if (stmt == act)
+         DECL_CONTEXT (t) = p->to_context;
+       }
+      else if (p->remap_decls_p)
        {
-         bsi_next (&bsi);
-         break;
+         /* Replace T with its duplicate.  T should no longer appear in the
+            parent function, so this looks wasteful; however, it may appear
+            in referenced_vars, and more importantly, as virtual operands of
+            statements, and in alias lists of other variables.  It would be
+            quite difficult to expunge it from all those places.  ??? It might
+            suffice to do this for addressable variables.  */
+         if ((TREE_CODE (t) == VAR_DECL
+              && !is_global_var (t))
+             || TREE_CODE (t) == CONST_DECL)
+           replace_by_duplicate_decl (tp, p->vars_map, p->to_context);
+         
+         if (SSA_VAR_P (t)
+             && gimple_in_ssa_p (cfun))
+           {
+             push_cfun (DECL_STRUCT_FUNCTION (p->to_context));
+             add_referenced_var (*tp);
+             pop_cfun ();
+           }
        }
+      *walk_subtrees = 0;
     }
+  else if (TYPE_P (t))
+    *walk_subtrees = 0;
 
-  bsi_tgt = bsi_start (new_bb);
-  while (!bsi_end_p (bsi))
-    {
-      act = bsi_stmt (bsi);
-      bsi_remove (&bsi, false);
-      bsi_insert_after (&bsi_tgt, act, BSI_NEW_STMT);
-    }
-
-  return new_bb;
+  return NULL_TREE;
 }
 
+/* Marks virtual operands of all statements in basic blocks BBS for
+   renaming.  */
 
-/* Moves basic block BB after block AFTER.  */
-
-static bool
-tree_move_block_after (basic_block bb, basic_block after)
+static void
+mark_virtual_ops_in_region (VEC (basic_block,heap) *bbs)
 {
-  if (bb->prev_bb == after)
-    return true;
+  tree phi;
+  block_stmt_iterator bsi;
+  basic_block bb;
+  unsigned i;
 
-  unlink_block (bb);
-  link_block (bb, after);
+  for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
+    {
+      for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+       mark_virtual_ops_for_renaming (phi);
 
-  return true;
+      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+       mark_virtual_ops_for_renaming (bsi_stmt (bsi));
+    }
 }
 
+/* Move basic block BB from function CFUN to function DEST_FN.  The
+   block is moved out of the original linked list and placed after
+   block AFTER in the new list.  Also, the block is removed from the
+   original array of blocks and placed in DEST_FN's array of blocks.
+   If UPDATE_EDGE_COUNT_P is true, the edge counts on both CFGs is
+   updated to reflect the moved edges.
 
-/* Return true if basic_block can be duplicated.  */
+   The local variables are remapped to new instances, VARS_MAP is used
+   to record the mapping.  */
 
-static bool
-tree_can_duplicate_bb_p (basic_block bb ATTRIBUTE_UNUSED)
+static void
+move_block_to_fn (struct function *dest_cfun, basic_block bb,
+                 basic_block after, bool update_edge_count_p,
+                 struct pointer_map_t *vars_map, htab_t new_label_map,
+                 int eh_offset)
 {
-  return true;
-}
+  struct control_flow_graph *cfg;
+  edge_iterator ei;
+  edge e;
+  block_stmt_iterator si;
+  struct move_stmt_d d;
+  unsigned old_len, new_len;
+  tree phi, next_phi;
 
+  /* Remove BB from dominance structures.  */
+  delete_from_dominance_info (CDI_DOMINATORS, bb);
+  if (current_loops)
+    remove_bb_from_loops (bb);
 
-/* Create a duplicate of the basic block BB.  NOTE: This does not
-   preserve SSA form.  */
+  /* Link BB to the new linked list.  */
+  move_block_after (bb, after);
 
-static basic_block
-tree_duplicate_bb (basic_block bb)
-{
-  basic_block new_bb;
-  block_stmt_iterator bsi, bsi_tgt;
-  tree phi;
+  /* Update the edge count in the corresponding flowgraphs.  */
+  if (update_edge_count_p)
+    FOR_EACH_EDGE (e, ei, bb->succs)
+      {
+       cfun->cfg->x_n_edges--;
+       dest_cfun->cfg->x_n_edges++;
+      }
 
-  new_bb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
+  /* Remove BB from the original basic block array.  */
+  VEC_replace (basic_block, cfun->cfg->x_basic_block_info, bb->index, NULL);
+  cfun->cfg->x_n_basic_blocks--;
 
-  /* Copy the PHI nodes.  We ignore PHI node arguments here because
-     the incoming edges have not been setup yet.  */
-  for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+  /* Grow DEST_CFUN's basic block array if needed.  */
+  cfg = dest_cfun->cfg;
+  cfg->x_n_basic_blocks++;
+  if (bb->index >= cfg->x_last_basic_block)
+    cfg->x_last_basic_block = bb->index + 1;
+
+  old_len = VEC_length (basic_block, cfg->x_basic_block_info);
+  if ((unsigned) cfg->x_last_basic_block >= old_len)
     {
-      tree copy = create_phi_node (PHI_RESULT (phi), new_bb);
-      create_new_def_for (PHI_RESULT (copy), copy, PHI_RESULT_PTR (copy));
+      new_len = cfg->x_last_basic_block + (cfg->x_last_basic_block + 3) / 4;
+      VEC_safe_grow_cleared (basic_block, gc, cfg->x_basic_block_info,
+                            new_len);
     }
 
-  /* Keep the chain of PHI nodes in the same order so that they can be
-     updated by ssa_redirect_edge.  */
-  set_phi_nodes (new_bb, phi_reverse (phi_nodes (new_bb)));
+  VEC_replace (basic_block, cfg->x_basic_block_info,
+               bb->index, bb);
 
-  bsi_tgt = bsi_start (new_bb);
-  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+  /* Remap the variables in phi nodes.  */
+  for (phi = phi_nodes (bb); phi; phi = next_phi)
     {
-      def_operand_p def_p;
-      ssa_op_iter op_iter;
-      tree stmt, copy;
-      int region;
-
-      stmt = bsi_stmt (bsi);
-      if (TREE_CODE (stmt) == LABEL_EXPR)
-       continue;
+      use_operand_p use;
+      tree op = PHI_RESULT (phi);
+      ssa_op_iter oi;
 
-      /* Create a new copy of STMT and duplicate STMT's virtual
-        operands.  */
-      copy = unshare_expr (stmt);
-      bsi_insert_after (&bsi_tgt, copy, BSI_NEW_STMT);
-      copy_virtual_operands (copy, stmt);
-      region = lookup_stmt_eh_region (stmt);
-      if (region >= 0)
-       add_stmt_to_eh_region (copy, region);
+      next_phi = PHI_CHAIN (phi);
+      if (!is_gimple_reg (op))
+       {
+         /* Remove the phi nodes for virtual operands (alias analysis will be
+            run for the new function, anyway).  */
+          remove_phi_node (phi, NULL, true);
+         continue;
+       }
 
-      /* Create new names for all the definitions created by COPY and
-        add replacement mappings for each new name.  */
-      FOR_EACH_SSA_DEF_OPERAND (def_p, copy, op_iter, SSA_OP_ALL_DEFS)
-       create_new_def_for (DEF_FROM_PTR (def_p), copy, def_p);
+      SET_PHI_RESULT (phi, replace_ssa_name (op, vars_map, dest_cfun->decl));
+      FOR_EACH_PHI_ARG (use, phi, oi, SSA_OP_USE)
+       {
+         op = USE_FROM_PTR (use);
+         if (TREE_CODE (op) == SSA_NAME)
+           SET_USE (use, replace_ssa_name (op, vars_map, dest_cfun->decl));
+       }
     }
 
-  return new_bb;
-}
+  /* The statements in BB need to be associated with a new TREE_BLOCK.
+     Labels need to be associated with a new label-to-block map.  */
+  memset (&d, 0, sizeof (d));
+  d.vars_map = vars_map;
+  d.from_context = cfun->decl;
+  d.to_context = dest_cfun->decl;
+  d.new_label_map = new_label_map;
 
+  for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+    {
+      tree stmt = bsi_stmt (si);
+      int region;
 
-/* Basic block BB_COPY was created by code duplication.  Add phi node
-   arguments for edges going out of BB_COPY.  The blocks that were
-   duplicated have BB_DUPLICATED set.  */
+      d.remap_decls_p = true;
+      if (TREE_BLOCK (stmt))
+       d.block = DECL_INITIAL (dest_cfun->decl);
 
-void
-add_phi_args_after_copy_bb (basic_block bb_copy)
-{
-  basic_block bb, dest;
-  edge e, e_copy;
-  edge_iterator ei;
-  tree phi, phi_copy, phi_next, def;
-      
-  bb = get_bb_original (bb_copy);
+      walk_tree (&stmt, move_stmt_r, &d, NULL);
 
-  FOR_EACH_EDGE (e_copy, ei, bb_copy->succs)
-    {
-      if (!phi_nodes (e_copy->dest))
-       continue;
+      if (TREE_CODE (stmt) == LABEL_EXPR)
+       {
+         tree label = LABEL_EXPR_LABEL (stmt);
+         int uid = LABEL_DECL_UID (label);
 
-      if (e_copy->dest->flags & BB_DUPLICATED)
-       dest = get_bb_original (e_copy->dest);
-      else
-       dest = e_copy->dest;
+         gcc_assert (uid > -1);
 
-      e = find_edge (bb, dest);
-      if (!e)
-       {
-         /* During loop unrolling the target of the latch edge is copied.
-            In this case we are not looking for edge to dest, but to
-            duplicated block whose original was dest.  */
-         FOR_EACH_EDGE (e, ei, bb->succs)
-           if ((e->dest->flags & BB_DUPLICATED)
-               && get_bb_original (e->dest) == dest)
-             break;
+         old_len = VEC_length (basic_block, cfg->x_label_to_block_map);
+         if (old_len <= (unsigned) uid)
+           {
+             new_len = 3 * uid / 2;
+             VEC_safe_grow_cleared (basic_block, gc,
+                                    cfg->x_label_to_block_map, new_len);
+           }
 
-         gcc_assert (e != NULL);
+         VEC_replace (basic_block, cfg->x_label_to_block_map, uid, bb);
+         VEC_replace (basic_block, cfun->cfg->x_label_to_block_map, uid, NULL);
+
+         gcc_assert (DECL_CONTEXT (label) == dest_cfun->decl);
+
+         if (uid >= dest_cfun->last_label_uid)
+           dest_cfun->last_label_uid = uid + 1;
        }
+      else if (TREE_CODE (stmt) == RESX_EXPR && eh_offset != 0)
+       TREE_OPERAND (stmt, 0) =
+         build_int_cst (NULL_TREE,
+                        TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0))
+                        + eh_offset);
 
-      for (phi = phi_nodes (e->dest), phi_copy = phi_nodes (e_copy->dest);
-          phi;
-          phi = phi_next, phi_copy = PHI_CHAIN (phi_copy))
+      region = lookup_stmt_eh_region (stmt);
+      if (region >= 0)
        {
-         phi_next = PHI_CHAIN (phi);
-         def = PHI_ARG_DEF_FROM_EDGE (phi, e);
-         add_phi_arg (phi_copy, def, e_copy);
+         add_stmt_to_eh_region_fn (dest_cfun, stmt, region + eh_offset);
+         remove_stmt_from_eh_region (stmt);
+         gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt);
+          gimple_remove_stmt_histograms (cfun, stmt);
        }
+
+      /* We cannot leave any operands allocated from the operand caches of
+        the current function.  */
+      free_stmt_operands (stmt);
+      push_cfun (dest_cfun);
+      update_stmt (stmt);
+      pop_cfun ();
     }
 }
 
-/* Blocks in REGION_COPY array of length N_REGION were created by
-   duplication of basic blocks.  Add phi node arguments for edges
-   going from these blocks.  */
+/* Examine the statements in BB (which is in SRC_CFUN); find and return
+   the outermost EH region.  Use REGION as the incoming base EH region.  */
 
-void
-add_phi_args_after_copy (basic_block *region_copy, unsigned n_region)
+static int
+find_outermost_region_in_block (struct function *src_cfun,
+                               basic_block bb, int region)
 {
-  unsigned i;
+  block_stmt_iterator si;
 
-  for (i = 0; i < n_region; i++)
-    region_copy[i]->flags |= BB_DUPLICATED;
+  for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+    {
+      tree stmt = bsi_stmt (si);
+      int stmt_region;
 
-  for (i = 0; i < n_region; i++)
-    add_phi_args_after_copy_bb (region_copy[i]);
+      if (TREE_CODE (stmt) == RESX_EXPR)
+       stmt_region = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0));
+      else
+       stmt_region = lookup_stmt_eh_region_fn (src_cfun, stmt);
+      if (stmt_region > 0)
+       {
+         if (region < 0)
+           region = stmt_region;
+         else if (stmt_region != region)
+           {
+             region = eh_region_outermost (src_cfun, stmt_region, region);
+             gcc_assert (region != -1);
+           }
+       }
+    }
 
-  for (i = 0; i < n_region; i++)
-    region_copy[i]->flags &= ~BB_DUPLICATED;
+  return region;
 }
 
-/* Duplicates a REGION (set of N_REGION basic blocks) with just a single
-   important exit edge EXIT.  By important we mean that no SSA name defined
-   inside region is live over the other exit edges of the region.  All entry
-   edges to the region must go to ENTRY->dest.  The edge ENTRY is redirected
-   to the duplicate of the region.  SSA form, dominance and loop information
-   is updated.  The new basic blocks are stored to REGION_COPY in the same
-   order as they had in REGION, provided that REGION_COPY is not NULL.
-   The function returns false if it is unable to copy the region,
-   true otherwise.  */
-
-bool
-tree_duplicate_sese_region (edge entry, edge exit,
-                           basic_block *region, unsigned n_region,
-                           basic_block *region_copy)
+static tree
+new_label_mapper (tree decl, void *data)
 {
-  unsigned i, n_doms;
-  bool free_region_copy = false, copying_header = false;
-  struct loop *loop = entry->dest->loop_father;
-  edge exit_copy;
-  basic_block *doms;
-  edge redirected;
-  int total_freq = 0, entry_freq = 0;
-  gcov_type total_count = 0, entry_count = 0;
+  htab_t hash = (htab_t) data;
+  struct tree_map *m;
+  void **slot;
 
-  if (!can_copy_bbs_p (region, n_region))
-    return false;
+  gcc_assert (TREE_CODE (decl) == LABEL_DECL);
 
-  /* Some sanity checking.  Note that we do not check for all possible
-     missuses of the functions.  I.e. if you ask to copy something weird,
-     it will work, but the state of structures probably will not be
-     correct.  */
-  for (i = 0; i < n_region; i++)
-    {
-      /* We do not handle subloops, i.e. all the blocks must belong to the
-        same loop.  */
-      if (region[i]->loop_father != loop)
-       return false;
+  m = xmalloc (sizeof (struct tree_map));
+  m->hash = DECL_UID (decl);
+  m->base.from = decl;
+  m->to = create_artificial_label ();
+  LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl);
 
-      if (region[i] != entry->dest
-         && region[i] == loop->header)
-       return false;
-    }
+  slot = htab_find_slot_with_hash (hash, m, m->hash, INSERT);
+  gcc_assert (*slot == NULL);
 
-  loop->copy = loop;
+  *slot = m;
 
-  /* In case the function is used for loop header copying (which is the primary
-     use), ensure that EXIT and its copy will be new latch and entry edges.  */
-  if (loop->header == entry->dest)
-    {
-      copying_header = true;
-      loop->copy = loop->outer;
+  return m->to;
+}
 
-      if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit->src))
-       return false;
+/* Move a single-entry, single-exit region delimited by ENTRY_BB and
+   EXIT_BB to function DEST_CFUN.  The whole region is replaced by a
+   single basic block in the original CFG and the new basic block is
+   returned.  DEST_CFUN must not have a CFG yet.
 
-      for (i = 0; i < n_region; i++)
-       if (region[i] != exit->src
-           && dominated_by_p (CDI_DOMINATORS, region[i], exit->src))
-         return false;
-    }
+   Note that the region need not be a pure SESE region.  Blocks inside
+   the region may contain calls to abort/exit.  The only restriction
+   is that ENTRY_BB should be the only entry point and it must
+   dominate EXIT_BB.
 
-  if (!region_copy)
+   All local variables referenced in the region are assumed to be in
+   the corresponding BLOCK_VARS and unexpanded variable lists
+   associated with DEST_CFUN.  */
+
+basic_block
+move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
+                       basic_block exit_bb)
+{
+  VEC(basic_block,heap) *bbs, *dom_bbs;
+  basic_block dom_entry = get_immediate_dominator (CDI_DOMINATORS, entry_bb);
+  basic_block after, bb, *entry_pred, *exit_succ, abb;
+  struct function *saved_cfun = cfun;
+  int *entry_flag, *exit_flag, eh_offset;
+  unsigned *entry_prob, *exit_prob;
+  unsigned i, num_entry_edges, num_exit_edges;
+  edge e;
+  edge_iterator ei;
+  htab_t new_label_map;
+  struct pointer_map_t *vars_map;
+  struct loop *loop = entry_bb->loop_father;
+
+  /* If ENTRY does not strictly dominate EXIT, this cannot be an SESE
+     region.  */
+  gcc_assert (entry_bb != exit_bb
+              && (!exit_bb
+                 || dominated_by_p (CDI_DOMINATORS, exit_bb, entry_bb)));
+
+  /* Collect all the blocks in the region.  Manually add ENTRY_BB
+     because it won't be added by dfs_enumerate_from.  */
+  bbs = NULL;
+  VEC_safe_push (basic_block, heap, bbs, entry_bb);
+  gather_blocks_in_sese_region (entry_bb, exit_bb, &bbs);
+
+  /* The blocks that used to be dominated by something in BBS will now be
+     dominated by the new block.  */
+  dom_bbs = get_dominated_by_region (CDI_DOMINATORS,
+                                    VEC_address (basic_block, bbs),
+                                    VEC_length (basic_block, bbs));
+
+  /* Detach ENTRY_BB and EXIT_BB from CFUN->CFG.  We need to remember
+     the predecessor edges to ENTRY_BB and the successor edges to
+     EXIT_BB so that we can re-attach them to the new basic block that
+     will replace the region.  */
+  num_entry_edges = EDGE_COUNT (entry_bb->preds);
+  entry_pred = (basic_block *) xcalloc (num_entry_edges, sizeof (basic_block));
+  entry_flag = (int *) xcalloc (num_entry_edges, sizeof (int));
+  entry_prob = XNEWVEC (unsigned, num_entry_edges);
+  i = 0;
+  for (ei = ei_start (entry_bb->preds); (e = ei_safe_edge (ei)) != NULL;)
     {
-      region_copy = XNEWVEC (basic_block, n_region);
-      free_region_copy = true;
+      entry_prob[i] = e->probability;
+      entry_flag[i] = e->flags;
+      entry_pred[i++] = e->src;
+      remove_edge (e);
     }
 
-  gcc_assert (!need_ssa_update_p ());
-
-  /* Record blocks outside the region that are dominated by something
-     inside.  */
-  doms = XNEWVEC (basic_block, n_basic_blocks);
-  initialize_original_copy_tables ();
-
-  n_doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region, doms);
-
-  if (entry->dest->count)
+  if (exit_bb)
     {
-      total_count = entry->dest->count;
-      entry_count = entry->count;
-      /* Fix up corner cases, to avoid division by zero or creation of negative
-        frequencies.  */
-      if (entry_count > total_count)
-       entry_count = total_count;
+      num_exit_edges = EDGE_COUNT (exit_bb->succs);
+      exit_succ = (basic_block *) xcalloc (num_exit_edges,
+                                          sizeof (basic_block));
+      exit_flag = (int *) xcalloc (num_exit_edges, sizeof (int));
+      exit_prob = XNEWVEC (unsigned, num_exit_edges);
+      i = 0;
+      for (ei = ei_start (exit_bb->succs); (e = ei_safe_edge (ei)) != NULL;)
+       {
+         exit_prob[i] = e->probability;
+         exit_flag[i] = e->flags;
+         exit_succ[i++] = e->dest;
+         remove_edge (e);
+       }
     }
   else
     {
-      total_freq = entry->dest->frequency;
-      entry_freq = EDGE_FREQUENCY (entry);
-      /* Fix up corner cases, to avoid division by zero or creation of negative
-        frequencies.  */
-      if (total_freq == 0)
-       total_freq = 1;
-      else if (entry_freq > total_freq)
-       entry_freq = total_freq;
+      num_exit_edges = 0;
+      exit_succ = NULL;
+      exit_flag = NULL;
+      exit_prob = NULL;
     }
 
-  copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop,
-           split_edge_bb_loc (entry));
-  if (total_count)
+  /* Switch context to the child function to initialize DEST_FN's CFG.  */
+  gcc_assert (dest_cfun->cfg == NULL);
+  push_cfun (dest_cfun);
+
+  init_empty_tree_cfg ();
+
+  /* Initialize EH information for the new function.  */
+  eh_offset = 0;
+  new_label_map = NULL;
+  if (saved_cfun->eh)
     {
-      scale_bbs_frequencies_gcov_type (region, n_region,
-                                      total_count - entry_count,
-                                      total_count);
-      scale_bbs_frequencies_gcov_type (region_copy, n_region, entry_count,
-                                      total_count);
+      int region = -1;
+
+      for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
+       region = find_outermost_region_in_block (saved_cfun, bb, region);
+
+      init_eh_for_function ();
+      if (region != -1)
+       {
+         new_label_map = htab_create (17, tree_map_hash, tree_map_eq, free);
+         eh_offset = duplicate_eh_regions (saved_cfun, new_label_mapper,
+                                           new_label_map, region, 0);
+       }
     }
-  else
+
+  pop_cfun ();
+
+  /* The ssa form for virtual operands in the source function will have to
+     be repaired.  We do not care for the real operands -- the sese region
+     must be closed with respect to those.  */
+  mark_virtual_ops_in_region (bbs);
+
+  /* Move blocks from BBS into DEST_CFUN.  */
+  gcc_assert (VEC_length (basic_block, bbs) >= 2);
+  after = dest_cfun->cfg->x_entry_block_ptr;
+  vars_map = pointer_map_create ();
+  for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
     {
-      scale_bbs_frequencies_int (region, n_region, total_freq - entry_freq,
-                                total_freq);
-      scale_bbs_frequencies_int (region_copy, n_region, entry_freq, total_freq);
+      /* No need to update edge counts on the last block.  It has
+        already been updated earlier when we detached the region from
+        the original CFG.  */
+      move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, vars_map,
+                       new_label_map, eh_offset);
+      after = bb;
     }
 
-  if (copying_header)
+  if (new_label_map)
+    htab_delete (new_label_map);
+  pointer_map_destroy (vars_map);
+
+  /* Rewire the entry and exit blocks.  The successor to the entry
+     block turns into the successor of DEST_FN's ENTRY_BLOCK_PTR in
+     the child function.  Similarly, the predecessor of DEST_FN's
+     EXIT_BLOCK_PTR turns into the predecessor of EXIT_BLOCK_PTR.  We
+     need to switch CFUN between DEST_CFUN and SAVED_CFUN so that the
+     various CFG manipulation function get to the right CFG.
+
+     FIXME, this is silly.  The CFG ought to become a parameter to
+     these helpers.  */
+  push_cfun (dest_cfun);
+  make_edge (ENTRY_BLOCK_PTR, entry_bb, EDGE_FALLTHRU);
+  if (exit_bb)
+    make_edge (exit_bb,  EXIT_BLOCK_PTR, 0);
+  pop_cfun ();
+
+  /* Back in the original function, the SESE region has disappeared,
+     create a new basic block in its place.  */
+  bb = create_empty_bb (entry_pred[0]);
+  if (current_loops)
+    add_bb_to_loop (bb, loop);
+  for (i = 0; i < num_entry_edges; i++)
     {
-      loop->header = exit->dest;
-      loop->latch = exit->src;
+      e = make_edge (entry_pred[i], bb, entry_flag[i]);
+      e->probability = entry_prob[i];
     }
 
-  /* Redirect the entry and add the phi node arguments.  */
-  redirected = redirect_edge_and_branch (entry, get_bb_copy (entry->dest));
-  gcc_assert (redirected != NULL);
-  flush_pending_stmts (entry);
-
-  /* Concerning updating of dominators:  We must recount dominators
-     for entry block and its copy.  Anything that is outside of the
-     region, but was dominated by something inside needs recounting as
-     well.  */
-  set_immediate_dominator (CDI_DOMINATORS, entry->dest, entry->src);
-  doms[n_doms++] = get_bb_original (entry->dest);
-  iterate_fix_dominators (CDI_DOMINATORS, doms, n_doms);
-  free (doms);
-
-  /* Add the other PHI node arguments.  */
-  add_phi_args_after_copy (region_copy, n_region);
+  for (i = 0; i < num_exit_edges; i++)
+    {
+      e = make_edge (bb, exit_succ[i], exit_flag[i]);
+      e->probability = exit_prob[i];
+    }
 
-  /* Update the SSA web.  */
-  update_ssa (TODO_update_ssa);
+  set_immediate_dominator (CDI_DOMINATORS, bb, dom_entry);
+  for (i = 0; VEC_iterate (basic_block, dom_bbs, i, abb); i++)
+    set_immediate_dominator (CDI_DOMINATORS, abb, bb);
+  VEC_free (basic_block, heap, dom_bbs);
 
-  if (free_region_copy)
-    free (region_copy);
+  if (exit_bb)
+    {
+      free (exit_prob);
+      free (exit_flag);
+      free (exit_succ);
+    }
+  free (entry_prob);
+  free (entry_flag);
+  free (entry_pred);
+  VEC_free (basic_block, heap, bbs);
 
-  free_original_copy_tables ();
-  return true;
+  return bb;
 }
 
 
@@ -4461,6 +6069,7 @@ void
 dump_function_to_file (tree fn, FILE *file, int flags)
 {
   tree arg, vars, var;
+  struct function *dsf;
   bool ignore_topmost_bind = false, any_var = false;
   basic_block bb;
   tree chain;
@@ -4477,14 +6086,19 @@ dump_function_to_file (tree fn, FILE *file, int flags)
     }
   fprintf (file, ")\n");
 
-  if (flags & TDF_DETAILS)
-    dump_eh_tree (file, DECL_STRUCT_FUNCTION (fn));
+  dsf = DECL_STRUCT_FUNCTION (fn);
+  if (dsf && (flags & TDF_DETAILS))
+    dump_eh_tree (file, dsf);
+
   if (flags & TDF_RAW)
     {
       dump_node (fn, TDF_SLIM | flags, file);
       return;
     }
 
+  /* Switch CFUN to point to FN.  */
+  push_cfun (DECL_STRUCT_FUNCTION (fn));
+
   /* When GIMPLE is lowered, the variables are no longer available in
      BIND_EXPRs, so display them separately.  */
   if (cfun && cfun->decl == fn && cfun->unexpanded_var_list)
@@ -4515,7 +6129,7 @@ dump_function_to_file (tree fn, FILE *file, int flags)
 
       FOR_EACH_BB (bb)
        dump_generic_bb (file, bb, 2, flags);
-       
+
       fprintf (file, "}\n");
       check_bb_profile (EXIT_BLOCK_PTR, file);
     }
@@ -4526,7 +6140,7 @@ dump_function_to_file (tree fn, FILE *file, int flags)
       /* Make a tree based dump.  */
       chain = DECL_SAVED_TREE (fn);
 
-      if (TREE_CODE (chain) == BIND_EXPR)
+      if (chain && TREE_CODE (chain) == BIND_EXPR)
        {
          if (ignore_topmost_bind)
            {
@@ -4552,6 +6166,18 @@ dump_function_to_file (tree fn, FILE *file, int flags)
     }
 
   fprintf (file, "\n\n");
+
+  /* Restore CFUN.  */
+  pop_cfun ();
+}
+
+
+/* Dump FUNCTION_DECL FN to stderr using FLAGS (see TDF_* in tree.h)  */
+
+void
+debug_function (tree fn, int flags)
+{
+  dump_function_to_file (fn, stderr, flags);
 }
 
 
@@ -4594,7 +6220,7 @@ print_loop (FILE *file, struct loop *loop, int indent)
 {
   char *s_indent;
   basic_block bb;
-  
+
   if (loop == NULL)
     return;
 
@@ -4604,7 +6230,7 @@ print_loop (FILE *file, struct loop *loop, int indent)
 
   /* Print the loop's header.  */
   fprintf (file, "%sloop_%d\n", s_indent, loop->num);
-  
+
   /* Print the loop's body.  */
   fprintf (file, "%s{\n", s_indent);
   FOR_EACH_BB (bb)
@@ -4616,13 +6242,13 @@ print_loop (FILE *file, struct loop *loop, int indent)
        fprintf (file, "}, succs = {");
        print_succ_bbs (file, bb);
        fprintf (file, "})\n");
-       
+
        /* Print the basic_block's body.  */
        fprintf (file, "%s  {\n", s_indent);
        tree_dump_bb (bb, file, indent + 4);
        fprintf (file, "%s  }\n", s_indent);
       }
-  
+
   print_loop (file, loop->inner, indent + 2);
   fprintf (file, "%s}\n", s_indent);
   print_loop (file, loop->next, indent);
@@ -4632,11 +6258,11 @@ print_loop (FILE *file, struct loop *loop, int indent)
 /* Follow a CFG edge from the entry point of the program, and on entry
    of a loop, pretty print the loop structure on FILE.  */
 
-void 
+void
 print_loop_ir (FILE *file)
 {
   basic_block bb;
-  
+
   bb = BASIC_BLOCK (NUM_FIXED_BLOCKS);
   if (bb && bb->loop_father)
     print_loop (file, bb->loop_father, 0);
@@ -4645,7 +6271,7 @@ print_loop_ir (FILE *file)
 
 /* Debugging loops structure at tree level.  */
 
-void 
+void
 debug_loop_ir (void)
 {
   print_loop_ir (stderr);
@@ -4668,9 +6294,11 @@ tree_block_ends_with_call_p (basic_block bb)
    otherwise.  */
 
 static bool
-tree_block_ends_with_condjump_p (basic_block bb)
+tree_block_ends_with_condjump_p (const_basic_block bb)
 {
-  tree stmt = last_stmt (bb);
+  /* This CONST_CAST is okay because last_stmt doesn't modify its
+     argument and the return value is not modified.  */
+  const_tree stmt = last_stmt (CONST_CAST_BB(bb));
   return (stmt && TREE_CODE (stmt) == COND_EXPR);
 }
 
@@ -4820,6 +6448,177 @@ tree_flow_call_edges_add (sbitmap blocks)
   return blocks_split;
 }
 
+/* Purge dead abnormal call edges from basic block BB.  */
+
+bool
+tree_purge_dead_abnormal_call_edges (basic_block bb)
+{
+  bool changed = tree_purge_dead_eh_edges (bb);
+
+  if (current_function_has_nonlocal_label)
+    {
+      tree stmt = last_stmt (bb);
+      edge_iterator ei;
+      edge e;
+
+      if (!(stmt && tree_can_make_abnormal_goto (stmt)))
+       for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
+         {
+           if (e->flags & EDGE_ABNORMAL)
+             {
+               remove_edge (e);
+               changed = true;
+             }
+           else
+             ei_next (&ei);
+         }
+
+      /* See tree_purge_dead_eh_edges below.  */
+      if (changed)
+       free_dominance_info (CDI_DOMINATORS);
+    }
+
+  return changed;
+}
+
+/* Stores all basic blocks dominated by BB to DOM_BBS.  */
+
+static void
+get_all_dominated_blocks (basic_block bb, VEC (basic_block, heap) **dom_bbs)
+{
+  basic_block son;
+
+  VEC_safe_push (basic_block, heap, *dom_bbs, bb);
+  for (son = first_dom_son (CDI_DOMINATORS, bb);
+       son;
+       son = next_dom_son (CDI_DOMINATORS, son))
+    get_all_dominated_blocks (son, dom_bbs);
+}
+
+/* Removes edge E and all the blocks dominated by it, and updates dominance
+   information.  The IL in E->src needs to be updated separately.
+   If dominance info is not available, only the edge E is removed.*/
+
+void
+remove_edge_and_dominated_blocks (edge e)
+{
+  VEC (basic_block, heap) *bbs_to_remove = NULL;
+  VEC (basic_block, heap) *bbs_to_fix_dom = NULL;
+  bitmap df, df_idom;
+  edge f;
+  edge_iterator ei;
+  bool none_removed = false;
+  unsigned i;
+  basic_block bb, dbb;
+  bitmap_iterator bi;
+
+  if (!dom_info_available_p (CDI_DOMINATORS))
+    {
+      remove_edge (e);
+      return;
+    }
+
+  /* No updating is needed for edges to exit.  */
+  if (e->dest == EXIT_BLOCK_PTR)
+    {
+      if (cfgcleanup_altered_bbs)
+       bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
+      remove_edge (e);
+      return;
+    }
+
+  /* First, we find the basic blocks to remove.  If E->dest has a predecessor
+     that is not dominated by E->dest, then this set is empty.  Otherwise,
+     all the basic blocks dominated by E->dest are removed.
+
+     Also, to DF_IDOM we store the immediate dominators of the blocks in
+     the dominance frontier of E (i.e., of the successors of the
+     removed blocks, if there are any, and of E->dest otherwise).  */
+  FOR_EACH_EDGE (f, ei, e->dest->preds)
+    {
+      if (f == e)
+       continue;
+
+      if (!dominated_by_p (CDI_DOMINATORS, f->src, e->dest))
+       {
+         none_removed = true;
+         break;
+       }
+    }
+
+  df = BITMAP_ALLOC (NULL);
+  df_idom = BITMAP_ALLOC (NULL);
+
+  if (none_removed)
+    bitmap_set_bit (df_idom,
+                   get_immediate_dominator (CDI_DOMINATORS, e->dest)->index);
+  else
+    {
+      get_all_dominated_blocks (e->dest, &bbs_to_remove);
+      for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+       {
+         FOR_EACH_EDGE (f, ei, bb->succs)
+           {
+             if (f->dest != EXIT_BLOCK_PTR)
+               bitmap_set_bit (df, f->dest->index);
+           }
+       }
+      for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+       bitmap_clear_bit (df, bb->index);
+
+      EXECUTE_IF_SET_IN_BITMAP (df, 0, i, bi)
+       {
+         bb = BASIC_BLOCK (i);
+         bitmap_set_bit (df_idom,
+                         get_immediate_dominator (CDI_DOMINATORS, bb)->index);
+       }
+    }
+
+  if (cfgcleanup_altered_bbs)
+    {
+      /* Record the set of the altered basic blocks.  */
+      bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
+      bitmap_ior_into (cfgcleanup_altered_bbs, df);
+    }
+
+  /* Remove E and the cancelled blocks.  */
+  if (none_removed)
+    remove_edge (e);
+  else
+    {
+      for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+       delete_basic_block (bb);
+    }
+
+  /* Update the dominance information.  The immediate dominator may change only
+     for blocks whose immediate dominator belongs to DF_IDOM:
+   
+     Suppose that idom(X) = Y before removal of E and idom(X) != Y after the
+     removal.  Let Z the arbitrary block such that idom(Z) = Y and
+     Z dominates X after the removal.  Before removal, there exists a path P
+     from Y to X that avoids Z.  Let F be the last edge on P that is
+     removed, and let W = F->dest.  Before removal, idom(W) = Y (since Y
+     dominates W, and because of P, Z does not dominate W), and W belongs to
+     the dominance frontier of E.  Therefore, Y belongs to DF_IDOM.  */ 
+  EXECUTE_IF_SET_IN_BITMAP (df_idom, 0, i, bi)
+    {
+      bb = BASIC_BLOCK (i);
+      for (dbb = first_dom_son (CDI_DOMINATORS, bb);
+          dbb;
+          dbb = next_dom_son (CDI_DOMINATORS, dbb))
+       VEC_safe_push (basic_block, heap, bbs_to_fix_dom, dbb);
+    }
+
+  iterate_fix_dominators (CDI_DOMINATORS, bbs_to_fix_dom, true);
+
+  BITMAP_FREE (df);
+  BITMAP_FREE (df_idom);
+  VEC_free (basic_block, heap, bbs_to_remove);
+  VEC_free (basic_block, heap, bbs_to_fix_dom);
+}
+
+/* Purge dead EH edges from basic block BB.  */
+
 bool
 tree_purge_dead_eh_edges (basic_block bb)
 {
@@ -4835,38 +6634,18 @@ tree_purge_dead_eh_edges (basic_block bb)
     {
       if (e->flags & EDGE_EH)
        {
-         remove_edge (e);
+         remove_edge_and_dominated_blocks (e);
          changed = true;
        }
       else
        ei_next (&ei);
     }
 
-  /* Removal of dead EH edges might change dominators of not
-     just immediate successors.  E.g. when bb1 is changed so that
-     it no longer can throw and bb1->bb3 and bb1->bb4 are dead
-     eh edges purged by this function in:
-           0
-         / \
-        v   v
-        1-->2
-        / \  |
-       v   v |
-       3-->4 |
-        \    v
-        --->5
-            |
-            -
-     idom(bb5) must be recomputed.  For now just free the dominance
-     info.  */
-  if (changed)
-    free_dominance_info (CDI_DOMINATORS);
-
   return changed;
 }
 
 bool
-tree_purge_all_dead_eh_edges (bitmap blocks)
+tree_purge_all_dead_eh_edges (const_bitmap blocks)
 {
   bool changed = false;
   unsigned i;
@@ -4910,8 +6689,8 @@ tree_execute_on_shrinking_pred (edge e)
    of 'first'. Both of them are dominated by 'new_head' basic block. When
    'new_head' was created by 'second's incoming edge it received phi arguments
    on the edge by split_edge(). Later, additional edge 'e' was created to
-   connect 'new_head' and 'first'. Now this routine adds phi args on this 
-   additional edge 'e' that new_head to second edge received as part of edge 
+   connect 'new_head' and 'first'. Now this routine adds phi args on this
+   additional edge 'e' that new_head to second edge received as part of edge
    splitting.
 */
 
@@ -4929,8 +6708,8 @@ tree_lv_adjust_loop_header_phi (basic_block first, basic_block second,
   /* Browse all 'second' basic block phi nodes and add phi args to
      edge 'e' for 'first' head. PHI args are always in correct order.  */
 
-  for (phi2 = phi_nodes (second), phi1 = phi_nodes (first); 
-       phi2 && phi1; 
+  for (phi2 = phi_nodes (second), phi1 = phi_nodes (first);
+       phi2 && phi1;
        phi2 = PHI_CHAIN (phi2),  phi1 = PHI_CHAIN (phi1))
     {
       tree def = PHI_ARG_DEF (phi2, e2->dest_idx);
@@ -4938,27 +6717,25 @@ tree_lv_adjust_loop_header_phi (basic_block first, basic_block second,
     }
 }
 
-/* Adds a if else statement to COND_BB with condition COND_EXPR.  
-   SECOND_HEAD is the destination of the THEN and FIRST_HEAD is 
+/* Adds a if else statement to COND_BB with condition COND_EXPR.
+   SECOND_HEAD is the destination of the THEN and FIRST_HEAD is
    the destination of the ELSE part.  */
 static void
-tree_lv_add_condition_to_bb (basic_block first_head, basic_block second_head,
-                            basic_block cond_bb, void *cond_e)
+tree_lv_add_condition_to_bb (basic_block first_head ATTRIBUTE_UNUSED,
+                            basic_block second_head ATTRIBUTE_UNUSED,
+                            basic_block cond_bb, void *cond_e)
 {
   block_stmt_iterator bsi;
-  tree goto1 = NULL_TREE;
-  tree goto2 = NULL_TREE;
   tree new_cond_expr = NULL_TREE;
   tree cond_expr = (tree) cond_e;
   edge e0;
 
   /* Build new conditional expr */
-  goto1 = build1 (GOTO_EXPR, void_type_node, tree_block_label (first_head));
-  goto2 = build1 (GOTO_EXPR, void_type_node, tree_block_label (second_head));
-  new_cond_expr = build3 (COND_EXPR, void_type_node, cond_expr, goto1, goto2);
+  new_cond_expr = build3 (COND_EXPR, void_type_node, cond_expr,
+                         NULL_TREE, NULL_TREE);
 
-  /* Add new cond in cond_bb.  */ 
-  bsi = bsi_start (cond_bb); 
+  /* Add new cond in cond_bb.  */
+  bsi = bsi_start (cond_bb);
   bsi_insert_after (&bsi, new_cond_expr, BSI_NEW_STMT);
   /* Adjust edges appropriately to connect new head with first head
      as well as second head.  */
@@ -4974,6 +6751,7 @@ struct cfg_hooks tree_cfg_hooks = {
   create_bb,                   /* create_basic_block  */
   tree_redirect_edge_and_branch,/* redirect_edge_and_branch  */
   tree_redirect_edge_and_branch_force,/* redirect_edge_and_branch_force  */
+  tree_can_remove_branch_p,    /* can_remove_branch_p  */
   remove_bb,                   /* delete_basic_block  */
   tree_split_block,            /* split_block  */
   tree_move_block_after,       /* move_block_after  */
@@ -4995,13 +6773,13 @@ struct cfg_hooks tree_cfg_hooks = {
   tree_lv_add_condition_to_bb, /* lv_add_condition_to_bb */
   tree_lv_adjust_loop_header_phi, /* lv_adjust_loop_header_phi*/
   extract_true_false_edges_from_block, /* extract_cond_bb_edges */
-  flush_pending_stmts          /* flush_pending_stmts */  
+  flush_pending_stmts          /* flush_pending_stmts */
 };
 
 
 /* Split all critical edges.  */
 
-static void
+static unsigned int
 split_critical_edges (void)
 {
   basic_block bb;
@@ -5021,9 +6799,10 @@ split_critical_edges (void)
          }
     }
   end_recording_case_labels ();
+  return 0;
 }
 
-struct tree_opt_pass pass_split_crit_edges = 
+struct tree_opt_pass pass_split_crit_edges =
 {
   "crited",                          /* name */
   NULL,                          /* gate */
@@ -5055,13 +6834,15 @@ gimplify_val (block_stmt_iterator *bsi, tree type, tree exp)
     return exp;
 
   t = make_rename_temp (type, NULL);
-  new_stmt = build2 (MODIFY_EXPR, type, t, exp);
+  new_stmt = build_gimple_modify_stmt (t, exp);
 
   orig_stmt = bsi_stmt (*bsi);
   SET_EXPR_LOCUS (new_stmt, EXPR_LOCUS (orig_stmt));
   TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
 
   bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
+  if (gimple_in_ssa_p (cfun))
+    mark_symbols_for_renaming (new_stmt);
 
   return t;
 }
@@ -5115,7 +6896,7 @@ gimplify_build1 (block_stmt_iterator *bsi, enum tree_code code, tree type,
 \f
 /* Emit return warnings.  */
 
-static void
+static unsigned int
 execute_warn_function_return (void)
 {
 #ifdef USE_MAPPED_LOCATION
@@ -5188,6 +6969,7 @@ execute_warn_function_return (void)
            }
        }
     }
+  return 0;
 }
 
 
@@ -5234,7 +7016,7 @@ struct tree_opt_pass pass_warn_function_return =
 
 /* Emit noreturn warnings.  */
 
-static void
+static unsigned int
 execute_warn_function_noreturn (void)
 {
   if (warn_missing_noreturn
@@ -5244,6 +7026,7 @@ execute_warn_function_noreturn (void)
     warning (OPT_Wmissing_noreturn, "%Jfunction might be possible candidate "
             "for attribute %<noreturn%>",
             cfun->decl);
+  return 0;
 }
 
 struct tree_opt_pass pass_warn_function_noreturn =