OSDN Git Service

2006-01-19 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / tree-inline.c
index 6d2ef06..9a93427 100644 (file)
@@ -1,5 +1,5 @@
 /* Tree inlining.
-   Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
    Contributed by Alexandre Oliva <aoliva@redhat.com>
 
 This file is part of GCC.
@@ -16,8 +16,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -34,7 +34,6 @@ Boston, MA 02111-1307, USA.  */
 #include "insn-config.h"
 #include "varray.h"
 #include "hashtab.h"
-#include "splay-tree.h"
 #include "langhooks.h"
 #include "basic-block.h"
 #include "tree-iterator.h"
@@ -49,13 +48,13 @@ Boston, MA 02111-1307, USA.  */
 #include "except.h"
 #include "debug.h"
 #include "pointer-set.h"
-#include "integrate.h"
+#include "ipa-prop.h"
 
 /* I'm not real happy about this, but we need to handle gimple and
    non-gimple trees.  */
 #include "tree-gimple.h"
 
-/* Inlining, Saving, Cloning
+/* Inlining, Cloning, Versioning, Parallelization
 
    Inlining: a function body is duplicated, but the PARM_DECLs are
    remapped into VAR_DECLs, and non-void RETURN_EXPRs become
@@ -64,21 +63,24 @@ Boston, MA 02111-1307, USA.  */
    to the info for the caller; the eh_region info in copied throwing
    statements and RESX_EXPRs is adjusted accordingly.
 
-   Saving: make a semantically-identical copy of the function body.
-   Necessary when we want to generate code for the body (a destructive
-   operation), but we expect to need this body in the future (e.g. for
-   inlining into another function).
-
    Cloning: (only in C++) We have one body for a con/de/structor, and
    multiple function decls, each with a unique parameter list.
    Duplicate the body, using the given splay tree; some parameters
    will become constants (like 0 or 1).
 
+   Versioning: a function body is duplicated and the result is a new
+   function rather than into blocks of an existing function as with
+   inlining.  Some parameters will become constants.
+
+   Parallelization: a region of a function is duplicated resulting in
+   a new function.  Variables may be replaced with complex expressions
+   to enable shared variable semantics.
+
    All of these will simultaneously lookup any callgraph edges.  If
    we're going to inline the duplicated function body, and the given
    function has some cloned callgraph nodes (one for each place this
    function will be inlined) those callgraph edges will be duplicated.
-   If we're saving or cloning the body, those callgraph edges will be
+   If we're cloning the body, those callgraph edges will be
    updated to point into the new body.  (Note that the original
    callgraph node and edge list will not be altered.)
 
@@ -103,68 +105,29 @@ int flag_inline_trees = 0;
    o Provide heuristics to clamp inlining of recursive template
      calls?  */
 
-/* Data required for function inlining.  */
-
-typedef struct inline_data
-{
-  /* FUNCTION_DECL for function being inlined.  */
-  tree callee;
-  /* FUNCTION_DECL for function being inlined into.  */
-  tree caller;
-  /* struct function for function being inlined.  Usually this is the same
-     as DECL_STRUCT_FUNCTION (callee), but can be different if saved_cfg
-     and saved_eh are in use.  */
-  struct function *callee_cfun;
-  /* The VAR_DECL for the return value.  */
-  tree retvar;
-  /* The map from local declarations in the inlined function to
-     equivalents in the function into which it is being inlined.  */
-  splay_tree decl_map;
-  /* We use the same mechanism to build clones that we do to perform
-     inlining.  However, there are a few places where we need to
-     distinguish between those two situations.  This flag is true if
-     we are cloning, rather than inlining.  */
-  bool cloning_p;
-  /* Similarly for saving function body.  */
-  bool saving_p;
-  /* Callgraph node of function we are inlining into.  */
-  struct cgraph_node *node;
-  /* Callgraph node of currently inlined function.  */
-  struct cgraph_node *current_node;
-  /* Current BLOCK.  */
-  tree block;
-  /* Exception region the inlined call lie in.  */
-  int eh_region;
-  /* Take region number in the function being copied, add this value and
-     get eh region number of the duplicate in the function we inline into.  */
-  int eh_region_offset;
-} inline_data;
-
 /* Prototypes.  */
 
-static tree declare_return_variable (inline_data *, tree, tree, tree *);
-static tree copy_body_r (tree *, int *, void *);
-static tree copy_generic_body (inline_data *);
+static tree declare_return_variable (copy_body_data *, tree, tree, tree *);
+static tree copy_generic_body (copy_body_data *);
 static bool inlinable_function_p (tree);
-static tree remap_decl (tree, inline_data *);
-static tree remap_type (tree, inline_data *);
-static void remap_block (tree *, inline_data *);
-static tree remap_decl (tree, inline_data *);
-static tree remap_decls (tree, inline_data *);
-static void copy_bind_expr (tree *, int *, inline_data *);
+static void remap_block (tree *, copy_body_data *);
+static tree remap_decls (tree, copy_body_data *);
+static void copy_bind_expr (tree *, int *, copy_body_data *);
 static tree mark_local_for_remap_r (tree *, int *, void *);
 static void unsave_expr_1 (tree);
 static tree unsave_r (tree *, int *, void *);
 static void declare_inline_vars (tree, tree);
 static void remap_save_expr (tree *, void *, int *);
-
-static inline bool inlining_p (inline_data *id);
+static void add_lexical_block (tree current_block, tree new_block);
+static tree copy_decl_to_var (tree, copy_body_data *);
+static tree copy_decl_no_change (tree, copy_body_data *);
+static tree copy_decl_maybe_to_var (tree, copy_body_data *);
 
 /* Insert a tree->tree mapping for ID.  Despite the name suggests
    that the trees should be variables, it is used for more than that.  */
 
-static void
-insert_decl_map (inline_data *id, tree key, tree value)
+void
+insert_decl_map (copy_body_data *id, tree key, tree value)
 {
   splay_tree_insert (id->decl_map, (splay_tree_key) key,
                     (splay_tree_value) value);
@@ -178,14 +141,14 @@ insert_decl_map (inline_data *id, tree key, tree value)
 
 /* Remap DECL during the copying of the BLOCK tree for the function.  */
 
-static tree
-remap_decl (tree decl, inline_data *id)
+tree
+remap_decl (tree decl, copy_body_data *id)
 {
   splay_tree_node n;
   tree fn;
 
   /* We only remap local variables in the current function.  */
-  fn = id->callee;
+  fn = id->src_fn;
 
   /* See if we have remapped this declaration.  */
 
@@ -196,21 +159,20 @@ remap_decl (tree decl, inline_data *id)
   if (!n)
     {
       /* Make a copy of the variable or label.  */
-      tree t;
-      t = copy_decl_for_inlining (decl, fn, id->caller);
-
+      tree t = id->copy_decl (decl, id);
+     
       /* Remember it, so that if we encounter this local entity again
         we can reuse this copy.  Do this early because remap_type may
         need this decl for TYPE_STUB_DECL.  */
       insert_decl_map (id, decl, t);
 
+      if (!DECL_P (t))
+       return t;
+
       /* Remap types, if necessary.  */
       TREE_TYPE (t) = remap_type (TREE_TYPE (t), id);
       if (TREE_CODE (t) == TYPE_DECL)
         DECL_ORIGINAL_TYPE (t) = remap_type (DECL_ORIGINAL_TYPE (t), id);
-      else if (TREE_CODE (t) == PARM_DECL)
-        DECL_ARG_TYPE_AS_WRITTEN (t)
-         = remap_type (DECL_ARG_TYPE_AS_WRITTEN (t), id);
 
       /* Remap sizes as necessary.  */
       walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL);
@@ -224,38 +186,6 @@ remap_decl (tree decl, inline_data *id)
            walk_tree (&DECL_QUALIFIER (t), copy_body_r, id, NULL);
        }
 
-#if 0
-      /* FIXME handle anon aggrs.  */
-      if (! DECL_NAME (t) && TREE_TYPE (t)
-         && lang_hooks.tree_inlining.anon_aggr_type_p (TREE_TYPE (t)))
-       {
-         /* For a VAR_DECL of anonymous type, we must also copy the
-            member VAR_DECLS here and rechain the DECL_ANON_UNION_ELEMS.  */
-         tree members = NULL;
-         tree src;
-
-         for (src = DECL_ANON_UNION_ELEMS (t); src;
-              src = TREE_CHAIN (src))
-           {
-             tree member = remap_decl (TREE_VALUE (src), id);
-
-             gcc_assert (!TREE_PURPOSE (src));
-             members = tree_cons (NULL, member, members);
-           }
-         DECL_ANON_UNION_ELEMS (t) = nreverse (members);
-       }
-#endif
-
-      /* If we are inlining and this is a variable (not a label), declare the
-        remapped variable in the callers' body.  */
-      if (inlining_p (id)
-         && (TREE_CODE (t) == VAR_DECL
-             || TREE_CODE (t) == PARM_DECL))
-       declare_inline_vars (id->block, t);
-
-      /* Remember it, so that if we encounter this local entity
-        again we can reuse this copy.  */
-      insert_decl_map (id, decl, t);
       return t;
     }
 
@@ -263,7 +193,7 @@ remap_decl (tree decl, inline_data *id)
 }
 
 static tree
-remap_type (tree type, inline_data *id)
+remap_type_1 (tree type, copy_body_data *id)
 {
   splay_tree_node node;
   tree new, t;
@@ -277,7 +207,7 @@ remap_type (tree type, inline_data *id)
     return (tree) node->value;
 
   /* The type only needs remapping if it's variably modified.  */
-  if (! variably_modified_type_p (type, id->callee))
+  if (! variably_modified_type_p (type, id->src_fn))
     {
       insert_decl_map (id, type, type);
       return type;
@@ -359,7 +289,18 @@ remap_type (tree type, inline_data *id)
     case RECORD_TYPE:
     case UNION_TYPE:
     case QUAL_UNION_TYPE:
-      walk_tree (&TYPE_FIELDS (new), copy_body_r, id, NULL);
+      {
+       tree f, nf = NULL;
+
+       for (f = TYPE_FIELDS (new); f ; f = TREE_CHAIN (f))
+         {
+           t = remap_decl (f, id);
+           DECL_CONTEXT (t) = new;
+           TREE_CHAIN (t) = nf;
+           nf = t;
+         }
+       TYPE_FIELDS (new) = nreverse (nf);
+      }
       break;
 
     case OFFSET_TYPE:
@@ -374,8 +315,31 @@ remap_type (tree type, inline_data *id)
   return new;
 }
 
+tree
+remap_type (tree type, copy_body_data *id)
+{
+  splay_tree_node node;
+
+  if (type == NULL)
+    return type;
+
+  /* See if we have remapped this type.  */
+  node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
+  if (node)
+    return (tree) node->value;
+
+  /* The type only needs remapping if it's variably modified.  */
+  if (! variably_modified_type_p (type, id->src_fn))
+    {
+      insert_decl_map (id, type, type);
+      return type;
+    }
+
+  return remap_type_1 (type, id);
+}
+
 static tree
-remap_decls (tree decls, inline_data *id)
+remap_decls (tree decls, copy_body_data *id)
 {
   tree old_var;
   tree new_decls = NULL_TREE;
@@ -385,6 +349,17 @@ remap_decls (tree decls, inline_data *id)
     {
       tree new_var;
 
+      /* We can not chain the local static declarations into the unexpanded_var_list
+         as we can't duplicate them or break one decl rule.  Go ahead and link
+         them into unexpanded_var_list.  */
+      if (!lang_hooks.tree_inlining.auto_var_in_fn_p (old_var, id->src_fn)
+         && !DECL_EXTERNAL (old_var))
+       {
+         cfun->unexpanded_var_list = tree_cons (NULL_TREE, old_var,
+                                                cfun->unexpanded_var_list);
+         continue;
+       }
+
       /* Remap the variable.  */
       new_var = remap_decl (old_var, id);
 
@@ -408,7 +383,7 @@ remap_decls (tree decls, inline_data *id)
    therein.  And hook the new block into the block-tree.  */
 
 static void
-remap_block (tree *block, inline_data *id)
+remap_block (tree *block, copy_body_data *id)
 {
   tree old_block;
   tree new_block;
@@ -419,38 +394,38 @@ remap_block (tree *block, inline_data *id)
   new_block = make_node (BLOCK);
   TREE_USED (new_block) = TREE_USED (old_block);
   BLOCK_ABSTRACT_ORIGIN (new_block) = old_block;
+  BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block);
   *block = new_block;
 
   /* Remap its variables.  */
   BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id);
 
-  fn = id->caller;
-#if 1
-  /* FIXME!  It shouldn't be so hard to manage blocks.  Rebuilding them in
-     rest_of_compilation is a good start.  */
-  if (id->cloning_p)
-    /* We're building a clone; DECL_INITIAL is still
-       error_mark_node, and current_binding_level is the parm
-       binding level.  */
+  fn = id->dst_fn;
+
+  if (id->transform_lang_insert_block)
     lang_hooks.decls.insert_block (new_block);
-  else
-    {
-      /* Attach this new block after the DECL_INITIAL block for the
-        function into which this block is being inlined.  In
-        rest_of_compilation we will straighten out the BLOCK tree.  */
-      tree *first_block;
-      if (DECL_INITIAL (fn))
-       first_block = &BLOCK_CHAIN (DECL_INITIAL (fn));
-      else
-       first_block = &DECL_INITIAL (fn);
-      BLOCK_CHAIN (new_block) = *first_block;
-      *first_block = new_block;
-    }
-#endif
+
   /* Remember the remapped block.  */
   insert_decl_map (id, old_block, new_block);
 }
 
+/* Copy the whole block tree and root it in id->block.  */
+static tree
+remap_blocks (tree block, copy_body_data *id)
+{
+  tree t;
+  tree new = block;
+
+  if (!block)
+    return NULL;
+
+  remap_block (&new, id);
+  gcc_assert (new != block);
+  for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t))
+    add_lexical_block (new, remap_blocks (t, id));
+  return new;
+}
+
 static void
 copy_statement_list (tree *tp)
 {
@@ -467,7 +442,7 @@ copy_statement_list (tree *tp)
 }
 
 static void
-copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id)
+copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id)
 {
   tree block = BIND_EXPR_BLOCK (*tp);
   /* Copy (and replace) the statement.  */
@@ -485,13 +460,14 @@ copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id)
 }
 
 /* Called from copy_body_id via walk_tree.  DATA is really an
-   `inline_data *'.  */
+   `copy_body_data *'.  */
 
-static tree
+tree
 copy_body_r (tree *tp, int *walk_subtrees, void *data)
 {
-  inline_data *id = (inline_data *) data;
-  tree fn = id->callee;
+  copy_body_data *id = (copy_body_data *) data;
+  tree fn = id->src_fn;
+  tree new_block;
 
   /* Begin by recognizing trees that we'll completely rewrite for the
      inlining context.  Our output for these trees is completely
@@ -499,9 +475,10 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
      into an edge).  Further down, we'll handle trees that get
      duplicated and/or tweaked.  */
 
-  /* If this is a RETURN_STMT, change it into an EXPR_STMT and a
-     GOTO_STMT with the RET_LABEL as its target.  */
-  if (TREE_CODE (*tp) == RETURN_EXPR && inlining_p (id))
+  /* When requested, RETURN_EXPRs should be transformed to just the
+     contained MODIFY_EXPR.  The branch semantics of the return will
+     be handled elsewhere by manipulating the CFG rather than a statement.  */
+  if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify)
     {
       tree assignment = TREE_OPERAND (*tp, 0);
 
@@ -519,7 +496,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
       else /* Else the RETURN_EXPR returns no value.  */
        {
          *tp = NULL;
-         return (void *)1;
+         return (tree) (void *)1;
        }
     }
 
@@ -543,9 +520,11 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
     copy_statement_list (tp);
   else if (TREE_CODE (*tp) == SAVE_EXPR)
     remap_save_expr (tp, id->decl_map, walk_subtrees);
-  else if (TREE_CODE (*tp) == LABEL_DECL)
+  else if (TREE_CODE (*tp) == LABEL_DECL
+          && (! DECL_CONTEXT (*tp)
+              || decl_function_context (*tp) == id->src_fn))
     /* These may need to be remapped for EH handling.  */
-    remap_decl (*tp, id);
+    *tp = remap_decl (*tp, id);
   else if (TREE_CODE (*tp) == BIND_EXPR)
     copy_bind_expr (tp, walk_subtrees, id);
   /* Types may need remapping as well.  */
@@ -611,6 +590,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
          n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
          if (n)
            {
+             tree new;
              /* If we happen to get an ADDR_EXPR in n->value, strip
                 it manually here as we'll eventually get ADDR_EXPRs
                 which lie about their types pointed to.  In this case
@@ -618,13 +598,14 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
                 but we absolutely rely on that.  As fold_indirect_ref
                 does other useful transformations, try that first, though.  */
              tree type = TREE_TYPE (TREE_TYPE ((tree)n->value));
-             *tp = fold_indirect_ref_1 (type, (tree)n->value);
+             new = unshare_expr ((tree)n->value);
+             *tp = fold_indirect_ref_1 (type, new);
              if (! *tp)
                {
-                 if (TREE_CODE ((tree)n->value) == ADDR_EXPR)
-                   *tp = TREE_OPERAND ((tree)n->value, 0);
+                 if (TREE_CODE (new) == ADDR_EXPR)
+                   *tp = TREE_OPERAND (new, 0);
                  else
-                   *tp = build1 (INDIRECT_REF, type, (tree)n->value);
+                   *tp = build1 (INDIRECT_REF, type, new);
                }
              *walk_subtrees = 0;
              return NULL;
@@ -634,9 +615,23 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
       /* Here is the "usual case".  Copy this tree node, and then
         tweak some special cases.  */
       copy_tree_r (tp, walk_subtrees, NULL);
-      if (id->block
-         && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (*tp))))
-       TREE_BLOCK (*tp) = id->block;
+       
+      /* If EXPR has block defined, map it to newly constructed block.
+         When inlining we want EXPRs without block appear in the block
+        of function call.  */
+      if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (*tp))))
+       {
+         new_block = id->block;
+         if (TREE_BLOCK (*tp))
+           {
+             splay_tree_node n;
+             n = splay_tree_lookup (id->decl_map,
+                                    (splay_tree_key) TREE_BLOCK (*tp));
+             gcc_assert (n);
+             new_block = (tree) n->value;
+           }
+         TREE_BLOCK (*tp) = new_block;
+       }
 
       if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset)
        TREE_OPERAND (*tp, 0) =
@@ -660,7 +655,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
       else if (TREE_CODE (*tp) == ADDR_EXPR)
        {
          walk_tree (&TREE_OPERAND (*tp, 0), copy_body_r, id, NULL);
-         recompute_tree_invarant_for_addr_expr (*tp);
+         recompute_tree_invariant_for_addr_expr (*tp);
          *walk_subtrees = 0;
        }
     }
@@ -673,14 +668,15 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
    later  */
 
 static basic_block
-copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
+copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, int count_scale)
 {
   block_stmt_iterator bsi, copy_bsi;
   basic_block copy_basic_block;
 
   /* create_basic_block() will append every new block to
      basic_block_info automatically.  */
-  copy_basic_block = create_basic_block (NULL, (void *) 0, bb->prev_bb->aux);
+  copy_basic_block = create_basic_block (NULL, (void *) 0,
+                                         (basic_block) bb->prev_bb->aux);
   copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE;
   copy_basic_block->frequency = (bb->frequency
                                     * frequency_scale / REG_BR_PROB_BASE);
@@ -705,16 +701,20 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
             callgraph edges and update or duplicate them.  */
          if (call && (decl = get_callee_fndecl (call)))
            {
-             if (id->saving_p)
+             struct cgraph_node *node;
+             struct cgraph_edge *edge;
+            
+             switch (id->transform_call_graph_edges)
                {
-                 struct cgraph_node *node;
-                 struct cgraph_edge *edge;
-
-                 /* We're saving a copy of the body, so we'll update the
-                    callgraph nodes in place.  Note that we avoid
-                    altering the original callgraph node; we begin with
-                    the first clone.  */
-                 for (node = id->node->next_clone;
+               case CB_CGE_DUPLICATE:
+                 edge = cgraph_edge (id->src_node, orig_stmt);
+                 if (edge)
+                   cgraph_clone_edge (edge, id->dst_node, stmt,
+                                      REG_BR_PROB_BASE, 1, true);
+                 break;
+
+               case CB_CGE_MOVE_CLONES:
+                 for (node = id->dst_node->next_clone;
                       node;
                       node = node->next_clone)
                    {
@@ -722,29 +722,28 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
                      gcc_assert (edge);
                      edge->call_stmt = stmt;
                    }
-               }
-             else
-               {
-                 struct cgraph_edge *edge;
+                 /* FALLTHRU */
 
-                 /* We're cloning or inlining this body; duplicate the
-                    associate callgraph nodes.  */
-                 edge = cgraph_edge (id->current_node, orig_stmt);
+               case CB_CGE_MOVE:
+                 edge = cgraph_edge (id->dst_node, orig_stmt);
                  if (edge)
-                   cgraph_clone_edge (edge, id->node, stmt,
-                                      REG_BR_PROB_BASE, 1);
+                   edge->call_stmt = stmt;
+                 break;
+
+               default:
+                 gcc_unreachable ();
                }
            }
          /* If you think we can abort here, you are wrong.
             There is no region 0 in tree land.  */
-         gcc_assert (lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt)
+         gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt)
                      != 0);
 
          if (tree_could_throw_p (stmt))
            {
-             int region = lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt);
+             int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt);
              /* Add an entry for the copied tree in the EH hashtable.
-                When saving or cloning or versioning, use the hashtable in
+                When cloning or versioning, use the hashtable in
                 cfun, and just copy the EH number.  When inlining, use the
                 hashtable in the caller, and adjust the region number.  */
              if (region > 0)
@@ -754,7 +753,7 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
                 and there is a "current region,"
                 then associate this tree with the current region
                 and add edges associated with this region.  */
-             if ((lookup_stmt_eh_region_fn (id->callee_cfun,
+             if ((lookup_stmt_eh_region_fn (id->src_cfun,
                                             orig_stmt) <= 0
                   && id->eh_region > 0)
                  && tree_could_throw_p (stmt))
@@ -771,7 +770,7 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
 static void
 copy_edges_for_bb (basic_block bb, int count_scale)
 {
-  basic_block new_bb = bb->aux;
+  basic_block new_bb = (basic_block) bb->aux;
   edge_iterator ei;
   edge old_edge;
   block_stmt_iterator bsi;
@@ -790,7 +789,7 @@ copy_edges_for_bb (basic_block bb, int count_scale)
        if (old_edge->dest->index == EXIT_BLOCK && !old_edge->flags
            && old_edge->dest->aux != EXIT_BLOCK_PTR)
          flags |= EDGE_FALLTHRU;
-       new = make_edge (new_bb, old_edge->dest->aux, flags);
+       new = make_edge (new_bb, (basic_block) old_edge->dest->aux, flags);
        new->count = old_edge->count * count_scale / REG_BR_PROB_BASE;
        new->probability = old_edge->probability;
       }
@@ -839,19 +838,19 @@ copy_edges_for_bb (basic_block bb, int count_scale)
 static tree
 remap_decl_1 (tree decl, void *data)
 {
-  return remap_decl (decl, data);
+  return remap_decl (decl, (copy_body_data *) data);
 }
 
 /* Make a copy of the body of FN so that it can be inserted inline in
    another function.  Walks FN via CFG, returns new fndecl.  */
 
 static tree
-copy_cfg_body (inline_data * id, gcov_type count, int frequency,
+copy_cfg_body (copy_body_data * id, gcov_type count, int frequency,
               basic_block entry_block_map, basic_block exit_block_map)
 {
-  tree callee_fndecl = id->callee;
+  tree callee_fndecl = id->src_fn;
   /* Original cfun for the callee, doesn't change.  */
-  struct function *callee_cfun = DECL_STRUCT_FUNCTION (callee_fndecl);
+  struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl);
   /* Copy, built by this function.  */
   struct function *new_cfun;
   /* Place to copy from; when a copy of the function was saved off earlier,
@@ -860,19 +859,18 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
     (struct function *) ggc_alloc_cleared (sizeof (struct function));
   basic_block bb;
   tree new_fndecl = NULL;
-  bool saving_or_cloning;
   int count_scale, frequency_scale;
 
-  if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count)
+  if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count)
     count_scale = (REG_BR_PROB_BASE * count
-                  / ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count);
+                  / ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count);
   else
     count_scale = 1;
 
-  if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency)
+  if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency)
     frequency_scale = (REG_BR_PROB_BASE * frequency
                       /
-                      ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency);
+                      ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency);
   else
     frequency_scale = count_scale;
 
@@ -885,24 +883,11 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
 
   *cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl);
 
-  /* If there is a saved_cfg+saved_args lurking in the
-     struct function, a copy of the callee body was saved there, and
-     the 'struct cgraph edge' nodes have been fudged to point into the
-     saved body.  Accordingly, we want to copy that saved body so the
-     callgraph edges will be recognized and cloned properly.  */
-  if (cfun_to_copy->saved_cfg)
-    {
-      cfun_to_copy->cfg = cfun_to_copy->saved_cfg;
-      cfun_to_copy->eh = cfun_to_copy->saved_eh;
-    }
-  id->callee_cfun = cfun_to_copy;
-
-  /* If saving or cloning a function body, create new basic_block_info
-     and label_to_block_maps.  Otherwise, we're duplicating a function
-     body for inlining; insert our new blocks and labels into the
-     existing varrays.  */
-  saving_or_cloning = (id->saving_p || id->cloning_p);
-  if (saving_or_cloning)
+  id->src_cfun = cfun_to_copy;
+
+  /* If requested, create new basic_block_info and label_to_block_maps.
+     Otherwise, insert our new blocks and labels into the existing cfg.  */
+  if (id->transform_new_cfg)
     {
       new_cfun =
        (struct function *) ggc_alloc_cleared (sizeof (struct function));
@@ -915,16 +900,16 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
       init_empty_tree_cfg ();
 
       ENTRY_BLOCK_PTR->count =
-       (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale /
+       (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale /
         REG_BR_PROB_BASE);
       ENTRY_BLOCK_PTR->frequency =
-       (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+       (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency *
         frequency_scale / REG_BR_PROB_BASE);
       EXIT_BLOCK_PTR->count =
-       (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale /
+       (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale /
         REG_BR_PROB_BASE);
       EXIT_BLOCK_PTR->frequency =
-       (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+       (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency *
         frequency_scale / REG_BR_PROB_BASE);
 
       entry_block_map = ENTRY_BLOCK_PTR;
@@ -934,16 +919,13 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
   ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = entry_block_map;
   EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = exit_block_map;
 
-
   /* Duplicate any exception-handling regions.  */
   if (cfun->eh)
     {
-      if (saving_or_cloning)
+      if (id->transform_new_cfg)
         init_eh_for_function ();
-      id->eh_region_offset = duplicate_eh_regions (cfun_to_copy,
-                                                  remap_decl_1,
-                                                  id, id->eh_region);
-      gcc_assert (inlining_p (id) || !id->eh_region_offset);
+      id->eh_region_offset
+       = duplicate_eh_regions (cfun_to_copy, remap_decl_1, id, id->eh_region);
     }
   /* Use aux pointers to map the original blocks to copy.  */
   FOR_EACH_BB_FN (bb, cfun_to_copy)
@@ -954,7 +936,7 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
   FOR_ALL_BB_FN (bb, cfun_to_copy)
     bb->aux = NULL;
 
-  if (saving_or_cloning)
+  if (id->transform_new_cfg)
     pop_cfun ();
 
   return new_fndecl;
@@ -964,10 +946,10 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
    another function.  */
 
 static tree
-copy_generic_body (inline_data *id)
+copy_generic_body (copy_body_data *id)
 {
   tree body;
-  tree fndecl = id->callee;
+  tree fndecl = id->src_fn;
 
   body = DECL_SAVED_TREE (fndecl);
   walk_tree (&body, copy_body_r, id, NULL);
@@ -976,10 +958,10 @@ copy_generic_body (inline_data *id)
 }
 
 static tree
-copy_body (inline_data *id, gcov_type count, int frequency,
+copy_body (copy_body_data *id, gcov_type count, int frequency,
           basic_block entry_block_map, basic_block exit_block_map)
 {
-  tree fndecl = id->callee;
+  tree fndecl = id->src_fn;
   tree body;
 
   /* If this body has a CFG, walk CFG and copy.  */
@@ -1006,7 +988,7 @@ self_inlining_addr_expr (tree value, tree fn)
 }
 
 static void
-setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
+setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
                     basic_block bb, tree *vars)
 {
   tree init_stmt;
@@ -1041,7 +1023,7 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
   /* Make an equivalent VAR_DECL.  Note that we must NOT remap the type
      here since the type of this decl must be visible to the calling
      function.  */
-  var = copy_decl_for_inlining (p, fn, id->caller);
+  var = copy_decl_to_var (p, id);
 
   /* See if the frontend wants to pass this by invisible reference.  If
      so, our new VAR_DECL will have REFERENCE_TYPE, and we need to
@@ -1092,7 +1074,7 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
 
       /* We want to use MODIFY_EXPR, not INIT_EXPR here so that we
         keep our trees in gimple form.  */
-      init_stmt = build (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
+      init_stmt = build2 (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
 
       /* If we did not create a gimple value and we did not create a gimple
         cast of a gimple value, then we will need to gimplify INIT_STMTS
@@ -1103,7 +1085,11 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
          && (!is_gimple_cast (rhs)
              || !is_gimple_val (TREE_OPERAND (rhs, 0))))
        gimplify_stmt (&init_stmt);
-      bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT);
+
+      /* If VAR represents a zero-sized variable, it's possible that the
+        assignment statment may result in no gimple statements.  */
+      if (init_stmt)
+        bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT);
     }
 }
 
@@ -1111,7 +1097,7 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
    top of the stack in ID from the ARGS (presented as a TREE_LIST).  */
 
 static void
-initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
+initialize_inlined_parameters (copy_body_data *id, tree args, tree static_chain,
                               tree fn, basic_block bb)
 {
   tree parms;
@@ -1122,8 +1108,6 @@ initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
 
   /* Figure out what the parameters are.  */
   parms = DECL_ARGUMENTS (fn);
-  if (fn == current_function_decl)
-    parms = cfun->saved_args;
 
   /* Loop through the parameter declarations, replacing each with an
      equivalent VAR_DECL, appropriately initialized.  */
@@ -1143,8 +1127,7 @@ initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
 
   /* Initialize the static chain.  */
   p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
-  if (fn == current_function_decl)
-    p = DECL_STRUCT_FUNCTION (fn)->saved_static_chain_decl;
+  gcc_assert (fn != current_function_decl);
   if (p)
     {
       /* No static chain?  Seems like a bug in tree-nested.c.  */
@@ -1170,11 +1153,11 @@ initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
    holds the result as seen by the caller.  */
 
 static tree
-declare_return_variable (inline_data *id, tree return_slot_addr,
+declare_return_variable (copy_body_data *id, tree return_slot_addr,
                         tree modify_dest, tree *use_p)
 {
-  tree callee = id->callee;
-  tree caller = id->caller;
+  tree callee = id->src_fn;
+  tree caller = id->dst_fn;
   tree result = DECL_RESULT (callee);
   tree callee_type = TREE_TYPE (result);
   tree caller_type = TREE_TYPE (TREE_TYPE (callee));
@@ -1199,6 +1182,10 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
        var = return_slot_addr;
       else
        var = build_fold_indirect_ref (return_slot_addr);
+      if (TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE
+         && !DECL_COMPLEX_GIMPLE_REG_P (result)
+         && DECL_P (var))
+       DECL_COMPLEX_GIMPLE_REG_P (var) = 0;
       use = NULL;
       goto done;
     }
@@ -1224,10 +1211,21 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
       /* If the callee cannot possibly modify MODIFY_DEST, then we can
         reuse it as the result of the call directly.  Don't do this if
         it would promote MODIFY_DEST to addressable.  */
-      else if (!TREE_STATIC (modify_dest)
-              && !TREE_ADDRESSABLE (modify_dest)
-              && !TREE_ADDRESSABLE (result))
-       use_it = true;
+      else if (TREE_ADDRESSABLE (result))
+       use_it = false;
+      else
+       {
+         tree base_m = get_base_address (modify_dest);
+
+         /* If the base isn't a decl, then it's a pointer, and we don't
+            know where that's going to go.  */
+         if (!DECL_P (base_m))
+           use_it = false;
+         else if (is_global_var (base_m))
+           use_it = false;
+         else if (!TREE_ADDRESSABLE (base_m))
+           use_it = true;
+       }
 
       if (use_it)
        {
@@ -1239,7 +1237,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
 
   gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
 
-  var = copy_decl_for_inlining (result, callee, caller);
+  var = copy_decl_to_var (result, id);
 
   DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
   DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
@@ -1299,7 +1297,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
          && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
        {
          inline_forbidden_reason
-           = G_("%Jfunction %qF can never be inlined because it uses "
+           = G_("function %q+F can never be inlined because it uses "
                 "alloca (override using the always_inline attribute)");
          return node;
        }
@@ -1311,7 +1309,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
       if (setjmp_call_p (t))
        {
          inline_forbidden_reason
-           = G_("%Jfunction %qF can never be inlined because it uses setjmp");
+           = G_("function %q+F can never be inlined because it uses setjmp");
          return node;
        }
 
@@ -1325,7 +1323,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
          case BUILT_IN_NEXT_ARG:
          case BUILT_IN_VA_END:
            inline_forbidden_reason
-             = G_("%Jfunction %qF can never be inlined because it "
+             = G_("function %q+F can never be inlined because it "
                   "uses variable argument lists");
            return node;
 
@@ -1336,14 +1334,14 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
               function calling __builtin_longjmp to be inlined into the
               function calling __builtin_setjmp, Things will Go Awry.  */
            inline_forbidden_reason
-             = G_("%Jfunction %qF can never be inlined because "
+             = G_("function %q+F can never be inlined because "
                   "it uses setjmp-longjmp exception handling");
            return node;
 
          case BUILT_IN_NONLOCAL_GOTO:
            /* Similarly.  */
            inline_forbidden_reason
-             = G_("%Jfunction %qF can never be inlined because "
+             = G_("function %q+F can never be inlined because "
                   "it uses non-local goto");
            return node;
 
@@ -1354,7 +1352,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
               been inlined into.  Similarly __builtin_return would
               return from the function the inline has been inlined into.  */
            inline_forbidden_reason
-             = G_("%Jfunction %qF can never be inlined because "
+             = G_("function %q+F can never be inlined because "
                   "it uses __builtin_return or __builtin_apply_args");
            return node;
 
@@ -1373,7 +1371,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
       if (TREE_CODE (t) != LABEL_DECL)
        {
          inline_forbidden_reason
-           = G_("%Jfunction %qF can never be inlined "
+           = G_("function %q+F can never be inlined "
                 "because it contains a computed goto");
          return node;
        }
@@ -1387,7 +1385,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
             because we cannot remap the destination label used in the
             function that is performing the non-local goto.  */
          inline_forbidden_reason
-           = G_("%Jfunction %qF can never be inlined "
+           = G_("function %q+F can never be inlined "
                 "because it receives a non-local goto");
          return node;
        }
@@ -1412,7 +1410,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
        if (variably_modified_type_p (TREE_TYPE (t), NULL))
          {
            inline_forbidden_reason
-             = G_("%Jfunction %qF can never be inlined "
+             = G_("function %q+F can never be inlined "
                   "because it uses variable sized variables");
            return node;
          }
@@ -1507,9 +1505,9 @@ inlinable_function_p (tree fn)
                         && !DECL_IN_SYSTEM_HEADER (fn));
 
       if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
-       sorry (inline_forbidden_reason, fn, fn);
+       sorry (inline_forbidden_reason, fn);
       else if (do_warning)
-       warning (0, inline_forbidden_reason, fn, fn);
+       warning (OPT_Winline, inline_forbidden_reason, fn);
 
       inlinable = false;
     }
@@ -1543,7 +1541,7 @@ estimate_move_cost (tree type)
 static tree
 estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
 {
-  int *count = data;
+  int *count = (int *) data;
   tree x = *tp;
 
   if (IS_TYPE_OR_DECL_P (x))
@@ -1681,6 +1679,8 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     case RSHIFT_EXPR:
     case LROTATE_EXPR:
     case RROTATE_EXPR:
+    case VEC_LSHIFT_EXPR:
+    case VEC_RSHIFT_EXPR:
 
     case BIT_IOR_EXPR:
     case BIT_XOR_EXPR:
@@ -1725,6 +1725,14 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
 
     case REALIGN_LOAD_EXPR:
 
+    case REDUC_MAX_EXPR:
+    case REDUC_MIN_EXPR:
+    case REDUC_PLUS_EXPR:
+    case WIDEN_SUM_EXPR:
+    case DOT_PROD_EXPR: 
+
+    case WIDEN_MULT_EXPR:
+
     case RESX_EXPR:
       *count += 1;
       break;
@@ -1851,7 +1859,6 @@ add_lexical_block (tree current_block, tree new_block)
     ;
   *blk_p = new_block;
   BLOCK_SUPERCONTEXT (new_block) = current_block;
-  BLOCK_SUBBLOCKS (new_block) = NULL_TREE;
 }
 
 /* If *TP is a CALL_EXPR, replace it with its inline expansion.  */
@@ -1859,7 +1866,7 @@ add_lexical_block (tree current_block, tree new_block)
 static bool
 expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
 {
-  inline_data *id;
+  copy_body_data *id;
   tree t;
   tree use_retvar;
   tree fn;
@@ -1876,11 +1883,10 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   bool successfully_inlined = FALSE;
   tree t_step;
   tree var;
-  struct cgraph_node *old_node;
   tree decl;
 
   /* See what we've got.  */
-  id = (inline_data *) data;
+  id = (copy_body_data *) data;
   t = *tp;
 
   /* Set input_location here so we get the right instantiation context
@@ -1916,10 +1922,10 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
 
   /* Objective C and fortran still calls tree_rest_of_compilation directly.
      Kill this check once this is fixed.  */
-  if (!id->current_node->analyzed)
+  if (!id->dst_node->analyzed)
     goto egress;
 
-  cg_edge = cgraph_edge (id->current_node, stmt);
+  cg_edge = cgraph_edge (id->dst_node, stmt);
 
   /* Constant propagation on argument done during previous inlining
      may create new direct call.  Produce an edge for it.  */
@@ -1932,7 +1938,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
          constant propagating arguments.  In all other cases we hit a bug
          (incorrect node sharing is most common reason for missing edges.  */
       gcc_assert (dest->needed || !flag_unit_at_a_time);
-      cgraph_create_edge (id->node, dest, stmt,
+      cgraph_create_edge (id->dst_node, dest, stmt,
                          bb->count, bb->loop_depth)->inline_failed
        = N_("originally indirect function call not considered for inlining");
       goto egress;
@@ -1942,24 +1948,30 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
      inlining.  */
   if (!cgraph_inline_p (cg_edge, &reason))
     {
-      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
+      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
+         /* Avoid warnings during early inline pass. */
+         && (!flag_unit_at_a_time || cgraph_global_info_ready))
        {
-         sorry ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
+         sorry ("inlining failed in call to %q+F: %s", fn, reason);
          sorry ("called from here");
        }
       else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
               && !DECL_IN_SYSTEM_HEADER (fn)
               && strlen (reason)
-              && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)))
+              && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn))
+              /* Avoid warnings during early inline pass. */
+              && (!flag_unit_at_a_time || cgraph_global_info_ready))
        {
-         warning (0, "%Jinlining failed in call to %qF: %s", fn, fn, reason);
-         warning (0, "called from here");
+         warning (OPT_Winline, "inlining failed in call to %q+F: %s",
+                  fn, reason);
+         warning (OPT_Winline, "called from here");
        }
       goto egress;
     }
+  fn = cg_edge->callee->decl;
 
 #ifdef ENABLE_CHECKING
-  if (cg_edge->callee->decl != id->node->decl)
+  if (cg_edge->callee->decl != id->dst_node->decl)
     verify_cgraph_node (cg_edge->callee);
 #endif
 
@@ -1985,7 +1997,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   else
     {
       tree stmt = bsi_stmt (stmt_bsi);
-      bsi_remove (&stmt_bsi);
+      bsi_remove (&stmt_bsi, false);
       bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
     }
   stmt_bsi = bsi_start (return_block);
@@ -1996,9 +2008,9 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
      statement expression is the return type of the function call.  */
   id->block = make_node (BLOCK);
   BLOCK_ABSTRACT_ORIGIN (id->block) = fn;
+  BLOCK_SOURCE_LOCATION (id->block) = input_location;
   add_lexical_block (TREE_BLOCK (stmt), id->block);
 
-
   /* Local declarations will be replaced by their equivalents in this
      map.  */
   st = id->decl_map;
@@ -2007,18 +2019,15 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
 
   /* Initialize the parameters.  */
   args = TREE_OPERAND (t, 1);
-  if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (t))
-    {
-      return_slot_addr = TREE_VALUE (args);
-      args = TREE_CHAIN (args);
-    }
-  else
-    return_slot_addr = NULL_TREE;
+
+  /* Record the function we are about to inline.  */
+  id->src_fn = fn;
+  id->src_node = cg_edge->callee;
 
   initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
 
-  /* Record the function we are about to inline.  */
-  id->callee = fn;
+  if (DECL_INITIAL (fn))
+    add_lexical_block (id->block, remap_blocks (DECL_INITIAL (fn), id));
 
   /* Return statements in the function body will be replaced by jumps
      to the RET_LABEL.  */
@@ -2027,10 +2036,10 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
 
   /* Find the lhs to which the result of this call is assigned.  */
-  modify_dest = stmt;
-  if (TREE_CODE (modify_dest) == MODIFY_EXPR)
+  return_slot_addr = NULL;
+  if (TREE_CODE (stmt) == MODIFY_EXPR)
     {
-      modify_dest = TREE_OPERAND (modify_dest, 0);
+      modify_dest = TREE_OPERAND (stmt, 0);
 
       /* The function which we are inlining might not return a value,
         in which case we should issue a warning that the function
@@ -2040,6 +2049,11 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
         uninitialized variable.  */
       if (DECL_P (modify_dest))
        TREE_NO_WARNING (modify_dest) = 1;
+      if (CALL_EXPR_RETURN_SLOT_OPT (t))
+       {
+         return_slot_addr = build_fold_addr_expr (modify_dest);
+         modify_dest = NULL;
+       }
     }
   else
     modify_dest = NULL;
@@ -2051,22 +2065,25 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   if (decl && !return_slot_addr && decl != modify_dest)
     declare_inline_vars (id->block, decl);
 
-  /* After we've initialized the parameters, we insert the body of the
-     function itself.  */
-  old_node = id->current_node;
-
-  /* Anoint the callee-to-be-duplicated as the "current_node."  When
-     CALL_EXPRs within callee are duplicated, the edges from callee to
-     callee's callees (caller's grandchildren) will be cloned.  */
-  id->current_node = cg_edge->callee;
-
   /* This is it.  Duplicate the callee body.  Assume callee is
      pre-gimplified.  Note that we must not alter the caller
      function in any way before this point, as this CALL_EXPR may be
      a self-referential call; if we're calling ourselves, we need to
      duplicate our body before altering anything.  */
   copy_body (id, bb->count, bb->frequency, bb, return_block);
-  id->current_node = old_node;
+
+  /* Add local vars in this inlined callee to caller.  */
+  t_step = id->src_cfun->unexpanded_var_list;
+  for (; t_step; t_step = TREE_CHAIN (t_step))
+    {
+      var = TREE_VALUE (t_step);
+      if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
+       cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
+                                              cfun->unexpanded_var_list);
+      else
+       cfun->unexpanded_var_list = tree_cons (NULL_TREE, remap_decl (var, id),
+                                              cfun->unexpanded_var_list);
+    }
 
   /* Clean up.  */
   splay_tree_delete (id->decl_map);
@@ -2082,7 +2099,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   else
     /* We're modifying a TSI owned by gimple_expand_calls_inline();
        tsi_delink() will leave the iterator in a sane state.  */
-    bsi_remove (&stmt_bsi);
+    bsi_remove (&stmt_bsi, true);
 
   bsi_next (&bsi);
   if (bsi_end_p (bsi))
@@ -2105,16 +2122,6 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
   /* Declare the 'auto' variables added with this inlined body.  */
   record_vars (BLOCK_VARS (id->block));
   id->block = NULL_TREE;
-
-  /* Add local static vars in this inlined callee to caller.  */
-  for (t_step = id->callee_cfun->unexpanded_var_list;
-       t_step;
-       t_step = TREE_CHAIN (t_step))
-    {
-      var = TREE_VALUE (t_step);
-      if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
-       record_vars (var);
-    }
   successfully_inlined = TRUE;
 
  egress:
@@ -2129,7 +2136,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
    to the CALL_EXPR, not the tree itself.  */
 
 static bool
-gimple_expand_calls_inline (basic_block bb, inline_data *id)
+gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
 {
   block_stmt_iterator bsi;
 
@@ -2156,7 +2163,7 @@ gimple_expand_calls_inline (basic_block bb, inline_data *id)
 void
 optimize_inline_calls (tree fn)
 {
-  inline_data id;
+  copy_body_data id;
   tree prev_fn;
   basic_block bb;
   /* There is no point in performing inlining if errors have already
@@ -2168,15 +2175,22 @@ optimize_inline_calls (tree fn)
   /* Clear out ID.  */
   memset (&id, 0, sizeof (id));
 
-  id.current_node = id.node = cgraph_node (fn);
-  id.caller = fn;
+  id.src_node = id.dst_node = cgraph_node (fn);
+  id.dst_fn = fn;
   /* Or any functions that aren't finished yet.  */
   prev_fn = NULL_TREE;
   if (current_function_decl)
     {
-      id.caller = current_function_decl;
+      id.dst_fn = current_function_decl;
       prev_fn = current_function_decl;
     }
+
+  id.copy_decl = copy_decl_maybe_to_var;
+  id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+  id.transform_new_cfg = false;
+  id.transform_return_to_modify = true;
+  id.transform_lang_insert_block = false;
+
   push_gimplify_context ();
 
   /* Reach the trees by walking over the CFG, and note the
@@ -2188,7 +2202,6 @@ optimize_inline_calls (tree fn)
   FOR_EACH_BB (bb)
     gimple_expand_calls_inline (bb, &id);
 
-
   pop_gimplify_context (NULL);
   /* Renumber the (code) basic_blocks consecutively.  */
   compact_blocks ();
@@ -2199,10 +2212,10 @@ optimize_inline_calls (tree fn)
     {
       struct cgraph_edge *e;
 
-      verify_cgraph_node (id.node);
+      verify_cgraph_node (id.dst_node);
 
       /* Double check that we inlined everything we are supposed to inline.  */
-      for (e = id.node->callees; e; e = e->next_callee)
+      for (e = id.dst_node->callees; e; e = e->next_callee)
        gcc_assert (e->inline_failed);
     }
 #endif
@@ -2220,19 +2233,21 @@ optimize_inline_calls (tree fn)
 void
 clone_body (tree clone, tree fn, void *arg_map)
 {
-  inline_data id;
+  copy_body_data id;
 
   /* Clone the body, as if we were making an inline call.  But, remap the
      parameters in the callee to the parameters of caller.  */
   memset (&id, 0, sizeof (id));
-  id.caller = clone;
-  id.callee = fn;
-  id.callee_cfun = DECL_STRUCT_FUNCTION (fn);
+  id.src_fn = fn;
+  id.dst_fn = clone;
+  id.src_cfun = DECL_STRUCT_FUNCTION (fn);
   id.decl_map = (splay_tree)arg_map;
 
-  /* Cloning is treated slightly differently from inlining.  Set
-     CLONING_P so that it's clear which operation we're performing.  */
-  id.cloning_p = true;
+  id.copy_decl = copy_decl_no_change;
+  id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+  id.transform_new_cfg = true;
+  id.transform_return_to_modify = false;
+  id.transform_lang_insert_block = true;
 
   /* We're not inside any EH region.  */
   id.eh_region = -1;
@@ -2241,68 +2256,6 @@ clone_body (tree clone, tree fn, void *arg_map)
   append_to_statement_list_force (copy_generic_body (&id), &DECL_SAVED_TREE (clone));
 }
 
-/* Save duplicate body in FN.  MAP is used to pass around splay tree
-   used to update arguments in restore_body.  */
-
-/* Make and return duplicate of body in FN.  Put copies of DECL_ARGUMENTS
-   in *arg_copy and of the static chain, if any, in *sc_copy.  */
-
-void
-save_body (tree fn, tree *arg_copy, tree *sc_copy)
-{
-  inline_data id;
-  tree newdecl, *parg;
-  basic_block fn_entry_block;
-
-  memset (&id, 0, sizeof (id));
-  id.callee = fn;
-  id.callee_cfun = DECL_STRUCT_FUNCTION (fn);
-  id.caller = fn;
-  id.node = cgraph_node (fn);
-  id.saving_p = true;
-  id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
-  *arg_copy = DECL_ARGUMENTS (fn);
-
-  for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg))
-    {
-      tree new = copy_node (*parg);
-
-      lang_hooks.dup_lang_specific_decl (new);
-      DECL_ABSTRACT_ORIGIN (new) = DECL_ORIGIN (*parg);
-      insert_decl_map (&id, *parg, new);
-      TREE_CHAIN (new) = TREE_CHAIN (*parg);
-      *parg = new;
-    }
-
-  *sc_copy = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
-  if (*sc_copy)
-    {
-      tree new = copy_node (*sc_copy);
-
-      lang_hooks.dup_lang_specific_decl (new);
-      DECL_ABSTRACT_ORIGIN (new) = DECL_ORIGIN (*sc_copy);
-      insert_decl_map (&id, *sc_copy, new);
-      TREE_CHAIN (new) = TREE_CHAIN (*sc_copy);
-      *sc_copy = new;
-    }
-
-  /* We're not inside any EH region.  */
-  id.eh_region = -1;
-
-  insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn));
-
-  /* Actually copy the body, including a new (struct function *) and CFG.
-     EH info is also duplicated so its labels point into the copied
-     CFG, not the original.  */
-  fn_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fn));
-  newdecl = copy_body (&id, fn_entry_block->count, fn_entry_block->frequency, NULL, NULL);
-  DECL_STRUCT_FUNCTION (fn)->saved_cfg = DECL_STRUCT_FUNCTION (newdecl)->cfg;
-  DECL_STRUCT_FUNCTION (fn)->saved_eh = DECL_STRUCT_FUNCTION (newdecl)->eh;
-
-  /* Clean up.  */
-  splay_tree_delete (id.decl_map);
-}
-
 /* Passed to walk_tree.  Copies the node pointed to, if appropriate.  */
 
 tree
@@ -2340,7 +2293,22 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
       if (TREE_CODE (*tp) == BIND_EXPR)
        BIND_EXPR_BLOCK (*tp) = NULL_TREE;
     }
+  else if (code == CONSTRUCTOR)
+    {
+      /* CONSTRUCTOR nodes need special handling because
+         we need to duplicate the vector of elements.  */
+      tree new;
+
+      new = copy_node (*tp);
+
+      /* Propagate mudflap marked-ness.  */
+      if (flag_mudflap && mf_marked_p (*tp))
+        mf_mark (new);
 
+      CONSTRUCTOR_ELTS (new) = VEC_copy (constructor_elt, gc,
+                                        CONSTRUCTOR_ELTS (*tp));
+      *tp = new;
+    }
   else if (TREE_CODE_CLASS (code) == tcc_type)
     *walk_subtrees = 0;
   else if (TREE_CODE_CLASS (code) == tcc_declaration)
@@ -2390,13 +2358,13 @@ remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
 
 /* Called via walk_tree.  If *TP points to a DECL_STMT for a local label,
    copies the declaration and enters it in the splay_tree in DATA (which is
-   really an `inline_data *').  */
+   really an `copy_body_data *').  */
 
 static tree
 mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
                        void *data)
 {
-  inline_data *id = (inline_data *) data;
+  copy_body_data *id = (copy_body_data *) data;
 
   /* Don't walk into types.  */
   if (TYPE_P (*tp))
@@ -2407,9 +2375,7 @@ mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
       tree decl = TREE_OPERAND (*tp, 0);
 
       /* Copy the decl and remember the copy.  */
-      insert_decl_map (id, decl,
-                      copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
-                                              DECL_CONTEXT (decl)));
+      insert_decl_map (id, decl, id->copy_decl (decl, id));
     }
 
   return NULL_TREE;
@@ -2447,7 +2413,7 @@ unsave_expr_1 (tree expr)
 static tree
 unsave_r (tree *tp, int *walk_subtrees, void *data)
 {
-  inline_data *id = (inline_data *) data;
+  copy_body_data *id = (copy_body_data *) data;
   splay_tree st = id->decl_map;
   splay_tree_node n;
 
@@ -2487,7 +2453,7 @@ unsave_r (tree *tp, int *walk_subtrees, void *data)
 tree
 unsave_expr_now (tree expr)
 {
-  inline_data id;
+  copy_body_data id;
 
   /* There's nothing to do for NULL_TREE.  */
   if (expr == 0)
@@ -2495,10 +2461,16 @@ unsave_expr_now (tree expr)
 
   /* Set up ID.  */
   memset (&id, 0, sizeof (id));
-  id.callee = current_function_decl;
-  id.caller = current_function_decl;
+  id.src_fn = current_function_decl;
+  id.dst_fn = current_function_decl;
   id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
 
+  id.copy_decl = copy_decl_no_change;
+  id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+  id.transform_new_cfg = false;
+  id.transform_return_to_modify = false;
+  id.transform_lang_insert_block = false;
+
   /* Walk the tree once to find local labels.  */
   walk_tree_without_duplicates (&expr, mark_local_for_remap_r, &id);
 
@@ -2543,9 +2515,307 @@ declare_inline_vars (tree block, tree vars)
     BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars);
 }
 
-/* Returns true if we're inlining.  */
-static inline bool
-inlining_p (inline_data *id)
+
+/* Copy NODE (which must be a DECL).  The DECL originally was in the FROM_FN,
+   but now it will be in the TO_FN.  PARM_TO_VAR means enable PARM_DECL to
+   VAR_DECL translation.  */
+
+static tree
+copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
+{
+  /* Don't generate debug information for the copy if we wouldn't have
+     generated it for the copy either.  */
+  DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
+  DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
+
+  /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what
+     declaration inspired this copy.  */ 
+  DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
+
+  /* The new variable/label has no RTL, yet.  */
+  if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL)
+      && !TREE_STATIC (copy) && !DECL_EXTERNAL (copy))
+    SET_DECL_RTL (copy, NULL_RTX);
+  
+  /* These args would always appear unused, if not for this.  */
+  TREE_USED (copy) = 1;
+
+  /* Set the context for the new declaration.  */
+  if (!DECL_CONTEXT (decl))
+    /* Globals stay global.  */
+    ;
+  else if (DECL_CONTEXT (decl) != id->src_fn)
+    /* Things that weren't in the scope of the function we're inlining
+       from aren't in the scope we're inlining to, either.  */
+    ;
+  else if (TREE_STATIC (decl))
+    /* Function-scoped static variables should stay in the original
+       function.  */
+    ;
+  else
+    /* Ordinary automatic local variables are now in the scope of the
+       new function.  */
+    DECL_CONTEXT (copy) = id->dst_fn;
+
+  return copy;
+}
+
+static tree
+copy_decl_to_var (tree decl, copy_body_data *id)
+{
+  tree copy, type;
+
+  gcc_assert (TREE_CODE (decl) == PARM_DECL
+             || TREE_CODE (decl) == RESULT_DECL);
+
+  type = TREE_TYPE (decl);
+
+  copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
+  TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
+  TREE_READONLY (copy) = TREE_READONLY (decl);
+  TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
+  DECL_COMPLEX_GIMPLE_REG_P (copy) = DECL_COMPLEX_GIMPLE_REG_P (decl);
+
+  return copy_decl_for_dup_finish (id, decl, copy);
+}
+
+static tree
+copy_decl_no_change (tree decl, copy_body_data *id)
+{
+  tree copy;
+
+  copy = copy_node (decl);
+
+  /* The COPY is not abstract; it will be generated in DST_FN.  */
+  DECL_ABSTRACT (copy) = 0;
+  lang_hooks.dup_lang_specific_decl (copy);
+
+  /* TREE_ADDRESSABLE isn't used to indicate that a label's address has
+     been taken; it's for internal bookkeeping in expand_goto_internal.  */
+  if (TREE_CODE (copy) == LABEL_DECL)
+    {
+      TREE_ADDRESSABLE (copy) = 0;
+      LABEL_DECL_UID (copy) = -1;
+    }
+
+  return copy_decl_for_dup_finish (id, decl, copy);
+}
+
+static tree
+copy_decl_maybe_to_var (tree decl, copy_body_data *id)
 {
-  return (!id->saving_p && !id->cloning_p);
+  if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL)
+    return copy_decl_to_var (decl, id);
+  else
+    return copy_decl_no_change (decl, id);
+}
+
+/* Return a copy of the function's argument tree.  */
+static tree
+copy_arguments_for_versioning (tree orig_parm, copy_body_data * id)
+{
+  tree *arg_copy, *parg;
+
+  arg_copy = &orig_parm;
+  for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg))
+    {
+      tree new = remap_decl (*parg, id);
+      lang_hooks.dup_lang_specific_decl (new);
+      TREE_CHAIN (new) = TREE_CHAIN (*parg);
+      *parg = new;
+    }
+  return orig_parm;
+}
+
+/* Return a copy of the function's static chain.  */
+static tree
+copy_static_chain (tree static_chain, copy_body_data * id)
+{
+  tree *chain_copy, *pvar;
+
+  chain_copy = &static_chain;
+  for (pvar = chain_copy; *pvar; pvar = &TREE_CHAIN (*pvar))
+    {
+      tree new = remap_decl (*pvar, id);
+      lang_hooks.dup_lang_specific_decl (new);
+      TREE_CHAIN (new) = TREE_CHAIN (*pvar);
+      *pvar = new;
+    }
+  return static_chain;
+}
+
+/* Return true if the function is allowed to be versioned.
+   This is a guard for the versioning functionality.  */
+bool
+tree_versionable_function_p (tree fndecl)
+{
+  if (fndecl == NULL_TREE)
+    return false;
+  /* ??? There are cases where a function is
+     uninlinable but can be versioned.  */
+  if (!tree_inlinable_function_p (fndecl))
+    return false;
+  
+  return true;
+}
+
+/* Create a copy of a function's tree.
+   OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes
+   of the original function and the new copied function
+   respectively.  In case we want to replace a DECL 
+   tree with another tree while duplicating the function's 
+   body, TREE_MAP represents the mapping between these 
+   trees. If UPDATE_CLONES is set, the call_stmt fields
+   of edges of clones of the function will be updated.  */
+void
+tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map,
+                         bool update_clones)
+{
+  struct cgraph_node *old_version_node;
+  struct cgraph_node *new_version_node;
+  copy_body_data id;
+  tree p, new_fndecl;
+  unsigned i;
+  struct ipa_replace_map *replace_info;
+  basic_block old_entry_block;
+  tree t_step;
+
+  gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL
+             && TREE_CODE (new_decl) == FUNCTION_DECL);
+  DECL_POSSIBLY_INLINED (old_decl) = 1;
+
+  old_version_node = cgraph_node (old_decl);
+  new_version_node = cgraph_node (new_decl);
+
+  allocate_struct_function (new_decl);
+  /* Cfun points to the new allocated function struct at this point.  */
+  cfun->function_end_locus = DECL_SOURCE_LOCATION (new_decl);
+
+  DECL_ARTIFICIAL (new_decl) = 1;
+  DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
+
+  /* Generate a new name for the new version. */
+  if (!update_clones)
+    DECL_NAME (new_decl) = create_tmp_var_name (NULL);
+  /* Create a new SYMBOL_REF rtx for the new name. */
+  if (DECL_RTL (old_decl) != NULL)
+    {
+      SET_DECL_RTL (new_decl, copy_rtx (DECL_RTL (old_decl)));
+      XEXP (DECL_RTL (new_decl), 0) =
+       gen_rtx_SYMBOL_REF (GET_MODE (XEXP (DECL_RTL (old_decl), 0)),
+                           IDENTIFIER_POINTER (DECL_NAME (new_decl)));
+    }
+
+  /* Prepare the data structures for the tree copy.  */
+  memset (&id, 0, sizeof (id));
+  
+  id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+  id.src_fn = old_decl;
+  id.dst_fn = new_decl;
+  id.src_node = old_version_node;
+  id.dst_node = new_version_node;
+  id.src_cfun = DECL_STRUCT_FUNCTION (old_decl);
+  
+  id.copy_decl = copy_decl_no_change;
+  id.transform_call_graph_edges
+    = update_clones ? CB_CGE_MOVE_CLONES : CB_CGE_MOVE;
+  id.transform_new_cfg = true;
+  id.transform_return_to_modify = false;
+  id.transform_lang_insert_block = false;
+
+  current_function_decl = new_decl;
+  
+  /* Copy the function's static chain.  */
+  p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl;
+  if (p)
+    DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl =
+      copy_static_chain (DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl,
+                        &id);
+  /* Copy the function's arguments.  */
+  if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
+    DECL_ARGUMENTS (new_decl) =
+      copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id);
+  
+  /* If there's a tree_map, prepare for substitution.  */
+  if (tree_map)
+    for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++)
+      {
+       replace_info = VARRAY_GENERIC_PTR (tree_map, i);
+       if (replace_info->replace_p)
+         insert_decl_map (&id, replace_info->old_tree,
+                          replace_info->new_tree);
+      }
+  
+  DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
+  
+  /* Renumber the lexical scoping (non-code) blocks consecutively.  */
+  number_blocks (id.dst_fn);
+  
+  if (DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list != NULL_TREE)
+    /* Add local vars.  */
+    for (t_step = DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list;
+        t_step; t_step = TREE_CHAIN (t_step))
+      {
+       tree var = TREE_VALUE (t_step);
+       if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
+         cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
+                                                cfun->unexpanded_var_list);
+       else
+         cfun->unexpanded_var_list =
+           tree_cons (NULL_TREE, remap_decl (var, &id),
+                      cfun->unexpanded_var_list);
+      }
+  
+  /* Copy the Function's body.  */
+  old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION
+    (DECL_STRUCT_FUNCTION (old_decl));
+  new_fndecl = copy_body (&id,
+                         old_entry_block->count,
+                         old_entry_block->frequency, NULL, NULL);
+  
+  DECL_SAVED_TREE (new_decl) = DECL_SAVED_TREE (new_fndecl);
+
+  DECL_STRUCT_FUNCTION (new_decl)->cfg =
+    DECL_STRUCT_FUNCTION (new_fndecl)->cfg;
+  DECL_STRUCT_FUNCTION (new_decl)->eh = DECL_STRUCT_FUNCTION (new_fndecl)->eh;
+  DECL_STRUCT_FUNCTION (new_decl)->ib_boundaries_block =
+    DECL_STRUCT_FUNCTION (new_fndecl)->ib_boundaries_block;
+  DECL_STRUCT_FUNCTION (new_decl)->last_label_uid =
+    DECL_STRUCT_FUNCTION (new_fndecl)->last_label_uid;
+
+  if (DECL_RESULT (old_decl) != NULL_TREE)
+    {
+      tree *res_decl = &DECL_RESULT (old_decl);
+      DECL_RESULT (new_decl) = remap_decl (*res_decl, &id);
+      lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl));
+    }
+  
+  current_function_decl = NULL;
+  /* Renumber the lexical scoping (non-code) blocks consecutively.  */
+  number_blocks (new_decl);
+
+  /* Clean up.  */
+  splay_tree_delete (id.decl_map);
+  fold_cond_expr_cond ();
+  return;
+}
+
+/* Duplicate a type, fields and all.  */
+
+tree
+build_duplicate_type (tree type)
+{
+  struct copy_body_data id;
+
+  memset (&id, 0, sizeof (id));
+  id.src_fn = current_function_decl;
+  id.dst_fn = current_function_decl;
+  id.src_cfun = cfun;
+  id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+
+  type = remap_type_1 (type, &id);
+
+  splay_tree_delete (id.decl_map);
+
+  return type;
 }