OSDN Git Service

2008-05-16 Kenneth Zadeck <zadeck@naturalbridge.com>
[pf3gnuchains/gcc-fork.git] / gcc / tree-cfg.c
index 889e7d7..2de19c4 100644 (file)
@@ -1,5 +1,5 @@
 /* Control flow functions for trees.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
@@ -7,7 +7,7 @@ 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,
@@ -16,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"
@@ -47,6 +46,7 @@ Boston, MA 02110-1301, USA.  */
 #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.  */
@@ -97,11 +97,12 @@ static edge tree_try_redirect_by_replacing_jump (edge, basic_block);
 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);
+static bool computed_goto_p (const_tree);
 
 /* Flowgraph optimization and cleanup.  */
 static void tree_merge_blocks (basic_block, basic_block);
@@ -212,8 +213,10 @@ execute_build_cfg (void)
   return 0;
 }
 
-struct tree_opt_pass pass_build_cfg =
+struct gimple_opt_pass pass_build_cfg =
 {
+ {
+  GIMPLE_PASS,
   "cfg",                               /* name */
   NULL,                                        /* gate */
   execute_build_cfg,                   /* execute */
@@ -225,8 +228,8 @@ struct tree_opt_pass pass_build_cfg =
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_verify_stmts | TODO_cleanup_cfg,        /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_verify_stmts | TODO_cleanup_cfg /* todo_flags_finish */
+ }
 };
 
 /* Search the CFG for any computed gotos.  If found, factor them to a
@@ -417,8 +420,7 @@ fold_cond_expr_cond (void)
          cond = fold (COND_EXPR_COND (stmt));
          zerop = integer_zerop (cond);
          onep = integer_onep (cond);
-         fold_undefer_overflow_warnings (((zerop || onep)
-                                          && !TREE_NO_WARNING (stmt)),
+         fold_undefer_overflow_warnings (zerop || onep,
                                          stmt,
                                          WARN_STRICT_OVERFLOW_CONDITIONAL);
          if (zerop)
@@ -518,9 +520,20 @@ make_edges (void)
 
            case OMP_SECTIONS:
              cur_region = new_omp_region (bb, code, cur_region);
+             fallthru = true;
+             break;
+
+           case OMP_SECTIONS_SWITCH:
              fallthru = false;
              break;
 
+
+            case OMP_ATOMIC_LOAD:
+            case OMP_ATOMIC_STORE:
+               fallthru = true;
+               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.  */
@@ -534,31 +547,47 @@ make_edges (void)
              switch (cur_region->type)
                {
                case OMP_FOR:
-                 /* ??? Technically there should be a some sort of loopback
-                    edge here, but it goes to a block that doesn't exist yet,
-                    and without it, updating the ssa form would be a real
-                    bear.  Fortunately, we don't yet do ssa before expanding
-                    these nodes.  */
+                 /* Mark all OMP_FOR and OMP_CONTINUE succs edges as abnormal
+                    to prevent splitting them.  */
+                 single_succ_edge (cur_region->entry)->flags |= EDGE_ABNORMAL;
+                 /* Make the loopback edge.  */
+                 make_edge (bb, single_succ (cur_region->entry),
+                            EDGE_ABNORMAL);
+
+                 /* 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, EDGE_ABNORMAL);
+                 make_edge (bb, bb->next_bb, EDGE_FALLTHRU | EDGE_ABNORMAL);
+                 fallthru = false;
                  break;
 
                case OMP_SECTIONS:
                  /* Wire up the edges into and out of the nested sections.  */
-                 /* ??? Similarly wrt loopback.  */
                  {
+                   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 (cur_region->entry, i->entry, 0);
+                       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;
 
                default:
                  gcc_unreachable ();
                }
-             fallthru = true;
              break;
 
            default:
@@ -602,20 +631,10 @@ make_cond_expr_edges (basic_block bb)
   else_bb = label_to_block (else_label);
 
   e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
-#ifdef USE_MAPPED_LOCATION
   e->goto_locus = EXPR_LOCATION (COND_EXPR_THEN (entry));
-#else
-  e->goto_locus = EXPR_LOCUS (COND_EXPR_THEN (entry));
-#endif
   e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
   if (e)
-    {
-#ifdef USE_MAPPED_LOCATION
-      e->goto_locus = EXPR_LOCATION (COND_EXPR_ELSE (entry));
-#else
-      e->goto_locus = EXPR_LOCUS (COND_EXPR_ELSE (entry));
-#endif
-    }
+    e->goto_locus = EXPR_LOCATION (COND_EXPR_ELSE (entry));
 
   /* We do not need the gotos anymore.  */
   COND_EXPR_THEN (entry) = NULL_TREE;
@@ -631,7 +650,7 @@ make_cond_expr_edges (basic_block bb)
    element.  */
 
 static bool
-edge_to_cases_cleanup (void *key ATTRIBUTE_UNUSED, void **value,
+edge_to_cases_cleanup (const void *key ATTRIBUTE_UNUSED, void **value,
                       void *data ATTRIBUTE_UNUSED)
 {
   tree t, next;
@@ -809,11 +828,7 @@ make_goto_expr_edges (basic_block bb)
     {
       tree dest = GOTO_DESTINATION (goto_t);
       edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
-#ifdef USE_MAPPED_LOCATION
       e->goto_locus = EXPR_LOCATION (goto_t);
-#else
-      e->goto_locus = EXPR_LOCUS (goto_t);
-#endif
       bsi_remove (&last, true);
       return;
     }
@@ -1046,18 +1061,23 @@ group_case_labels (void)
          tree labels = SWITCH_LABELS (stmt);
          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_case = NULL_TREE;
+         tree default_label = NULL_TREE;
 
          /* The default label is always the last case in a switch
-            statement after gimplification.  */
-         default_label = CASE_LABEL (default_case);
+            statement after gimplification if it was not optimized
+            away.  */
+         if (!CASE_LOW (TREE_VEC_ELT (labels, old_size - 1))
+             && !CASE_HIGH (TREE_VEC_ELT (labels, old_size - 1)))
+           {
+             default_case = TREE_VEC_ELT (labels, old_size - 1);
+             default_label = CASE_LABEL (default_case);
+             old_size--;
+           }
 
-         /* Look for possible opportunities to merge cases.
-            Ignore the last element of the label vector because it
-            must be the default case.  */
+         /* Look for possible opportunities to merge cases.  */
           i = 0;
-         while (i < old_size - 1)
+         while (i < old_size)
            {
              tree base_case, base_label, base_high;
              base_case = TREE_VEC_ELT (labels, i);
@@ -1081,7 +1101,7 @@ group_case_labels (void)
              /* Try to merge case labels.  Break out when we reach the end
                 of the label vector or when we cannot merge the next case
                 label with the current one.  */
-             while (i < old_size - 1)
+             while (i < old_size)
                {
                  tree merge_case = TREE_VEC_ELT (labels, i);
                  tree merge_label = CASE_LABEL (merge_case);
@@ -1123,7 +1143,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;
 
@@ -1144,7 +1164,9 @@ tree_can_merge_blocks_p (basic_block a, basic_block b)
 
   /* 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;
 
@@ -1275,9 +1297,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)
@@ -1298,7 +1321,21 @@ tree_merge_blocks (basic_block a, basic_block b)
        }
       else
         {
-          replace_uses_by (def, use);
+         /* If we deal with a PHI for virtual operands, we can simply
+            propagate these without fussing with folding or updating
+            the stmt.  */
+         if (!is_gimple_reg (def))
+           {
+             imm_use_iterator iter;
+             use_operand_p use_p;
+             tree stmt;
+
+             FOR_EACH_IMM_USE_STMT (stmt, iter, def)
+               FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+                 SET_USE (use_p, use);
+           }
+         else
+            replace_uses_by (def, use);
           remove_phi_node (phi, NULL, true);
         }
     }
@@ -1405,7 +1442,7 @@ remove_useless_stmts_warn_notreached (tree stmt)
       location_t loc = EXPR_LOCATION (stmt);
       if (LOCATION_LINE (loc) > 0)
        {
-         warning (0, "%Hwill never be executed", &loc);
+         warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc);
          return true;
        }
     }
@@ -1755,9 +1792,11 @@ static void
 update_call_expr_flags (tree call)
 {
   tree decl = get_callee_fndecl (call);
+  int flags;
   if (!decl)
     return;
-  if (call_expr_flags (call) & (ECF_CONST | ECF_PURE))
+  flags = call_expr_flags (call);
+  if (flags & (ECF_CONST | ECF_PURE) && !(flags & ECF_LOOPING_CONST_OR_PURE))
     TREE_SIDE_EFFECTS (call) = 0;
   if (TREE_NOTHROW (decl))
     TREE_NOTHROW (call) = 1;
@@ -1772,9 +1811,9 @@ notice_special_calls (tree t)
   int flags = call_expr_flags (t);
 
   if (flags & ECF_MAY_BE_ALLOCA)
-    current_function_calls_alloca = true;
+    cfun->calls_alloca = true;
   if (flags & ECF_RETURNS_TWICE)
-    current_function_calls_setjmp = true;
+    cfun->calls_setjmp = true;
 }
 
 
@@ -1784,8 +1823,8 @@ notice_special_calls (tree t)
 void
 clear_special_calls (void)
 {
-  current_function_calls_alloca = false;
-  current_function_calls_setjmp = false;
+  cfun->calls_alloca = false;
+  cfun->calls_setjmp = false;
 }
 
 
@@ -1881,6 +1920,33 @@ remove_useless_stmts_1 (tree *tp, struct rus_data *data)
       data->last_goto = NULL;
       break;
 
+    case OMP_PARALLEL:
+      /* Make sure the outermost BIND_EXPR in OMP_BODY isn't removed
+        as useless.  */
+      remove_useless_stmts_1 (&BIND_EXPR_BODY (OMP_BODY (*tp)), data);
+      data->last_goto = NULL;
+      break;
+
+    case OMP_SECTIONS:
+    case OMP_SINGLE:
+    case OMP_SECTION:
+    case OMP_MASTER :
+    case OMP_ORDERED:
+    case OMP_CRITICAL:
+      remove_useless_stmts_1 (&OMP_BODY (*tp), data);
+      data->last_goto = NULL;
+      break;
+
+    case OMP_FOR:
+      remove_useless_stmts_1 (&OMP_FOR_BODY (*tp), data);
+      data->last_goto = NULL;
+      if (OMP_FOR_PRE_BODY (*tp))
+       {
+         remove_useless_stmts_1 (&OMP_FOR_PRE_BODY (*tp), data);
+         data->last_goto = NULL;
+       }
+      break;
+
     default:
       data->last_goto = NULL;
       break;
@@ -1904,8 +1970,10 @@ remove_useless_stmts (void)
 }
 
 
-struct tree_opt_pass pass_remove_useless_stmts =
+struct gimple_opt_pass pass_remove_useless_stmts =
 {
+ {
+  GIMPLE_PASS,
   "useless",                           /* name */
   NULL,                                        /* gate */
   remove_useless_stmts,                        /* execute */
@@ -1917,8 +1985,8 @@ struct tree_opt_pass pass_remove_useless_stmts =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func,                      /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_dump_func                       /* todo_flags_finish */
+ }
 };
 
 /* Remove PHI nodes associated with basic block BB and all edges out of BB.  */
@@ -1950,11 +2018,7 @@ static void
 remove_bb (basic_block bb)
 {
   block_stmt_iterator i;
-#ifdef USE_MAPPED_LOCATION
   source_location loc = UNKNOWN_LOCATION;
-#else
-  source_locus loc = 0;
-#endif
 
   if (dump_file)
     {
@@ -2022,15 +2086,8 @@ remove_bb (basic_block bb)
             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);
-#else
-             source_locus t;
-             t = EXPR_LOCUS (stmt);
-             if (t && LOCATION_LINE (*t) > 0)
-               loc = t;
-#endif
            }
        }
     }
@@ -2039,13 +2096,8 @@ remove_bb (basic_block bb)
      block is unreachable.  We walk statements backwards in the
      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)
-    warning (OPT_Wunreachable_code, "%Hwill never be executed", loc);
-#endif
 
   remove_phi_nodes_and_edges_for_unreachable_block (bb);
   bb->il.tree = NULL;
@@ -2422,7 +2474,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
@@ -2436,17 +2488,17 @@ 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
         function has nonlocal labels.  */
-      if (TREE_SIDE_EFFECTS (call) && current_function_has_nonlocal_label)
+      if (TREE_SIDE_EFFECTS (call) && cfun->has_nonlocal_label)
        return true;
 
       /* A CALL_EXPR also alters control flow if it does not return.  */
@@ -2465,8 +2517,8 @@ is_ctrl_altering_stmt (tree t)
 
 /* Return true if T is a computed goto.  */
 
-bool
-computed_goto_p (tree t)
+static bool
+computed_goto_p (const_tree t)
 {
   return (TREE_CODE (t) == GOTO_EXPR
          && TREE_CODE (GOTO_DESTINATION (t)) != LABEL_DECL);
@@ -2476,7 +2528,7 @@ computed_goto_p (tree t)
 /* Return true if T is a simple local goto.  */
 
 bool
-simple_goto_p (tree t)
+simple_goto_p (const_tree t)
 {
   return (TREE_CODE (t) == GOTO_EXPR
          && TREE_CODE (GOTO_DESTINATION (t)) == LABEL_DECL);
@@ -2487,7 +2539,7 @@ simple_goto_p (tree t)
    Transfers of control flow associated with EH are excluded.  */
 
 bool
-tree_can_make_abnormal_goto (tree t)
+tree_can_make_abnormal_goto (const_tree t)
 {
   if (computed_goto_p (t))
     return true;
@@ -2496,7 +2548,7 @@ tree_can_make_abnormal_goto (tree t)
   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 TREE_SIDE_EFFECTS (t) && cfun->has_nonlocal_label;
   return false;
 }
 
@@ -2508,7 +2560,7 @@ tree_can_make_abnormal_goto (tree t)
    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;
@@ -2543,7 +2595,7 @@ 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);
 }
@@ -2577,7 +2629,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
@@ -2587,7 +2638,6 @@ last_stmt (basic_block bb)
   return !bsi_end_p (b) ? bsi_stmt (b) : NULL_TREE;
 }
 
-
 /* 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.  */
@@ -2650,7 +2700,7 @@ set_bb_for_stmt (tree t, basic_block bb)
          if (uid == -1)
            {
              unsigned old_len = VEC_length (basic_block, label_to_block_map);
-             LABEL_DECL_UID (t) = uid = cfun->last_label_uid++;
+             LABEL_DECL_UID (t) = uid = cfun->cfg->last_label_uid++;
              if (old_len <= (unsigned) uid)
                {
                  unsigned new_len = 3 * uid / 2;
@@ -3020,24 +3070,28 @@ bsi_insert_on_edge_immediate (edge e, tree stmt)
 static void
 reinstall_phi_args (edge new_edge, edge old_edge)
 {
-  tree var, phi;
+  tree phi;
+  edge_var_map_vector v;
+  edge_var_map *vm;
+  int i;
 
-  if (!PENDING_STMT (old_edge))
+  v = redirect_edge_var_map_vector (old_edge);
+  if (!v)
     return;
 
-  for (var = PENDING_STMT (old_edge), phi = phi_nodes (new_edge->dest);
-       var && phi;
-       var = TREE_CHAIN (var), phi = PHI_CHAIN (phi))
+  for (i = 0, phi = phi_nodes (new_edge->dest);
+       VEC_iterate (edge_var_map, v, i, vm) && phi;
+       i++, phi = PHI_CHAIN (phi))
     {
-      tree result = TREE_PURPOSE (var);
-      tree arg = TREE_VALUE (var);
+      tree result = redirect_edge_var_map_result (vm);
+      tree arg = redirect_edge_var_map_def (vm);
 
       gcc_assert (result == PHI_RESULT (phi));
 
       add_phi_arg (phi, arg, new_edge);
     }
 
-  PENDING_STMT (old_edge) = NULL;
+  redirect_edge_var_map_clear (old_edge);
 }
 
 /* Returns the basic block after which the new basic block created
@@ -3094,7 +3148,6 @@ static tree
 verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 {
   tree t = *tp, x;
-  bool in_phi = (data != NULL);
 
   if (TYPE_P (t))
     *walk_subtrees = 0;
@@ -3138,38 +3191,20 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 
     case ADDR_EXPR:
       {
-       bool old_invariant;
        bool old_constant;
        bool old_side_effects;
-       bool new_invariant;
        bool new_constant;
        bool new_side_effects;
 
-        /* ??? tree-ssa-alias.c may have overlooked dead PHI nodes, missing
-          dead PHIs that take the address of something.  But if the PHI
-          result is dead, the fact that it takes the address of anything
-          is irrelevant.  Because we can not tell from here if a PHI result
-          is dead, we just skip this check for PHIs altogether.  This means
-          we may be missing "valid" checks, but what can you do?
-          This was PR19217.  */
-        if (in_phi)
-         break;
+       gcc_assert (is_gimple_address (t));
 
-       old_invariant = TREE_INVARIANT (t);
        old_constant = TREE_CONSTANT (t);
        old_side_effects = TREE_SIDE_EFFECTS (t);
 
        recompute_tree_invariant_for_addr_expr (t);
-       new_invariant = TREE_INVARIANT (t);
        new_side_effects = TREE_SIDE_EFFECTS (t);
        new_constant = TREE_CONSTANT (t);
 
-       if (old_invariant != new_invariant)
-         {
-           error ("invariant not recomputed when ADDR_EXPR changed");
-           return t;
-         }
-
         if (old_constant != new_constant)
          {
            error ("constant not recomputed when ADDR_EXPR changed");
@@ -3196,6 +3231,7 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
            error ("address taken, but ADDRESSABLE bit not set");
            return x;
          }
+
        break;
       }
 
@@ -3213,14 +3249,15 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
        }
       break;
 
-    case NOP_EXPR:
-    case CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+       gcc_unreachable ();
+
+    CASE_CONVERT:
     case FIX_TRUNC_EXPR:
     case FLOAT_EXPR:
     case NEGATE_EXPR:
     case ABS_EXPR:
     case BIT_NOT_EXPR:
-    case NON_LVALUE_EXPR:
     case TRUTH_NOT_EXPR:
       CHECK_OP (0, "invalid operand to unary operator");
       break;
@@ -3251,14 +3288,34 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
            }
          else if (TREE_CODE (t) == BIT_FIELD_REF)
            {
-             CHECK_OP (1, "invalid operand to BIT_FIELD_REF");
-             CHECK_OP (2, "invalid operand to BIT_FIELD_REF");
+             if (!host_integerp (TREE_OPERAND (t, 1), 1)
+                 || !host_integerp (TREE_OPERAND (t, 2), 1))
+               {
+                 error ("invalid position or size operand to BIT_FIELD_REF");
+                 return t;
+               }
+             else if (INTEGRAL_TYPE_P (TREE_TYPE (t))
+                      && (TYPE_PRECISION (TREE_TYPE (t))
+                          != TREE_INT_CST_LOW (TREE_OPERAND (t, 1))))
+               {
+                 error ("integral result type precision does not match "
+                        "field size of BIT_FIELD_REF");
+                 return t;
+               }
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+                 && (GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (t)))
+                     != TREE_INT_CST_LOW (TREE_OPERAND (t, 1))))
+               {
+                 error ("mode precision of non-integral result does not "
+                        "match field size of BIT_FIELD_REF");
+                 return t;
+               }
            }
 
          t = TREE_OPERAND (t, 0);
        }
 
-      if (!CONSTANT_CLASS_P (t) && !is_gimple_lvalue (t))
+      if (!is_gimple_min_invariant (t) && !is_gimple_lvalue (t))
        {
          error ("invalid reference prefix");
          return t;
@@ -3346,212 +3403,1007 @@ 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 (OMP_DIRECTIVE_P (stmt))
+  if (!is_gimple_val (op))
     {
-      /* 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;
+      error ("invalid operand in unary expression");
+      return true;
     }
 
-  if (!is_gimple_stmt (stmt))
+  /* 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)))
     {
-      error ("is not a valid GIMPLE statement");
-      goto fail;
+      error ("type mismatch in unary expression");
+      debug_generic_expr (type);
+      debug_generic_expr (TREE_TYPE (op));
+      return true;
     }
 
-  addr = walk_tree (&stmt, verify_expr, NULL, NULL);
-  if (addr)
+  return false;
+}
+
+/* Verifies if EXPR is a valid GIMPLE binary expression.  Returns true
+   if there is an error, otherwise false.  */
+
+static bool
+verify_gimple_binary_expr (const_tree expr)
+{
+  tree op0 = TREE_OPERAND (expr, 0);
+  tree op1 = TREE_OPERAND (expr, 1);
+  tree type = TREE_TYPE (expr);
+
+  if (!is_gimple_val (op0) || !is_gimple_val (op1))
     {
-      debug_generic_stmt (addr);
+      error ("invalid operands in binary 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 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)))
     {
-      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 binary expression");
+      debug_generic_stmt (type);
+      debug_generic_stmt (TREE_TYPE (op0));
+      debug_generic_stmt (TREE_TYPE (op1));
+      return true;
     }
 
   return false;
-
- fail:
-  debug_generic_stmt (stmt);
-  return true;
 }
 
-
-/* Return true when the T can be shared.  */
+/* Verify if EXPR is either a GIMPLE ID or a GIMPLE indirect reference.
+   Returns true if there is an error, otherwise false.  */
 
 static bool
-tree_node_can_be_shared (tree t)
+verify_gimple_min_lval (tree expr)
 {
-  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;
+  tree op;
 
-  if (TREE_CODE (t) == CASE_LABEL_EXPR)
-    return true;
+  if (is_gimple_id (expr))
+    return false;
 
-  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 (TREE_CODE (expr) != INDIRECT_REF
+      && TREE_CODE (expr) != ALIGN_INDIRECT_REF
+      && TREE_CODE (expr) != MISALIGNED_INDIRECT_REF)
+    {
+      error ("invalid expression for min lvalue");
+      return true;
+    }
 
-  if (DECL_P (t))
-    return true;
+  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 false;
 }
 
+/* Verify if EXPR is a valid GIMPLE reference expression.  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_reference (tree expr)
 {
-  struct pointer_set_t *visited = (struct pointer_set_t *) data;
-
-  if (tree_node_can_be_shared (*tp))
+  while (handled_component_p (expr))
     {
-      *walk_subtrees = false;
-      return NULL;
-    }
+      tree op = TREE_OPERAND (expr, 0);
 
-  if (pointer_set_insert (visited, *tp))
-    return *tp;
+      if (TREE_CODE (expr) == ARRAY_REF
+         || TREE_CODE (expr) == ARRAY_RANGE_REF)
+       {
+         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 ("invalid operands to array reference");
+             debug_generic_stmt (expr);
+             return true;
+           }
+       }
 
-  return NULL;
-}
+      /* 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;
+       }
 
+      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;
+       }
 
-/* Helper function for verify_gimple_tuples.  */
+      if (TREE_CODE (expr) == COMPONENT_REF
+         && !useless_type_conversion_p (TREE_TYPE (expr),
+                                        TREE_TYPE (TREE_OPERAND (expr, 1))))
+       {
+         error ("type mismatch in component reference");
+         debug_generic_stmt (TREE_TYPE (expr));
+         debug_generic_stmt (TREE_TYPE (TREE_OPERAND (expr, 1)));
+         return true;
+       }
 
-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;
+      /* For VIEW_CONVERT_EXPRs which are allowed here, too, there
+        is nothing to verify.  Gross mismatches at most invoke
+        undefined behavior.  */
 
-    default:
-      return NULL_TREE;
+      expr = op;
     }
+
+  return verify_gimple_min_lval (expr);
 }
 
-/* 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.  */
+/* 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.  */
 
 static bool
-verify_gimple_tuples (tree t)
+one_pointer_to_useless_type_conversion_p (tree dest, tree src_obj)
 {
-  return walk_tree (&t, verify_gimple_tuples_1, NULL, NULL) != NULL;
+  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;
 }
 
-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;
+/* Return true if TYPE1 is a fixed-point type and if conversions to and
+   from TYPE2 can be handled by FIXED_CONVERT_EXPR.  */
 
-  if (!pointer_set_contains (visited, node->stmt))
-    {
-      error ("Dead STMT in EH table");
-      debug_generic_stmt (node->stmt);
-      eh_error_found = true;
-    }
-  return 0;
+static bool
+valid_fixed_convert_types_p (tree type1, tree type2)
+{
+  return (FIXED_POINT_TYPE_P (type1)
+         && (INTEGRAL_TYPE_P (type2)
+             || SCALAR_FLOAT_TYPE_P (type2)
+             || FIXED_POINT_TYPE_P (type2)));
 }
 
-/* Verify the GIMPLE statement chain.  */
+/* Verify the GIMPLE expression EXPR.  Returns true if there is an
+   error, otherwise false.  */
 
-void
-verify_stmts (void)
+static bool
+verify_gimple_expr (tree expr)
 {
-  basic_block bb;
-  block_stmt_iterator bsi;
-  bool err = false;
-  struct pointer_set_t *visited, *visited_stmts;
-  tree addr;
+  tree type = TREE_TYPE (expr);
 
-  timevar_push (TV_TREE_STMT_VERIFY);
-  visited = pointer_set_create ();
-  visited_stmts = pointer_set_create ();
+  if (is_gimple_val (expr))
+    return false;
 
-  FOR_EACH_BB (bb)
+  /* Special codes we cannot handle via their class.  */
+  switch (TREE_CODE (expr))
     {
-      tree phi;
-      int i;
-
-      for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
-       {
-         int phi_num_args = PHI_NUM_ARGS (phi);
+    CASE_CONVERT:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in conversion");
+           return true;
+         }
 
-         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;
-           }
+       /* 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;
 
-         for (i = 0; i < phi_num_args; i++)
-           {
-             tree t = PHI_ARG_DEF (phi, i);
-             tree addr;
+       /* 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;
 
-             /* 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;
-               }
+       /* 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;
 
-             addr = walk_tree (&t, verify_expr, (void *) 1, NULL);
-             if (addr)
+       /* 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 FIXED_CONVERT_EXPR:
+      {
+       tree op = TREE_OPERAND (expr, 0);
+       if (!is_gimple_val (op))
+         {
+           error ("invalid operand in conversion");
+           return true;
+         }
+
+       if (!valid_fixed_convert_types_p (type, TREE_TYPE (op))
+           && !valid_fixed_convert_types_p (TREE_TYPE (op), type))
+         {
+           error ("invalid types in fixed-point 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:
+      gcc_unreachable ();
+
+    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.  */
+      {
+       tree decl = CALL_EXPR_FN (expr);
+
+       if (TREE_CODE (decl) == FUNCTION_DECL 
+           && DECL_LOOPING_CONST_OR_PURE_P (decl)
+           && (!DECL_PURE_P (decl))
+           && (!TREE_READONLY (decl)))
+         {
+           error ("invalid pure const state for function");
+           return true;
+         }
+       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:
+    case PREDICT_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);
+      if (addr != stmt)
+       {
+         inform ("in statement");
+         debug_generic_stmt (stmt);
+       }
+      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)
                {
-                 debug_generic_stmt (addr);
+                 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_min_invariant (t))
+               {
+                 error ("PHI def is not a GIMPLE value");
+                 debug_generic_stmt (phi);
+                 debug_generic_stmt (t);
                  err |= true;
                }
 
@@ -3831,13 +4683,16 @@ tree_verify_flow_info (void)
 
            /* Verify that the case labels are sorted.  */
            prev = TREE_VEC_ELT (vec, 0);
-           for (i = 1; i < n - 1; ++i)
+           for (i = 1; i < n; ++i)
              {
                tree c = TREE_VEC_ELT (vec, i);
                if (! CASE_LOW (c))
                  {
-                   error ("found default case not at end of case vector");
-                   err = 1;
+                   if (i != n - 1)
+                     {
+                       error ("found default case not at end of case vector");
+                       err = 1;
+                     }
                    continue;
                  }
                if (! tree_int_cst_lt (CASE_LOW (prev), CASE_LOW (c)))
@@ -3851,11 +4706,9 @@ tree_verify_flow_info (void)
                  }
                prev = c;
              }
-           if (CASE_LOW (TREE_VEC_ELT (vec, n - 1)))
-             {
-               error ("no default case found at end of case vector");
-               err = 1;
-             }
+           /* VRP will remove the default case if it can prove it will
+              never be executed.  So do not verify there always exists
+              a default case here.  */
 
            FOR_EACH_EDGE (e, ei, bb->succs)
              {
@@ -4103,6 +4956,13 @@ tree_redirect_edge_and_branch (edge e, basic_block dest)
       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.  */
@@ -4122,7 +4982,7 @@ tree_redirect_edge_and_branch (edge e, basic_block dest)
    it to the destination of the other edge from E->src.  */
 
 static bool
-tree_can_remove_branch_p (edge e)
+tree_can_remove_branch_p (const_edge e)
 {
   if (e->flags & EDGE_ABNORMAL)
     return false;
@@ -4218,7 +5078,7 @@ tree_move_block_after (basic_block bb, basic_block after)
 /* Return true if basic_block can be duplicated.  */
 
 static bool
-tree_can_duplicate_bb_p (basic_block bb ATTRIBUTE_UNUSED)
+tree_can_duplicate_bb_p (const_basic_block bb ATTRIBUTE_UNUSED)
 {
   return true;
 }
@@ -4270,13 +5130,59 @@ tree_duplicate_bb (basic_block bb)
        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);
+      /* 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 (!phi_nodes (e_copy->dest))
+    return;
+
+  bb = bb_copy->flags & BB_DUPLICATED ? get_bb_original (bb_copy) : bb_copy;
+
+  if (e_copy->dest->flags & BB_DUPLICATED)
+    dest = get_bb_original (e_copy->dest);
+  else
+    dest = e_copy->dest;
+
+  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;
+       }
+
+      gcc_assert (e != NULL);
     }
 
-  return new_bb;
+  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);
+    }
 }
 
 
@@ -4287,54 +5193,23 @@ tree_duplicate_bb (basic_block bb)
 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);
+  edge e_copy;
 
   FOR_EACH_EDGE (e_copy, ei, bb_copy->succs)
     {
-      if (!phi_nodes (e_copy->dest))
-       continue;
-
-      if (e_copy->dest->flags & BB_DUPLICATED)
-       dest = get_bb_original (e_copy->dest);
-      else
-       dest = e_copy->dest;
-
-      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;
-
-         gcc_assert (e != NULL);
-       }
-
-      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);
-       }
+      add_phi_args_after_copy_edge (e_copy);
     }
 }
 
 /* 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.  */
+   going from these blocks.  If E_COPY is not NULL, also add
+   phi node arguments for its destination.*/
 
 void
-add_phi_args_after_copy (basic_block *region_copy, unsigned n_region)
+add_phi_args_after_copy (basic_block *region_copy, unsigned n_region,
+                        edge e_copy)
 {
   unsigned i;
 
@@ -4343,6 +5218,8 @@ add_phi_args_after_copy (basic_block *region_copy, unsigned n_region)
 
   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);
 
   for (i = 0; i < n_region; i++)
     region_copy[i]->flags &= ~BB_DUPLICATED;
@@ -4480,10 +5357,180 @@ tree_duplicate_sese_region (edge entry, edge exit,
   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);
-  free (doms);
+  VEC_free (basic_block, heap, doms);
 
   /* Add the other PHI node arguments.  */
-  add_phi_args_after_copy (region_copy, n_region);
+  add_phi_args_after_copy (region_copy, n_region, NULL);
+
+  /* Update the SSA web.  */
+  update_ssa (TODO_update_ssa);
+
+  if (free_region_copy)
+    free (region_copy);
+
+  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.
+
+   For example, 
+   some_code;
+   if (cond)
+     A;
+   else
+     B;
+
+   is transformed to
+
+   if (cond)
+     {
+       some_code;
+       A;
+     }
+   else
+     {
+       some_code;
+       B;
+     }
+*/
+
+bool
+tree_duplicate_sese_tail (edge entry, edge exit,
+                         basic_block *region, unsigned n_region,
+                         basic_block *region_copy)
+{
+  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;
+
+  gcc_assert (EDGE_COUNT (exit->src->succs) == 2);
+  exits[0] = exit;
+  exits[1] = EDGE_SUCC (exit->src, EDGE_SUCC (exit->src, 0) == exit);
+
+  if (!can_copy_bbs_p (region, n_region))
+    return false;
+
+  /* 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++)
+    {
+      /* 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;
+    }
+
+  initialize_original_copy_tables ();
+  set_loop_copy (orig_loop, loop);
+
+  if (!region_copy)
+    {
+      region_copy = XNEWVEC (basic_block, n_region);
+      free_region_copy = true;
+    }
+
+  gcc_assert (!need_ssa_update_p ());
+
+  /* Record blocks outside the region that are dominated by something
+     inside.  */
+  doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region);
+
+  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;
+    }
+
+  copy_bbs (region, n_region, region_copy, exits, 2, nexits, orig_loop,
+           split_edge_bb_loc (exit));
+  if (total_count)
+    {
+      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);
+    }
+
+  /* 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);
+
+  sorig = single_succ_edge (switch_bb);
+  sorig->flags = exits[1]->flags;
+  snew = make_edge (switch_bb, nentry_bb, exits[0]->flags);
+
+  /* Register the new edge from SWITCH_BB in loop exit lists.  */
+  rescan_loop_exit (snew, true, false);
+
+  /* Add the PHI node arguments.  */
+  add_phi_args_after_copy (region_copy, n_region, snew);
+
+  /* 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;
+
+  /* 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);
@@ -4504,7 +5551,7 @@ DEF_VEC_ALLOC_P(basic_block,heap);
    adding blocks when the dominator traversal reaches EXIT.  This
    function silently assumes that ENTRY strictly dominates EXIT.  */
 
-static void
+void
 gather_blocks_in_sese_region (basic_block entry, basic_block exit,
                              VEC(basic_block,heap) **bbs_p)
 {
@@ -4520,13 +5567,88 @@ gather_blocks_in_sese_region (basic_block entry, basic_block exit,
     }
 }
 
+/* Replaces *TP with a duplicate (belonging to function TO_CONTEXT).
+   The duplicates are recorded in VARS_MAP.  */
+
+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;
+
+  if (DECL_CONTEXT (t) == to_context)
+    return;
+
+  loc = pointer_map_contains (vars_map, t);
+
+  if (!loc)
+    {
+      loc = pointer_map_insert (vars_map, t);
+
+      if (SSA_VAR_P (t))
+       {
+         new_t = copy_var_decl (t, DECL_NAME (t), TREE_TYPE (t));
+         f->local_decls = tree_cons (NULL_TREE, new_t, f->local_decls);
+       }
+      else
+       {
+         gcc_assert (TREE_CODE (t) == CONST_DECL);
+         new_t = copy_node (t);
+       }
+      DECL_CONTEXT (new_t) = to_context;
+
+      *loc = new_t;
+    }
+  else
+    new_t = *loc;
+
+  *tp = new_t;
+}
+
+/* Creates an ssa name in TO_CONTEXT equivalent to NAME.
+   VARS_MAP maps old ssa names and var_decls to the new ones.  */
+
+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);
+
+  gcc_assert (is_gimple_reg (name));
+
+  loc = pointer_map_contains (vars_map, name);
+
+  if (!loc)
+    {
+      replace_by_duplicate_decl (&decl, vars_map, to_context);
+
+      push_cfun (DECL_STRUCT_FUNCTION (to_context));
+      if (gimple_in_ssa_p (cfun))
+       add_referenced_var (decl);
+
+      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 ();
+
+      loc = pointer_map_insert (vars_map, name);
+      *loc = new_name;
+    }
+  else
+    new_name = *loc;
+
+  return new_name;
+}
 
 struct move_stmt_d
 {
   tree block;
   tree from_context;
   tree to_context;
-  bitmap vars_to_remove;
+  struct pointer_map_t *vars_map;
   htab_t new_label_map;
   bool remap_decls_p;
 };
@@ -4561,9 +5683,11 @@ move_stmt_r (tree *tp, int *walk_subtrees, void *data)
 
       p->remap_decls_p = save_remap_decls_p;
     }
-  else if (DECL_P (t) && DECL_CONTEXT (t) == p->from_context)
+  else if (DECL_P (t) || TREE_CODE (t) == SSA_NAME)
     {
-      if (TREE_CODE (t) == LABEL_DECL)
+      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)
            {
@@ -4578,20 +5702,26 @@ move_stmt_r (tree *tp, int *walk_subtrees, void *data)
        }
       else if (p->remap_decls_p)
        {
-         DECL_CONTEXT (t) = p->to_context;
-
-         if (TREE_CODE (t) == VAR_DECL)
+         /* 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))
            {
-             struct function *f = DECL_STRUCT_FUNCTION (p->to_context);
-             f->unexpanded_var_list
-               = tree_cons (0, t, f->unexpanded_var_list);
-
-             /* Mark T to be removed from the original function,
-                otherwise it will be given a DECL_RTL when the
-                original function is expanded.  */
-             bitmap_set_bit (p->vars_to_remove, DECL_UID (t));
+             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;
@@ -4599,6 +5729,34 @@ move_stmt_r (tree *tp, int *walk_subtrees, void *data)
   return NULL_TREE;
 }
 
+/* Marks virtual operands of all statements in basic blocks BBS for
+   renaming.  */
+
+void
+mark_virtual_ops_in_bb (basic_block bb)
+{
+  tree phi;
+  block_stmt_iterator bsi;
+
+  for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+    mark_virtual_ops_for_renaming (phi);
+
+  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+    mark_virtual_ops_for_renaming (bsi_stmt (bsi));
+}
+
+/* Marks virtual operands of all statements in basic blocks BBS for
+   renaming.  */
+
+static void
+mark_virtual_ops_in_region (VEC (basic_block,heap) *bbs)
+{
+  basic_block bb;
+  unsigned i;
+
+  for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
+    mark_virtual_ops_in_bb (bb);
+}
 
 /* Move basic block BB from function CFUN to function DEST_FN.  The
    block is moved out of the original linked list and placed after
@@ -4607,13 +5765,14 @@ move_stmt_r (tree *tp, int *walk_subtrees, void *data)
    If UPDATE_EDGE_COUNT_P is true, the edge counts on both CFGs is
    updated to reflect the moved edges.
 
-   On exit, local variables that need to be removed from
-   CFUN->UNEXPANDED_VAR_LIST will have been added to VARS_TO_REMOVE.  */
+   The local variables are remapped to new instances, VARS_MAP is used
+   to record the mapping.  */
 
 static void
 move_block_to_fn (struct function *dest_cfun, basic_block bb,
                  basic_block after, bool update_edge_count_p,
-                 bitmap vars_to_remove, htab_t new_label_map, int eh_offset)
+                 struct pointer_map_t *vars_map, htab_t new_label_map,
+                 int eh_offset)
 {
   struct control_flow_graph *cfg;
   edge_iterator ei;
@@ -4621,9 +5780,12 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
   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);
 
   /* Link BB to the new linked list.  */
   move_block_after (bb, after);
@@ -4657,20 +5819,45 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
   VEC_replace (basic_block, cfg->x_basic_block_info,
                bb->index, bb);
 
+  /* Remap the variables in phi nodes.  */
+  for (phi = phi_nodes (bb); phi; phi = next_phi)
+    {
+      use_operand_p use;
+      tree op = PHI_RESULT (phi);
+      ssa_op_iter oi;
+
+      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;
+       }
+
+      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));
+       }
+    }
+
   /* 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_to_remove = vars_to_remove;
+  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;
 
-      d.from_context = cfun->decl;
-      d.to_context = dest_cfun->decl;
       d.remap_decls_p = true;
-      d.new_label_map = new_label_map;
       if (TREE_BLOCK (stmt))
        d.block = DECL_INITIAL (dest_cfun->decl);
 
@@ -4696,8 +5883,8 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
 
          gcc_assert (DECL_CONTEXT (label) == dest_cfun->decl);
 
-         if (uid >= dest_cfun->last_label_uid)
-           dest_cfun->last_label_uid = uid + 1;
+         if (uid >= dest_cfun->cfg->last_label_uid)
+           dest_cfun->cfg->last_label_uid = uid + 1;
        }
       else if (TREE_CODE (stmt) == RESX_EXPR && eh_offset != 0)
        TREE_OPERAND (stmt, 0) =
@@ -4713,6 +5900,13 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
          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 ();
     }
 }
 
@@ -4763,6 +5957,8 @@ new_label_mapper (tree decl, void *data)
   m->base.from = decl;
   m->to = create_artificial_label ();
   LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl);
+  if (LABEL_DECL_UID (m->to) >= cfun->cfg->last_label_uid)
+    cfun->cfg->last_label_uid = LABEL_DECL_UID (m->to) + 1;
 
   slot = htab_find_slot_with_hash (hash, m, m->hash, INSERT);
   gcc_assert (*slot == NULL);
@@ -4790,21 +5986,18 @@ basic_block
 move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
                        basic_block exit_bb)
 {
-  VEC(basic_block,heap) *bbs;
-  basic_block after, bb, *entry_pred, *exit_succ;
-  struct function *saved_cfun;
+  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;
-  bitmap vars_to_remove;
   htab_t new_label_map;
-
-  saved_cfun = cfun;
-
-  /* Collect all the blocks in the region.  Manually add ENTRY_BB
-     because it won't be added by dfs_enumerate_from.  */
-  calculate_dominance_info (CDI_DOMINATORS);
+  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.  */
@@ -4812,10 +6005,18 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_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
@@ -4823,9 +6024,11 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
   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;)
     {
+      entry_prob[i] = e->probability;
       entry_flag[i] = e->flags;
       entry_pred[i++] = e->src;
       remove_edge (e);
@@ -4837,9 +6040,11 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
       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);
@@ -4850,11 +6055,12 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
       num_exit_edges = 0;
       exit_succ = NULL;
       exit_flag = NULL;
+      exit_prob = NULL;
     }
 
   /* Switch context to the child function to initialize DEST_FN's CFG.  */
   gcc_assert (dest_cfun->cfg == NULL);
-  cfun = dest_cfun;
+  push_cfun (dest_cfun);
 
   init_empty_tree_cfg ();
 
@@ -4877,46 +6083,30 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
        }
     }
 
-  cfun = saved_cfun;
+  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_to_remove = BITMAP_ALLOC (NULL);
+  vars_map = pointer_map_create ();
   for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
     {
       /* 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_to_remove,
+      move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, vars_map,
                        new_label_map, eh_offset);
       after = bb;
     }
 
   if (new_label_map)
     htab_delete (new_label_map);
-
-  /* Remove the variables marked in VARS_TO_REMOVE from
-     CFUN->UNEXPANDED_VAR_LIST.  Otherwise, they will be given a
-     DECL_RTL in the context of CFUN.  */
-  if (!bitmap_empty_p (vars_to_remove))
-    {
-      tree *p;
-
-      for (p = &cfun->unexpanded_var_list; *p; )
-       {
-         tree var = TREE_VALUE (*p);
-         if (bitmap_bit_p (vars_to_remove, DECL_UID (var)))
-           {
-             *p = TREE_CHAIN (*p);
-             continue;
-           }
-
-         p = &TREE_CHAIN (*p);
-       }
-    }
-
-  BITMAP_FREE (vars_to_remove);
+  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
@@ -4927,30 +6117,43 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
 
      FIXME, this is silly.  The CFG ought to become a parameter to
      these helpers.  */
-  cfun = dest_cfun;
+  push_cfun (dest_cfun);
   make_edge (ENTRY_BLOCK_PTR, entry_bb, EDGE_FALLTHRU);
   if (exit_bb)
     make_edge (exit_bb,  EXIT_BLOCK_PTR, 0);
-  cfun = saved_cfun;
+  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++)
-    make_edge (entry_pred[i], bb, entry_flag[i]);
+    {
+      e = make_edge (entry_pred[i], bb, entry_flag[i]);
+      e->probability = entry_prob[i];
+    }
 
   for (i = 0; i < num_exit_edges; i++)
-    make_edge (bb, exit_succ[i], exit_flag[i]);
+    {
+      e = make_edge (bb, exit_succ[i], exit_flag[i]);
+      e->probability = exit_prob[i];
+    }
+
+  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 (exit_bb)
     {
+      free (exit_prob);
       free (exit_flag);
       free (exit_succ);
     }
+  free (entry_prob);
   free (entry_flag);
   free (entry_pred);
-  free_dominance_info (CDI_DOMINATORS);
-  free_dominance_info (CDI_POST_DOMINATORS);
   VEC_free (basic_block, heap, bbs);
 
   return bb;
@@ -4967,20 +6170,26 @@ dump_function_to_file (tree fn, FILE *file, int flags)
   bool ignore_topmost_bind = false, any_var = false;
   basic_block bb;
   tree chain;
-  struct function *saved_cfun;
 
   fprintf (file, "%s (", lang_hooks.decl_printable_name (fn, 2));
 
   arg = DECL_ARGUMENTS (fn);
   while (arg)
     {
+      print_generic_expr (file, TREE_TYPE (arg), dump_flags);
+      fprintf (file, " ");
       print_generic_expr (file, arg, dump_flags);
+      if (flags & TDF_VERBOSE)
+       print_node (file, "", arg, 4);
       if (TREE_CHAIN (arg))
        fprintf (file, ", ");
       arg = TREE_CHAIN (arg);
     }
   fprintf (file, ")\n");
 
+  if (flags & TDF_VERBOSE)
+    print_node (file, "", fn, 2);
+
   dsf = DECL_STRUCT_FUNCTION (fn);
   if (dsf && (flags & TDF_DETAILS))
     dump_eh_tree (file, dsf);
@@ -4992,21 +6201,22 @@ dump_function_to_file (tree fn, FILE *file, int flags)
     }
 
   /* Switch CFUN to point to FN.  */
-  saved_cfun = cfun;
-  cfun = DECL_STRUCT_FUNCTION (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)
+  if (cfun && cfun->decl == fn && cfun->local_decls)
     {
       ignore_topmost_bind = true;
 
       fprintf (file, "{\n");
-      for (vars = cfun->unexpanded_var_list; vars; vars = TREE_CHAIN (vars))
+      for (vars = cfun->local_decls; vars; vars = TREE_CHAIN (vars))
        {
          var = TREE_VALUE (vars);
 
          print_generic_decl (file, var, flags);
+         if (flags & TDF_VERBOSE)
+           print_node (file, "", var, 4);
          fprintf (file, "\n");
 
          any_var = true;
@@ -5064,7 +6274,7 @@ dump_function_to_file (tree fn, FILE *file, int flags)
   fprintf (file, "\n\n");
 
   /* Restore CFUN.  */
-  cfun = saved_cfun;
+  pop_cfun ();
 }
 
 
@@ -5077,12 +6287,6 @@ debug_function (tree fn, int flags)
 }
 
 
-/* Pretty print of the loops intermediate representation.  */
-static void print_loop (FILE *, struct loop *, int);
-static void print_pred_bbs (FILE *, basic_block bb);
-static void print_succ_bbs (FILE *, basic_block bb);
-
-
 /* Print on FILE the indexes for the predecessors of basic_block BB.  */
 
 static void
@@ -5108,11 +6312,42 @@ print_succ_bbs (FILE *file, basic_block bb)
     fprintf (file, "bb_%d ", e->dest->index);
 }
 
+/* Print to FILE the basic block BB following the VERBOSITY level.  */
+
+void 
+print_loops_bb (FILE *file, basic_block bb, int indent, int verbosity)
+{
+  char *s_indent = (char *) alloca ((size_t) indent + 1);
+  memset ((void *) s_indent, ' ', (size_t) indent);
+  s_indent[indent] = '\0';
+
+  /* Print basic_block's header.  */
+  if (verbosity >= 2)
+    {
+      fprintf (file, "%s  bb_%d (preds = {", s_indent, bb->index);
+      print_pred_bbs (file, bb);
+      fprintf (file, "}, succs = {");
+      print_succ_bbs (file, bb);
+      fprintf (file, "})\n");
+    }
+
+  /* Print basic_block's body.  */
+  if (verbosity >= 3)
+    {
+      fprintf (file, "%s  {\n", s_indent);
+      tree_dump_bb (bb, file, indent + 4);
+      fprintf (file, "%s  }\n", s_indent);
+    }
+}
+
+static void print_loop_and_siblings (FILE *, struct loop *, int, int);
 
-/* Pretty print LOOP on FILE, indented INDENT spaces.  */
+/* Pretty print LOOP on FILE, indented INDENT spaces.  Following
+   VERBOSITY level this outputs the contents of the loop, or just its
+   structure.  */
 
 static void
-print_loop (FILE *file, struct loop *loop, int indent)
+print_loop (FILE *file, struct loop *loop, int indent, int verbosity)
 {
   char *s_indent;
   basic_block bb;
@@ -5124,55 +6359,90 @@ print_loop (FILE *file, struct loop *loop, int indent)
   memset ((void *) s_indent, ' ', (size_t) indent);
   s_indent[indent] = '\0';
 
-  /* Print the loop's header.  */
-  fprintf (file, "%sloop_%d\n", s_indent, loop->num);
+  /* Print loop's header.  */
+  fprintf (file, "%sloop_%d (header = %d, latch = %d", s_indent, 
+          loop->num, loop->header->index, loop->latch->index);
+  fprintf (file, ", niter = ");
+  print_generic_expr (file, loop->nb_iterations, 0);
 
-  /* Print the loop's body.  */
-  fprintf (file, "%s{\n", s_indent);
-  FOR_EACH_BB (bb)
-    if (bb->loop_father == loop)
-      {
-       /* Print the basic_block's header.  */
-       fprintf (file, "%s  bb_%d (preds = {", s_indent, bb->index);
-       print_pred_bbs (file, bb);
-       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);
-      }
+  if (loop->any_upper_bound)
+    {
+      fprintf (file, ", upper_bound = ");
+      dump_double_int (file, loop->nb_iterations_upper_bound, true);
+    }
+
+  if (loop->any_estimate)
+    {
+      fprintf (file, ", estimate = ");
+      dump_double_int (file, loop->nb_iterations_estimate, true);
+    }
+  fprintf (file, ")\n");
+
+  /* Print loop's body.  */
+  if (verbosity >= 1)
+    {
+      fprintf (file, "%s{\n", s_indent);
+      FOR_EACH_BB (bb)
+       if (bb->loop_father == loop)
+         print_loops_bb (file, bb, indent, verbosity);
 
-  print_loop (file, loop->inner, indent + 2);
-  fprintf (file, "%s}\n", s_indent);
-  print_loop (file, loop->next, indent);
+      print_loop_and_siblings (file, loop->inner, indent + 2, verbosity);
+      fprintf (file, "%s}\n", s_indent);
+    }
 }
 
+/* Print the LOOP and its sibling loops on FILE, indented INDENT
+   spaces.  Following VERBOSITY level this outputs the contents of the
+   loop, or just its structure.  */
+
+static void
+print_loop_and_siblings (FILE *file, struct loop *loop, int indent, int verbosity)
+{
+  if (loop == NULL)
+    return;
+
+  print_loop (file, loop, indent, verbosity);
+  print_loop_and_siblings (file, loop->next, indent, verbosity);
+}
 
 /* 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
-print_loop_ir (FILE *file)
+print_loops (FILE *file, int verbosity)
 {
   basic_block bb;
 
   bb = BASIC_BLOCK (NUM_FIXED_BLOCKS);
   if (bb && bb->loop_father)
-    print_loop (file, bb->loop_father, 0);
+    print_loop_and_siblings (file, bb->loop_father, 0, verbosity);
 }
 
 
-/* Debugging loops structure at tree level.  */
+/* Debugging loops structure at tree level, at some VERBOSITY level.  */
+
+void
+debug_loops (int verbosity)
+{
+  print_loops (stderr, verbosity);
+}
+
+/* Print on stderr the code of LOOP, at some VERBOSITY level.  */
 
 void
-debug_loop_ir (void)
+debug_loop (struct loop *loop, int verbosity)
 {
-  print_loop_ir (stderr);
+  print_loop (stderr, loop, 0, verbosity);
 }
 
+/* Print on stderr the code of loop number NUM, at some VERBOSITY
+   level.  */
+
+void
+debug_loop_num (unsigned num, int verbosity)
+{
+  debug_loop (get_loop (num), verbosity);
+}
 
 /* Return true if BB ends with a call, possibly followed by some
    instructions that must stay with the call.  Return false,
@@ -5190,9 +6460,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);
 }
 
@@ -5203,7 +6475,8 @@ tree_block_ends_with_condjump_p (basic_block bb)
 static bool
 need_fake_edge_p (tree t)
 {
-  tree call;
+  tree call, fndecl = NULL_TREE;
+  int call_flags;
 
   /* NORETURN and LONGJMP calls already have an edge to exit.
      CONST and PURE calls do not need one.
@@ -5213,8 +6486,19 @@ need_fake_edge_p (tree t)
      the counter incrementation code from -fprofile-arcs
      leads to different results from -fbranch-probabilities.  */
   call = get_call_expr_in (t);
-  if (call
-      && !(call_expr_flags (call) & ECF_NORETURN))
+  if (call)
+    {
+      fndecl = get_callee_fndecl (call);
+      call_flags = call_expr_flags (call);
+    }
+
+  if (call && fndecl && DECL_BUILT_IN (fndecl)
+      && (call_flags & ECF_NOTHROW)
+      && !(call_flags & ECF_NORETURN)
+      && !(call_flags & ECF_RETURNS_TWICE))
+   return false;
+
+  if (call && !(call_flags & ECF_NORETURN))
     return true;
 
   if (TREE_CODE (t) == ASM_EXPR
@@ -5349,7 +6633,7 @@ tree_purge_dead_abnormal_call_edges (basic_block bb)
 {
   bool changed = tree_purge_dead_eh_edges (bb);
 
-  if (current_function_has_nonlocal_label)
+  if (cfun->has_nonlocal_label)
     {
       tree stmt = last_stmt (bb);
       edge_iterator ei;
@@ -5539,7 +6823,7 @@ tree_purge_dead_eh_edges (basic_block bb)
 }
 
 bool
-tree_purge_all_dead_eh_edges (bitmap blocks)
+tree_purge_all_dead_eh_edges (const_bitmap blocks)
 {
   bool changed = false;
   unsigned i;
@@ -5696,8 +6980,10 @@ split_critical_edges (void)
   return 0;
 }
 
-struct tree_opt_pass pass_split_crit_edges =
+struct gimple_opt_pass pass_split_crit_edges =
 {
+ {
+  GIMPLE_PASS,
   "crited",                          /* name */
   NULL,                          /* gate */
   split_critical_edges,          /* execute */
@@ -5709,8 +6995,8 @@ struct tree_opt_pass pass_split_crit_edges =
   PROP_no_crit_edges,            /* properties_provided */
   0,                             /* properties_destroyed */
   0,                             /* todo_flags_start */
-  TODO_dump_func,                /* todo_flags_finish */
-  0                              /* letter */
+  TODO_dump_func                 /* todo_flags_finish */
+ }
 };
 
 \f
@@ -5793,11 +7079,7 @@ gimplify_build1 (block_stmt_iterator *bsi, enum tree_code code, tree type,
 static unsigned int
 execute_warn_function_return (void)
 {
-#ifdef USE_MAPPED_LOCATION
   source_location location;
-#else
-  location_t *locus;
-#endif
   tree last;
   edge e;
   edge_iterator ei;
@@ -5806,31 +7088,17 @@ execute_warn_function_return (void)
   if (TREE_THIS_VOLATILE (cfun->decl)
       && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0)
     {
-#ifdef USE_MAPPED_LOCATION
       location = UNKNOWN_LOCATION;
-#else
-      locus = NULL;
-#endif
       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
        {
          last = last_stmt (e->src);
          if (TREE_CODE (last) == RETURN_EXPR
-#ifdef USE_MAPPED_LOCATION
              && (location = EXPR_LOCATION (last)) != UNKNOWN_LOCATION)
-#else
-             && (locus = EXPR_LOCUS (last)) != NULL)
-#endif
            break;
        }
-#ifdef USE_MAPPED_LOCATION
       if (location == UNKNOWN_LOCATION)
        location = cfun->function_end_locus;
       warning (0, "%H%<noreturn%> function does return", &location);
-#else
-      if (!locus)
-       locus = &cfun->function_end_locus;
-      warning (0, "%H%<noreturn%> function does return", locus);
-#endif
     }
 
   /* If we see "return;" in some basic block, then we do reach the end
@@ -5847,17 +7115,10 @@ execute_warn_function_return (void)
              && TREE_OPERAND (last, 0) == NULL
              && !TREE_NO_WARNING (last))
            {
-#ifdef USE_MAPPED_LOCATION
              location = EXPR_LOCATION (last);
              if (location == UNKNOWN_LOCATION)
                  location = cfun->function_end_locus;
-             warning (0, "%Hcontrol reaches end of non-void function", &location);
-#else
-             locus = EXPR_LOCUS (last);
-             if (!locus)
-               locus = &cfun->function_end_locus;
-             warning (0, "%Hcontrol reaches end of non-void function", locus);
-#endif
+             warning (OPT_Wreturn_type, "%Hcontrol reaches end of non-void function", &location);
              TREE_NO_WARNING (cfun->decl) = 1;
              break;
            }
@@ -5891,8 +7152,10 @@ extract_true_false_edges_from_block (basic_block b,
     }
 }
 
-struct tree_opt_pass pass_warn_function_return =
+struct gimple_opt_pass pass_warn_function_return =
 {
+ {
+  GIMPLE_PASS,
   NULL,                                        /* name */
   NULL,                                        /* gate */
   execute_warn_function_return,                /* execute */
@@ -5904,8 +7167,8 @@ struct tree_opt_pass pass_warn_function_return =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  0,                                   /* todo_flags_finish */
-  0                                    /* letter */
+  0                                    /* todo_flags_finish */
+ }
 };
 
 /* Emit noreturn warnings.  */
@@ -5916,15 +7179,17 @@ execute_warn_function_noreturn (void)
   if (warn_missing_noreturn
       && !TREE_THIS_VOLATILE (cfun->decl)
       && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0
-      && !lang_hooks.function.missing_noreturn_ok_p (cfun->decl))
+      && !lang_hooks.missing_noreturn_ok_p (cfun->decl))
     warning (OPT_Wmissing_noreturn, "%Jfunction might be possible candidate "
             "for attribute %<noreturn%>",
             cfun->decl);
   return 0;
 }
 
-struct tree_opt_pass pass_warn_function_noreturn =
+struct gimple_opt_pass pass_warn_function_noreturn =
 {
+ {
+  GIMPLE_PASS,
   NULL,                                        /* name */
   NULL,                                        /* gate */
   execute_warn_function_noreturn,      /* execute */
@@ -5936,6 +7201,6 @@ struct tree_opt_pass pass_warn_function_noreturn =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  0,                                   /* todo_flags_finish */
-  0                                    /* letter */
+  0                                    /* todo_flags_finish */
+ }
 };