OSDN Git Service

* tree-into-ssa.c: Re-organize internal functions.
[pf3gnuchains/gcc-fork.git] / gcc / tree-inline.c
index 24bf3d1..120bab0 100644 (file)
@@ -1,5 +1,5 @@
 /* Tree inlining.
-   Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Alexandre Oliva <aoliva@redhat.com>
 
 This file is part of GCC.
@@ -35,13 +35,16 @@ Boston, MA 02111-1307, USA.  */
 #include "integrate.h"
 #include "varray.h"
 #include "hashtab.h"
+#include "pointer-set.h"
 #include "splay-tree.h"
 #include "langhooks.h"
 #include "cgraph.h"
 #include "intl.h"
 #include "tree-mudflap.h"
+#include "tree-flow.h"
 #include "function.h"
 #include "diagnostic.h"
+#include "debug.h"
 
 /* I'm not real happy about this, but we need to handle gimple and
    non-gimple trees.  */
@@ -93,8 +96,6 @@ typedef struct inline_data
   /* Nonzero if we are currently within the cleanup for a
      TARGET_EXPR.  */
   int in_target_cleanup_p;
-  /* A list of the functions current function has inlined.  */
-  varray_type inlined_fns;
   /* 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
@@ -122,7 +123,6 @@ typedef struct inline_data
    decisions about when a function is too big to inline.  */
 #define INSNS_PER_STMT (10)
 
-static tree declare_return_variable (inline_data *, tree, tree *);
 static tree copy_body_r (tree *, int *, void *);
 static tree copy_body (inline_data *);
 static tree expand_call_inline (tree *, int *, void *);
@@ -136,8 +136,10 @@ static void remap_block (tree *, inline_data *);
 static tree remap_decls (tree, inline_data *);
 static void copy_bind_expr (tree *, int *, inline_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 bind_expr, tree vars);
+static void remap_save_expr (tree *, void *, int *);
 
 /* Insert a tree->tree mapping for ID.  Despite the name suggests
    that the trees should be variables, it is used for more than that.  */
@@ -155,35 +157,21 @@ insert_decl_map (inline_data *id, tree key, tree value)
                       (splay_tree_value) value);
 }
 
-/* Remap DECL during the copying of the BLOCK tree for the function.  */
+/* Remap DECL during the copying of the BLOCK tree for the function.
+   We are only called to remap local variables in the current function.  */
 
 static tree
 remap_decl (tree decl, inline_data *id)
 {
-  splay_tree_node n;
-  tree fn;
-
-  /* We only remap local variables in the current function.  */
-  fn = VARRAY_TOP_TREE (id->fns);
-#if 0
-  /* We need to remap statics, too, so that they get expanded even if the
-     inline function is never emitted out of line.  We might as well also
-     remap extern decls so that they show up in the debug info.  */
-  if (! lang_hooks.tree_inlining.auto_var_in_fn_p (decl, fn))
-    return NULL_TREE;
-#endif
-
-  /* See if we have remapped this declaration.  */
-  n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+  splay_tree_node n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+  tree fn = VARRAY_TOP_TREE (id->fns);
 
-  /* If we didn't already have an equivalent for this declaration,
-     create one now.  */
+  /* See if we have remapped this declaration.  If we didn't already have an
+     equivalent for this declaration, create one now.  */
   if (!n)
     {
-      tree t;
-
       /* Make a copy of the variable or label.  */
-      t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0));
+      tree t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0));
 
       /* Remap types, if necessary.  */
       TREE_TYPE (t) = remap_type (TREE_TYPE (t), id);
@@ -197,6 +185,14 @@ remap_decl (tree decl, inline_data *id)
       walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL);
       walk_tree (&DECL_SIZE_UNIT (t), copy_body_r, id, NULL);
 
+      /* If fields, do likewise for offset and qualifier.  */
+      if (TREE_CODE (t) == FIELD_DECL)
+       {
+         walk_tree (&DECL_FIELD_OFFSET (t), copy_body_r, id, NULL);
+         if (TREE_CODE (DECL_CONTEXT (t)) == QUAL_UNION_TYPE)
+           walk_tree (&DECL_QUALIFIER (t), copy_body_r, id, NULL);
+       }
+
 #if 0
       /* FIXME handle anon aggrs.  */
       if (! DECL_NAME (t) && TREE_TYPE (t)
@@ -212,8 +208,7 @@ remap_decl (tree decl, inline_data *id)
            {
              tree member = remap_decl (TREE_VALUE (src), id);
 
-             if (TREE_PURPOSE (src))
-               abort ();
+             gcc_assert (!TREE_PURPOSE (src));
              members = tree_cons (NULL, member, members);
            }
          DECL_ANON_UNION_ELEMS (t) = nreverse (members);
@@ -243,15 +238,36 @@ remap_type (tree type, inline_data *id)
   if (node)
     return (tree) node->value;
 
-  /* The type only needs remapping if it's variably modified.  */
-  if (! variably_modified_type_p (type))
+  /* The type only needs remapping if it's variably modified by a variable
+     in the function we are inlining.  */
+  if (! variably_modified_type_p (type, VARRAY_TOP_TREE (id->fns)))
     {
       insert_decl_map (id, type, type);
       return type;
     }
-  
-  /* We do need a copy.  build and register it now.  */
-  new = copy_node (type);
+
+  /* We do need a copy.  build and register it now.  If this is a pointer or
+     reference type, remap the designated type and make a new pointer or
+     reference type.  */
+  if (TREE_CODE (type) == POINTER_TYPE)
+    {
+      new = build_pointer_type_for_mode (remap_type (TREE_TYPE (type), id),
+                                        TYPE_MODE (type),
+                                        TYPE_REF_CAN_ALIAS_ALL (type));
+      insert_decl_map (id, type, new);
+      return new;
+    }
+  else if (TREE_CODE (type) == REFERENCE_TYPE)
+    {
+      new = build_reference_type_for_mode (remap_type (TREE_TYPE (type), id),
+                                           TYPE_MODE (type),
+                                           TYPE_REF_CAN_ALIAS_ALL (type));
+      insert_decl_map (id, type, new);
+      return new;
+    }
+  else
+    new = copy_node (type);
+
   insert_decl_map (id, type, new);
 
   /* This is a new type, not a copy of an old type.  Need to reassociate
@@ -289,20 +305,7 @@ remap_type (tree type, inline_data *id)
       if (t && TREE_CODE (t) != INTEGER_CST)
         walk_tree (&TYPE_MAX_VALUE (new), copy_body_r, id, NULL);
       return new;
-    
-    case POINTER_TYPE:
-      TREE_TYPE (new) = t = remap_type (TREE_TYPE (new), id);
-      TYPE_NEXT_PTR_TO (new) = TYPE_POINTER_TO (t);
-      TYPE_POINTER_TO (t) = new;
-      return new;
-
-    case REFERENCE_TYPE:
-      TREE_TYPE (new) = t = remap_type (TREE_TYPE (new), id);
-      TYPE_NEXT_REF_TO (new) = TYPE_REFERENCE_TO (t);
-      TYPE_REFERENCE_TO (t) = new;
-      return new;
 
-    case METHOD_TYPE:
     case FUNCTION_TYPE:
       TREE_TYPE (new) = remap_type (TREE_TYPE (new), id);
       walk_tree (&TYPE_ARG_TYPES (new), copy_body_r, id, NULL);
@@ -320,11 +323,10 @@ remap_type (tree type, inline_data *id)
       break;
 
     case FILE_TYPE:
-    case SET_TYPE:
     case OFFSET_TYPE:
     default:
       /* Shouldn't have been thought variable sized.  */
-      abort ();
+      gcc_unreachable ();
     }
 
   walk_tree (&TYPE_SIZE (new), copy_body_r, id, NULL);
@@ -352,12 +354,9 @@ remap_decls (tree decls, inline_data *id)
         already declared somewhere else, so don't declare it here.  */
       if (!new_var || new_var == id->retvar)
        ;
-#ifdef ENABLE_CHECKING
-      else if (!DECL_P (new_var))
-       abort ();
-#endif
       else
        {
+         gcc_assert (DECL_P (new_var));
          TREE_CHAIN (new_var) = new_decls;
          new_decls = new_var;
        }
@@ -446,25 +445,20 @@ copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id)
     BIND_EXPR_VARS (*tp) = remap_decls (BIND_EXPR_VARS (*tp), id);
 }
 
-/* Called from copy_body via walk_tree.  DATA is really an
-   `inline_data *'.  */
+/* Called from copy_body via walk_tree.  DATA is really an `inline_data *'.  */
+
 static tree
 copy_body_r (tree *tp, int *walk_subtrees, void *data)
 {
-  inline_data* id;
-  tree fn;
-
-  /* Set up.  */
-  id = (inline_data *) data;
-  fn = VARRAY_TOP_TREE (id->fns);
+  inline_data *id = (inline_data *) data;
+  tree fn = VARRAY_TOP_TREE (id->fns);
 
 #if 0
   /* All automatic variables should have a DECL_CONTEXT indicating
      what function they come from.  */
   if ((TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == LABEL_DECL)
       && DECL_NAMESPACE_SCOPE_P (*tp))
-    if (! DECL_EXTERNAL (*tp) && ! TREE_STATIC (*tp))
-      abort ();
+    gcc_assert (DECL_EXTERNAL (*tp) || TREE_STATIC (*tp));
 #endif
 
   /* If this is a RETURN_EXPR, change it into a MODIFY_EXPR and a
@@ -485,9 +479,8 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
       if (assignment)
         {
          /* Do not create a statement containing a naked RESULT_DECL.  */
-         if (lang_hooks.gimple_before_inlining)
-           if (TREE_CODE (assignment) == RESULT_DECL)
-             gimplify_stmt (&assignment);
+         if (TREE_CODE (assignment) == RESULT_DECL)
+           gimplify_stmt (&assignment);
 
          *tp = build (BIND_EXPR, void_type_node, NULL, NULL, NULL);
          append_to_statement_list (assignment, &BIND_EXPR_BODY (*tp));
@@ -500,58 +493,48 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
   /* Local variables and labels need to be replaced by equivalent
      variables.  We don't want to copy static variables; there's only
      one of those, no matter how many times we inline the containing
-     function.  */
+     function.  Similarly for globals from an outer function.  */
   else if (lang_hooks.tree_inlining.auto_var_in_fn_p (*tp, fn))
     {
       tree new_decl;
 
       /* Remap the declaration.  */
       new_decl = remap_decl (*tp, id);
-      if (! new_decl)
-       abort ();
+      gcc_assert (new_decl);
       /* Replace this variable with the copy.  */
       STRIP_TYPE_NOPS (new_decl);
       *tp = new_decl;
+      *walk_subtrees = 0;
     }
-#if 0
-  else if (nonstatic_local_decl_p (*tp)
-          && DECL_CONTEXT (*tp) != VARRAY_TREE (id->fns, 0))
-    abort ();
-#endif
   else if (TREE_CODE (*tp) == STATEMENT_LIST)
     copy_statement_list (tp);
   else if (TREE_CODE (*tp) == SAVE_EXPR)
-    remap_save_expr (tp, id->decl_map, VARRAY_TREE (id->fns, 0),
-                    walk_subtrees);
-  else if (TREE_CODE (*tp) == UNSAVE_EXPR)
-    /* UNSAVE_EXPRs should not be generated until expansion time.  */
-    abort ();
+    remap_save_expr (tp, id->decl_map, walk_subtrees);
   else if (TREE_CODE (*tp) == BIND_EXPR)
     copy_bind_expr (tp, walk_subtrees, id);
-  else if (TREE_CODE (*tp) == LABELED_BLOCK_EXPR)
-    {
-      /* We need a new copy of this labeled block; the EXIT_BLOCK_EXPR
-         will refer to it, so save a copy ready for remapping.  We
-         save it in the decl_map, although it isn't a decl.  */
-      tree new_block = copy_node (*tp);
-      insert_decl_map (id, *tp, new_block);
-      *tp = new_block;
-    }
-  else if (TREE_CODE (*tp) == EXIT_BLOCK_EXPR)
-    {
-      splay_tree_node n
-       = splay_tree_lookup (id->decl_map,
-                            (splay_tree_key) TREE_OPERAND (*tp, 0));
-      /* We _must_ have seen the enclosing LABELED_BLOCK_EXPR.  */
-      if (! n)
-       abort ();
-      *tp = copy_node (*tp);
-      TREE_OPERAND (*tp, 0) = (tree) n->value;
-    }
   /* Types may need remapping as well.  */
   else if (TYPE_P (*tp))
     *tp = remap_type (*tp, id);
 
+  /* If this is a constant, we have to copy the node iff the type will be
+     remapped.  copy_tree_r will not copy a constant.  */
+  else if (TREE_CODE_CLASS (TREE_CODE (*tp)) == tcc_constant)
+    {
+      tree new_type = remap_type (TREE_TYPE (*tp), id);
+
+      if (new_type == TREE_TYPE (*tp))
+       *walk_subtrees = 0;
+
+      else if (TREE_CODE (*tp) == INTEGER_CST)
+       *tp = build_int_cst_wide (new_type, TREE_INT_CST_LOW (*tp),
+                                 TREE_INT_CST_HIGH (*tp));
+      else
+       {
+         *tp = copy_node (*tp);
+         TREE_TYPE (*tp) = new_type;
+       }
+    }
+
   /* Otherwise, just copy the node.  Note that copy_tree_r already
      knows not to copy VAR_DECLs, etc., so this is safe.  */
   else
@@ -576,34 +559,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
              STRIP_TYPE_NOPS (value);
              if (TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value))
                {
-                 *tp = value;
-                 return copy_body_r (tp, walk_subtrees, data);
-               }
-           }
-       }
-      else if (TREE_CODE (*tp) == ADDR_EXPR
-              && (lang_hooks.tree_inlining.auto_var_in_fn_p
-                  (TREE_OPERAND (*tp, 0), fn)))
-       {
-         /* Get rid of &* from inline substitutions.  It can occur when
-            someone takes the address of a parm or return slot passed by
-            invisible reference.  */
-         tree decl = TREE_OPERAND (*tp, 0), value;
-         splay_tree_node n;
-
-         n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
-         if (n)
-           {
-             value = (tree) n->value;
-             if (TREE_CODE (value) == INDIRECT_REF)
-               {
-                 /* Assume that the argument types properly match the
-                    parameter types.  We can't compare them well enough
-                    without a comptypes langhook, and we don't want to
-                    call convert and introduce a NOP_EXPR to convert
-                    between two equivalent types (i.e. that only differ
-                    in use of typedef names).  */
-                 *tp = TREE_OPERAND (value, 0);
+                 *tp = build_empty_stmt ();
                  return copy_body_r (tp, walk_subtrees, data);
                }
            }
@@ -620,7 +576,9 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
            {
              value = (tree) n->value;
              STRIP_NOPS (value);
-             if (TREE_CODE (value) == ADDR_EXPR)
+             if (TREE_CODE (value) == ADDR_EXPR
+                 && (lang_hooks.types_compatible_p
+                     (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0)))))
                {
                  *tp = TREE_OPERAND (value, 0);
                  return copy_body_r (tp, walk_subtrees, data);
@@ -640,17 +598,15 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
              for (node = id->node->next_clone; node; node = node->next_clone)
                {
                  edge = cgraph_edge (node, old_node);
-                 if (edge)
-                   edge->call_expr = *tp;
-                 else
-                   abort ();
+                 gcc_assert (edge);
+                 edge->call_expr = *tp;
                }
            }
          else
            {
-              struct cgraph_edge *edge;
+              struct cgraph_edge *edge
+               = cgraph_edge (id->current_node, old_node);
 
-             edge = cgraph_edge (id->current_node, old_node);
              if (edge)
                cgraph_clone_edge (edge, id->node, *tp);
            }
@@ -665,6 +621,16 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
          TREE_OPERAND (*tp, 1) = TREE_OPERAND (*tp, 3);
          TREE_OPERAND (*tp, 3) = NULL_TREE;
        }
+
+      /* Variable substitution need not be simple.  In particular, the
+        INDIRECT_REF substitution above.  Make sure that TREE_CONSTANT
+        and friends are up-to-date.  */
+      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);
+         *walk_subtrees = 0;
+       }
     }
 
   /* Keep iterating.  */
@@ -690,14 +656,28 @@ copy_body (inline_data *id)
   return body;
 }
 
+/* Return true if VALUE is an ADDR_EXPR of an automatic variable
+   defined in function FN, or of a data member thereof.  */
+
+static bool
+self_inlining_addr_expr (tree value, tree fn)
+{
+  tree var;
+
+  if (TREE_CODE (value) != ADDR_EXPR)
+    return false;
+
+  var = get_base_address (TREE_OPERAND (value, 0));
+             
+  return var && lang_hooks.tree_inlining.auto_var_in_fn_p (var, fn);
+}
+
 static void
-setup_one_parameter (inline_data *id, tree p, tree value,
-                    tree fn, tree *init_stmts, tree *vars,
-                    bool *gimplify_init_stmts_p)
+setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
+                    tree *init_stmts, tree *vars, bool *gimplify_init_stmts_p)
 {
   tree init_stmt;
   tree var;
-  tree var_sub;
 
   /* If the parameter is never assigned to, we may not need to
      create a new variable here at all.  Instead, we may be able
@@ -711,56 +691,40 @@ setup_one_parameter (inline_data *id, tree p, tree value,
         Theoretically, we could check the expression to see if
         all of the variables that determine its value are
         read-only, but we don't bother.  */
-      if ((TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value))
-         /* We may produce non-gimple trees by adding NOPs or introduce
-            invalid sharing when operand is not really constant.
-            It is not big deal to prohibit constant propagation here as
-            we will constant propagate in DOM1 pass anyway.  */
-         && (!lang_hooks.gimple_before_inlining
-             || (is_gimple_min_invariant (value)
-                 && TREE_TYPE (value) == TREE_TYPE (p))))
+      /* We may produce non-gimple trees by adding NOPs or introduce
+        invalid sharing when operand is not really constant.
+        It is not big deal to prohibit constant propagation here as
+        we will constant propagate in DOM1 pass anyway.  */
+      if (is_gimple_min_invariant (value)
+         && lang_hooks.types_compatible_p (TREE_TYPE (value), TREE_TYPE (p))
+         /* We have to be very careful about ADDR_EXPR.  Make sure
+            the base variable isn't a local variable of the inlined
+            function, e.g., when doing recursive inlining, direct or
+            mutually-recursive or whatever, which is why we don't
+            just test whether fn == current_function_decl.  */
+         && ! self_inlining_addr_expr (value, fn))
        {
-         /* If this is a declaration, wrap it a NOP_EXPR so that
-            we don't try to put the VALUE on the list of BLOCK_VARS.  */
-         if (DECL_P (value))
-           value = build1 (NOP_EXPR, TREE_TYPE (value), value);
-
-         /* If this is a constant, make sure it has the right type.  */
-         else if (TREE_TYPE (value) != TREE_TYPE (p))
-           value = fold (build1 (NOP_EXPR, TREE_TYPE (p), value));
-
          insert_decl_map (id, p, value);
          return;
        }
     }
 
-  /* Make an equivalent VAR_DECL.  */
+  /* 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, VARRAY_TREE (id->fns, 0));
 
-  /* 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
-     replace uses of the PARM_DECL with dereferences.  */
-  if (TREE_TYPE (var) != TREE_TYPE (p)
-      && POINTER_TYPE_P (TREE_TYPE (var))
-      && TREE_TYPE (TREE_TYPE (var)) == TREE_TYPE (p))
-    {
-      insert_decl_map (id, var, var);
-      var_sub = build1 (INDIRECT_REF, TREE_TYPE (p), var);
-    }
-  else
-    var_sub = var;
-
   /* Register the VAR_DECL as the equivalent for the PARM_DECL;
      that way, when the PARM_DECL is encountered, it will be
      automatically replaced by the VAR_DECL.  */
-  insert_decl_map (id, p, var_sub);
+  insert_decl_map (id, p, var);
 
   /* Declare this new variable.  */
   TREE_CHAIN (var) = *vars;
   *vars = var;
 
   /* Make gimplifier happy about this variable.  */
-  var->decl.seen_in_bind_expr = lang_hooks.gimple_before_inlining;
+  DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
 
   /* Even if P was TREE_READONLY, the new VAR should not be.
      In the original code, we would have constructed a
@@ -846,52 +810,120 @@ 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;
   if (p)
     {
       /* No static chain?  Seems like a bug in tree-nested.c.  */
-      if (!static_chain)
-       abort ();
+      gcc_assert (static_chain);
 
-       setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars,
-                           &gimplify_init_stmts_p);
+      setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars,
+                          &gimplify_init_stmts_p);
     }
 
-  if (gimplify_init_stmts_p && lang_hooks.gimple_before_inlining)
-    gimplify_body (&init_stmts, fn);
+  if (gimplify_init_stmts_p)
+    gimplify_body (&init_stmts, current_function_decl, false);
 
   declare_inline_vars (bind_expr, vars);
   return init_stmts;
 }
 
-/* Declare a return variable to replace the RESULT_DECL for the
-   function we are calling.  An appropriate DECL_STMT is returned.
-   The USE_STMT is filled in to contain a use of the declaration to
-   indicate the return value of the function.  */
+/* Declare a return variable to replace the RESULT_DECL for the function we
+   are calling.  RETURN_SLOT_ADDR, if non-null, was a fake parameter that
+   took the address of the result.  MODIFY_DEST, if non-null, was the LHS of
+   the MODIFY_EXPR to which this call is the RHS.
+
+   The return value is a (possibly null) value that is the result of the
+   function as seen by the callee.  *USE_P is a (possibly null) value that
+   holds the result as seen by the caller.  */
 
 static tree
-declare_return_variable (inline_data *id, tree return_slot_addr, tree *use_p)
+declare_return_variable (inline_data *id, tree return_slot_addr,
+                        tree modify_dest, tree *use_p)
 {
-  tree fn = VARRAY_TOP_TREE (id->fns);
-  tree result = DECL_RESULT (fn);
-  int need_return_decl = 1;
-  tree var;
+  tree callee = VARRAY_TOP_TREE (id->fns);
+  tree caller = VARRAY_TREE (id->fns, 0);
+  tree result = DECL_RESULT (callee);
+  tree callee_type = TREE_TYPE (result);
+  tree caller_type = TREE_TYPE (TREE_TYPE (callee));
+  tree var, use;
 
   /* We don't need to do anything for functions that don't return
      anything.  */
-  if (!result || VOID_TYPE_P (TREE_TYPE (result)))
+  if (!result || VOID_TYPE_P (callee_type))
     {
       *use_p = NULL_TREE;
       return NULL_TREE;
     }
 
-  var = (lang_hooks.tree_inlining.copy_res_decl_for_inlining
-        (result, fn, VARRAY_TREE (id->fns, 0), id->decl_map,
-         &need_return_decl, return_slot_addr));
-  
+  /* If there was a return slot, then the return value is the
+     dereferenced address of that object.  */
+  if (return_slot_addr)
+    {
+      /* The front end shouldn't have used both return_slot_addr and
+        a modify expression.  */
+      gcc_assert (!modify_dest);
+      if (DECL_BY_REFERENCE (result))
+       var = return_slot_addr;
+      else
+       var = build_fold_indirect_ref (return_slot_addr);
+      use = NULL;
+      goto done;
+    }
+
+  /* All types requiring non-trivial constructors should have been handled.  */
+  gcc_assert (!TREE_ADDRESSABLE (callee_type));
+
+  /* Attempt to avoid creating a new temporary variable.  */
+  if (modify_dest)
+    {
+      bool use_it = false;
+
+      /* We can't use MODIFY_DEST if there's type promotion involved.  */
+      if (!lang_hooks.types_compatible_p (caller_type, callee_type))
+       use_it = false;
+
+      /* ??? If we're assigning to a variable sized type, then we must
+        reuse the destination variable, because we've no good way to
+        create variable sized temporaries at this point.  */
+      else if (TREE_CODE (TYPE_SIZE_UNIT (caller_type)) != INTEGER_CST)
+       use_it = true;
+
+      /* 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;
+
+      if (use_it)
+       {
+         var = modify_dest;
+         use = NULL;
+         goto done;
+       }
+    }
+
+  gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
+
+  var = copy_decl_for_inlining (result, callee, caller);
+  DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
+  DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
+    = tree_cons (NULL_TREE, var,
+                DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list);
+
   /* Do not have the rest of GCC warn about this variable as it should
-     not be visible to the user.   */
+     not be visible to the user.  */
   TREE_NO_WARNING (var) = 1;
 
+  /* Build the use expr.  If the return type of the function was
+     promoted, convert it back to the expected type.  */
+  use = var;
+  if (!lang_hooks.types_compatible_p (TREE_TYPE (var), caller_type))
+    use = fold_convert (caller_type, var);
+
+ done:
   /* Register the VAR_DECL as the equivalent for the RESULT_DECL; that
      way, when the RESULT_DECL is encountered, it will be
      automatically replaced by the VAR_DECL.  */
@@ -900,30 +932,8 @@ declare_return_variable (inline_data *id, tree return_slot_addr, tree *use_p)
   /* Remember this so we can ignore it in remap_decls.  */
   id->retvar = var;
 
-  /* Build the use expr.  If the return type of the function was
-     promoted, convert it back to the expected type.  */
-  if (return_slot_addr)
-    /* The function returns through an explicit return slot, not a normal
-       return value.  */
-    *use_p = NULL_TREE;
-  else if (TREE_TYPE (var) == TREE_TYPE (TREE_TYPE (fn)))
-    *use_p = var;
-  else if (TREE_CODE (var) == INDIRECT_REF)
-    *use_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (fn)),
-                    TREE_OPERAND (var, 0));
-  else if (TREE_ADDRESSABLE (TREE_TYPE (var)))
-    abort ();
-  else
-    *use_p = build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)), var);
-
-  /* Build the declaration statement if FN does not return an
-     aggregate.  */
-  if (need_return_decl)
-    return var;
-  /* If FN does return an aggregate, there's no need to declare the
-     return variable; we're using a variable in our caller's frame.  */
-  else
-    return NULL_TREE;
+  *use_p = use;
+  return var;
 }
 
 /* Returns nonzero if a function can be inlined as a tree.  */
@@ -956,7 +966,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
          && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
        {
          inline_forbidden_reason
-           = N_("%Jfunction '%F' can never be inlined because it uses "
+           = N_("%Jfunction %qF can never be inlined because it uses "
                 "alloca (override using the always_inline attribute)");
          return node;
        }
@@ -964,12 +974,11 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
       if (! t)
        break;
 
-
       /* We cannot inline functions that call setjmp.  */
       if (setjmp_call_p (t))
        {
          inline_forbidden_reason
-           = N_("%Jfunction '%F' can never be inlined because it uses setjmp");
+           = N_("%Jfunction %qF can never be inlined because it uses setjmp");
          return node;
        }
 
@@ -983,7 +992,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
-             = N_("%Jfunction '%F' can never be inlined because it "
+             = N_("%Jfunction %qF can never be inlined because it "
                   "uses variable argument lists");
            return node;
 
@@ -994,14 +1003,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
-             = N_("%Jfunction '%F' can never be inlined because "
+             = N_("%Jfunction %qF can never be inlined because "
                   "it uses setjmp-longjmp exception handling");
            return node;
 
          case BUILT_IN_NONLOCAL_GOTO:
            /* Similarly.  */
            inline_forbidden_reason
-             = N_("%Jfunction '%F' can never be inlined because "
+             = N_("%Jfunction %qF can never be inlined because "
                   "it uses non-local goto");
            return node;
 
@@ -1010,20 +1019,6 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
          }
       break;
 
-    case BIND_EXPR:
-      for (t = BIND_EXPR_VARS (node); t ; t = TREE_CHAIN (t))
-       {
-          /* We cannot inline functions that contain other functions.  */
-         if (TREE_CODE (t) == FUNCTION_DECL && DECL_INITIAL (t))
-           {
-             inline_forbidden_reason
-               = N_("%Jfunction '%F' can never be inlined "
-                    "because it contains a nested function");
-             return node;
-           }
-       }
-      break;
-
     case GOTO_EXPR:
       t = TREE_OPERAND (node, 0);
 
@@ -1034,7 +1029,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
       if (TREE_CODE (t) != LABEL_DECL)
        {
          inline_forbidden_reason
-           = N_("%Jfunction '%F' can never be inlined "
+           = N_("%Jfunction %qF can never be inlined "
                 "because it contains a computed goto");
          return node;
        }
@@ -1048,8 +1043,9 @@ 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
-           = N_("%Jfunction '%F' can never be inlined "
+           = N_("%Jfunction %qF can never be inlined "
                 "because it receives a non-local goto");
+         return node;
        }
       break;
 
@@ -1064,12 +1060,15 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
         UNION_TYPE nodes, then it goes into infinite recursion on a
         structure containing a pointer to its own type.  If it doesn't,
         then the type node for S doesn't get adjusted properly when
-        F is inlined, and we abort in find_function_data.  */
+        F is inlined, and we abort in find_function_data.
+
+        ??? This is likely no longer true, but it's too late in the 4.0
+        cycle to try to find out.  This should be checked for 4.1.  */
       for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
-       if (variably_modified_type_p (TREE_TYPE (t)))
+       if (variably_modified_type_p (TREE_TYPE (t), NULL))
          {
            inline_forbidden_reason
-             = N_("%Jfunction '%F' can never be inlined "
+             = N_("%Jfunction %qF can never be inlined "
                   "because it uses variable sized variables");
            return node;
          }
@@ -1086,8 +1085,9 @@ static tree
 inline_forbidden_p (tree fndecl)
 {
   location_t saved_loc = input_location;
-  tree ret = walk_tree_without_duplicates
-               (&DECL_SAVED_TREE (fndecl), inline_forbidden_p_1, fndecl);
+  tree ret = walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
+                                          inline_forbidden_p_1, fndecl);
+
   input_location = saved_loc;
   return ret;
 }
@@ -1136,12 +1136,6 @@ inlinable_function_p (tree fn)
   else if (!DECL_INLINE (fn) && !flag_unit_at_a_time)
     inlinable = false;
 
-#ifdef INLINER_FOR_JAVA
-  /* Synchronized methods can't be inlined.  This is a bug.  */
-  else if (METHOD_SYNCHRONIZED (fn))
-    inlinable = false;
-#endif /* INLINER_FOR_JAVA */
-
   else if (inline_forbidden_p (fn))
     {
       /* See if we should warn about uninlinable functions.  Previously,
@@ -1157,8 +1151,7 @@ inlinable_function_p (tree fn)
                         && DECL_DECLARED_INLINE_P (fn)
                         && !DECL_IN_SYSTEM_HEADER (fn));
 
-      if (lookup_attribute ("always_inline",
-                           DECL_ATTRIBUTES (fn)))
+      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
        sorry (inline_forbidden_reason, fn, fn);
       else if (do_warning)
        warning (inline_forbidden_reason, fn, fn);
@@ -1174,13 +1167,14 @@ inlinable_function_p (tree fn)
 
 /* Used by estimate_num_insns.  Estimate number of instructions seen
    by given statement.  */
+
 static tree
 estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
 {
   int *count = data;
   tree x = *tp;
 
-  if (TYPE_P (x) || DECL_P (x))
+  if (IS_TYPE_OR_DECL_P (x))
     {
       *walk_subtrees = 0;
       return NULL;
@@ -1188,11 +1182,11 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
   /* Assume that constants and references counts nothing.  These should
      be majorized by amount of operations among them we count later
      and are common target of CSE and similar optimizations.  */
-  if (TREE_CODE_CLASS (TREE_CODE (x)) == 'c'
-      || TREE_CODE_CLASS (TREE_CODE (x)) == 'r')
+  else if (CONSTANT_CLASS_P (x) || REFERENCE_CLASS_P (x))
     return NULL;
+
   switch (TREE_CODE (x))
-    { 
+    {
     /* Containers have no cost.  */
     case TREE_LIST:
     case TREE_VEC:
@@ -1200,26 +1194,22 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     case COMPONENT_REF:
     case BIT_FIELD_REF:
     case INDIRECT_REF:
-    case BUFFER_REF:
+    case ALIGN_INDIRECT_REF:
+    case MISALIGNED_INDIRECT_REF:
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
-    case VTABLE_REF:
+    case OBJ_TYPE_REF:
     case EXC_PTR_EXPR: /* ??? */
     case FILTER_EXPR: /* ??? */
     case COMPOUND_EXPR:
     case BIND_EXPR:
-    case LABELED_BLOCK_EXPR:
     case WITH_CLEANUP_EXPR:
     case NOP_EXPR:
     case VIEW_CONVERT_EXPR:
     case SAVE_EXPR:
-    case UNSAVE_EXPR:
     case ADDR_EXPR:
-    case REFERENCE_EXPR:
     case COMPLEX_EXPR:
-    case REALPART_EXPR:
-    case IMAGPART_EXPR:
-    case EXIT_BLOCK_EXPR:
+    case RANGE_EXPR:
     case CASE_LABEL_EXPR:
     case SSA_NAME:
     case CATCH_EXPR:
@@ -1227,7 +1217,6 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     case STATEMENT_LIST:
     case ERROR_MARK:
     case NON_LVALUE_EXPR:
-    case ENTRY_VALUE_EXPR:
     case FDESC_EXPR:
     case VA_ARG_EXPR:
     case TRY_CATCH_EXPR:
@@ -1238,7 +1227,9 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     case EXIT_EXPR:
     case LOOP_EXPR:
     case PHI_NODE:
+    case WITH_SIZE_EXPR:
       break;
+
     /* We don't account constants for now.  Assume that the cost is amortized
        by operations that do use them.  We may re-consider this decision once
        we are able to optimize the tree before estimating it's size and break
@@ -1337,8 +1328,10 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
 
     case ASM_EXPR:
 
+    case REALIGN_LOAD_EXPR:
+
     case RESX_EXPR:
-      *count++;
+      *count += 1;
       break;
 
     /* Few special cases of expensive operations.  This is useful
@@ -1359,7 +1352,7 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
       {
        tree decl = get_callee_fndecl (x);
 
-       if (decl && DECL_BUILT_IN (decl))
+       if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
          switch (DECL_FUNCTION_CODE (decl))
            {
            case BUILT_IN_CONSTANT_P:
@@ -1375,12 +1368,13 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
       }
     default:
       /* Abort here se we know we don't miss any nodes.  */
-      abort ();
+      gcc_unreachable ();
     }
   return NULL;
 }
 
 /* Estimate number of instructions that will be created by expanding EXPR.  */
+
 int
 estimate_num_insns (tree expr)
 {
@@ -1403,10 +1397,10 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
   tree fn;
   tree arg_inits;
   tree *inlined_body;
-  tree inline_result;
   splay_tree st;
   tree args;
   tree return_slot_addr;
+  tree modify_dest;
   location_t saved_location;
   struct cgraph_edge *edge;
   const char *reason;
@@ -1426,7 +1420,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
   if (TREE_CODE (*tp) == TARGET_EXPR)
     {
 #if 0
-      int i, len = first_rtl_op (TARGET_EXPR);
+      int i, len = TREE_CODE_LENGTH (TARGET_EXPR);
 
       /* We're walking our own subtrees.  */
       *walk_subtrees = 0;
@@ -1496,8 +1490,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
          where previous inlining turned indirect call into direct call by
          constant propagating arguments.  In all other cases we hit a bug
          (incorrect node sharing is most common reason for missing edges.  */
-      if (!dest->needed)
-       abort ();
+      gcc_assert (dest->needed || !flag_unit_at_a_time);
       cgraph_create_edge (id->node, dest, t)->inline_failed
        = N_("originally indirect function call not considered for inlining");
       goto egress;
@@ -1509,14 +1502,15 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
     {
       if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
        {
-         sorry ("%Jinlining failed in call to '%F': %s", fn, fn, reason);
+         sorry ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
          sorry ("called from here");
        }
       else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
               && !DECL_IN_SYSTEM_HEADER (fn)
-              && strlen (reason))
+              && strlen (reason)
+              && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)))
        {
-         warning ("%Jinlining failed in call to '%F': %s", fn, fn, reason);
+         warning ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
          warning ("called from here");
        }
       goto egress;
@@ -1535,7 +1529,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
      statements within the function to jump to.  The type of the
      statement expression is the return type of the function call.  */
   stmt = NULL;
-  expr = build (BIND_EXPR, TREE_TYPE (TREE_TYPE (fn)), NULL_TREE,
+  expr = build (BIND_EXPR, void_type_node, NULL_TREE,
                stmt, make_node (BLOCK));
   BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn;
 
@@ -1567,7 +1561,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
         Note we need to save and restore the saved tree statement iterator
         to avoid having it clobbered by expand_calls_inline.  */
       tree_stmt_iterator save_tsi;
-     
+
       save_tsi = id->tsi;
       expand_calls_inline (&arg_inits, id);
       id->tsi = save_tsi;
@@ -1580,42 +1574,63 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
      recursing into it.  */
   VARRAY_PUSH_TREE (id->fns, fn);
 
-  /* Record the function we are about to inline if optimize_function
-     has not been called on it yet and we don't have it in the list.  */
-  if (! DECL_INLINED_FNS (fn))
-    {
-      int i;
-
-      for (i = VARRAY_ACTIVE_SIZE (id->inlined_fns) - 1; i >= 0; i--)
-       if (VARRAY_TREE (id->inlined_fns, i) == fn)
-         break;
-      if (i < 0)
-       VARRAY_PUSH_TREE (id->inlined_fns, fn);
-    }
-
   /* Return statements in the function body will be replaced by jumps
      to the RET_LABEL.  */
   id->ret_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
   DECL_ARTIFICIAL (id->ret_label) = 1;
+  DECL_IGNORED_P (id->ret_label) = 1;
   DECL_CONTEXT (id->ret_label) = VARRAY_TREE (id->fns, 0);
   insert_decl_map (id, id->ret_label, id->ret_label);
 
-  if (! DECL_INITIAL (fn)
-      || TREE_CODE (DECL_INITIAL (fn)) != BLOCK)
-    abort ();
+  gcc_assert (DECL_INITIAL (fn));
+  gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
+
+  /* Find the lhs to which the result of this call is assigned.  */
+  modify_dest = tsi_stmt (id->tsi);
+  if (TREE_CODE (modify_dest) == MODIFY_EXPR)
+    {
+      modify_dest = TREE_OPERAND (modify_dest, 0);
+
+      /* The function which we are inlining might not return a value,
+        in which case we should issue a warning that the function
+        does not return a value.  In that case the optimizers will
+        see that the variable to which the value is assigned was not
+        initialized.  We do not want to issue a warning about that
+        uninitialized variable.  */
+      if (DECL_P (modify_dest))
+       TREE_NO_WARNING (modify_dest) = 1;
+    }
+  else
+    modify_dest = NULL;
 
   /* Declare the return variable for the function.  */
-  decl = declare_return_variable (id, return_slot_addr, &use_retvar);
-  if (decl)
-    declare_inline_vars (expr, decl);
+  decl = declare_return_variable (id, return_slot_addr,
+                                 modify_dest, &use_retvar);
 
   /* After we've initialized the parameters, we insert the body of the
      function itself.  */
   {
     struct cgraph_node *old_node = id->current_node;
+    tree copy;
 
     id->current_node = edge->callee;
-    append_to_statement_list (copy_body (id), &BIND_EXPR_BODY (expr));
+    copy = copy_body (id);
+
+    /* If the function uses a return slot, then it may legitimately
+       fall through while still returning a value, so we have to skip
+       the warning here.  */
+    if (warn_return_type
+       && !TREE_NO_WARNING (fn)
+       && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fn)))
+       && return_slot_addr == NULL_TREE
+       && block_may_fallthru (copy))
+      {
+       warning ("control may reach end of non-void function %qD being inlined",
+                fn);
+       TREE_NO_WARNING (fn) = 1;
+      }
+
+    append_to_statement_list (copy, &BIND_EXPR_BODY (expr));
     id->current_node = old_node;
   }
   inlined_body = &BIND_EXPR_BODY (expr);
@@ -1629,72 +1644,45 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
       append_to_statement_list (label, &BIND_EXPR_BODY (expr));
     }
 
-  /* Finally, mention the returned value so that the value of the
-     statement-expression is the returned value of the function.  */
-  if (use_retvar)
-    /* Set TREE_TYPE on BIND_EXPR?  */
-    append_to_statement_list_force (use_retvar, &BIND_EXPR_BODY (expr));
-
   /* Clean up.  */
   splay_tree_delete (id->decl_map);
   id->decl_map = st;
 
-  /* The new expression has side-effects if the old one did.  */
-  TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t);
+  /* Although, from the semantic viewpoint, the new expression has
+     side-effects only if the old one did, it is not possible, from
+     the technical viewpoint, to evaluate the body of a function
+     multiple times without serious havoc.  */
+  TREE_SIDE_EFFECTS (expr) = 1;
 
-  /* If we are working with gimple form, then we need to keep the tree
-     in gimple form.  If we are not in gimple form, we can just replace
-     *tp with the new BIND_EXPR.  */ 
-  if (lang_hooks.gimple_before_inlining)
-    {
-      tree save_decl;
-
-      /* Keep the new trees in gimple form.  */
-      BIND_EXPR_BODY (expr)
-       = rationalize_compound_expr (BIND_EXPR_BODY (expr));
-
-      /* We want to create a new variable to hold the result of the
-        inlined body.  This new variable needs to be added to the
-        function which we are inlining into, thus the saving and
-        restoring of current_function_decl.  */
-      save_decl = current_function_decl;
-      current_function_decl = id->node->decl;
-      inline_result = voidify_wrapper_expr (expr, NULL);
-      current_function_decl = save_decl;
-
-      /* If the inlined function returns a result that we care about,
-        then we're going to need to splice in a MODIFY_EXPR.  Otherwise
-        the call was a standalone statement and we can just replace it
-        with the BIND_EXPR inline representation of the called function.  */
-      if (TREE_CODE (tsi_stmt (id->tsi)) != CALL_EXPR)
-       {
-         tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
-         *tp = inline_result;
-       }
-      else
-       *tp = expr;
+  tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
 
-      /* When we gimplify a function call, we may clear TREE_SIDE_EFFECTS
-        on the call if it is to a "const" function.  Thus the copy of
-        TREE_SIDE_EFFECTS from the CALL_EXPR to the BIND_EXPR above
-        with result in TREE_SIDE_EFFECTS not being set for the inlined
-        copy of a "const" function.
+  /* If the inlined function returns a result that we care about,
+     then we're going to need to splice in a MODIFY_EXPR.  Otherwise
+     the call was a standalone statement and we can just replace it
+     with the BIND_EXPR inline representation of the called function.  */
+  if (!use_retvar || !modify_dest)
+    *tsi_stmt_ptr (id->tsi) = build_empty_stmt ();
+  else
+    *tp = use_retvar;
 
-        Unfortunately, that is wrong as inlining the function
-        can create/expose interesting side effects (such as setting
-        of a return value).
+  /* When we gimplify a function call, we may clear TREE_SIDE_EFFECTS on
+     the call if it is to a "const" function.  Thus the copy of
+     TREE_SIDE_EFFECTS from the CALL_EXPR to the BIND_EXPR above with
+     result in TREE_SIDE_EFFECTS not being set for the inlined copy of a
+     "const" function.
 
-        The easiest solution is to simply recalculate TREE_SIDE_EFFECTS
-        for the toplevel expression.  */
-      recalculate_side_effects (expr);
-    }
-  else
-    *tp = expr;
+     Unfortunately, that is wrong as inlining the function can create/expose
+     interesting side effects (such as setting of a return value).
 
-  /* If the value of the new expression is ignored, that's OK.  We
-     don't warn about this for CALL_EXPRs, so we shouldn't warn about
-     the equivalent inlined version either.  */
-  TREE_USED (*tp) = 1;
+     The easiest solution is to simply recalculate TREE_SIDE_EFFECTS for
+     the toplevel expression.  */
+  recalculate_side_effects (expr);
+  
+  /* Output the inlining info for this abstract function, since it has been
+     inlined.  If we don't do this now, we can lose the information about the
+     variables in the function when the blocks get blown away as soon as we
+     remove the cgraph node.  */
+  (*debug_hooks->outlining_inline_function) (edge->callee->decl);
 
   /* Update callgraph if needed.  */
   cgraph_remove_node (edge->callee);
@@ -1715,10 +1703,10 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
 }
 
 static void
-gimple_expand_calls_inline (tree *stmt_p, inline_data *id)
+expand_calls_inline (tree *stmt_p, inline_data *id)
 {
   tree stmt = *stmt_p;
-  enum tree_code code = TREE_CODE (stmt); 
+  enum tree_code code = TREE_CODE (stmt);
   int dummy;
 
   switch (code)
@@ -1731,7 +1719,7 @@ gimple_expand_calls_inline (tree *stmt_p, inline_data *id)
        for (i = tsi_start (stmt); !tsi_end_p (i); )
          {
            id->tsi = i;
-           gimple_expand_calls_inline (tsi_stmt_ptr (i), id);
+           expand_calls_inline (tsi_stmt_ptr (i), id);
 
            new = tsi_stmt (i);
            if (TREE_CODE (new) == STATEMENT_LIST)
@@ -1746,40 +1734,53 @@ gimple_expand_calls_inline (tree *stmt_p, inline_data *id)
       break;
 
     case COND_EXPR:
-      gimple_expand_calls_inline (&COND_EXPR_THEN (stmt), id);
-      gimple_expand_calls_inline (&COND_EXPR_ELSE (stmt), id);
+      expand_calls_inline (&COND_EXPR_THEN (stmt), id);
+      expand_calls_inline (&COND_EXPR_ELSE (stmt), id);
       break;
+
     case CATCH_EXPR:
-      gimple_expand_calls_inline (&CATCH_BODY (stmt), id);
+      expand_calls_inline (&CATCH_BODY (stmt), id);
       break;
+
     case EH_FILTER_EXPR:
-      gimple_expand_calls_inline (&EH_FILTER_FAILURE (stmt), id);
+      expand_calls_inline (&EH_FILTER_FAILURE (stmt), id);
       break;
+
     case TRY_CATCH_EXPR:
     case TRY_FINALLY_EXPR:
-      gimple_expand_calls_inline (&TREE_OPERAND (stmt, 0), id);
-      gimple_expand_calls_inline (&TREE_OPERAND (stmt, 1), id);
+      expand_calls_inline (&TREE_OPERAND (stmt, 0), id);
+      expand_calls_inline (&TREE_OPERAND (stmt, 1), id);
       break;
+
     case BIND_EXPR:
-      gimple_expand_calls_inline (&BIND_EXPR_BODY (stmt), id);
+      expand_calls_inline (&BIND_EXPR_BODY (stmt), id);
       break;
 
     case COMPOUND_EXPR:
       /* We're gimple.  We should have gotten rid of all these.  */
-      abort ();
+      gcc_unreachable ();
 
     case RETURN_EXPR:
       stmt_p = &TREE_OPERAND (stmt, 0);
       stmt = *stmt_p;
       if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
        break;
+
       /* FALLTHRU */
+
     case MODIFY_EXPR:
       stmt_p = &TREE_OPERAND (stmt, 1);
       stmt = *stmt_p;
+      if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
+       {
+         stmt_p = &TREE_OPERAND (stmt, 0);
+         stmt = *stmt_p;
+       }
       if (TREE_CODE (stmt) != CALL_EXPR)
        break;
+
       /* FALLTHRU */
+
     case CALL_EXPR:
       expand_call_inline (stmt_p, &dummy, id);
       break;
@@ -1789,30 +1790,6 @@ gimple_expand_calls_inline (tree *stmt_p, inline_data *id)
     }
 }
 
-/* Walk over the entire tree *TP, replacing CALL_EXPRs with inline
-   expansions as appropriate.  */
-
-static void
-expand_calls_inline (tree *tp, inline_data *id)
-{
-  /* If we are not in gimple form, then we want to walk the tree
-     recursively as we do not know anything about the structure
-     of the tree.  */
-
-  if (!lang_hooks.gimple_before_inlining)
-    {
-      walk_tree (tp, expand_call_inline, id, id->tree_pruner);
-      return;
-    }
-
-  /* We are in gimple form.  We want to stay in gimple form.  Walk
-     the statements, inlining calls in each statement.  By walking
-     the statements, we have enough information to keep the tree
-     in gimple form as we insert inline bodies.  */
-
-  gimple_expand_calls_inline (tp, id);
-}
-
 /* Expand calls to inline functions in the body of FN.  */
 
 void
@@ -1842,11 +1819,7 @@ optimize_inline_calls (tree fn)
       prev_fn = current_function_decl;
     }
 
-  prev_fn = (lang_hooks.tree_inlining.add_pending_fn_decls
-            (&id.fns, prev_fn));
-
-  /* Create the list of functions this call will inline.  */
-  VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");
+  prev_fn = lang_hooks.tree_inlining.add_pending_fn_decls (&id.fns, prev_fn);
 
   /* Keep track of the low-water mark, i.e., the point where the first
      real inlining is represented in ID.FNS.  */
@@ -1854,21 +1827,11 @@ optimize_inline_calls (tree fn)
 
   /* Replace all calls to inline functions with the bodies of those
      functions.  */
-  id.tree_pruner = htab_create (37, htab_hash_pointer,
-                               htab_eq_pointer, NULL);
+  id.tree_pruner = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
   expand_calls_inline (&DECL_SAVED_TREE (fn), &id);
 
   /* Clean up.  */
   htab_delete (id.tree_pruner);
-  if (DECL_LANG_SPECIFIC (fn))
-    {
-      tree ifn = make_tree_vec (VARRAY_ACTIVE_SIZE (id.inlined_fns));
-
-      if (VARRAY_ACTIVE_SIZE (id.inlined_fns))
-       memcpy (&TREE_VEC_ELT (ifn, 0), &VARRAY_TREE (id.inlined_fns, 0),
-               VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree));
-      DECL_INLINED_FNS (fn) = ifn;
-    }
 
 #ifdef ENABLE_CHECKING
     {
@@ -1878,25 +1841,23 @@ optimize_inline_calls (tree fn)
 
       /* Double check that we inlined everything we are supposed to inline.  */
       for (e = id.node->callees; e; e = e->next_callee)
-       if (!e->inline_failed)
-         abort ();
+       gcc_assert (e->inline_failed);
     }
 #endif
 }
 
-/* FN is a function that has a complete body, and CLONE is a function
-   whose body is to be set to a copy of FN, mapping argument
-   declarations according to the ARG_MAP splay_tree.  */
+/* FN is a function that has a complete body, and CLONE is a function whose
+   body is to be set to a copy of FN, mapping argument declarations according
+   to the ARG_MAP splay_tree.  */
 
 void
 clone_body (tree clone, tree fn, void *arg_map)
 {
   inline_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.  If
-     there's an in-charge parameter, map it to an appropriate
-     constant.  */
+  /* Clone the body, as if we were making an inline call.  But, remap the
+     parameters in the callee to the parameters of caller.  If there's an
+     in-charge parameter, map it to an appropriate constant.  */
   memset (&id, 0, sizeof (id));
   VARRAY_TREE_INIT (id.fns, 2, "fns");
   VARRAY_PUSH_TREE (id.fns, clone);
@@ -1911,10 +1872,11 @@ clone_body (tree clone, tree fn, void *arg_map)
   append_to_statement_list_force (copy_body (&id), &DECL_SAVED_TREE (clone));
 }
 
-/* Save duplicate of 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.  */
+
 tree
-save_body (tree fn, tree *arg_copy)
+save_body (tree fn, tree *arg_copy, tree *sc_copy)
 {
   inline_data id;
   tree body, *parg;
@@ -1926,15 +1888,30 @@ save_body (tree fn, tree *arg_copy)
   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;
+    }
+
   insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn));
 
   /* Actually copy the body.  */
@@ -1945,30 +1922,114 @@ save_body (tree fn, tree *arg_copy)
   return body;
 }
 
-/* Apply FUNC to all the sub-trees of TP in a pre-order traversal.
-   FUNC is called with the DATA and the address of each sub-tree.  If
-   FUNC returns a non-NULL value, the traversal is aborted, and the
-   value returned by FUNC is returned.  If HTAB is non-NULL it is used
-   to record the nodes visited, and to avoid visiting a node more than
-   once.  */
-
-tree
-walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
-{
-  htab_t htab = (htab_t) htab_;
-  enum tree_code code;
-  int walk_subtrees;
-  tree result;
-
 #define WALK_SUBTREE(NODE)                             \
   do                                                   \
     {                                                  \
-      result = walk_tree (&(NODE), func, data, htab);  \
+      result = walk_tree (&(NODE), func, data, pset);  \
       if (result)                                      \
        return result;                                  \
     }                                                  \
   while (0)
 
+/* This is a subroutine of walk_tree that walks field of TYPE that are to
+   be walked whenever a type is seen in the tree.  Rest of operands and return
+   value are as for walk_tree.  */
+
+static tree
+walk_type_fields (tree type, walk_tree_fn func, void *data,
+                 struct pointer_set_t *pset)
+{
+  tree result = NULL_TREE;
+
+  switch (TREE_CODE (type))
+    {
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+      /* We have to worry about mutually recursive pointers.  These can't
+        be written in C.  They can in Ada.  It's pathological, but
+        there's an ACATS test (c38102a) that checks it.  Deal with this
+        by checking if we're pointing to another pointer, that one
+        points to another pointer, that one does too, and we have no htab.
+        If so, get a hash table.  We check three levels deep to avoid
+        the cost of the hash table if we don't need one.  */
+      if (POINTER_TYPE_P (TREE_TYPE (type))
+         && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (type)))
+         && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (TREE_TYPE (type))))
+         && !pset)
+       {
+         result = walk_tree_without_duplicates (&TREE_TYPE (type),
+                                                func, data);
+         if (result)
+           return result;
+
+         break;
+       }
+
+      /* ... fall through ... */
+
+    case COMPLEX_TYPE:
+      WALK_SUBTREE (TREE_TYPE (type));
+      break;
+
+    case METHOD_TYPE:
+      WALK_SUBTREE (TYPE_METHOD_BASETYPE (type));
+
+      /* Fall through.  */
+
+    case FUNCTION_TYPE:
+      WALK_SUBTREE (TREE_TYPE (type));
+      {
+       tree arg;
+
+       /* We never want to walk into default arguments.  */
+       for (arg = TYPE_ARG_TYPES (type); arg; arg = TREE_CHAIN (arg))
+         WALK_SUBTREE (TREE_VALUE (arg));
+      }
+      break;
+
+    case ARRAY_TYPE:
+      /* Don't follow this nodes's type if a pointer for fear that we'll
+        have infinite recursion.  Those types are uninteresting anyway.  */
+      if (!POINTER_TYPE_P (TREE_TYPE (type))
+         && TREE_CODE (TREE_TYPE (type)) != OFFSET_TYPE)
+       WALK_SUBTREE (TREE_TYPE (type));
+      WALK_SUBTREE (TYPE_DOMAIN (type));
+      break;
+
+    case BOOLEAN_TYPE:
+    case ENUMERAL_TYPE:
+    case INTEGER_TYPE:
+    case CHAR_TYPE:
+    case REAL_TYPE:
+      WALK_SUBTREE (TYPE_MIN_VALUE (type));
+      WALK_SUBTREE (TYPE_MAX_VALUE (type));
+      break;
+
+    case OFFSET_TYPE:
+      WALK_SUBTREE (TREE_TYPE (type));
+      WALK_SUBTREE (TYPE_OFFSET_BASETYPE (type));
+      break;
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+/* Apply FUNC to all the sub-trees of TP in a pre-order traversal.  FUNC is
+   called with the DATA and the address of each sub-tree.  If FUNC returns a
+   non-NULL value, the traversal is aborted, and the value returned by FUNC
+   is returned.  If PSET is non-NULL it is used to record the nodes visited,
+   and to avoid visiting a node more than once.  */
+
+tree
+walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset)
+{
+  enum tree_code code;
+  int walk_subtrees;
+  tree result;
+
 #define WALK_SUBTREE_TAIL(NODE)                                \
   do                                                   \
     {                                                  \
@@ -1982,17 +2043,10 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
   if (!*tp)
     return NULL_TREE;
 
-  if (htab)
-    {
-      void **slot;
-
-      /* Don't walk the same tree twice, if the user has requested
-         that we avoid doing so.  */
-      slot = htab_find_slot (htab, *tp, INSERT);
-      if (*slot)
-       return NULL_TREE;
-      *slot = *tp;
-    }
+  /* Don't walk the same tree twice, if the user has requested
+     that we avoid doing so.  */
+  if (pset && pointer_set_insert (pset, *tp))
+    return NULL_TREE;
 
   /* Call the function.  */
   walk_subtrees = 1;
@@ -2016,24 +2070,76 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
     }
 
   result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func,
-                                                  data, htab);
+                                                  data, pset);
   if (result || ! walk_subtrees)
     return result;
 
-  if (code != EXIT_BLOCK_EXPR
-      && code != SAVE_EXPR
-      && code != BIND_EXPR
-      && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
+  /* If this is a DECL_EXPR, walk into various fields of the type that it's
+     defining.  We only want to walk into these fields of a type in this
+     case.  Note that decls get walked as part of the processing of a
+     BIND_EXPR.
+
+     ??? Precisely which fields of types that we are supposed to walk in
+     this case vs. the normal case aren't well defined.  */
+  if (code == DECL_EXPR
+      && TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL
+      && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK)
+    {
+      tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp));
+
+      /* Call the function for the type.  See if it returns anything or
+        doesn't want us to continue.  If we are to continue, walk both
+        the normal fields and those for the declaration case.  */
+      result = (*func) (type_p, &walk_subtrees, data);
+      if (result || !walk_subtrees)
+       return NULL_TREE;
+
+      result = walk_type_fields (*type_p, func, data, pset);
+      if (result)
+       return result;
+
+      WALK_SUBTREE (TYPE_SIZE (*type_p));
+      WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p));
+
+      /* If this is a record type, also walk the fields.  */
+      if (TREE_CODE (*type_p) == RECORD_TYPE
+         || TREE_CODE (*type_p) == UNION_TYPE
+         || TREE_CODE (*type_p) == QUAL_UNION_TYPE)
+       {
+         tree field;
+
+         for (field = TYPE_FIELDS (*type_p); field;
+              field = TREE_CHAIN (field))
+           {
+             /* We'd like to look at the type of the field, but we can easily
+                get infinite recursion.  So assume it's pointed to elsewhere
+                in the tree.  Also, ignore things that aren't fields.  */
+             if (TREE_CODE (field) != FIELD_DECL)
+               continue;
+
+             WALK_SUBTREE (DECL_FIELD_OFFSET (field));
+             WALK_SUBTREE (DECL_SIZE (field));
+             WALK_SUBTREE (DECL_SIZE_UNIT (field));
+             if (TREE_CODE (*type_p) == QUAL_UNION_TYPE)
+               WALK_SUBTREE (DECL_QUALIFIER (field));
+           }
+       }
+    }
+
+  else if (code != SAVE_EXPR
+          && code != BIND_EXPR
+          && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
     {
       int i, len;
 
       /* Walk over all the sub-trees of this operand.  */
-      len = first_rtl_op (code);
+      len = TREE_CODE_LENGTH (code);
       /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same.
         But, we only want to walk once.  */
       if (code == TARGET_EXPR
          && TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1))
        --len;
+
       /* Go through the subtrees.  We need to do this in forward order so
          that the scope of a FOR_EXPR is handled properly.  */
 #ifdef DEBUG_WALK_TREE
@@ -2054,19 +2160,16 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
        }
 #endif
     }
-  else if (TREE_CODE_CLASS (code) == 'd')
+
+  /* If this is a type, walk the needed fields in the type.  */
+  else if (TYPE_P (*tp))
     {
-      WALK_SUBTREE_TAIL (TREE_TYPE (*tp));
+      result = walk_type_fields (*tp, func, data, pset);
+      if (result)
+       return result;
     }
   else
     {
-      if (TREE_CODE_CLASS (code) == 't')
-       {
-         WALK_SUBTREE (TYPE_SIZE (*tp));
-         WALK_SUBTREE (TYPE_SIZE_UNIT (*tp));
-         /* Also examine various special fields, below.  */
-       }
-
       /* Not one of the easy cases.  We must explicitly go through the
         children.  */
       switch (code)
@@ -2077,26 +2180,15 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
        case REAL_CST:
        case VECTOR_CST:
        case STRING_CST:
-       case REAL_TYPE:
-       case COMPLEX_TYPE:
-       case VECTOR_TYPE:
-       case VOID_TYPE:
-       case BOOLEAN_TYPE:
-       case UNION_TYPE:
-       case ENUMERAL_TYPE:
        case BLOCK:
-       case RECORD_TYPE:
        case PLACEHOLDER_EXPR:
        case SSA_NAME:
+       case FIELD_DECL:
+       case RESULT_DECL:
          /* None of thse have subtrees other than those already walked
             above.  */
          break;
 
-       case POINTER_TYPE:
-       case REFERENCE_TYPE:
-         WALK_SUBTREE_TAIL (TREE_TYPE (*tp));
-         break;
-
        case TREE_LIST:
          WALK_SUBTREE (TREE_VALUE (*tp));
          WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
@@ -2124,37 +2216,6 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
        case CONSTRUCTOR:
          WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp));
 
-       case METHOD_TYPE:
-         WALK_SUBTREE (TYPE_METHOD_BASETYPE (*tp));
-         /* Fall through.  */
-
-       case FUNCTION_TYPE:
-         WALK_SUBTREE (TREE_TYPE (*tp));
-         {
-           tree arg = TYPE_ARG_TYPES (*tp);
-
-           /* We never want to walk into default arguments.  */
-           for (; arg; arg = TREE_CHAIN (arg))
-             WALK_SUBTREE (TREE_VALUE (arg));
-         }
-         break;
-
-       case ARRAY_TYPE:
-         WALK_SUBTREE (TREE_TYPE (*tp));
-         WALK_SUBTREE_TAIL (TYPE_DOMAIN (*tp));
-
-       case INTEGER_TYPE:
-       case CHAR_TYPE:
-         WALK_SUBTREE (TYPE_MIN_VALUE (*tp));
-         WALK_SUBTREE_TAIL (TYPE_MAX_VALUE (*tp));
-
-       case OFFSET_TYPE:
-         WALK_SUBTREE (TREE_TYPE (*tp));
-         WALK_SUBTREE_TAIL (TYPE_OFFSET_BASETYPE (*tp));
-
-       case EXIT_BLOCK_EXPR:
-         WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 1));
-
        case SAVE_EXPR:
          WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0));
 
@@ -2166,12 +2227,11 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
                /* Walk the DECL_INITIAL and DECL_SIZE.  We don't want to walk
                   into declarations that are just mentioned, rather than
                   declared; they don't really belong to this part of the tree.
-                  And, we can see cycles: the initializer for a declaration can
-                  refer to the declaration itself.  */
+                  And, we can see cycles: the initializer for a declaration
+                  can refer to the declaration itself.  */
                WALK_SUBTREE (DECL_INITIAL (decl));
                WALK_SUBTREE (DECL_SIZE (decl));
                WALK_SUBTREE (DECL_SIZE_UNIT (decl));
-               WALK_SUBTREE (TREE_TYPE (decl));
              }
            WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp));
          }
@@ -2185,7 +2245,9 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
          break;
 
        default:
-         abort ();
+         /* ??? This could be a language-defined node.  We really should make
+            a hook for it, but right now just ignore it.  */
+         break;
        }
     }
 
@@ -2196,18 +2258,17 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
 #undef WALK_SUBTREE_TAIL
 }
 
-/* Like walk_tree, but does not walk duplicate nodes more than
-   once.  */
+/* Like walk_tree, but does not walk duplicate nodes more than once.  */
 
 tree
 walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data)
 {
   tree result;
-  htab_t htab;
+  struct pointer_set_t *pset;
 
-  htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
-  result = walk_tree (tp, func, data, htab);
-  htab_delete (htab);
+  pset = pointer_set_create ();
+  result = walk_tree (tp, func, data, pset);
+  pointer_set_destroy (pset);
   return result;
 }
 
@@ -2220,7 +2281,6 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 
   /* We make copies of most nodes.  */
   if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
-      || TREE_CODE_CLASS (code) == 'c'
       || code == TREE_LIST
       || code == TREE_VEC
       || code == TYPE_DECL)
@@ -2249,23 +2309,24 @@ 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 (TREE_CODE_CLASS (code) == 't')
+
+  else if (TREE_CODE_CLASS (code) == tcc_type)
     *walk_subtrees = 0;
-  else if (TREE_CODE_CLASS (code) == 'd')
+  else if (TREE_CODE_CLASS (code) == tcc_declaration)
     *walk_subtrees = 0;
-  else if (code == STATEMENT_LIST)
-    abort ();
-
+  else if (TREE_CODE_CLASS (code) == tcc_constant)
+    *walk_subtrees = 0;
+  else
+    gcc_assert (code != STATEMENT_LIST);
   return NULL_TREE;
 }
 
 /* The SAVE_EXPR pointed to by TP is being copied.  If ST contains
-   information indicating to what new SAVE_EXPR this one should be
-   mapped, use that one.  Otherwise, create a new node and enter it in
-   ST.  FN is the function into which the copy will be placed.  */
+   information indicating to what new SAVE_EXPR this one should be mapped,
+   use that one.  Otherwise, create a new node and enter it in ST.  */
 
-void
-remap_save_expr (tree *tp, void *st_, tree fn, int *walk_subtrees)
+static void
+remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
 {
   splay_tree st = (splay_tree) st_;
   splay_tree_node n;
@@ -2279,11 +2340,6 @@ remap_save_expr (tree *tp, void *st_, tree fn, int *walk_subtrees)
     {
       t = copy_node (*tp);
 
-      /* The SAVE_EXPR is now part of the function into which we
-        are inlining this body.  */
-      SAVE_EXPR_CONTEXT (t) = fn;
-      /* And we haven't evaluated it yet.  */
-      SAVE_EXPR_RTL (t) = NULL_RTX;
       /* Remember this SAVE_EXPR.  */
       splay_tree_insert (st, (splay_tree_key) *tp, (splay_tree_value) t);
       /* Make sure we don't remap an already-remapped SAVE_EXPR.  */
@@ -2300,45 +2356,56 @@ remap_save_expr (tree *tp, void *st_, tree fn, int *walk_subtrees)
   *tp = t;
 }
 
-/* Called via walk_tree.  If *TP points to a DECL_STMT for a local
-   declaration, copies the declaration and enters it in the splay_tree
-   in DATA (which is really an `inline_data *').  */
+/* 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 *').  */
 
 static tree
 mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
                        void *data)
 {
-  tree t = *tp;
   inline_data *id = (inline_data *) data;
-  tree decl;
 
   /* Don't walk into types.  */
-  if (TYPE_P (t))
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+
+  else if (TREE_CODE (*tp) == LABEL_EXPR)
     {
-      *walk_subtrees = 0;
-      return NULL_TREE;
+      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)));
     }
 
-  if (TREE_CODE (t) == LABEL_EXPR)
-    decl = TREE_OPERAND (t, 0);
-  else
-    /* We don't need to handle anything else ahead of time.  */
-    decl = NULL_TREE;
+  return NULL_TREE;
+}
+
+/* Perform any modifications to EXPR required when it is unsaved.  Does
+   not recurse into EXPR's subtrees.  */
 
-  if (decl)
+static void
+unsave_expr_1 (tree expr)
+{
+  switch (TREE_CODE (expr))
     {
-      tree copy;
+    case TARGET_EXPR:
+      /* Don't mess with a TARGET_EXPR that hasn't been expanded.
+         It's OK for this to happen if it was part of a subtree that
+         isn't immediately expanded, such as operand 2 of another
+         TARGET_EXPR.  */
+      if (TREE_OPERAND (expr, 1))
+       break;
 
-      /* Make a copy.  */
-      copy = copy_decl_for_inlining (decl, 
-                                    DECL_CONTEXT (decl), 
-                                    DECL_CONTEXT (decl));
+      TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 3);
+      TREE_OPERAND (expr, 3) = NULL_TREE;
+      break;
 
-      /* Remember the copy.  */
-      insert_decl_map (id, decl, copy);
+    default:
+      break;
     }
-
-  return NULL_TREE;
 }
 
 /* Called via walk_tree when an expression is unsaved.  Using the
@@ -2358,17 +2425,18 @@ unsave_r (tree *tp, int *walk_subtrees, void *data)
     {
       /* Lookup the declaration.  */
       n = splay_tree_lookup (st, (splay_tree_key) *tp);
-      
+
       /* If it's there, remap it.  */
       if (n)
        *tp = (tree) n->value;
     }
+
   else if (TREE_CODE (*tp) == STATEMENT_LIST)
     copy_statement_list (tp);
   else if (TREE_CODE (*tp) == BIND_EXPR)
     copy_bind_expr (tp, walk_subtrees, id);
   else if (TREE_CODE (*tp) == SAVE_EXPR)
-    remap_save_expr (tp, st, current_function_decl, walk_subtrees);
+    remap_save_expr (tp, st, walk_subtrees);
   else
     {
       copy_tree_r (tp, walk_subtrees, NULL);
@@ -2381,11 +2449,11 @@ unsave_r (tree *tp, int *walk_subtrees, void *data)
   return NULL_TREE;
 }
 
-/* Default lang hook for "unsave_expr_now".  Copies everything in EXPR and
-   replaces variables, labels and SAVE_EXPRs local to EXPR.  */
+/* Copies everything in EXPR and replaces variables, labels
+   and SAVE_EXPRs local to EXPR.  */
 
 tree
-lhd_unsave_expr_now (tree expr)
+unsave_expr_now (tree expr)
 {
   inline_data id;
 
@@ -2412,6 +2480,7 @@ lhd_unsave_expr_now (tree expr)
 }
 
 /* Allow someone to determine if SEARCH is a child of TOP from gdb.  */
+
 static tree
 debug_find_tree_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
 {
@@ -2421,27 +2490,21 @@ debug_find_tree_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
     return NULL;
 }
 
-extern bool debug_find_tree (tree top, tree search);
-
 bool
 debug_find_tree (tree top, tree search)
 {
   return walk_tree_without_duplicates (&top, debug_find_tree_1, search) != 0;
 }
 
-
 /* Declare the variables created by the inliner.  Add all the variables in
    VARS to BIND_EXPR.  */
 
 static void
 declare_inline_vars (tree bind_expr, tree vars)
 {
-  if (lang_hooks.gimple_before_inlining)
-    {
-      tree t;
-      for (t = vars; t; t = TREE_CHAIN (t))
-       vars->decl.seen_in_bind_expr = 1;
-    }
+  tree t;
+  for (t = vars; t; t = TREE_CHAIN (t))
+    DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
 
   add_var_to_bind_expr (bind_expr, vars);
 }