OSDN Git Service

* cse.c (cse_reg_info_free_list, cse_reg_info_used_list,
[pf3gnuchains/gcc-fork.git] / gcc / tree-inline.c
index b34844d..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
@@ -135,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.  */
@@ -182,7 +185,7 @@ 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 fields, do likewise for offset and qualifier.  */
       if (TREE_CODE (t) == FIELD_DECL)
        {
          walk_tree (&DECL_FIELD_OFFSET (t), copy_body_r, id, NULL);
@@ -205,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);
@@ -321,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);
@@ -353,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;
        }
@@ -460,8 +458,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
      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
@@ -503,47 +500,41 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
 
       /* 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, 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
@@ -568,7 +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;
+                 *tp = build_empty_stmt ();
                  return copy_body_r (tp, walk_subtrees, data);
                }
            }
@@ -607,10 +598,8 @@ 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
@@ -632,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.  */
@@ -657,6 +656,22 @@ 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)
@@ -681,7 +696,13 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
         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)))
+         && 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))
        {
          insert_decl_map (id, p, value);
          return;
@@ -690,7 +711,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. */
+     function.  */
   var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0));
 
   /* Register the VAR_DECL as the equivalent for the PARM_DECL;
@@ -789,18 +810,19 @@ 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)
-    gimplify_body (&init_stmts, current_function_decl);
+    gimplify_body (&init_stmts, current_function_decl, false);
 
   declare_inline_vars (bind_expr, vars);
   return init_stmts;
@@ -840,8 +862,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
     {
       /* The front end shouldn't have used both return_slot_addr and
         a modify expression.  */
-      if (modify_dest)
-       abort ();
+      gcc_assert (!modify_dest);
       if (DECL_BY_REFERENCE (result))
        var = return_slot_addr;
       else
@@ -851,8 +872,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
     }
 
   /* All types requiring non-trivial constructors should have been handled.  */
-  if (TREE_ADDRESSABLE (callee_type))
-    abort ();
+  gcc_assert (!TREE_ADDRESSABLE (callee_type));
 
   /* Attempt to avoid creating a new temporary variable.  */
   if (modify_dest)
@@ -885,8 +905,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
        }
     }
 
-  if (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) != INTEGER_CST)
-    abort ();
+  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;
@@ -895,7 +914,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
                 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
@@ -947,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;
        }
@@ -959,7 +978,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
       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;
        }
 
@@ -973,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;
 
@@ -984,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;
 
@@ -1000,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);
 
@@ -1024,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;
        }
@@ -1038,7 +1043,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
-           = N_("%Jfunction '%F' can never be inlined "
+           = N_("%Jfunction %qF can never be inlined "
                 "because it receives a non-local goto");
          return node;
        }
@@ -1055,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), 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;
          }
@@ -1166,7 +1174,7 @@ 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;
@@ -1174,8 +1182,7 @@ 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.  */
-  else 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))
@@ -1187,6 +1194,8 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     case COMPONENT_REF:
     case BIT_FIELD_REF:
     case INDIRECT_REF:
+    case ALIGN_INDIRECT_REF:
+    case MISALIGNED_INDIRECT_REF:
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
     case OBJ_TYPE_REF:
@@ -1194,14 +1203,13 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
     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 ADDR_EXPR:
     case COMPLEX_EXPR:
-    case EXIT_BLOCK_EXPR:
+    case RANGE_EXPR:
     case CASE_LABEL_EXPR:
     case SSA_NAME:
     case CATCH_EXPR:
@@ -1320,6 +1328,8 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
 
     case ASM_EXPR:
 
+    case REALIGN_LOAD_EXPR:
+
     case RESX_EXPR:
       *count += 1;
       break;
@@ -1342,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:
@@ -1358,7 +1368,7 @@ 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;
 }
@@ -1410,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;
@@ -1480,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;
@@ -1493,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;
@@ -1564,34 +1574,32 @@ 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);
+    {
+      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;
 
@@ -1603,9 +1611,26 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
      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);
@@ -1623,8 +1648,11 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
   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;
 
   tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
 
@@ -1649,6 +1677,12 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
      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);
@@ -1724,7 +1758,7 @@ expand_calls_inline (tree *stmt_p, inline_data *id)
 
     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);
@@ -1763,7 +1797,6 @@ optimize_inline_calls (tree fn)
 {
   inline_data id;
   tree prev_fn;
-  tree ifn;
 
   /* There is no point in performing inlining if errors have already
      occurred -- and we might crash if we try to inline invalid
@@ -1788,9 +1821,6 @@ optimize_inline_calls (tree fn)
 
   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");
-
   /* Keep track of the low-water mark, i.e., the point where the first
      real inlining is represented in ID.FNS.  */
   id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns);
@@ -1802,11 +1832,6 @@ optimize_inline_calls (tree fn)
 
   /* Clean up.  */
   htab_delete (id.tree_pruner);
-  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
     {
@@ -1816,8 +1841,7 @@ 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
 }
@@ -1901,7 +1925,7 @@ save_body (tree fn, tree *arg_copy, tree *sc_copy)
 #define WALK_SUBTREE(NODE)                             \
   do                                                   \
     {                                                  \
-      result = walk_tree (&(NODE), func, data, htab);  \
+      result = walk_tree (&(NODE), func, data, pset);  \
       if (result)                                      \
        return result;                                  \
     }                                                  \
@@ -1912,7 +1936,8 @@ save_body (tree fn, tree *arg_copy, tree *sc_copy)
    value are as for walk_tree.  */
 
 static tree
-walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
+walk_type_fields (tree type, walk_tree_fn func, void *data,
+                 struct pointer_set_t *pset)
 {
   tree result = NULL_TREE;
 
@@ -1921,7 +1946,7 @@ walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
     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 pathlogical, but
+        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.
@@ -1930,7 +1955,7 @@ walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
       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))))
-         && !htab)
+         && !pset)
        {
          result = walk_tree_without_duplicates (&TREE_TYPE (type),
                                                 func, data);
@@ -1964,7 +1989,7 @@ walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
 
     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. */
+        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));
@@ -1995,13 +2020,12 @@ walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
 /* 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,
+   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, void *htab_)
+walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset)
 {
-  htab_t htab = (htab_t) htab_;
   enum tree_code code;
   int walk_subtrees;
   tree result;
@@ -2019,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;
@@ -2053,7 +2070,7 @@ 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;
 
@@ -2077,7 +2094,7 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
       if (result || !walk_subtrees)
        return NULL_TREE;
 
-      result = walk_type_fields (*type_p, func, data, htab_);
+      result = walk_type_fields (*type_p, func, data, pset);
       if (result)
        return result;
 
@@ -2109,15 +2126,14 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
        }
     }
 
-  else if (code != EXIT_BLOCK_EXPR
-          && code != SAVE_EXPR
+  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
@@ -2148,7 +2164,7 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
   /* If this is a type, walk the needed fields in the type.  */
   else if (TYPE_P (*tp))
     {
-      result = walk_type_fields (*tp, func, data, htab_);
+      result = walk_type_fields (*tp, func, data, pset);
       if (result)
        return result;
     }
@@ -2200,9 +2216,6 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
        case CONSTRUCTOR:
          WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp));
 
-       case EXIT_BLOCK_EXPR:
-         WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 1));
-
        case SAVE_EXPR:
          WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0));
 
@@ -2251,11 +2264,11 @@ 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;
 }
 
@@ -2268,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)
@@ -2298,13 +2310,14 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
        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;
 }
 
@@ -2312,7 +2325,7 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
    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
+static void
 remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
 {
   splay_tree st = (splay_tree) st_;
@@ -2370,6 +2383,31 @@ mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
   return NULL_TREE;
 }
 
+/* Perform any modifications to EXPR required when it is unsaved.  Does
+   not recurse into EXPR's subtrees.  */
+
+static void
+unsave_expr_1 (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    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;
+
+      TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 3);
+      TREE_OPERAND (expr, 3) = NULL_TREE;
+      break;
+
+    default:
+      break;
+    }
+}
+
 /* Called via walk_tree when an expression is unsaved.  Using the
    splay_tree pointed to by ST (which is really a `splay_tree'),
    remaps all local declarations to appropriate replacements.  */
@@ -2411,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;