OSDN Git Service

* cp-tree.h (build_lang_field_decl): Remove.
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index cf6c3df..b646be8 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1989, 92-97, 1998, 1999 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -19,18 +19,20 @@ the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
-#include <stdio.h>
+#include "system.h"
 #include "rtl.h"
 #include "tree.h"
 #include "flags.h"
 #include "expr.h"
+#include "function.h"
 #include "regs.h"
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
 #include "insn-flags.h"
+#include "toplev.h"
+#include "output.h"
+
+#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
+#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
+#endif
 
 /* Decide whether a function's arguments should be processed
    from first to last or from last to first.
@@ -46,8 +48,8 @@ Boston, MA 02111-1307, USA.  */
 
 #endif
 
-/* Like STACK_BOUNDARY but in units of bytes, not bits.  */
-#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
+#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
 
 /* Data structure and subroutines used within expand_call.  */
 
@@ -123,12 +125,44 @@ static int highest_outgoing_arg_in_use;
 int stack_arg_under_construction;
 #endif
 
-static int calls_function      PROTO((tree, int));
-static int calls_function_1    PROTO((tree, int));
-static void emit_call_1                PROTO((rtx, tree, tree, int, int, rtx, rtx,
-                                      int, rtx, int));
+static int calls_function      PROTO ((tree, int));
+static int calls_function_1    PROTO ((tree, int));
+static void emit_call_1                PROTO ((rtx, tree, tree, HOST_WIDE_INT,
+                                       HOST_WIDE_INT, HOST_WIDE_INT, rtx,
+                                       rtx, int, rtx, int));
+static void special_function_p PROTO ((char *, tree, int *, int *,
+                                       int *, int *));
+static void precompute_register_parameters     PROTO ((int, struct arg_data *,
+                                                       int *));
 static void store_one_arg      PROTO ((struct arg_data *, rtx, int, int,
-                                       tree, int));
+                                       int));
+static void store_unaligned_arguments_into_pseudos PROTO ((struct arg_data *,
+                                                          int));
+static int finalize_must_preallocate           PROTO ((int, int,
+                                                       struct arg_data *,
+                                                       struct args_size *));
+static void precompute_arguments               PROTO ((int, int, int,
+                                                       struct arg_data *,
+                                                       struct args_size *));
+static int compute_argument_block_size         PROTO ((int, 
+                                                       struct args_size *));
+static void initialize_argument_information    PROTO ((int,
+                                                       struct arg_data *,
+                                                       struct args_size *,
+                                                       int, tree, tree,
+                                                       CUMULATIVE_ARGS *,
+                                                       int, rtx *, int *,
+                                                       int *, int *));
+static void compute_argument_addresses         PROTO ((struct arg_data *,
+                                                       rtx, int));
+static rtx rtx_for_function_call               PROTO ((tree, tree));
+static void load_register_parameters           PROTO ((struct arg_data *,
+                                                       int, rtx *));
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+static rtx save_fixed_argument_area    PROTO ((int, rtx, int *, int *));
+static void restore_fixed_argument_area        PROTO ((rtx, rtx, int, int));
+#endif
 \f
 /* If WHICH is 1, return 1 if EXP contains a call to the built-in function
    `alloca'.
@@ -185,8 +219,7 @@ calls_function_1 (exp, which)
          if ((DECL_BUILT_IN (fndecl)
               && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA)
              || (DECL_SAVED_INSNS (fndecl)
-                 && (FUNCTION_FLAGS (DECL_SAVED_INSNS (fndecl))
-                     & FUNCTION_FLAGS_CALLS_ALLOCA)))
+                 && DECL_SAVED_INSNS (fndecl)->calls_alloca))
            return 1;
        }
 
@@ -313,7 +346,7 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
    says that the pointer to this aggregate is to be popped by the callee.
 
    STACK_SIZE is the number of bytes of arguments on the stack,
-   rounded up to STACK_BOUNDARY; zero if the size is variable.
+   rounded up to PREFERRED_STACK_BOUNDARY; zero if the size is variable.
    This is both to put into the call insn and
    to generate explicit popping code if necessary.
 
@@ -341,24 +374,28 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
    IS_CONST is true if this is a `const' call.  */
 
 static void
-emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size, 
-             next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
-            is_const)
+emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
+            struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop,
+            call_fusage, is_const)
      rtx funexp;
-     tree fndecl;
-     tree funtype;
-     int stack_size;
-     int struct_value_size;
+     tree fndecl ATTRIBUTE_UNUSED;
+     tree funtype ATTRIBUTE_UNUSED;
+     HOST_WIDE_INT stack_size;
+     HOST_WIDE_INT rounded_stack_size;
+     HOST_WIDE_INT struct_value_size;
      rtx next_arg_reg;
      rtx valreg;
      int old_inhibit_defer_pop;
      rtx call_fusage;
      int is_const;
 {
-  rtx stack_size_rtx = GEN_INT (stack_size);
+  rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
   rtx struct_value_size_rtx = GEN_INT (struct_value_size);
   rtx call_insn;
+#ifndef ACCUMULATE_OUTGOING_ARGS
   int already_popped = 0;
+  HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
+#endif
 
   /* Ensure address is valid.  SYMBOL_REF is already valid, so no need,
      and we don't want to load it into a register as an optimization,
@@ -368,11 +405,9 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size,
 
 #ifndef ACCUMULATE_OUTGOING_ARGS
 #if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
-  if (HAVE_call_pop && HAVE_call_value_pop
-      && (RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0 
-          || stack_size == 0))
+  if (HAVE_call_pop && HAVE_call_value_pop && n_popped > 0)
     {
-      rtx n_pop = GEN_INT (RETURN_POPS_ARGS (fndecl, funtype, stack_size));
+      rtx n_pop = GEN_INT (n_popped);
       rtx pat;
 
       /* If this subroutine pops its own args, record that in the call insn
@@ -381,10 +416,10 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size,
       if (valreg)
        pat = gen_call_value_pop (valreg,
                                  gen_rtx_MEM (FUNCTION_MODE, funexp),
-                                 stack_size_rtx, next_arg_reg, n_pop);
+                                 rounded_stack_size_rtx, next_arg_reg, n_pop);
       else
        pat = gen_call_pop (gen_rtx_MEM (FUNCTION_MODE, funexp),
-                           stack_size_rtx, next_arg_reg, n_pop);
+                           rounded_stack_size_rtx, next_arg_reg, n_pop);
 
       emit_call_insn (pat);
       already_popped = 1;
@@ -399,11 +434,11 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size,
       if (valreg)
        emit_call_insn (gen_call_value (valreg,
                                        gen_rtx_MEM (FUNCTION_MODE, funexp),
-                                       stack_size_rtx, next_arg_reg,
+                                       rounded_stack_size_rtx, next_arg_reg,
                                        NULL_RTX));
       else
        emit_call_insn (gen_call (gen_rtx_MEM (FUNCTION_MODE, funexp),
-                                 stack_size_rtx, next_arg_reg,
+                                 rounded_stack_size_rtx, next_arg_reg,
                                  struct_value_size_rtx));
     }
   else
@@ -450,574 +485,438 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size,
      If returning from the subroutine does pop the args, indicate that the
      stack pointer will be changed.  */
 
-  if (stack_size != 0 && RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0)
+  if (n_popped > 0)
     {
       if (!already_popped)
        CALL_INSN_FUNCTION_USAGE (call_insn)
          = gen_rtx_EXPR_LIST (VOIDmode,
                               gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx),
                               CALL_INSN_FUNCTION_USAGE (call_insn));
-      stack_size -= RETURN_POPS_ARGS (fndecl, funtype, stack_size);
-      stack_size_rtx = GEN_INT (stack_size);
+      rounded_stack_size -= n_popped;
+      rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
     }
 
-  if (stack_size != 0)
+  if (rounded_stack_size != 0)
     {
       if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const)
-       pending_stack_adjust += stack_size;
+       pending_stack_adjust += rounded_stack_size;
       else
-       adjust_stack (stack_size_rtx);
+       adjust_stack (rounded_stack_size_rtx);
     }
 #endif
 }
 
-/* Generate all the code for a function call
-   and return an rtx for its value.
-   Store the value in TARGET (specified as an rtx) if convenient.
-   If the value is stored in TARGET then TARGET is returned.
-   If IGNORE is nonzero, then we ignore the value of the function call.  */
+/* Determine if the function identified by NAME and FNDECL is one with
+   special properties we wish to know about.
 
-rtx
-expand_call (exp, target, ignore)
-     tree exp;
-     rtx target;
-     int ignore;
-{
-  /* List of actual parameters.  */
-  tree actparms = TREE_OPERAND (exp, 1);
-  /* RTX for the function to be called.  */
-  rtx funexp;
-  /* Tree node for the function to be called (not the address!).  */
-  tree funtree;
-  /* Data type of the function.  */
-  tree funtype;
-  /* Declaration of the function being called,
-     or 0 if the function is computed (not known by name).  */
-  tree fndecl = 0;
-  char *name = 0;
+   For example, if the function might return more than one time (setjmp), then
+   set RETURNS_TWICE to a nonzero value.
 
-  /* Register in which non-BLKmode value will be returned,
-     or 0 if no value or if value is BLKmode.  */
-  rtx valreg;
-  /* Address where we should return a BLKmode value;
-     0 if value not BLKmode.  */
-  rtx structure_value_addr = 0;
-  /* Nonzero if that address is being passed by treating it as
-     an extra, implicit first parameter.  Otherwise,
-     it is passed by being copied directly into struct_value_rtx.  */
-  int structure_value_addr_parm = 0;
-  /* Size of aggregate value wanted, or zero if none wanted
-     or if we are using the non-reentrant PCC calling convention
-     or expecting the value in registers.  */
-  int struct_value_size = 0;
-  /* Nonzero if called function returns an aggregate in memory PCC style,
-     by returning the address of where to find it.  */
-  int pcc_struct_value = 0;
+   Similarly set IS_LONGJMP for if the function is in the longjmp family.
 
-  /* Number of actual parameters in this call, including struct value addr.  */
-  int num_actuals;
-  /* Number of named args.  Args after this are anonymous ones
-     and they must all go on the stack.  */
-  int n_named_args;
-  /* Count arg position in order args appear.  */
-  int argpos;
+   Set IS_MALLOC for any of the standard memory allocation functions which
+   allocate from the heap.
 
-  /* Vector of information about each argument.
-     Arguments are numbered in the order they will be pushed,
-     not the order they are written.  */
-  struct arg_data *args;
+   Set MAY_BE_ALLOCA for any memory allocation function that might allocate
+   space from the stack such as alloca.  */
 
-  /* Total size in bytes of all the stack-parms scanned so far.  */
-  struct args_size args_size;
-  /* Size of arguments before any adjustments (such as rounding).  */
-  struct args_size original_args_size;
-  /* Data on reg parms scanned so far.  */
-  CUMULATIVE_ARGS args_so_far;
-  /* Nonzero if a reg parm has been scanned.  */
-  int reg_parm_seen;
-  /* Nonzero if this is an indirect function call.  */
+static void
+special_function_p (name, fndecl, returns_twice, is_longjmp,
+                   is_malloc, may_be_alloca)
+     char *name;
+     tree fndecl;
+     int *returns_twice;
+     int *is_longjmp;
+     int *is_malloc;
+     int *may_be_alloca;
+{
+  *returns_twice = 0;
+  *is_longjmp = 0;
+  *is_malloc = 0;
+  *may_be_alloca = 0;
+
+  if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
+      /* Exclude functions not at the file scope, or not `extern',
+        since they are not the magic functions we would otherwise
+        think they are.  */
+      && DECL_CONTEXT (fndecl) == NULL_TREE && TREE_PUBLIC (fndecl))
+    {
+      char *tname = name;
 
-  /* Nonzero if we must avoid push-insns in the args for this call. 
-     If stack space is allocated for register parameters, but not by the
-     caller, then it is preallocated in the fixed part of the stack frame.
-     So the entire argument block must then be preallocated (i.e., we
-     ignore PUSH_ROUNDING in that case).  */
+      /* We assume that alloca will always be called by name.  It
+        makes no sense to pass it as a pointer-to-function to
+        anything that does not understand its behavior.  */
+      *may_be_alloca
+       = (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
+            && name[0] == 'a'
+            && ! strcmp (name, "alloca"))
+           || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
+               && name[0] == '_'
+               && ! strcmp (name, "__builtin_alloca"))));
 
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
-  int must_preallocate = 1;
-#else
-#ifdef PUSH_ROUNDING
-  int must_preallocate = 0;
-#else
-  int must_preallocate = 1;
-#endif
-#endif
+      /* Disregard prefix _, __ or __x.  */
+      if (name[0] == '_')
+       {
+         if (name[1] == '_' && name[2] == 'x')
+           tname += 3;
+         else if (name[1] == '_')
+           tname += 2;
+         else
+           tname += 1;
+       }
 
-  /* Size of the stack reserved for parameter registers.  */
-  int reg_parm_stack_space = 0;
+      if (tname[0] == 's')
+       {
+         *returns_twice
+            = ((tname[1] == 'e'
+                && (! strcmp (tname, "setjmp")
+                    || ! strcmp (tname, "setjmp_syscall")))
+               || (tname[1] == 'i'
+                   && ! strcmp (tname, "sigsetjmp"))
+               || (tname[1] == 'a'
+                   && ! strcmp (tname, "savectx")));
+         if (tname[1] == 'i'
+             && ! strcmp (tname, "siglongjmp"))
+           *is_longjmp = 1;
+       }
+      else if ((tname[0] == 'q' && tname[1] == 's'
+               && ! strcmp (tname, "qsetjmp"))
+              || (tname[0] == 'v' && tname[1] == 'f'
+                  && ! strcmp (tname, "vfork")))
+       *returns_twice = 1;
 
-  /* 1 if scanning parms front to back, -1 if scanning back to front.  */
-  int inc;
-  /* Address of space preallocated for stack parms
-     (on machines that lack push insns), or 0 if space not preallocated.  */
-  rtx argblock = 0;
+      else if (tname[0] == 'l' && tname[1] == 'o'
+              && ! strcmp (tname, "longjmp"))
+       *is_longjmp = 1;
+      /* XXX should have "malloc" attribute on functions instead
+        of recognizing them by name.  */
+      else if (! strcmp (tname, "malloc")
+              || ! strcmp (tname, "calloc")
+              || ! strcmp (tname, "realloc")
+              /* Note use of NAME rather than TNAME here.  These functions
+                 are only reserved when preceded with __.  */
+              || ! strcmp (name, "__vn")       /* mangled __builtin_vec_new */
+              || ! strcmp (name, "__nw")       /* mangled __builtin_new */
+              || ! strcmp (name, "__builtin_new")
+              || ! strcmp (name, "__builtin_vec_new"))
+       *is_malloc = 1;
+    }
+}
 
-  /* Nonzero if it is plausible that this is a call to alloca.  */
-  int may_be_alloca;
-  /* Nonzero if this is a call to malloc or a related function. */
-  int is_malloc;
-  /* Nonzero if this is a call to setjmp or a related function.  */
-  int returns_twice;
-  /* Nonzero if this is a call to `longjmp'.  */
-  int is_longjmp;
-  /* Nonzero if this is a call to an inline function.  */
-  int is_integrable = 0;
-  /* Nonzero if this is a call to a `const' function.
-     Note that only explicitly named functions are handled as `const' here.  */
-  int is_const = 0;
-  /* Nonzero if this is a call to a `volatile' function.  */
-  int is_volatile = 0;
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-  /* Define the boundary of the register parm stack space that needs to be
-     save, if any.  */
-  int low_to_save = -1, high_to_save;
-  rtx save_area = 0;           /* Place that it is saved */
-#endif
+/* Precompute all register parameters as described by ARGS, storing values
+   into fields within the ARGS array.
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
-  char *initial_stack_usage_map = stack_usage_map;
-#endif
+   NUM_ACTUALS indicates the total number elements in the ARGS array.
 
-  rtx old_stack_level = 0;
-  int old_pending_adj = 0;
-  int old_stack_arg_under_construction;
-  int old_inhibit_defer_pop = inhibit_defer_pop;
-  rtx call_fusage = 0;
-  register tree p;
-  register int i, j;
+   Set REG_PARM_SEEN if we encounter a register parameter.  */
 
-  /* The value of the function call can be put in a hard register.  But
-     if -fcheck-memory-usage, code which invokes functions (and thus
-     damages some hard registers) can be inserted before using the value.
-     So, target is always a pseudo-register in that case.  */
-  if (flag_check_memory_usage)
-    target = 0;
+static void
+precompute_register_parameters (num_actuals, args, reg_parm_seen)
+     int num_actuals;
+     struct arg_data *args;
+     int *reg_parm_seen;
+{
+  int i;
 
-  /* See if we can find a DECL-node for the actual function.
-     As a result, decide whether this is a call to an integrable function.  */
+  *reg_parm_seen = 0;
 
-  p = TREE_OPERAND (exp, 0);
-  if (TREE_CODE (p) == ADDR_EXPR)
-    {
-      fndecl = TREE_OPERAND (p, 0);
-      if (TREE_CODE (fndecl) != FUNCTION_DECL)
-       fndecl = 0;
-      else
-       {
-         if (!flag_no_inline
-             && fndecl != current_function_decl
-             && DECL_INLINE (fndecl)
-             && DECL_SAVED_INSNS (fndecl)
-             && RTX_INTEGRATED_P (DECL_SAVED_INSNS (fndecl)))
-           is_integrable = 1;
-         else if (! TREE_ADDRESSABLE (fndecl))
-           {
-             /* In case this function later becomes inlinable,
-                record that there was already a non-inline call to it.
+  for (i = 0; i < num_actuals; i++)
+    if (args[i].reg != 0 && ! args[i].pass_on_stack)
+      {
+       *reg_parm_seen = 1;
 
-                Use abstraction instead of setting TREE_ADDRESSABLE
-                directly.  */
-             if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
-                 && optimize > 0)
-               {
-                 warning_with_decl (fndecl, "can't inline call to `%s'");
-                 warning ("called from here");
-               }
-             mark_addressable (fndecl);
-           }
+       if (args[i].value == 0)
+         {
+           push_temp_slots ();
+           args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
+                                        VOIDmode, 0);
+           preserve_temp_slots (args[i].value);
+           pop_temp_slots ();
 
-         if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
-             && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
-           is_const = 1;
+           /* ANSI doesn't require a sequence point here,
+              but PCC has one, so this will avoid some problems.  */
+           emit_queue ();
+         }
 
-         if (TREE_THIS_VOLATILE (fndecl))
-           is_volatile = 1;
-       }
-    }
+       /* If we are to promote the function arg to a wider mode,
+          do it now.  */
 
-  /* If we don't have specific function to call, see if we have a 
-     constant or `noreturn' function from the type.  */
-  if (fndecl == 0)
-    {
-      is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
-      is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
-    }
+       if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
+         args[i].value
+           = convert_modes (args[i].mode,
+                            TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+                            args[i].value, args[i].unsignedp);
 
-#ifdef REG_PARM_STACK_SPACE
-#ifdef MAYBE_REG_PARM_STACK_SPACE
-  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
-#else
-  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
-#endif
-#endif
+       /* If the value is expensive, and we are inside an appropriately 
+          short loop, put the value into a pseudo and then put the pseudo
+          into the hard reg.
 
-  /* Warn if this value is an aggregate type,
-     regardless of which calling convention we are using for it.  */
-  if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
-    warning ("function call has aggregate value");
+          For small register classes, also do this if this call uses
+          register parameters.  This is to avoid reload conflicts while
+          loading the parameters registers.  */
 
-  /* Set up a place to return a structure.  */
+       if ((! (GET_CODE (args[i].value) == REG
+               || (GET_CODE (args[i].value) == SUBREG
+                   && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
+           && args[i].mode != BLKmode
+           && rtx_cost (args[i].value, SET) > 2
+           && ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
+               || preserve_subexpressions_p ()))
+         args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
+      }
+}
 
-  /* Cater to broken compilers.  */
-  if (aggregate_value_p (exp))
-    {
-      /* This call returns a big structure.  */
-      is_const = 0;
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
 
-#ifdef PCC_STATIC_STRUCT_RETURN
-      {
-       pcc_struct_value = 1;
-       /* Easier than making that case work right.  */
-       if (is_integrable)
-         {
-           /* In case this is a static function, note that it has been
-              used.  */
-           if (! TREE_ADDRESSABLE (fndecl))
-             mark_addressable (fndecl);
-           is_integrable = 0;
-         }
-      }
-#else /* not PCC_STATIC_STRUCT_RETURN */
-      {
-       struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
-
-       if (target && GET_CODE (target) == MEM)
-         structure_value_addr = XEXP (target, 0);
-       else
-         {
-           /* Assign a temporary to hold the value.  */
-           tree d;
+  /* The argument list is the property of the called routine and it
+     may clobber it.  If the fixed area has been used for previous
+     parameters, we must save and restore it.  */
+static rtx
+save_fixed_argument_area (reg_parm_stack_space, argblock,
+                         low_to_save, high_to_save)
+     int reg_parm_stack_space;
+     rtx argblock;
+     int *low_to_save;
+     int *high_to_save;
+{
+  int i;
+  rtx save_area = NULL_RTX;
 
-           /* For variable-sized objects, we must be called with a target
-              specified.  If we were to allocate space on the stack here,
-              we would have no way of knowing when to free it.  */
+  /* Compute the boundary of the that needs to be saved, if any.  */
+#ifdef ARGS_GROW_DOWNWARD
+  for (i = 0; i < reg_parm_stack_space + 1; i++)
+#else
+  for (i = 0; i < reg_parm_stack_space; i++)
+#endif
+    {
+      if (i >= highest_outgoing_arg_in_use
+         || stack_usage_map[i] == 0)
+       continue;
 
-           if (struct_value_size < 0)
-             abort ();
+      if (*low_to_save == -1)
+       *low_to_save = i;
 
-           /* This DECL is just something to feed to mark_addressable;
-              it doesn't get pushed.  */
-           d = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
-           DECL_RTL (d) = assign_temp (TREE_TYPE (exp), 1, 0, 1);
-           mark_addressable (d);
-           structure_value_addr = XEXP (DECL_RTL (d), 0);
-           MEM_IN_STRUCT_P (structure_value_addr)
-             = AGGREGATE_TYPE_P (TREE_TYPE (exp));
-           target = 0;
-         }
-      }
-#endif /* not PCC_STATIC_STRUCT_RETURN */
+      *high_to_save = i;
     }
 
-  /* If called function is inline, try to integrate it.  */
-
-  if (is_integrable)
+  if (*low_to_save >= 0)
     {
-      rtx temp;
-      rtx before_call = get_last_insn ();
+      int num_to_save = *high_to_save - *low_to_save + 1;
+      enum machine_mode save_mode
+       = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+      rtx stack_area;
 
-      temp = expand_inline_function (fndecl, actparms, target,
-                                    ignore, TREE_TYPE (exp),
-                                    structure_value_addr);
+      /* If we don't have the required alignment, must do this in BLKmode.  */
+      if ((*low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+                               BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+       save_mode = BLKmode;
 
-      /* If inlining succeeded, return.  */
-      if ((HOST_WIDE_INT) temp != -1)
+#ifdef ARGS_GROW_DOWNWARD
+      stack_area = gen_rtx_MEM (save_mode,
+                               memory_address (save_mode,
+                                               plus_constant (argblock,
+                                                              - *high_to_save)));
+#else
+      stack_area = gen_rtx_MEM (save_mode,
+                               memory_address (save_mode,
+                                               plus_constant (argblock,
+                                                              *low_to_save)));
+#endif
+      if (save_mode == BLKmode)
        {
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* If the outgoing argument list must be preserved, push
-            the stack before executing the inlined function if it
-            makes any calls.  */
-
-         for (i = reg_parm_stack_space - 1; i >= 0; i--)
-           if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
-             break;
+         save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+         emit_block_move (validize_mem (save_area), stack_area,
+                          GEN_INT (num_to_save),
+                          PARM_BOUNDARY / BITS_PER_UNIT);
+       }
+      else
+       {
+         save_area = gen_reg_rtx (save_mode);
+         emit_move_insn (save_area, stack_area);
+       }
+    }
+  return save_area;
+}
 
-         if (stack_arg_under_construction || i >= 0)
-           {
-             rtx first_insn
-               = before_call ? NEXT_INSN (before_call) : get_insns ();
-             rtx insn, seq;
+static void
+restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save)
+     rtx save_area;
+     rtx argblock;
+     int high_to_save;
+     int low_to_save;
+{
+  enum machine_mode save_mode = GET_MODE (save_area);
+#ifdef ARGS_GROW_DOWNWARD
+  rtx stack_area
+    = gen_rtx_MEM (save_mode,
+                  memory_address (save_mode,
+                                  plus_constant (argblock,
+                                                 - high_to_save)));
+#else
+  rtx stack_area
+    = gen_rtx_MEM (save_mode,
+                  memory_address (save_mode,
+                                  plus_constant (argblock,
+                                                 low_to_save)));
+#endif
 
-             /* Look for a call in the inline function code.
-                If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is
-                nonzero then there is a call and it is not necessary
-                to scan the insns.  */
+  if (save_mode != BLKmode)
+    emit_move_insn (stack_area, save_area);
+  else
+    emit_block_move (stack_area, validize_mem (save_area),
+                    GEN_INT (high_to_save - low_to_save + 1),
+                    PARM_BOUNDARY / BITS_PER_UNIT);
+}
+#endif
+         
+/* If any elements in ARGS refer to parameters that are to be passed in
+   registers, but not in memory, and whose alignment does not permit a
+   direct copy into registers.  Copy the values into a group of pseudos
+   which we will later copy into the appropriate hard registers. 
 
-             if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0)
-               for (insn = first_insn; insn; insn = NEXT_INSN (insn))
-                 if (GET_CODE (insn) == CALL_INSN)
-                   break;
+   Pseudos for each unaligned argument will be stored into the array
+   args[argnum].aligned_regs.  The caller is responsible for deallocating
+   the aligned_regs array if it is nonzero.  */
 
-             if (insn)
-               {
-                 /* Reserve enough stack space so that the largest
-                    argument list of any function call in the inline
-                    function does not overlap the argument list being
-                    evaluated.  This is usually an overestimate because
-                    allocate_dynamic_stack_space reserves space for an
-                    outgoing argument list in addition to the requested
-                    space, but there is no way to ask for stack space such
-                    that an argument list of a certain length can be
-                    safely constructed.  */
+static void
+store_unaligned_arguments_into_pseudos (args, num_actuals)
+     struct arg_data *args;
+     int num_actuals;
+{
+  int i, j;
+     
+  for (i = 0; i < num_actuals; i++)
+    if (args[i].reg != 0 && ! args[i].pass_on_stack
+       && args[i].mode == BLKmode
+       && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+           < (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
+      {
+       int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+       int big_endian_correction = 0;
 
-                 int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl));
-#ifdef REG_PARM_STACK_SPACE
-                 /* Add the stack space reserved for register arguments
-                    in the inline function.  What is really needed is the
-                    largest value of reg_parm_stack_space in the inline
-                    function, but that is not available.  Using the current
-                    value of reg_parm_stack_space is wrong, but gives
-                    correct results on all supported machines.  */
-                 adjust += reg_parm_stack_space;
-#endif
-                 start_sequence ();
-                 emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
-                 allocate_dynamic_stack_space (GEN_INT (adjust),
-                                               NULL_RTX, BITS_PER_UNIT);
-                 seq = get_insns ();
-                 end_sequence ();
-                 emit_insns_before (seq, first_insn);
-                 emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
-               }
-           }
-#endif
+       args[i].n_aligned_regs
+         = args[i].partial ? args[i].partial
+           : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
 
-         /* If the result is equivalent to TARGET, return TARGET to simplify
-            checks in store_expr.  They can be equivalent but not equal in the
-            case of a function that returns BLKmode.  */
-         if (temp != target && rtx_equal_p (temp, target))
-           return target;
-         return temp;
-       }
+       args[i].aligned_regs = (rtx *) xmalloc (sizeof (rtx)
+                                               * args[i].n_aligned_regs);
 
-      /* If inlining failed, mark FNDECL as needing to be compiled
-        separately after all.  If function was declared inline,
-        give a warning.  */
-      if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
-         && optimize > 0 && ! TREE_ADDRESSABLE (fndecl))
-       {
-         warning_with_decl (fndecl, "inlining failed in call to `%s'");
-         warning ("called from here");
-       }
-      mark_addressable (fndecl);
-    }
+       /* Structures smaller than a word are aligned to the least
+          significant byte (to the right).  On a BYTES_BIG_ENDIAN machine,
+          this means we must skip the empty high order bytes when
+          calculating the bit offset.  */
+       if (BYTES_BIG_ENDIAN && bytes < UNITS_PER_WORD)
+         big_endian_correction = (BITS_PER_WORD  - (bytes * BITS_PER_UNIT));
 
-  /* When calling a const function, we must pop the stack args right away,
-     so that the pop is deleted or moved with the call.  */
-  if (is_const)
-    NO_DEFER_POP;
+       for (j = 0; j < args[i].n_aligned_regs; j++)
+         {
+           rtx reg = gen_reg_rtx (word_mode);
+           rtx word = operand_subword_force (args[i].value, j, BLKmode);
+           int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD);
+           int bitalign = TYPE_ALIGN (TREE_TYPE (args[i].tree_value));
+
+           args[i].aligned_regs[j] = reg;
+
+           /* There is no need to restrict this code to loading items
+              in TYPE_ALIGN sized hunks.  The bitfield instructions can
+              load up entire word sized registers efficiently.
+
+              ??? This may not be needed anymore.
+              We use to emit a clobber here but that doesn't let later
+              passes optimize the instructions we emit.  By storing 0 into
+              the register later passes know the first AND to zero out the
+              bitfield being set in the register is unnecessary.  The store
+              of 0 will be deleted as will at least the first AND.  */
+
+           emit_move_insn (reg, const0_rtx);
+
+           bytes -= bitsize / BITS_PER_UNIT;
+           store_bit_field (reg, bitsize, big_endian_correction, word_mode,
+                            extract_bit_field (word, bitsize, 0, 1,
+                                               NULL_RTX, word_mode,
+                                               word_mode,
+                                               bitalign / BITS_PER_UNIT,
+                                               BITS_PER_WORD),
+                            bitalign / BITS_PER_UNIT, BITS_PER_WORD);
+         }
+      }
+}
 
-  function_call_count++;
+/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
+   ACTPARMS. 
 
-  if (fndecl && DECL_NAME (fndecl))
-    name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+   NUM_ACTUALS is the total number of parameters.
 
-#if 0
-  /* Unless it's a call to a specific function that isn't alloca,
-     if it has one argument, we must assume it might be alloca.  */
+   N_NAMED_ARGS is the total number of named arguments.
 
-  may_be_alloca
-    = (!(fndecl != 0 && strcmp (name, "alloca"))
-       && actparms != 0
-       && TREE_CHAIN (actparms) == 0);
-#else
-  /* We assume that alloca will always be called by name.  It
-     makes no sense to pass it as a pointer-to-function to
-     anything that does not understand its behavior.  */
-  may_be_alloca
-    = (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
-                && name[0] == 'a'
-                && ! strcmp (name, "alloca"))
-               || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
-                   && name[0] == '_'
-                   && ! strcmp (name, "__builtin_alloca"))));
-#endif
+   FNDECL is the tree code for the target of this call (if known)
 
-  /* See if this is a call to a function that can return more than once
-     or a call to longjmp.  */
+   ARGS_SO_FAR holds state needed by the target to know where to place
+   the next argument.
 
-  returns_twice = 0;
-  is_longjmp = 0;
-  is_malloc = 0;
+   REG_PARM_STACK_SPACE is the number of bytes of stack space reserved
+   for arguments which are passed in registers.
 
-  if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15)
-    {
-      char *tname = name;
+   OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level
+   and may be modified by this routine.
 
-      /* Disregard prefix _, __ or __x.  */
-      if (name[0] == '_')
-       {
-         if (name[1] == '_' && name[2] == 'x')
-           tname += 3;
-         else if (name[1] == '_')
-           tname += 2;
-         else
-           tname += 1;
-       }
+   OLD_PENDING_ADJ, MUST_PREALLOCATE and IS_CONST are pointers to integer
+   flags which may may be modified by this routine.  */
 
-      if (tname[0] == 's')
-       {
-         returns_twice
-           = ((tname[1] == 'e'
-               && (! strcmp (tname, "setjmp")
-                   || ! strcmp (tname, "setjmp_syscall")))
-              || (tname[1] == 'i'
-                  && ! strcmp (tname, "sigsetjmp"))
-              || (tname[1] == 'a'
-                  && ! strcmp (tname, "savectx")));
-         if (tname[1] == 'i'
-             && ! strcmp (tname, "siglongjmp"))
-           is_longjmp = 1;
-       }
-      else if ((tname[0] == 'q' && tname[1] == 's'
-               && ! strcmp (tname, "qsetjmp"))
-              || (tname[0] == 'v' && tname[1] == 'f'
-                  && ! strcmp (tname, "vfork")))
-       returns_twice = 1;
+static void
+initialize_argument_information (num_actuals, args, args_size, n_named_args,
+                                actparms, fndecl, args_so_far,
+                                reg_parm_stack_space, old_stack_level,
+                                old_pending_adj, must_preallocate, is_const)
+     int num_actuals ATTRIBUTE_UNUSED;
+     struct arg_data *args;
+     struct args_size *args_size;
+     int n_named_args ATTRIBUTE_UNUSED;
+     tree actparms;
+     tree fndecl;
+     CUMULATIVE_ARGS *args_so_far;
+     int reg_parm_stack_space;
+     rtx *old_stack_level;
+     int *old_pending_adj;
+     int *must_preallocate;
+     int *is_const;
+{
+  /* 1 if scanning parms front to back, -1 if scanning back to front.  */
+  int inc;
 
-      else if (tname[0] == 'l' && tname[1] == 'o'
-              && ! strcmp (tname, "longjmp"))
-       is_longjmp = 1;
-      /* XXX should have "malloc" attribute on functions instead
-        of recognizing them by name.  */
-      else if (! strcmp (tname, "malloc")
-              || ! strcmp (tname, "calloc")
-              || ! strcmp (tname, "realloc")
-              || ! strcmp (tname, "__builtin_new")
-              || ! strcmp (tname, "__builtin_vec_new"))
-       is_malloc = 1;
-    }
+  /* Count arg position in order args appear.  */
+  int argpos;
 
-  if (may_be_alloca)
-    current_function_calls_alloca = 1;
+  int i;
+  tree p;
+  
+  args_size->constant = 0;
+  args_size->var = 0;
 
-  /* Don't let pending stack adjusts add up to too much.
-     Also, do all pending adjustments now
-     if there is any chance this might be a call to alloca.  */
+  /* In this loop, we consider args in the order they are written.
+     We fill up ARGS from the front or from the back if necessary
+     so that in any case the first arg to be pushed ends up at the front.  */
 
-  if (pending_stack_adjust >= 32
-      || (pending_stack_adjust > 0 && may_be_alloca))
-    do_pending_stack_adjust ();
+#ifdef PUSH_ARGS_REVERSED
+  i = num_actuals - 1, inc = -1;
+  /* In this case, must reverse order of args
+     so that we compute and push the last arg first.  */
+#else
+  i = 0, inc = 1;
+#endif
 
-  /* Operand 0 is a pointer-to-function; get the type of the function.  */
-  funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
-  if (TREE_CODE (funtype) != POINTER_TYPE)
-    abort ();
-  funtype = TREE_TYPE (funtype);
+  /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
+  for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
+    {
+      tree type = TREE_TYPE (TREE_VALUE (p));
+      int unsignedp;
+      enum machine_mode mode;
 
-  /* Push the temporary stack slot level so that we can free any temporaries
-     we make.  */
-  push_temp_slots ();
+      args[i].tree_value = TREE_VALUE (p);
 
-  /* Start updating where the next arg would go.
-
-     On some machines (such as the PA) indirect calls have a different
-     calling convention than normal calls.  The last argument in
-     INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
-     or not.  */
-  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
-
-  /* If struct_value_rtx is 0, it means pass the address
-     as if it were an extra parameter.  */
-  if (structure_value_addr && struct_value_rtx == 0)
-    {
-      /* If structure_value_addr is a REG other than
-        virtual_outgoing_args_rtx, we can use always use it.  If it
-        is not a REG, we must always copy it into a register.
-        If it is virtual_outgoing_args_rtx, we must copy it to another
-        register in some cases.  */
-      rtx temp = (GET_CODE (structure_value_addr) != REG
-#ifdef ACCUMULATE_OUTGOING_ARGS
-                 || (stack_arg_under_construction
-                     && structure_value_addr == virtual_outgoing_args_rtx)
-#endif
-                 ? copy_addr_to_reg (structure_value_addr)
-                 : structure_value_addr);
-
-      actparms
-       = tree_cons (error_mark_node,
-                    make_tree (build_pointer_type (TREE_TYPE (funtype)),
-                               temp),
-                    actparms);
-      structure_value_addr_parm = 1;
-    }
-
-  /* Count the arguments and set NUM_ACTUALS.  */
-  for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++;
-  num_actuals = i;
-
-  /* Compute number of named args.
-     Normally, don't include the last named arg if anonymous args follow.
-     We do include the last named arg if STRICT_ARGUMENT_NAMING is defined.
-     (If no anonymous args follow, the result of list_length is actually
-     one too large.  This is harmless.)
-
-     If SETUP_INCOMING_VARARGS is defined and STRICT_ARGUMENT_NAMING is not,
-     this machine will be able to place unnamed args that were passed in
-     registers into the stack.  So treat all args as named.  This allows the
-     insns emitting for a specific argument list to be independent of the
-     function declaration.
-
-     If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable
-     way to pass unnamed args in registers, so we must force them into
-     memory.  */
-#if !defined(SETUP_INCOMING_VARARGS) || defined(STRICT_ARGUMENT_NAMING)
-  if (TYPE_ARG_TYPES (funtype) != 0)
-    n_named_args
-      = (list_length (TYPE_ARG_TYPES (funtype))
-#ifndef STRICT_ARGUMENT_NAMING
-        /* Don't include the last named arg.  */
-        - 1
-#endif
-        /* Count the struct value address, if it is passed as a parm.  */
-        + structure_value_addr_parm);
-  else
-#endif
-    /* If we know nothing, treat all args as named.  */
-    n_named_args = num_actuals;
-
-  /* Make a vector to hold all the information about each arg.  */
-  args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
-  bzero ((char *) args, num_actuals * sizeof (struct arg_data));
-
-  args_size.constant = 0;
-  args_size.var = 0;
-
-  /* In this loop, we consider args in the order they are written.
-     We fill up ARGS from the front or from the back if necessary
-     so that in any case the first arg to be pushed ends up at the front.  */
-
-#ifdef PUSH_ARGS_REVERSED
-  i = num_actuals - 1, inc = -1;
-  /* In this case, must reverse order of args
-     so that we compute and push the last arg first.  */
-#else
-  i = 0, inc = 1;
-#endif
-
-  /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
-  for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
-    {
-      tree type = TREE_TYPE (TREE_VALUE (p));
-      int unsignedp;
-      enum machine_mode mode;
-
-      args[i].tree_value = TREE_VALUE (p);
-
-      /* Replace erroneous argument with constant zero.  */
-      if (type == error_mark_node || TYPE_SIZE (type) == 0)
-       args[i].tree_value = integer_zero_node, type = integer_type_node;
+      /* Replace erroneous argument with constant zero.  */
+      if (type == error_mark_node || TYPE_SIZE (type) == 0)
+       args[i].tree_value = integer_zero_node, type = integer_type_node;
 
       /* If TYPE is a transparent union, pass things the way we would
         pass the first field of the union.  We have already verified that
@@ -1044,7 +943,7 @@ expand_call (exp, target, ignore)
           && contains_placeholder_p (TYPE_SIZE (type)))
          || TREE_ADDRESSABLE (type)
 #ifdef FUNCTION_ARG_PASS_BY_REFERENCE
-         || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type),
+         || FUNCTION_ARG_PASS_BY_REFERENCE (*args_so_far, TYPE_MODE (type),
                                             type, argpos < n_named_args)
 #endif
          )
@@ -1053,7 +952,7 @@ expand_call (exp, target, ignore)
              references instead of making a copy.  */
          if (current_function_is_thunk
 #ifdef FUNCTION_ARG_CALLEE_COPIES
-             || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type),
+             || (FUNCTION_ARG_CALLEE_COPIES (*args_so_far, TYPE_MODE (type),
                                             type, argpos < n_named_args)
                  /* If it's in a register, we must make a copy of it too.  */
                  /* ??? Is this a sufficient test?  Is there a better one? */
@@ -1063,6 +962,22 @@ expand_call (exp, target, ignore)
 #endif
              )
            {
+             /* C++ uses a TARGET_EXPR to indicate that we want to make a
+                new object from the argument.  If we are passing by
+                invisible reference, the callee will do that for us, so we
+                can strip off the TARGET_EXPR.  This is not always safe,
+                but it is safe in the only case where this is a useful
+                optimization; namely, when the argument is a plain object.
+                In that case, the frontend is just asking the backend to
+                make a bitwise copy of the argument. */
+                
+             if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
+                 && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND
+                                                 (args[i].tree_value, 1)))
+                     == 'd')
+                 && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
+               args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
+
              args[i].tree_value = build1 (ADDR_EXPR,
                                           build_pointer_type (type),
                                           args[i].tree_value);
@@ -1085,10 +1000,10 @@ expand_call (exp, target, ignore)
                     for it.  */
                  rtx size_rtx = expr_size (TREE_VALUE (p));
 
-                 if (old_stack_level == 0)
+                 if (*old_stack_level == 0)
                    {
-                     emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
-                     old_pending_adj = pending_stack_adjust;
+                     emit_stack_save (SAVE_BLOCK, old_stack_level, NULL_RTX);
+                     *old_pending_adj = pending_stack_adjust;
                      pending_stack_adjust = 0;
                    }
 
@@ -1103,223 +1018,976 @@ expand_call (exp, target, ignore)
                  copy = assign_stack_temp (TYPE_MODE (type), size, 0);
                }
 
-             MEM_IN_STRUCT_P (copy) = AGGREGATE_TYPE_P (type);
+             MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
+
+             store_expr (args[i].tree_value, copy, 0);
+             *is_const = 0;
+
+             args[i].tree_value = build1 (ADDR_EXPR,
+                                          build_pointer_type (type),
+                                          make_tree (type, copy));
+             type = build_pointer_type (type);
+           }
+       }
+
+      mode = TYPE_MODE (type);
+      unsignedp = TREE_UNSIGNED (type);
+
+#ifdef PROMOTE_FUNCTION_ARGS
+      mode = promote_mode (type, mode, &unsignedp, 1);
+#endif
+
+      args[i].unsignedp = unsignedp;
+      args[i].mode = mode;
+      args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
+                                 argpos < n_named_args);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+      if (args[i].reg)
+       args[i].partial
+         = FUNCTION_ARG_PARTIAL_NREGS (*args_so_far, mode, type,
+                                       argpos < n_named_args);
+#endif
+
+      args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
+
+      /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
+        it means that we are to pass this arg in the register(s) designated
+        by the PARALLEL, but also to pass it in the stack.  */
+      if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
+         && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
+       args[i].pass_on_stack = 1;
+
+      /* If this is an addressable type, we must preallocate the stack
+        since we must evaluate the object into its final location.
+
+        If this is to be passed in both registers and the stack, it is simpler
+        to preallocate.  */
+      if (TREE_ADDRESSABLE (type)
+         || (args[i].pass_on_stack && args[i].reg != 0))
+       *must_preallocate = 1;
+
+      /* If this is an addressable type, we cannot pre-evaluate it.  Thus,
+        we cannot consider this function call constant.  */
+      if (TREE_ADDRESSABLE (type))
+       *is_const = 0;
+
+      /* Compute the stack-size of this argument.  */
+      if (args[i].reg == 0 || args[i].partial != 0
+         || reg_parm_stack_space > 0
+         || args[i].pass_on_stack)
+       locate_and_pad_parm (mode, type,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+                            1,
+#else
+                            args[i].reg != 0,
+#endif
+                            fndecl, args_size, &args[i].offset,
+                            &args[i].size);
+
+#ifndef ARGS_GROW_DOWNWARD
+      args[i].slot_offset = *args_size;
+#endif
+
+      /* If a part of the arg was put into registers,
+        don't include that part in the amount pushed.  */
+      if (reg_parm_stack_space == 0 && ! args[i].pass_on_stack)
+       args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
+                                 / (PARM_BOUNDARY / BITS_PER_UNIT)
+                                 * (PARM_BOUNDARY / BITS_PER_UNIT));
+      
+      /* Update ARGS_SIZE, the total stack space for args so far.  */
+
+      args_size->constant += args[i].size.constant;
+      if (args[i].size.var)
+       {
+         ADD_PARM_SIZE (*args_size, args[i].size.var);
+       }
+
+      /* Since the slot offset points to the bottom of the slot,
+        we must record it after incrementing if the args grow down.  */
+#ifdef ARGS_GROW_DOWNWARD
+      args[i].slot_offset = *args_size;
+
+      args[i].slot_offset.constant = -args_size->constant;
+      if (args_size->var)
+       {
+         SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
+       }
+#endif
+
+      /* Increment ARGS_SO_FAR, which has info about which arg-registers
+        have been used, etc.  */
+
+      FUNCTION_ARG_ADVANCE (*args_so_far, TYPE_MODE (type), type,
+                           argpos < n_named_args);
+    }
+}
+
+/* Update ARGS_SIZE to contain the total size for the argument block.
+   Return the original constant component of the argument block's size.
+
+   REG_PARM_STACK_SPACE holds the number of bytes of stack space reserved
+   for arguments passed in registers.  */
+
+static int
+compute_argument_block_size (reg_parm_stack_space, args_size)
+     int reg_parm_stack_space;
+     struct args_size *args_size;
+{
+  int unadjusted_args_size = args_size->constant;
+
+  /* Compute the actual size of the argument block required.  The variable
+     and constant sizes must be combined, the size may have to be rounded,
+     and there may be a minimum required size.  */
+
+  if (args_size->var)
+    {
+      args_size->var = ARGS_SIZE_TREE (*args_size);
+      args_size->constant = 0;
+
+#ifdef PREFERRED_STACK_BOUNDARY
+      if (PREFERRED_STACK_BOUNDARY != BITS_PER_UNIT)
+       args_size->var = round_up (args_size->var, STACK_BYTES);
+#endif
+
+      if (reg_parm_stack_space > 0)
+       {
+         args_size->var
+           = size_binop (MAX_EXPR, args_size->var,
+                         size_int (reg_parm_stack_space));
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+         /* The area corresponding to register parameters is not to count in
+            the size of the block we need.  So make the adjustment.  */
+         args_size->var
+           = size_binop (MINUS_EXPR, args_size->var,
+                         size_int (reg_parm_stack_space));
+#endif
+       }
+    }
+  else
+    {
+#ifdef PREFERRED_STACK_BOUNDARY
+      args_size->constant = (((args_size->constant
+                              + pending_stack_adjust
+                              + STACK_BYTES - 1)
+                             / STACK_BYTES * STACK_BYTES)
+                            - pending_stack_adjust);
+#endif
+
+      args_size->constant = MAX (args_size->constant,
+                                reg_parm_stack_space);
+
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+      if (reg_parm_stack_space == 0)
+       args_size->constant = 0;
+#endif
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+      args_size->constant -= reg_parm_stack_space;
+#endif
+    }
+  return unadjusted_args_size;
+}
+
+/* Precompute parameters has needed for a function call.
+
+   IS_CONST indicates the target function is a pure function.
+
+   MUST_PREALLOCATE indicates that we must preallocate stack space for
+   any stack arguments.
+
+   NUM_ACTUALS is the number of arguments.
+
+   ARGS is an array containing information for each argument; this routine
+   fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
+
+   ARGS_SIZE contains information about the size of the arg list.  */
+
+static void
+precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
+     int is_const;
+     int must_preallocate;
+     int num_actuals;
+     struct arg_data *args;
+     struct args_size *args_size;
+{
+  int i;
+
+  /* If this function call is cse'able, precompute all the parameters.
+     Note that if the parameter is constructed into a temporary, this will
+     cause an additional copy because the parameter will be constructed
+     into a temporary location and then copied into the outgoing arguments.
+     If a parameter contains a call to alloca and this function uses the
+     stack, precompute the parameter.  */
+
+  /* If we preallocated the stack space, and some arguments must be passed
+     on the stack, then we must precompute any parameter which contains a
+     function call which will store arguments on the stack.
+     Otherwise, evaluating the parameter may clobber previous parameters
+     which have already been stored into the stack.  */
+
+  for (i = 0; i < num_actuals; i++)
+    if (is_const
+       || ((args_size->var != 0 || args_size->constant != 0)
+           && calls_function (args[i].tree_value, 1))
+       || (must_preallocate
+           && (args_size->var != 0 || args_size->constant != 0)
+           && calls_function (args[i].tree_value, 0)))
+      {
+       /* If this is an addressable type, we cannot pre-evaluate it.  */
+       if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
+         abort ();
+
+       push_temp_slots ();
+
+       args[i].initial_value = args[i].value
+         = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
+
+       preserve_temp_slots (args[i].value);
+       pop_temp_slots ();
+
+       /* ANSI doesn't require a sequence point here,
+          but PCC has one, so this will avoid some problems.  */
+       emit_queue ();
+
+       args[i].initial_value = args[i].value
+         = protect_from_queue (args[i].initial_value, 0);
+
+       if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
+         args[i].value
+           = convert_modes (args[i].mode, 
+                            TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+                            args[i].value, args[i].unsignedp);
+      }
+}
+
+/* Given the current state of MUST_PREALLOCATE and information about
+   arguments to a function call in NUM_ACTUALS, ARGS and ARGS_SIZE,
+   compute and return the final value for MUST_PREALLOCATE.  */
+
+static int
+finalize_must_preallocate (must_preallocate, num_actuals, args, args_size)
+     int must_preallocate;
+     int num_actuals;
+     struct arg_data *args;
+     struct args_size *args_size;
+{
+  /* See if we have or want to preallocate stack space.
+
+     If we would have to push a partially-in-regs parm
+     before other stack parms, preallocate stack space instead.
+
+     If the size of some parm is not a multiple of the required stack
+     alignment, we must preallocate.
+
+     If the total size of arguments that would otherwise create a copy in
+     a temporary (such as a CALL) is more than half the total argument list
+     size, preallocation is faster.
+
+     Another reason to preallocate is if we have a machine (like the m88k)
+     where stack alignment is required to be maintained between every
+     pair of insns, not just when the call is made.  However, we assume here
+     that such machines either do not have push insns (and hence preallocation
+     would occur anyway) or the problem is taken care of with
+     PUSH_ROUNDING.  */
+
+  if (! must_preallocate)
+    {
+      int partial_seen = 0;
+      int copy_to_evaluate_size = 0;
+      int i;
+
+      for (i = 0; i < num_actuals && ! must_preallocate; i++)
+       {
+         if (args[i].partial > 0 && ! args[i].pass_on_stack)
+           partial_seen = 1;
+         else if (partial_seen && args[i].reg == 0)
+           must_preallocate = 1;
+
+         if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+             && (TREE_CODE (args[i].tree_value) == CALL_EXPR
+                 || TREE_CODE (args[i].tree_value) == TARGET_EXPR
+                 || TREE_CODE (args[i].tree_value) == COND_EXPR
+                 || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
+           copy_to_evaluate_size
+             += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+       }
+
+      if (copy_to_evaluate_size * 2 >= args_size->constant
+         && args_size->constant > 0)
+       must_preallocate = 1;
+    }
+  return must_preallocate;
+}
+
+/* If we preallocated stack space, compute the address of each argument
+   and store it into the ARGS array.
+
+   We need not ensure it is a valid memory address here; it will be 
+   validized when it is used.
+
+   ARGBLOCK is an rtx for the address of the outgoing arguments.  */
+
+static void
+compute_argument_addresses (args, argblock, num_actuals)
+     struct arg_data *args;
+     rtx argblock;
+     int num_actuals;
+{
+  if (argblock)
+    {
+      rtx arg_reg = argblock;
+      int i, arg_offset = 0;
+
+      if (GET_CODE (argblock) == PLUS)
+       arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
+
+      for (i = 0; i < num_actuals; i++)
+       {
+         rtx offset = ARGS_SIZE_RTX (args[i].offset);
+         rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
+         rtx addr;
+
+         /* Skip this parm if it will not be passed on the stack.  */
+         if (! args[i].pass_on_stack && args[i].reg != 0)
+           continue;
+
+         if (GET_CODE (offset) == CONST_INT)
+           addr = plus_constant (arg_reg, INTVAL (offset));
+         else
+           addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
+
+         addr = plus_constant (addr, arg_offset);
+         args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+         MEM_SET_IN_STRUCT_P 
+           (args[i].stack,
+            AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value)));
+
+         if (GET_CODE (slot_offset) == CONST_INT)
+           addr = plus_constant (arg_reg, INTVAL (slot_offset));
+         else
+           addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
+
+         addr = plus_constant (addr, arg_offset);
+         args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+       }
+    }
+}
+                                              
+/* Given a FNDECL and EXP, return an rtx suitable for use as a target address
+   in a call instruction.
+
+   FNDECL is the tree node for the target function.  For an indirect call
+   FNDECL will be NULL_TREE.
+
+   EXP is the CALL_EXPR for this call.  */
+
+static rtx
+rtx_for_function_call (fndecl, exp)
+     tree fndecl;
+     tree exp;
+{
+  rtx funexp;
+
+  /* Get the function to call, in the form of RTL.  */
+  if (fndecl)
+    {
+      /* If this is the first use of the function, see if we need to
+        make an external definition for it.  */
+      if (! TREE_USED (fndecl))
+       {
+         assemble_external (fndecl);
+         TREE_USED (fndecl) = 1;
+       }
+
+      /* Get a SYMBOL_REF rtx for the function address.  */
+      funexp = XEXP (DECL_RTL (fndecl), 0);
+    }
+  else
+    /* Generate an rtx (probably a pseudo-register) for the address.  */
+    {
+      rtx funaddr;
+      push_temp_slots ();
+      funaddr = funexp = 
+         expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+      pop_temp_slots ();       /* FUNEXP can't be BLKmode */
+
+      /* Check the function is executable.  */
+      if (current_function_check_memory_usage)
+       {
+#ifdef POINTERS_EXTEND_UNSIGNED
+         /* It might be OK to convert funexp in place, but there's
+            a lot going on between here and when it happens naturally
+            that this seems safer. */
+          funaddr = convert_memory_address (Pmode, funexp);
+#endif
+         emit_library_call (chkr_check_exec_libfunc, 1,
+                            VOIDmode, 1,
+                            funaddr, Pmode);
+       }
+      emit_queue ();
+    }
+  return funexp;
+}
+
+/* Do the register loads required for any wholly-register parms or any
+   parms which are passed both on the stack and in a register.  Their
+   expressions were already evaluated. 
+
+   Mark all register-parms as living through the call, putting these USE
+   insns in the CALL_INSN_FUNCTION_USAGE field.  */
+
+static void
+load_register_parameters (args, num_actuals, call_fusage)
+     struct arg_data *args;
+     int num_actuals;
+     rtx *call_fusage;
+{
+  int i, j;
+
+#ifdef LOAD_ARGS_REVERSED
+  for (i = num_actuals - 1; i >= 0; i--)
+#else
+  for (i = 0; i < num_actuals; i++)
+#endif
+    {
+      rtx reg = args[i].reg;
+      int partial = args[i].partial;
+      int nregs;
+
+      if (reg)
+       {
+         /* Set to non-negative if must move a word at a time, even if just
+            one word (e.g, partial == 1 && mode == DFmode).  Set to -1 if
+            we just use a normal move insn.  This value can be zero if the
+            argument is a zero size structure with no fields.  */
+         nregs = (partial ? partial
+                  : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+                     ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
+                         + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+                     : -1));
+
+         /* Handle calls that pass values in multiple non-contiguous
+            locations.  The Irix 6 ABI has examples of this.  */
+
+         if (GET_CODE (reg) == PARALLEL)
+           {
+             emit_group_load (reg, args[i].value,
+                              int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
+                              (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+                               / BITS_PER_UNIT));
+           }
+
+         /* If simple case, just do move.  If normal partial, store_one_arg
+            has already loaded the register for us.  In all other cases,
+            load the register(s) from memory.  */
+
+         else if (nregs == -1)
+           emit_move_insn (reg, args[i].value);
+
+         /* If we have pre-computed the values to put in the registers in
+            the case of non-aligned structures, copy them in now.  */
+
+         else if (args[i].n_aligned_regs != 0)
+           for (j = 0; j < args[i].n_aligned_regs; j++)
+             emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
+                             args[i].aligned_regs[j]);
+
+         else if (partial == 0 || args[i].pass_on_stack)
+           move_block_to_reg (REGNO (reg),
+                              validize_mem (args[i].value), nregs,
+                              args[i].mode);
+
+         /* Handle calls that pass values in multiple non-contiguous
+            locations.  The Irix 6 ABI has examples of this.  */
+         if (GET_CODE (reg) == PARALLEL)
+           use_group_regs (call_fusage, reg);
+         else if (nregs == -1)
+           use_reg (call_fusage, reg);
+         else
+           use_regs (call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
+       }
+    }
+}
+
+/* Generate all the code for a function call
+   and return an rtx for its value.
+   Store the value in TARGET (specified as an rtx) if convenient.
+   If the value is stored in TARGET then TARGET is returned.
+   If IGNORE is nonzero, then we ignore the value of the function call.  */
+
+rtx
+expand_call (exp, target, ignore)
+     tree exp;
+     rtx target;
+     int ignore;
+{
+  /* List of actual parameters.  */
+  tree actparms = TREE_OPERAND (exp, 1);
+  /* RTX for the function to be called.  */
+  rtx funexp;
+  /* Data type of the function.  */
+  tree funtype;
+  /* Declaration of the function being called,
+     or 0 if the function is computed (not known by name).  */
+  tree fndecl = 0;
+  char *name = 0;
+
+  /* Register in which non-BLKmode value will be returned,
+     or 0 if no value or if value is BLKmode.  */
+  rtx valreg;
+  /* Address where we should return a BLKmode value;
+     0 if value not BLKmode.  */
+  rtx structure_value_addr = 0;
+  /* Nonzero if that address is being passed by treating it as
+     an extra, implicit first parameter.  Otherwise,
+     it is passed by being copied directly into struct_value_rtx.  */
+  int structure_value_addr_parm = 0;
+  /* Size of aggregate value wanted, or zero if none wanted
+     or if we are using the non-reentrant PCC calling convention
+     or expecting the value in registers.  */
+  HOST_WIDE_INT struct_value_size = 0;
+  /* Nonzero if called function returns an aggregate in memory PCC style,
+     by returning the address of where to find it.  */
+  int pcc_struct_value = 0;
+
+  /* Number of actual parameters in this call, including struct value addr.  */
+  int num_actuals;
+  /* Number of named args.  Args after this are anonymous ones
+     and they must all go on the stack.  */
+  int n_named_args;
+
+  /* Vector of information about each argument.
+     Arguments are numbered in the order they will be pushed,
+     not the order they are written.  */
+  struct arg_data *args;
+
+  /* Total size in bytes of all the stack-parms scanned so far.  */
+  struct args_size args_size;
+  /* Size of arguments before any adjustments (such as rounding).  */
+  int unadjusted_args_size;
+  /* Data on reg parms scanned so far.  */
+  CUMULATIVE_ARGS args_so_far;
+  /* Nonzero if a reg parm has been scanned.  */
+  int reg_parm_seen;
+  /* Nonzero if this is an indirect function call.  */
+
+  /* Nonzero if we must avoid push-insns in the args for this call. 
+     If stack space is allocated for register parameters, but not by the
+     caller, then it is preallocated in the fixed part of the stack frame.
+     So the entire argument block must then be preallocated (i.e., we
+     ignore PUSH_ROUNDING in that case).  */
+
+#ifdef PUSH_ROUNDING
+  int must_preallocate = 0;
+#else
+  int must_preallocate = 1;
+#endif
+
+  /* Size of the stack reserved for parameter registers.  */
+  int reg_parm_stack_space = 0;
+
+  /* Address of space preallocated for stack parms
+     (on machines that lack push insns), or 0 if space not preallocated.  */
+  rtx argblock = 0;
+
+  /* Nonzero if it is plausible that this is a call to alloca.  */
+  int may_be_alloca;
+  /* Nonzero if this is a call to malloc or a related function. */
+  int is_malloc;
+  /* Nonzero if this is a call to setjmp or a related function.  */
+  int returns_twice;
+  /* Nonzero if this is a call to `longjmp'.  */
+  int is_longjmp;
+  /* Nonzero if this is a call to an inline function.  */
+  int is_integrable = 0;
+  /* Nonzero if this is a call to a `const' function.
+     Note that only explicitly named functions are handled as `const' here.  */
+  int is_const = 0;
+  /* Nonzero if this is a call to a `volatile' function.  */
+  int is_volatile = 0;
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+  /* Define the boundary of the register parm stack space that needs to be
+     save, if any.  */
+  int low_to_save = -1, high_to_save;
+  rtx save_area = 0;           /* Place that it is saved */
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+  char *initial_stack_usage_map = stack_usage_map;
+  int old_stack_arg_under_construction;
+#endif
+
+  rtx old_stack_level = 0;
+  int old_pending_adj = 0;
+  int old_inhibit_defer_pop = inhibit_defer_pop;
+  rtx call_fusage = 0;
+  register tree p;
+  register int i;
+
+  /* The value of the function call can be put in a hard register.  But
+     if -fcheck-memory-usage, code which invokes functions (and thus
+     damages some hard registers) can be inserted before using the value.
+     So, target is always a pseudo-register in that case.  */
+  if (current_function_check_memory_usage)
+    target = 0;
+
+  /* See if we can find a DECL-node for the actual function.
+     As a result, decide whether this is a call to an integrable function.  */
+
+  p = TREE_OPERAND (exp, 0);
+  if (TREE_CODE (p) == ADDR_EXPR)
+    {
+      fndecl = TREE_OPERAND (p, 0);
+      if (TREE_CODE (fndecl) != FUNCTION_DECL)
+       fndecl = 0;
+      else
+       {
+         if (!flag_no_inline
+             && fndecl != current_function_decl
+             && DECL_INLINE (fndecl)
+             && DECL_SAVED_INSNS (fndecl)
+             && DECL_SAVED_INSNS (fndecl)->inlinable)
+           is_integrable = 1;
+         else if (! TREE_ADDRESSABLE (fndecl))
+           {
+             /* In case this function later becomes inlinable,
+                record that there was already a non-inline call to it.
+
+                Use abstraction instead of setting TREE_ADDRESSABLE
+                directly.  */
+             if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+                 && optimize > 0)
+               {
+                 warning_with_decl (fndecl, "can't inline call to `%s'");
+                 warning ("called from here");
+               }
+             mark_addressable (fndecl);
+           }
+
+         if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
+             && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
+           is_const = 1;
+
+         if (TREE_THIS_VOLATILE (fndecl))
+           is_volatile = 1;
+       }
+    }
+
+  /* If we don't have specific function to call, see if we have a 
+     constant or `noreturn' function from the type.  */
+  if (fndecl == 0)
+    {
+      is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
+      is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
+    }
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
+
+#if defined(PUSH_ROUNDING) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+  if (reg_parm_stack_space > 0)
+    must_preallocate = 1;
+#endif
+
+  /* Warn if this value is an aggregate type,
+     regardless of which calling convention we are using for it.  */
+  if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+    warning ("function call has aggregate value");
+
+  /* Set up a place to return a structure.  */
+
+  /* Cater to broken compilers.  */
+  if (aggregate_value_p (exp))
+    {
+      /* This call returns a big structure.  */
+      is_const = 0;
+
+#ifdef PCC_STATIC_STRUCT_RETURN
+      {
+       pcc_struct_value = 1;
+       /* Easier than making that case work right.  */
+       if (is_integrable)
+         {
+           /* In case this is a static function, note that it has been
+              used.  */
+           if (! TREE_ADDRESSABLE (fndecl))
+             mark_addressable (fndecl);
+           is_integrable = 0;
+         }
+      }
+#else /* not PCC_STATIC_STRUCT_RETURN */
+      {
+       struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
+
+       if (target && GET_CODE (target) == MEM)
+         structure_value_addr = XEXP (target, 0);
+       else
+         {
+           /* Assign a temporary to hold the value.  */
+           tree d;
+
+           /* For variable-sized objects, we must be called with a target
+              specified.  If we were to allocate space on the stack here,
+              we would have no way of knowing when to free it.  */
 
-             store_expr (args[i].tree_value, copy, 0);
-             is_const = 0;
+           if (struct_value_size < 0)
+             abort ();
 
-             args[i].tree_value = build1 (ADDR_EXPR,
-                                          build_pointer_type (type),
-                                          make_tree (type, copy));
-             type = build_pointer_type (type);
-           }
-       }
+           /* This DECL is just something to feed to mark_addressable;
+              it doesn't get pushed.  */
+           d = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
+           DECL_RTL (d) = assign_temp (TREE_TYPE (exp), 1, 0, 1);
+           mark_addressable (d);
+           structure_value_addr = XEXP (DECL_RTL (d), 0);
+           TREE_USED (d) = 1;
+           target = 0;
+         }
+      }
+#endif /* not PCC_STATIC_STRUCT_RETURN */
+    }
 
-      mode = TYPE_MODE (type);
-      unsignedp = TREE_UNSIGNED (type);
+  /* If called function is inline, try to integrate it.  */
 
-#ifdef PROMOTE_FUNCTION_ARGS
-      mode = promote_mode (type, mode, &unsignedp, 1);
+  if (is_integrable)
+    {
+      rtx temp;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      rtx before_call = get_last_insn ();
 #endif
 
-      args[i].unsignedp = unsignedp;
-      args[i].mode = mode;
-      args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
-                                 argpos < n_named_args);
-#ifdef FUNCTION_ARG_PARTIAL_NREGS
-      if (args[i].reg)
-       args[i].partial
-         = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
-                                       argpos < n_named_args);
-#endif
+      temp = expand_inline_function (fndecl, actparms, target,
+                                    ignore, TREE_TYPE (exp),
+                                    structure_value_addr);
 
-      args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
+      /* If inlining succeeded, return.  */
+      if (temp != (rtx) (HOST_WIDE_INT) -1)
+       {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+         /* If the outgoing argument list must be preserved, push
+            the stack before executing the inlined function if it
+            makes any calls.  */
 
-      /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
-        it means that we are to pass this arg in the register(s) designated
-        by the PARALLEL, but also to pass it in the stack.  */
-      if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
-         && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
-       args[i].pass_on_stack = 1;
+         for (i = reg_parm_stack_space - 1; i >= 0; i--)
+           if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
+             break;
 
-      /* If this is an addressable type, we must preallocate the stack
-        since we must evaluate the object into its final location.
+         if (stack_arg_under_construction || i >= 0)
+           {
+             rtx first_insn
+               = before_call ? NEXT_INSN (before_call) : get_insns ();
+             rtx insn, seq;
 
-        If this is to be passed in both registers and the stack, it is simpler
-        to preallocate.  */
-      if (TREE_ADDRESSABLE (type)
-         || (args[i].pass_on_stack && args[i].reg != 0))
-       must_preallocate = 1;
+             /* Look for a call in the inline function code.
+                If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
+                nonzero then there is a call and it is not necessary
+                to scan the insns.  */
 
-      /* If this is an addressable type, we cannot pre-evaluate it.  Thus,
-        we cannot consider this function call constant.  */
-      if (TREE_ADDRESSABLE (type))
-       is_const = 0;
+             if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
+               for (insn = first_insn; insn; insn = NEXT_INSN (insn))
+                 if (GET_CODE (insn) == CALL_INSN)
+                   break;
 
-      /* Compute the stack-size of this argument.  */
-      if (args[i].reg == 0 || args[i].partial != 0
-#ifdef REG_PARM_STACK_SPACE
-         || reg_parm_stack_space > 0
-#endif
-         || args[i].pass_on_stack)
-       locate_and_pad_parm (mode, type,
-#ifdef STACK_PARMS_IN_REG_PARM_AREA
-                            1,
-#else
-                            args[i].reg != 0,
-#endif
-                            fndecl, &args_size, &args[i].offset,
-                            &args[i].size);
+             if (insn)
+               {
+                 /* Reserve enough stack space so that the largest
+                    argument list of any function call in the inline
+                    function does not overlap the argument list being
+                    evaluated.  This is usually an overestimate because
+                    allocate_dynamic_stack_space reserves space for an
+                    outgoing argument list in addition to the requested
+                    space, but there is no way to ask for stack space such
+                    that an argument list of a certain length can be
+                    safely constructed. 
 
-#ifndef ARGS_GROW_DOWNWARD
-      args[i].slot_offset = args_size;
-#endif
+                    Add the stack space reserved for register arguments, if
+                    any, in the inline function.  What is really needed is the
+                    largest value of reg_parm_stack_space in the inline
+                    function, but that is not available.  Using the current
+                    value of reg_parm_stack_space is wrong, but gives
+                    correct results on all supported machines.  */
 
-#ifndef REG_PARM_STACK_SPACE
-      /* If a part of the arg was put into registers,
-        don't include that part in the amount pushed.  */
-      if (! args[i].pass_on_stack)
-       args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
-                                 / (PARM_BOUNDARY / BITS_PER_UNIT)
-                                 * (PARM_BOUNDARY / BITS_PER_UNIT));
+                 int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+                               + reg_parm_stack_space);
+
+                 start_sequence ();
+                 emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+                 allocate_dynamic_stack_space (GEN_INT (adjust),
+                                               NULL_RTX, BITS_PER_UNIT);
+                 seq = get_insns ();
+                 end_sequence ();
+                 emit_insns_before (seq, first_insn);
+                 emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+               }
+           }
 #endif
-      
-      /* Update ARGS_SIZE, the total stack space for args so far.  */
 
-      args_size.constant += args[i].size.constant;
-      if (args[i].size.var)
-       {
-         ADD_PARM_SIZE (args_size, args[i].size.var);
+         /* If the result is equivalent to TARGET, return TARGET to simplify
+            checks in store_expr.  They can be equivalent but not equal in the
+            case of a function that returns BLKmode.  */
+         if (temp != target && rtx_equal_p (temp, target))
+           return target;
+         return temp;
        }
 
-      /* Since the slot offset points to the bottom of the slot,
-        we must record it after incrementing if the args grow down.  */
-#ifdef ARGS_GROW_DOWNWARD
-      args[i].slot_offset = args_size;
-
-      args[i].slot_offset.constant = -args_size.constant;
-      if (args_size.var)
+      /* If inlining failed, mark FNDECL as needing to be compiled
+        separately after all.  If function was declared inline,
+        give a warning.  */
+      if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+         && optimize > 0 && ! TREE_ADDRESSABLE (fndecl))
        {
-         SUB_PARM_SIZE (args[i].slot_offset, args_size.var);
+         warning_with_decl (fndecl, "inlining failed in call to `%s'");
+         warning ("called from here");
        }
-#endif
+      mark_addressable (fndecl);
+    }
 
-      /* Increment ARGS_SO_FAR, which has info about which arg-registers
-        have been used, etc.  */
+  function_call_count++;
 
-      FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type,
-                           argpos < n_named_args);
-    }
+  if (fndecl && DECL_NAME (fndecl))
+    name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
 
-#ifdef FINAL_REG_PARM_STACK_SPACE
-  reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
-                                                    args_size.var);
-#endif
-      
-  /* Compute the actual size of the argument block required.  The variable
-     and constant sizes must be combined, the size may have to be rounded,
-     and there may be a minimum required size.  */
+  /* See if this is a call to a function that can return more than once
+     or a call to longjmp or malloc.  */
+  special_function_p (name, fndecl, &returns_twice, &is_longjmp,
+                     &is_malloc, &may_be_alloca);
 
-  original_args_size = args_size;
-  if (args_size.var)
-    {
-      /* If this function requires a variable-sized argument list, don't try to
-        make a cse'able block for this call.  We may be able to do this
-        eventually, but it is too complicated to keep track of what insns go
-        in the cse'able block and which don't.  */
+  if (may_be_alloca)
+    current_function_calls_alloca = 1;
 
-      is_const = 0;
-      must_preallocate = 1;
+  /* Operand 0 is a pointer-to-function; get the type of the function.  */
+  funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+  if (! POINTER_TYPE_P (funtype))
+    abort ();
+  funtype = TREE_TYPE (funtype);
 
-      args_size.var = ARGS_SIZE_TREE (args_size);
-      args_size.constant = 0;
+  /* When calling a const function, we must pop the stack args right away,
+     so that the pop is deleted or moved with the call.  */
+  if (is_const)
+    NO_DEFER_POP;
 
-#ifdef STACK_BOUNDARY
-      if (STACK_BOUNDARY != BITS_PER_UNIT)
-       args_size.var = round_up (args_size.var, STACK_BYTES);
-#endif
+  /* Don't let pending stack adjusts add up to too much.
+     Also, do all pending adjustments now
+     if there is any chance this might be a call to alloca.  */
 
-#ifdef REG_PARM_STACK_SPACE
-      if (reg_parm_stack_space > 0)
-       {
-         args_size.var
-           = size_binop (MAX_EXPR, args_size.var,
-                         size_int (REG_PARM_STACK_SPACE (fndecl)));
+  if (pending_stack_adjust >= 32
+      || (pending_stack_adjust > 0 && may_be_alloca))
+    do_pending_stack_adjust ();
 
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
-         /* The area corresponding to register parameters is not to count in
-            the size of the block we need.  So make the adjustment.  */
-         args_size.var
-           = size_binop (MINUS_EXPR, args_size.var,
-                         size_int (reg_parm_stack_space));
-#endif
-       }
-#endif
-    }
-  else
+  /* Push the temporary stack slot level so that we can free any temporaries
+     we make.  */
+  push_temp_slots ();
+
+  /* Start updating where the next arg would go.
+
+     On some machines (such as the PA) indirect calls have a different
+     calling convention than normal calls.  The last argument in
+     INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
+     or not.  */
+  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
+
+  /* If struct_value_rtx is 0, it means pass the address
+     as if it were an extra parameter.  */
+  if (structure_value_addr && struct_value_rtx == 0)
     {
-#ifdef STACK_BOUNDARY
-      args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
-                            / STACK_BYTES) * STACK_BYTES);
+      /* If structure_value_addr is a REG other than
+        virtual_outgoing_args_rtx, we can use always use it.  If it
+        is not a REG, we must always copy it into a register.
+        If it is virtual_outgoing_args_rtx, we must copy it to another
+        register in some cases.  */
+      rtx temp = (GET_CODE (structure_value_addr) != REG
+#ifdef ACCUMULATE_OUTGOING_ARGS
+                 || (stack_arg_under_construction
+                     && structure_value_addr == virtual_outgoing_args_rtx)
 #endif
+                 ? copy_addr_to_reg (structure_value_addr)
+                 : structure_value_addr);
 
-#ifdef REG_PARM_STACK_SPACE
-      args_size.constant = MAX (args_size.constant,
-                               reg_parm_stack_space);
-#ifdef MAYBE_REG_PARM_STACK_SPACE
-      if (reg_parm_stack_space == 0)
-       args_size.constant = 0;
-#endif
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
-      args_size.constant -= reg_parm_stack_space;
-#endif
-#endif
+      actparms
+       = tree_cons (error_mark_node,
+                    make_tree (build_pointer_type (TREE_TYPE (funtype)),
+                               temp),
+                    actparms);
+      structure_value_addr_parm = 1;
     }
 
-  /* See if we have or want to preallocate stack space.
+  /* Count the arguments and set NUM_ACTUALS.  */
+  for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++;
+  num_actuals = i;
 
-     If we would have to push a partially-in-regs parm
-     before other stack parms, preallocate stack space instead.
+  /* Compute number of named args.
+     Normally, don't include the last named arg if anonymous args follow.
+     We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero.
+     (If no anonymous args follow, the result of list_length is actually
+     one too large.  This is harmless.)
+
+     If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is
+     zero, this machine will be able to place unnamed args that were passed in
+     registers into the stack.  So treat all args as named.  This allows the
+     insns emitting for a specific argument list to be independent of the
+     function declaration.
 
-     If the size of some parm is not a multiple of the required stack
-     alignment, we must preallocate.
+     If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any reliable
+     way to pass unnamed args in registers, so we must force them into
+     memory.  */
 
-     If the total size of arguments that would otherwise create a copy in
-     a temporary (such as a CALL) is more than half the total argument list
-     size, preallocation is faster.
+  if ((STRICT_ARGUMENT_NAMING
+       || ! PRETEND_OUTGOING_VARARGS_NAMED)
+      && TYPE_ARG_TYPES (funtype) != 0)
+    n_named_args
+      = (list_length (TYPE_ARG_TYPES (funtype))
+        /* Don't include the last named arg.  */
+        - (STRICT_ARGUMENT_NAMING ? 0 : 1)
+        /* Count the struct value address, if it is passed as a parm.  */
+        + structure_value_addr_parm);
+  else
+    /* If we know nothing, treat all args as named.  */
+    n_named_args = num_actuals;
 
-     Another reason to preallocate is if we have a machine (like the m88k)
-     where stack alignment is required to be maintained between every
-     pair of insns, not just when the call is made.  However, we assume here
-     that such machines either do not have push insns (and hence preallocation
-     would occur anyway) or the problem is taken care of with
-     PUSH_ROUNDING.  */
+  /* Make a vector to hold all the information about each arg.  */
+  args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
+  bzero ((char *) args, num_actuals * sizeof (struct arg_data));
 
-  if (! must_preallocate)
+  /* Build up entries inthe ARGS array, compute the size of the arguments
+     into ARGS_SIZE, etc.  */
+  initialize_argument_information (num_actuals, args, &args_size, n_named_args,
+                                  actparms, fndecl, &args_so_far,
+                                  reg_parm_stack_space, &old_stack_level,
+                                  &old_pending_adj, &must_preallocate,
+                                  &is_const);
+
+#ifdef FINAL_REG_PARM_STACK_SPACE
+  reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+                                                    args_size.var);
+#endif
+      
+  if (args_size.var)
     {
-      int partial_seen = 0;
-      int copy_to_evaluate_size = 0;
+      /* If this function requires a variable-sized argument list, don't try to
+        make a cse'able block for this call.  We may be able to do this
+        eventually, but it is too complicated to keep track of what insns go
+        in the cse'able block and which don't.  */
 
-      for (i = 0; i < num_actuals && ! must_preallocate; i++)
-       {
-         if (args[i].partial > 0 && ! args[i].pass_on_stack)
-           partial_seen = 1;
-         else if (partial_seen && args[i].reg == 0)
-           must_preallocate = 1;
+      is_const = 0;
+      must_preallocate = 1;
+    }
 
-         if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
-             && (TREE_CODE (args[i].tree_value) == CALL_EXPR
-                 || TREE_CODE (args[i].tree_value) == TARGET_EXPR
-                 || TREE_CODE (args[i].tree_value) == COND_EXPR
-                 || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
-           copy_to_evaluate_size
-             += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
-       }
+  /* Compute the actual size of the argument block required.  The variable
+     and constant sizes must be combined, the size may have to be rounded,
+     and there may be a minimum required size.  */
+  unadjusted_args_size
+    = compute_argument_block_size (reg_parm_stack_space, &args_size);
 
-      if (copy_to_evaluate_size * 2 >= args_size.constant
-         && args_size.constant > 0)
-       must_preallocate = 1;
-    }
+  /* Now make final decision about preallocating stack space.  */
+  must_preallocate = finalize_must_preallocate (must_preallocate,
+                                               num_actuals, args, &args_size);
 
   /* If the structure value address will reference the stack pointer, we must
      stabilize it.  We don't need to do this if we know that we are not going
@@ -1335,51 +2003,9 @@ expand_call (exp, target, ignore)
          ))
     structure_value_addr = copy_to_reg (structure_value_addr);
 
-  /* If this function call is cse'able, precompute all the parameters.
-     Note that if the parameter is constructed into a temporary, this will
-     cause an additional copy because the parameter will be constructed
-     into a temporary location and then copied into the outgoing arguments.
-     If a parameter contains a call to alloca and this function uses the
-     stack, precompute the parameter.  */
-
-  /* If we preallocated the stack space, and some arguments must be passed
-     on the stack, then we must precompute any parameter which contains a
-     function call which will store arguments on the stack.
-     Otherwise, evaluating the parameter may clobber previous parameters
-     which have already been stored into the stack.  */
-
-  for (i = 0; i < num_actuals; i++)
-    if (is_const
-       || ((args_size.var != 0 || args_size.constant != 0)
-           && calls_function (args[i].tree_value, 1))
-       || (must_preallocate && (args_size.var != 0 || args_size.constant != 0)
-           && calls_function (args[i].tree_value, 0)))
-      {
-       /* If this is an addressable type, we cannot pre-evaluate it.  */
-       if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
-         abort ();
-
-       push_temp_slots ();
-
-       args[i].initial_value = args[i].value
-         = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
-
-       preserve_temp_slots (args[i].value);
-       pop_temp_slots ();
-
-       /* ANSI doesn't require a sequence point here,
-          but PCC has one, so this will avoid some problems.  */
-       emit_queue ();
-
-       args[i].initial_value = args[i].value
-         = protect_from_queue (args[i].initial_value, 0);
-
-       if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
-         args[i].value
-           = convert_modes (args[i].mode, 
-                            TYPE_MODE (TREE_TYPE (args[i].tree_value)),
-                            args[i].value, args[i].unsignedp);
-      }
+  /* Precompute any arguments as needed.  */
+  precompute_arguments (is_const, must_preallocate, num_actuals,
+                        args, &args_size);
 
   /* Now we are about to start emitting insns that can be deleted
      if a libcall is deleted.  */
@@ -1438,7 +2064,7 @@ expand_call (exp, target, ignore)
             Another approach might be to try to reorder the argument
             evaluations to avoid this conflicting stack usage.  */
 
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
          /* Since we will be writing into the entire argument area, the
             map must be allocated for its entire size, not just the part that
             is the responsibility of the caller.  */
@@ -1510,7 +2136,7 @@ expand_call (exp, target, ignore)
      to initialize an argument.  */
   if (stack_arg_under_construction)
     {
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
       rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant);
 #else
       rtx push_size = GEN_INT (args_size.constant);
@@ -1520,236 +2146,70 @@ expand_call (exp, target, ignore)
          emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
          old_pending_adj = pending_stack_adjust;
          pending_stack_adjust = 0;
-         /* stack_arg_under_construction says whether a stack arg is
-            being constructed at the old stack level.  Pushing the stack
-            gets a clean outgoing argument block.  */
-         old_stack_arg_under_construction = stack_arg_under_construction;
-         stack_arg_under_construction = 0;
-         /* Make a new map for the new argument list.  */
-         stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use);
-         bzero (stack_usage_map, highest_outgoing_arg_in_use);
-         highest_outgoing_arg_in_use = 0;
-       }
-      allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT);
-    }
-  /* If argument evaluation might modify the stack pointer, copy the
-     address of the argument list to a register.  */
-  for (i = 0; i < num_actuals; i++)
-    if (args[i].pass_on_stack)
-      {
-       argblock = copy_addr_to_reg (argblock);
-       break;
-      }
-#endif
-
-
-  /* If we preallocated stack space, compute the address of each argument.
-     We need not ensure it is a valid memory address here; it will be 
-     validized when it is used.  */
-  if (argblock)
-    {
-      rtx arg_reg = argblock;
-      int arg_offset = 0;
-
-      if (GET_CODE (argblock) == PLUS)
-       arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
-
-      for (i = 0; i < num_actuals; i++)
-       {
-         rtx offset = ARGS_SIZE_RTX (args[i].offset);
-         rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
-         rtx addr;
-
-         /* Skip this parm if it will not be passed on the stack.  */
-         if (! args[i].pass_on_stack && args[i].reg != 0)
-           continue;
-
-         if (GET_CODE (offset) == CONST_INT)
-           addr = plus_constant (arg_reg, INTVAL (offset));
-         else
-           addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
-
-         addr = plus_constant (addr, arg_offset);
-         args[i].stack = gen_rtx_MEM (args[i].mode, addr);
-         MEM_IN_STRUCT_P (args[i].stack)
-           = AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value));
-
-         if (GET_CODE (slot_offset) == CONST_INT)
-           addr = plus_constant (arg_reg, INTVAL (slot_offset));
-         else
-           addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
-
-         addr = plus_constant (addr, arg_offset);
-         args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
-       }
-    }
-                                              
-#ifdef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
-  /* If we push args individually in reverse order, perform stack alignment
-     before the first push (the last arg).  */
-  if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant
-                               - original_args_size.constant));
-#endif
-#endif
-
-  /* Don't try to defer pops if preallocating, not even from the first arg,
-     since ARGBLOCK probably refers to the SP.  */
-  if (argblock)
-    NO_DEFER_POP;
-
-  /* Get the function to call, in the form of RTL.  */
-  if (fndecl)
-    {
-      /* If this is the first use of the function, see if we need to
-        make an external definition for it.  */
-      if (! TREE_USED (fndecl))
-       {
-         assemble_external (fndecl);
-         TREE_USED (fndecl) = 1;
-       }
-
-      /* Get a SYMBOL_REF rtx for the function address.  */
-      funexp = XEXP (DECL_RTL (fndecl), 0);
-    }
-  else
-    /* Generate an rtx (probably a pseudo-register) for the address.  */
-    {
-      push_temp_slots ();
-      funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
-      pop_temp_slots ();       /* FUNEXP can't be BLKmode */
-
-      /* Check the function is executable.  */
-      if (flag_check_memory_usage)
-       emit_library_call (chkr_check_exec_libfunc, 1,
-                          VOIDmode, 1,
-                          funexp, ptr_mode);
-      emit_queue ();
-    }
-
-  /* Figure out the register where the value, if any, will come back.  */
-  valreg = 0;
-  if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
-      && ! structure_value_addr)
-    {
-      if (pcc_struct_value)
-       valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
-                                     fndecl);
-      else
-       valreg = hard_function_value (TREE_TYPE (exp), fndecl);
-    }
-
-  /* Precompute all register parameters.  It isn't safe to compute anything
-     once we have started filling any specific hard regs.  */
-  reg_parm_seen = 0;
-  for (i = 0; i < num_actuals; i++)
-    if (args[i].reg != 0 && ! args[i].pass_on_stack)
-      {
-       reg_parm_seen = 1;
-
-       if (args[i].value == 0)
-         {
-           push_temp_slots ();
-           args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
-                                        VOIDmode, 0);
-           preserve_temp_slots (args[i].value);
-           pop_temp_slots ();
-
-           /* ANSI doesn't require a sequence point here,
-              but PCC has one, so this will avoid some problems.  */
-           emit_queue ();
-         }
-
-       /* If we are to promote the function arg to a wider mode,
-          do it now.  */
-
-       if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
-         args[i].value
-           = convert_modes (args[i].mode,
-                            TYPE_MODE (TREE_TYPE (args[i].tree_value)),
-                            args[i].value, args[i].unsignedp);
-
-       /* If the value is expensive, and we are inside an appropriately 
-          short loop, put the value into a pseudo and then put the pseudo
-          into the hard reg.
-
-          For small register classes, also do this if this call uses
-          register parameters.  This is to avoid reload conflicts while
-          loading the parameters registers.  */
-
-       if ((! (GET_CODE (args[i].value) == REG
-               || (GET_CODE (args[i].value) == SUBREG
-                   && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
-           && args[i].mode != BLKmode
-           && rtx_cost (args[i].value, SET) > 2
-           && ((SMALL_REGISTER_CLASSES && reg_parm_seen)
-               || preserve_subexpressions_p ()))
-         args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
+         /* stack_arg_under_construction says whether a stack arg is
+            being constructed at the old stack level.  Pushing the stack
+            gets a clean outgoing argument block.  */
+         old_stack_arg_under_construction = stack_arg_under_construction;
+         stack_arg_under_construction = 0;
+         /* Make a new map for the new argument list.  */
+         stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use);
+         bzero (stack_usage_map, highest_outgoing_arg_in_use);
+         highest_outgoing_arg_in_use = 0;
+       }
+      allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT);
+    }
+  /* If argument evaluation might modify the stack pointer, copy the
+     address of the argument list to a register.  */
+  for (i = 0; i < num_actuals; i++)
+    if (args[i].pass_on_stack)
+      {
+       argblock = copy_addr_to_reg (argblock);
+       break;
       }
+#endif
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-  /* The argument list is the property of the called routine and it
-     may clobber it.  If the fixed area has been used for previous
-     parameters, we must save and restore it.
-
-     Here we compute the boundary of the that needs to be saved, if any.  */
+  compute_argument_addresses (args, argblock, num_actuals);
 
-#ifdef ARGS_GROW_DOWNWARD
-  for (i = 0; i < reg_parm_stack_space + 1; i++)
-#else
-  for (i = 0; i < reg_parm_stack_space; i++)
+#ifdef PUSH_ARGS_REVERSED
+#ifdef PREFERRED_STACK_BOUNDARY
+  /* If we push args individually in reverse order, perform stack alignment
+     before the first push (the last arg).  */
+  if (argblock == 0)
+    anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
+#endif
 #endif
-    {
-      if (i >=  highest_outgoing_arg_in_use
-         || stack_usage_map[i] == 0)
-       continue;
 
-      if (low_to_save == -1)
-       low_to_save = i;
+  /* Don't try to defer pops if preallocating, not even from the first arg,
+     since ARGBLOCK probably refers to the SP.  */
+  if (argblock)
+    NO_DEFER_POP;
 
-      high_to_save = i;
-    }
+  funexp = rtx_for_function_call (fndecl, exp);
 
-  if (low_to_save >= 0)
+  /* Figure out the register where the value, if any, will come back.  */
+  valreg = 0;
+  if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+      && ! structure_value_addr)
     {
-      int num_to_save = high_to_save - low_to_save + 1;
-      enum machine_mode save_mode
-       = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
-      rtx stack_area;
-
-      /* If we don't have the required alignment, must do this in BLKmode.  */
-      if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
-                              BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
-       save_mode = BLKmode;
-
-#ifdef ARGS_GROW_DOWNWARD
-      stack_area = gen_rtx_MEM (save_mode,
-                               memory_address (save_mode,
-                                               plus_constant (argblock,
-                                                              - high_to_save)));
-#else
-      stack_area = gen_rtx_MEM (save_mode,
-                               memory_address (save_mode,
-                                               plus_constant (argblock,
-                                                              low_to_save)));
-#endif
-      if (save_mode == BLKmode)
-       {
-         save_area = assign_stack_temp (BLKmode, num_to_save, 0);
-         MEM_IN_STRUCT_P (save_area) = 0;
-         emit_block_move (validize_mem (save_area), stack_area,
-                          GEN_INT (num_to_save),
-                          PARM_BOUNDARY / BITS_PER_UNIT);
-       }
+      if (pcc_struct_value)
+       valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+                                     fndecl);
       else
-       {
-         save_area = gen_reg_rtx (save_mode);
-         emit_move_insn (save_area, stack_area);
-       }
+       valreg = hard_function_value (TREE_TYPE (exp), fndecl);
     }
+
+  /* Precompute all register parameters.  It isn't safe to compute anything
+     once we have started filling any specific hard regs.  */
+  precompute_register_parameters (num_actuals, args, &reg_parm_seen);
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+
+  /* Save the fixed argument area if it's part of the caller's frame and
+     is clobbered by argument setup for this call.  */
+  save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+                                       &low_to_save, &high_to_save);
 #endif
-         
+                       
 
   /* Now store (and compute if necessary) all non-register parms.
      These come before register parms, since they can require block-moves,
@@ -1760,75 +2220,14 @@ expand_call (exp, target, ignore)
   for (i = 0; i < num_actuals; i++)
     if (args[i].reg == 0 || args[i].pass_on_stack)
       store_one_arg (&args[i], argblock, may_be_alloca,
-                    args_size.var != 0, fndecl, reg_parm_stack_space);
+                    args_size.var != 0, reg_parm_stack_space);
 
   /* If we have a parm that is passed in registers but not in memory
      and whose alignment does not permit a direct copy into registers,
      make a group of pseudos that correspond to each register that we
      will later fill.  */
-
   if (STRICT_ALIGNMENT)
-    for (i = 0; i < num_actuals; i++)
-      if (args[i].reg != 0 && ! args[i].pass_on_stack
-       && args[i].mode == BLKmode
-         && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
-             < MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
-       {
-         int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
-         int big_endian_correction = 0;
-
-         args[i].n_aligned_regs
-           = args[i].partial ? args[i].partial
-             : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
-
-         args[i].aligned_regs = (rtx *) alloca (sizeof (rtx)
-                                                * args[i].n_aligned_regs);
-
-         /* Structures smaller than a word are aligned to the least
-            significant byte (to the right).  On a BYTES_BIG_ENDIAN machine,
-            this means we must skip the empty high order bytes when
-            calculating the bit offset.  */
-         if (BYTES_BIG_ENDIAN && bytes < UNITS_PER_WORD)
-           big_endian_correction = (BITS_PER_WORD  - (bytes * BITS_PER_UNIT));
-
-         for (j = 0; j < args[i].n_aligned_regs; j++)
-           {
-             rtx reg = gen_reg_rtx (word_mode);
-             rtx word = operand_subword_force (args[i].value, j, BLKmode);
-             int bitsize = TYPE_ALIGN (TREE_TYPE (args[i].tree_value));
-             int bitpos;
-
-             args[i].aligned_regs[j] = reg;
-
-             /* Clobber REG and move each partword into it.  Ensure we don't
-                go past the end of the structure.  Note that the loop below
-                works because we've already verified that padding
-                and endianness are compatible.
-
-                We use to emit a clobber here but that doesn't let later
-                passes optimize the instructions we emit.  By storing 0 into
-                the register later passes know the first AND to zero out the
-                bitfield being set in the register is unnecessary.  The store
-                of 0 will be deleted as will at least the first AND.  */
-
-             emit_move_insn (reg, const0_rtx);
-
-             for (bitpos = 0;
-                  bitpos < BITS_PER_WORD && bytes > 0;
-                  bitpos += bitsize, bytes -= bitsize / BITS_PER_UNIT)
-               {
-                 int xbitpos = bitpos + big_endian_correction;
-
-                 store_bit_field (reg, bitsize, xbitpos, word_mode,
-                                  extract_bit_field (word, bitsize, bitpos, 1,
-                                                     NULL_RTX, word_mode,
-                                                     word_mode,
-                                                     bitsize / BITS_PER_UNIT,
-                                                     BITS_PER_WORD),
-                                  bitsize / BITS_PER_UNIT, BITS_PER_WORD);
-               }
-           }
-       }
+    store_unaligned_arguments_into_pseudos (args, num_actuals);
 
   /* Now store any partially-in-registers parm.
      This is the last place a block-move can happen.  */
@@ -1836,15 +2235,14 @@ expand_call (exp, target, ignore)
     for (i = 0; i < num_actuals; i++)
       if (args[i].partial != 0 && ! args[i].pass_on_stack)
        store_one_arg (&args[i], argblock, may_be_alloca,
-                      args_size.var != 0, fndecl, reg_parm_stack_space);
+                      args_size.var != 0, reg_parm_stack_space);
 
 #ifndef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   /* If we pushed args in forward order, perform stack alignment
      after pushing the last arg.  */
   if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant
-                               - original_args_size.constant));
+    anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
 #endif
 #endif
 
@@ -1865,10 +2263,10 @@ expand_call (exp, target, ignore)
                                                NULL_RTX)));
 
       /* Mark the memory for the aggregate as write-only.  */
-      if (flag_check_memory_usage)
+      if (current_function_check_memory_usage)
        emit_library_call (chkr_set_right_libfunc, 1,
                           VOIDmode, 3,
-                          structure_value_addr, ptr_mode, 
+                          structure_value_addr, Pmode, 
                           GEN_INT (struct_value_size), TYPE_MODE (sizetype),
                           GEN_INT (MEMORY_USE_WO),
                           TYPE_MODE (integer_type_node));
@@ -1879,71 +2277,7 @@ expand_call (exp, target, ignore)
 
   funexp = prepare_call_address (funexp, fndecl, &call_fusage, reg_parm_seen);
 
-  /* Now do the register loads required for any wholly-register parms or any
-     parms which are passed both on the stack and in a register.  Their
-     expressions were already evaluated. 
-
-     Mark all register-parms as living through the call, putting these USE
-     insns in the CALL_INSN_FUNCTION_USAGE field.  */
-
-#ifdef LOAD_ARGS_REVERSED
-  for (i = num_actuals - 1; i >= 0; i--)
-#else
-  for (i = 0; i < num_actuals; i++)
-#endif
-    {
-      rtx reg = args[i].reg;
-      int partial = args[i].partial;
-      int nregs;
-
-      if (reg)
-       {
-         /* Set to non-negative if must move a word at a time, even if just
-            one word (e.g, partial == 1 && mode == DFmode).  Set to -1 if
-            we just use a normal move insn.  This value can be zero if the
-            argument is a zero size structure with no fields.  */
-         nregs = (partial ? partial
-                  : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
-                     ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
-                         + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
-                     : -1));
-
-         /* Handle calls that pass values in multiple non-contiguous
-            locations.  The Irix 6 ABI has examples of this.  */
-
-         if (GET_CODE (reg) == PARALLEL)
-           emit_group_load (reg, args[i].value);
-
-         /* If simple case, just do move.  If normal partial, store_one_arg
-            has already loaded the register for us.  In all other cases,
-            load the register(s) from memory.  */
-
-         else if (nregs == -1)
-           emit_move_insn (reg, args[i].value);
-
-         /* If we have pre-computed the values to put in the registers in
-            the case of non-aligned structures, copy them in now.  */
-
-         else if (args[i].n_aligned_regs != 0)
-           for (j = 0; j < args[i].n_aligned_regs; j++)
-             emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
-                             args[i].aligned_regs[j]);
-
-         else if (partial == 0 || args[i].pass_on_stack)
-           move_block_to_reg (REGNO (reg),
-                              validize_mem (args[i].value), nregs,
-                              args[i].mode);
-
-         /* Handle calls that pass values in multiple non-contiguous
-            locations.  The Irix 6 ABI has examples of this.  */
-         if (GET_CODE (reg) == PARALLEL)
-           use_group_regs (&call_fusage, reg);
-         else if (nregs == -1)
-           use_reg (&call_fusage, reg);
-         else
-           use_regs (&call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
-       }
-    }
+  load_register_parameters (args, num_actuals, &call_fusage);
 
   /* Perform postincrements before actually calling the function.  */
   emit_queue ();
@@ -1951,7 +2285,8 @@ expand_call (exp, target, ignore)
   /* All arguments and registers used for the call must be set up by now!  */
 
   /* Generate the actual call instruction.  */
-  emit_call_1 (funexp, fndecl, funtype, args_size.constant, struct_value_size,
+  emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
+              args_size.constant, struct_value_size,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
               valreg, old_inhibit_defer_pop, call_fusage, is_const);
 
@@ -2063,7 +2398,8 @@ expand_call (exp, target, ignore)
          target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
                                memory_address (TYPE_MODE (TREE_TYPE (exp)),
                                                structure_value_addr));
-         MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
+         MEM_SET_IN_STRUCT_P (target,
+                              AGGREGATE_TYPE_P (TREE_TYPE (exp)));
        }
     }
   else if (pcc_struct_value)
@@ -2073,21 +2409,23 @@ expand_call (exp, target, ignore)
         never use this value more than once in one expression.  */
       target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
                            copy_to_reg (valreg));
-      MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
+      MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
     }
   /* Handle calls that return values in multiple non-contiguous locations.
      The Irix 6 ABI has examples of this.  */
   else if (GET_CODE (valreg) == PARALLEL)
     {
+      int bytes = int_size_in_bytes (TREE_TYPE (exp));
+
       if (target == 0)
        {
-         int bytes = int_size_in_bytes (TREE_TYPE (exp));
          target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0);
-         MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
+         MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
          preserve_temp_slots (target);
        }
 
-      emit_group_store (target, valreg);
+      emit_group_store (target, valreg, bytes,
+                       TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
     }
   else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
           && GET_MODE (target) == GET_MODE (valreg))
@@ -2099,78 +2437,7 @@ expand_call (exp, target, ignore)
        when function inlining is being done.  */
     emit_move_insn (target, valreg);
   else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
-    {
-      /* Some machines (the PA for example) want to return all small
-        structures in registers regardless of the structure's alignment.
-        
-        Deal with them explicitly by copying from the return registers
-        into the target MEM locations.  */
-      int bytes = int_size_in_bytes (TREE_TYPE (exp));
-      int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-      int i;
-      enum machine_mode tmpmode;
-      rtx src, dst;
-      int bitsize = MIN (TYPE_ALIGN (TREE_TYPE (exp)), BITS_PER_WORD);
-      int bitpos, xbitpos, big_endian_correction = 0;
-      
-      if (target == 0)
-       {
-         target = assign_stack_temp (BLKmode, bytes, 0);
-         MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
-         preserve_temp_slots (target);
-       }
-
-      /* This code assumes valreg is at least a full word.  If it isn't,
-        copy it into a new pseudo which is a full word.  */
-      if (GET_MODE (valreg) != BLKmode
-         && GET_MODE_SIZE (GET_MODE (valreg)) < UNITS_PER_WORD)
-       valreg = convert_to_mode (word_mode, valreg,
-                                 TREE_UNSIGNED (TREE_TYPE (exp)));
-
-      /* Structures whose size is not a multiple of a word are aligned
-        to the least significant byte (to the right).  On a BYTES_BIG_ENDIAN
-        machine, this means we must skip the empty high order bytes when
-        calculating the bit offset.  */
-      if (BYTES_BIG_ENDIAN && bytes % UNITS_PER_WORD)
-       big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
-                                                 * BITS_PER_UNIT));
-
-      /* Copy the structure BITSIZE bites at a time.
-
-        We could probably emit more efficient code for machines
-        which do not use strict alignment, but it doesn't seem
-        worth the effort at the current time.  */
-      for (bitpos = 0, xbitpos = big_endian_correction;
-          bitpos < bytes * BITS_PER_UNIT;
-          bitpos += bitsize, xbitpos += bitsize)
-       {
-
-         /* We need a new source operand each time xbitpos is on a 
-            word boundary and when xbitpos == big_endian_correction
-            (the first time through).  */
-         if (xbitpos % BITS_PER_WORD == 0
-             || xbitpos == big_endian_correction)
-           src = operand_subword_force (valreg,
-                                        xbitpos / BITS_PER_WORD, 
-                                        BLKmode);
-
-         /* We need a new destination operand each time bitpos is on
-            a word boundary.  */
-         if (bitpos % BITS_PER_WORD == 0)
-           dst = operand_subword (target, bitpos / BITS_PER_WORD, 1, BLKmode);
-             
-         /* Use xbitpos for the source extraction (right justified) and
-            xbitpos for the destination store (left justified).  */
-         store_bit_field (dst, bitsize, bitpos % BITS_PER_WORD, word_mode,
-                          extract_bit_field (src, bitsize,
-                                             xbitpos % BITS_PER_WORD, 1,
-                                             NULL_RTX, word_mode,
-                                             word_mode,
-                                             bitsize / BITS_PER_UNIT,
-                                             BITS_PER_WORD),
-                          bitsize / BITS_PER_UNIT, BITS_PER_WORD);
-       }
-    }
+    target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
   else
     target = copy_to_reg (valreg);
 
@@ -2213,31 +2480,10 @@ expand_call (exp, target, ignore)
     {
 #ifdef REG_PARM_STACK_SPACE
       if (save_area)
-       {
-         enum machine_mode save_mode = GET_MODE (save_area);
-#ifdef ARGS_GROW_DOWNWARD
-         rtx stack_area
-           = gen_rtx_MEM (save_mode,
-                          memory_address (save_mode,
-                                          plus_constant (argblock,
-                                                         - high_to_save)));
-#else
-         rtx stack_area
-           = gen_rtx_MEM (save_mode,
-                          memory_address (save_mode,
-                                          plus_constant (argblock,
-                                                         low_to_save)));
+       restore_fixed_argument_area (save_area, argblock,
+                                    high_to_save, low_to_save);
 #endif
 
-         if (save_mode != BLKmode)
-           emit_move_insn (stack_area, save_area);
-         else
-           emit_block_move (stack_area, validize_mem (save_area),
-                            GEN_INT (high_to_save - low_to_save + 1),
-                            PARM_BOUNDARY / BITS_PER_UNIT);
-       }
-#endif
-         
       /* If we saved any argument areas, restore them.  */
       for (i = 0; i < num_actuals; i++)
        if (args[i].save_area)
@@ -2265,11 +2511,16 @@ expand_call (exp, target, ignore)
      Check for the handler slots since we might not have a save area
      for non-local gotos.  */
 
-  if (may_be_alloca && nonlocal_goto_handler_slot != 0)
+  if (may_be_alloca && nonlocal_goto_handler_slots != 0)
     emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
 
   pop_temp_slots ();
 
+  /* Free up storage we no longer need.  */
+  for (i = 0; i < num_actuals; ++i)
+    if (args[i].aligned_regs)
+      free (args[i].aligned_regs);
+
   return target;
 }
 \f
@@ -2297,7 +2548,7 @@ void
 emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
                          int nargs, ...))
 {
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   rtx orgfun;
   int no_queue;
   enum machine_mode outmode;
@@ -2319,7 +2570,6 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
   struct arg *argvec;
   int old_inhibit_defer_pop = inhibit_defer_pop;
   rtx call_fusage = 0;
-  /* Size of the stack reserved for parameter registers.  */
   int reg_parm_stack_space = 0;
 #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
   /* Define the boundary of the register parm stack space that needs to be
@@ -2335,16 +2585,17 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
 #endif
 
 #ifdef REG_PARM_STACK_SPACE
+  /* Size of the stack reserved for parameter registers.  */
 #ifdef MAYBE_REG_PARM_STACK_SPACE
   reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
 #else
-  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+  reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
 #endif
 #endif
 
   VA_START (p, nargs);
 
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   orgfun = va_arg (p, rtx);
   no_queue = va_arg (p, int);
   outmode = va_arg (p, enum machine_mode);
@@ -2430,16 +2681,11 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
       if (argvec[count].size.var)
        abort ();
 
-#ifndef REG_PARM_STACK_SPACE
-      if (argvec[count].partial)
+      if (reg_parm_stack_space == 0 && argvec[count].partial)
        argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
-#endif
 
       if (argvec[count].reg == 0 || argvec[count].partial != 0
-#ifdef REG_PARM_STACK_SPACE
-         || 1
-#endif
-         )
+         || reg_parm_stack_space > 0)
        args_size.constant += argvec[count].size.constant;
 
       FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
@@ -2456,18 +2702,17 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
   assemble_external_libcall (fun);
 
   original_args_size = args_size;
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
                         / STACK_BYTES) * STACK_BYTES);
 #endif
 
-#ifdef REG_PARM_STACK_SPACE
   args_size.constant = MAX (args_size.constant,
                            reg_parm_stack_space);
+
 #ifndef OUTGOING_REG_PARM_STACK_SPACE
   args_size.constant -= reg_parm_stack_space;
 #endif
-#endif
 
   if (args_size.constant > current_function_outgoing_args_size)
     current_function_outgoing_args_size = args_size.constant;
@@ -2486,7 +2731,8 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
      evaluations to avoid this conflicting stack usage.  */
 
   needed = args_size.constant;
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
   /* Since we will be writing into the entire argument area, the
      map must be allocated for its entire size, not just the part that
      is the responsibility of the caller.  */
@@ -2524,7 +2770,7 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
 #endif
 
 #ifdef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   /* If we push args individually in reverse order, perform stack alignment
      before the first push (the last arg).  */
   if (argblock == 0)
@@ -2590,7 +2836,6 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
       if (save_mode == BLKmode)
        {
          save_area = assign_stack_temp (BLKmode, num_to_save, 0);
-         MEM_IN_STRUCT_P (save_area) = 0;
          emit_block_move (validize_mem (save_area), stack_area,
                           GEN_INT (num_to_save),
                           PARM_BOUNDARY / BITS_PER_UNIT);
@@ -2613,7 +2858,9 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
       register rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
+#ifdef ACCUMULATE_OUTGOING_ARGS
       int lower_bound, upper_bound, i;
+#endif
 
       if (! (reg != 0 && partial == 0))
        {
@@ -2633,17 +2880,14 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
 
          for (i = lower_bound; i < upper_bound; i++)
            if (stack_usage_map[i]
-#ifdef REG_PARM_STACK_SPACE
                /* Don't store things in the fixed argument area at this point;
                   it has already been saved.  */
-               && i > reg_parm_stack_space
-#endif
-               )
+               && i > reg_parm_stack_space)
              break;
 
          if (i != upper_bound)
            {
-             /* We need to make a save area.  See what mode we can make it.  */
+             /* We need to make a save area.  See what mode we can make it. */
              enum machine_mode save_mode
                = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
                                 MODE_INT, 1);
@@ -2656,7 +2900,8 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
            }
 #endif
          emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
-                         argblock, GEN_INT (argvec[argnum].offset.constant));
+                         argblock, GEN_INT (argvec[argnum].offset.constant),
+                         reg_parm_stack_space);
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
          /* Now mark the segment we just used.  */
@@ -2669,7 +2914,7 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
     }
 
 #ifndef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   /* If we pushed args in forward order, perform stack alignment
      after pushing the last arg.  */
   if (argblock == 0)
@@ -2692,7 +2937,6 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
      are to be pushed.  */
   for (count = 0; count < nargs; count++, argnum += inc)
     {
-      register enum machine_mode mode = argvec[argnum].mode;
       register rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
@@ -2727,7 +2971,7 @@ emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
                get_identifier (XSTR (orgfun, 0)), 
               build_function_type (outmode == VOIDmode ? void_type_node
                                    : type_for_mode (outmode, 0), NULL_TREE),
-               args_size.constant, 0,
+              original_args_size.constant, args_size.constant, 0,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
               outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
               old_inhibit_defer_pop + 1, call_fusage, no_queue);
@@ -2794,7 +3038,7 @@ rtx
 emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
                                enum machine_mode outmode, int nargs, ...))
 {
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   rtx orgfun;
   rtx value;
   int no_queue;
@@ -2817,13 +3061,14 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
   struct arg *argvec;
   int old_inhibit_defer_pop = inhibit_defer_pop;
   rtx call_fusage = 0;
-  /* Size of the stack reserved for parameter registers.  */
-  int reg_parm_stack_space = 0;
   rtx mem_value = 0;
   int pcc_struct_value = 0;
   int struct_value_size = 0;
   int is_const;
+  int reg_parm_stack_space = 0;
+#ifdef ACCUMULATE_OUTGOING_ARGS
   int needed;
+#endif
 
 #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
   /* Define the boundary of the register parm stack space that needs to be
@@ -2833,6 +3078,7 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
 #endif
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
+  /* Size of the stack reserved for parameter registers.  */
   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
 #endif
@@ -2841,13 +3087,13 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
 #ifdef MAYBE_REG_PARM_STACK_SPACE
   reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
 #else
-  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+  reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
 #endif
 #endif
 
   VA_START (p, nargs);
 
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   orgfun = va_arg (p, rtx);
   value = va_arg (p, rtx);
   no_queue = va_arg (p, int);
@@ -2932,10 +3178,7 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
 
 
       if (argvec[count].reg == 0 || argvec[count].partial != 0
-#ifdef REG_PARM_STACK_SPACE
-         || 1
-#endif
-         )
+         || reg_parm_stack_space > 0)
        args_size.constant += argvec[count].size.constant;
 
       FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree) 0, 1);
@@ -3002,16 +3245,11 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
       if (argvec[count].size.var)
        abort ();
 
-#ifndef REG_PARM_STACK_SPACE
-      if (argvec[count].partial)
+      if (reg_parm_stack_space == 0 && argvec[count].partial)
        argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
-#endif
 
       if (argvec[count].reg == 0 || argvec[count].partial != 0
-#ifdef REG_PARM_STACK_SPACE
-         || 1
-#endif
-         )
+         || reg_parm_stack_space > 0)
        args_size.constant += argvec[count].size.constant;
 
       FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
@@ -3027,18 +3265,17 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
   assemble_external_libcall (fun);
 
   original_args_size = args_size;
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
                         / STACK_BYTES) * STACK_BYTES);
 #endif
 
-#ifdef REG_PARM_STACK_SPACE
   args_size.constant = MAX (args_size.constant,
                            reg_parm_stack_space);
+
 #ifndef OUTGOING_REG_PARM_STACK_SPACE
   args_size.constant -= reg_parm_stack_space;
 #endif
-#endif
 
   if (args_size.constant > current_function_outgoing_args_size)
     current_function_outgoing_args_size = args_size.constant;
@@ -3057,7 +3294,8 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
      evaluations to avoid this conflicting stack usage.  */
 
   needed = args_size.constant;
-#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
   /* Since we will be writing into the entire argument area, the
      map must be allocated for its entire size, not just the part that
      is the responsibility of the caller.  */
@@ -3095,7 +3333,7 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
 #endif
 
 #ifdef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   /* If we push args individually in reverse order, perform stack alignment
      before the first push (the last arg).  */
   if (argblock == 0)
@@ -3161,7 +3399,6 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
       if (save_mode == BLKmode)
        {
          save_area = assign_stack_temp (BLKmode, num_to_save, 0);
-         MEM_IN_STRUCT_P (save_area) = 0;
          emit_block_move (validize_mem (save_area), stack_area,
                           GEN_INT (num_to_save),
                           PARM_BOUNDARY / BITS_PER_UNIT);
@@ -3184,7 +3421,9 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
       register rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
+#ifdef ACCUMULATE_OUTGOING_ARGS
       int lower_bound, upper_bound, i;
+#endif
 
       if (! (reg != 0 && partial == 0))
        {
@@ -3204,17 +3443,14 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
 
          for (i = lower_bound; i < upper_bound; i++)
            if (stack_usage_map[i]
-#ifdef REG_PARM_STACK_SPACE
                /* Don't store things in the fixed argument area at this point;
                   it has already been saved.  */
-               && i > reg_parm_stack_space
-#endif
-               )
+               && i > reg_parm_stack_space)
              break;
 
          if (i != upper_bound)
            {
-             /* We need to make a save area.  See what mode we can make it.  */
+             /* We need to make a save area.  See what mode we can make it. */
              enum machine_mode save_mode
                = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
                                 MODE_INT, 1);
@@ -3228,7 +3464,8 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
            }
 #endif
          emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
-                         argblock, GEN_INT (argvec[argnum].offset.constant));
+                         argblock, GEN_INT (argvec[argnum].offset.constant),
+                         reg_parm_stack_space);
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
          /* Now mark the segment we just used.  */
@@ -3241,7 +3478,7 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
     }
 
 #ifndef PUSH_ARGS_REVERSED
-#ifdef STACK_BOUNDARY
+#ifdef PREFERRED_STACK_BOUNDARY
   /* If we pushed args in forward order, perform stack alignment
      after pushing the last arg.  */
   if (argblock == 0)
@@ -3264,7 +3501,6 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
      are to be pushed.  */
   for (count = 0; count < nargs; count++, argnum += inc)
     {
-      register enum machine_mode mode = argvec[argnum].mode;
       register rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
@@ -3308,7 +3544,8 @@ emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
   emit_call_1 (fun, 
                get_identifier (XSTR (orgfun, 0)),
               build_function_type (type_for_mode (outmode, 0), NULL_TREE),
-               args_size.constant, struct_value_size,
+               original_args_size.constant, args_size.constant,
+              struct_value_size,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
               mem_value == 0 ? hard_libcall_value (outmode) : NULL_RTX,
               old_inhibit_defer_pop + 1, call_fusage, is_const);
@@ -3435,20 +3672,21 @@ target_for_arg (type, size, args_addr, offset)
    FNDECL is the declaration of the function we are calling.  */
 
 static void
-store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
+store_one_arg (arg, argblock, may_be_alloca, variable_size,
               reg_parm_stack_space)
      struct arg_data *arg;
      rtx argblock;
      int may_be_alloca;
-     int variable_size;
-     tree fndecl;
+     int variable_size ATTRIBUTE_UNUSED;
      int reg_parm_stack_space;
 {
   register tree pval = arg->tree_value;
   rtx reg = 0;
   int partial = 0;
   int used = 0;
+#ifdef ACCUMULATE_OUTGOING_ARGS
   int i, lower_bound, upper_bound;
+#endif
 
   if (TREE_CODE (pval) == ERROR_MARK)
     return;
@@ -3482,12 +3720,9 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
 
       for (i = lower_bound; i < upper_bound; i++)
        if (stack_usage_map[i]
-#ifdef REG_PARM_STACK_SPACE
            /* Don't store things in the fixed argument area at this point;
               it has already been saved.  */
-           && i > reg_parm_stack_space
-#endif
-           )
+           && i > reg_parm_stack_space)
          break;
 
       if (i != upper_bound)
@@ -3504,8 +3739,9 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
            {
              arg->save_area = assign_stack_temp (BLKmode,
                                                  arg->size.constant, 0);
-             MEM_IN_STRUCT_P (arg->save_area)
-               = AGGREGATE_TYPE_P (TREE_TYPE (arg->tree_value));
+             MEM_SET_IN_STRUCT_P (arg->save_area,
+                                  AGGREGATE_TYPE_P (TREE_TYPE
+                                                    (arg->tree_value))); 
              preserve_temp_slots (arg->save_area);
              emit_block_move (validize_mem (arg->save_area), stack_area,
                               GEN_INT (arg->size.constant),
@@ -3518,6 +3754,14 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
            }
        }
     }
+
+  /* Now that we have saved any slots that will be overwritten by this
+     store, mark all slots this store will use.  We must do this before
+     we actually expand the argument since the expansion itself may
+     trigger library calls which might need to use the same stack slot.  */
+  if (argblock && ! variable_size && arg->stack)
+    for (i = lower_bound; i < upper_bound; i++)
+      stack_usage_map[i] = 1;
 #endif
 
   /* If this isn't going to be placed on both the stack and in registers,
@@ -3587,15 +3831,13 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
 
   if (arg->value == arg->stack)
     {
-      /* If the value is already in the stack slot, we are done.  */
-      if (flag_check_memory_usage && GET_CODE (arg->stack) == MEM)
+      /* If the value is already in the stack slot, we are done moving
+        data.  */
+      if (current_function_check_memory_usage && GET_CODE (arg->stack) == MEM)
        {
-         if (arg->mode == BLKmode)
-           abort ();
-
          emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
-                            XEXP (arg->stack, 0), ptr_mode, 
-                            GEN_INT (GET_MODE_SIZE (arg->mode)),
+                            XEXP (arg->stack, 0), Pmode, 
+                            ARGS_SIZE_RTX (arg->size),
                             TYPE_MODE (sizetype),
                             GEN_INT (MEMORY_USE_RW),
                             TYPE_MODE (integer_type_node));
@@ -3633,9 +3875,9 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
 
       /* This isn't already where we want it on the stack, so put it there.
         This can either be done with push or copy insns.  */
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-                     0, partial, reg, used - size,
-                     argblock, ARGS_SIZE_RTX (arg->offset));
+      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, 0,
+                     partial, reg, used - size, argblock,
+                     ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space);
     }
   else
     {
@@ -3667,7 +3909,8 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
 
       emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
                      TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
-                     reg, excess, argblock, ARGS_SIZE_RTX (arg->offset));
+                     reg, excess, argblock, ARGS_SIZE_RTX (arg->offset),
+                     reg_parm_stack_space);
     }
 
 
@@ -3694,11 +3937,4 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
   preserve_temp_slots (NULL_RTX);
   free_temp_slots ();
   pop_temp_slots ();
-
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  /* Now mark the segment we just used.  */
-  if (argblock && ! variable_size && arg->stack)
-    for (i = lower_bound; i < upper_bound; i++)
-      stack_usage_map[i] = 1;
-#endif
 }