OSDN Git Service

* calls.c (expand_call): When modes of target and valreg match, force
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index 378c54f..135f949 100644 (file)
@@ -1,6 +1,6 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
    Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,6 +21,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
 #include "flags.h"
@@ -35,10 +37,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "sbitmap.h"
 #include "langhooks.h"
 #include "target.h"
-
-#if !defined FUNCTION_OK_FOR_SIBCALL
-#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
-#endif
+#include "cgraph.h"
+#include "except.h"
 
 /* Decide whether a function's arguments should be processed
    from first to last or from last to first.
@@ -48,9 +48,11 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #ifdef PUSH_ROUNDING
 
+#ifndef PUSH_ARGS_REVERSED
 #if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
 #define PUSH_ARGS_REVERSED  PUSH_ARGS
 #endif
+#endif
 
 #endif
 
@@ -91,7 +93,7 @@ struct arg_data
   /* Number of registers to use.  0 means put the whole arg in registers.
      Also 0 if not passed in registers.  */
   int partial;
-  /* Non-zero if argument must be passed on stack.
+  /* Nonzero if argument must be passed on stack.
      Note that some arguments may be passed on the stack
      even though pass_on_stack is zero, just because FUNCTION_ARG says so.
      pass_on_stack identifies arguments that *cannot* go in registers.  */
@@ -126,7 +128,7 @@ struct arg_data
   struct args_size alignment_pad;
 };
 
-/* A vector of one char per byte of stack space.  A byte if non-zero if
+/* A vector of one char per byte of stack space.  A byte if nonzero if
    the corresponding stack location has been used.
    This vector is used to prevent a function call within an argument from
    clobbering any stack already set up.  */
@@ -152,35 +154,6 @@ int stack_arg_under_construction;
 static int calls_function      PARAMS ((tree, int));
 static int calls_function_1    PARAMS ((tree, int));
 
-/* Nonzero if this is a call to a `const' function.  */
-#define ECF_CONST              1
-/* Nonzero if this is a call to a `volatile' function.  */
-#define ECF_NORETURN           2
-/* Nonzero if this is a call to malloc or a related function.  */
-#define ECF_MALLOC             4
-/* Nonzero if it is plausible that this is a call to alloca.  */
-#define ECF_MAY_BE_ALLOCA      8
-/* Nonzero if this is a call to a function that won't throw an exception.  */
-#define ECF_NOTHROW            16
-/* Nonzero if this is a call to setjmp or a related function.  */
-#define ECF_RETURNS_TWICE      32
-/* Nonzero if this is a call to `longjmp'.  */
-#define ECF_LONGJMP            64
-/* Nonzero if this is a syscall that makes a new process in the image of
-   the current one.  */
-#define ECF_FORK_OR_EXEC       128
-#define ECF_SIBCALL            256
-/* Nonzero if this is a call to "pure" function (like const function,
-   but may read memory.  */
-#define ECF_PURE               512
-/* Nonzero if this is a call to a function that returns with the stack
-   pointer depressed.  */
-#define ECF_SP_DEPRESSED       1024
-/* Nonzero if this call is known to always return.  */
-#define ECF_ALWAYS_RETURN      2048
-/* Create libcall block around the call.  */
-#define ECF_LIBCALL_BLOCK      4096
-
 static void emit_call_1                PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
                                         HOST_WIDE_INT, HOST_WIDE_INT, rtx,
                                         rtx, int, rtx, int,
@@ -211,20 +184,22 @@ static void compute_argument_addresses            PARAMS ((struct arg_data *,
                                                         rtx, int));
 static rtx rtx_for_function_call               PARAMS ((tree, tree));
 static void load_register_parameters           PARAMS ((struct arg_data *,
-                                                        int, rtx *, int));
+                                                        int, rtx *, int,
+                                                        int, int *));
 static rtx emit_library_call_value_1           PARAMS ((int, rtx, rtx,
                                                         enum libcall_type,
                                                         enum machine_mode,
                                                         int, va_list));
 static int special_function_p                  PARAMS ((tree, int));
-static int flags_from_decl_or_type             PARAMS ((tree));
 static rtx try_to_integrate                    PARAMS ((tree, tree, rtx,
                                                         int, tree, rtx));
 static int check_sibcall_argument_overlap_1    PARAMS ((rtx));
-static int check_sibcall_argument_overlap      PARAMS ((rtx, struct arg_data *));
+static int check_sibcall_argument_overlap      PARAMS ((rtx, struct arg_data *,
+                                                        int));
 
 static int combine_pending_stack_adjustment_and_call
                                                 PARAMS ((int, struct args_size *, int));
+static tree fix_unsafe_tree            PARAMS ((tree));
 
 #ifdef REG_PARM_STACK_SPACE
 static rtx save_fixed_argument_area    PARAMS ((int, rtx, int *, int *));
@@ -614,6 +589,8 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
   if (ecf_flags & ECF_NOTHROW)
     REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
                                               REG_NOTES (call_insn));
+  else
+    note_eh_region_may_contain_throw ();
 
   if (ecf_flags & ECF_NORETURN)
     REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
@@ -691,9 +668,6 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
 
    Similarly set LONGJMP for if the function is in the longjmp family.
 
-   Set MALLOC for any of the standard memory allocation functions which
-   allocate from the heap.
-
    Set MAY_BE_ALLOCA for any memory allocation function that might allocate
    space from the stack such as alloca.  */
 
@@ -773,19 +747,6 @@ special_function_p (fndecl, flags)
                       || ((tname[5] == 'p' || tname[5] == 'e')
                           && tname[6] == '\0'))))
        flags |= ECF_FORK_OR_EXEC;
-
-      /* Do not add any more malloc-like functions to this list,
-         instead mark them as malloc functions using the malloc attribute.
-         Note, realloc is not suitable for attribute malloc since
-         it may return the same address across multiple calls.
-         C++ operator new is not suitable because it is not required
-         to return a unique pointer; indeed, the standard placement new
-        just returns its argument.  */
-      else if (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == Pmode
-              && (! strcmp (tname, "malloc")
-                  || ! strcmp (tname, "calloc")
-                  || ! strcmp (tname, "strdup")))
-       flags |= ECF_MALLOC;
     }
   return flags;
 }
@@ -799,25 +760,49 @@ setjmp_call_p (fndecl)
   return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
 }
 
+/* Return true when exp contains alloca call.  */
+bool
+alloca_call_p (exp)
+     tree exp;
+{
+  if (TREE_CODE (exp) == CALL_EXPR
+      && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+      && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+         == FUNCTION_DECL)
+      && (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+                             0) & ECF_MAY_BE_ALLOCA))
+    return true;
+  return false;
+}
+
 /* Detect flags (function attributes) from the function decl or type node.  */
 
-static int
+int
 flags_from_decl_or_type (exp)
      tree exp;
 {
   int flags = 0;
   tree type = exp;
-  /* ??? We can't set IS_MALLOC for function types?  */
+
   if (DECL_P (exp))
     {
+      struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
       type = TREE_TYPE (exp);
 
+      if (i)
+       {
+         if (i->pure_function)
+           flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
+         if (i->const_function)
+           flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
+       }
+
       /* The function exp may have the `malloc' attribute.  */
-      if (DECL_P (exp) && DECL_IS_MALLOC (exp))
+      if (DECL_IS_MALLOC (exp))
        flags |= ECF_MALLOC;
 
       /* The function exp may have the `pure' attribute.  */
-      if (DECL_P (exp) && DECL_IS_PURE (exp))
+      if (DECL_IS_PURE (exp))
        flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
 
       if (TREE_NOTHROW (exp))
@@ -876,6 +861,12 @@ precompute_register_parameters (num_actuals, args, reg_parm_seen)
            emit_queue ();
          }
 
+       /* If the value is a non-legitimate constant, force it into a
+          pseudo now.  TLS symbols sometimes need a call to resolve.  */
+       if (CONSTANT_P (args[i].value)
+           && !LEGITIMATE_CONSTANT_P (args[i].value))
+         args[i].value = force_reg (args[i].mode, args[i].value);
+
        /* If we are to promote the function arg to a wider mode,
           do it now.  */
 
@@ -918,66 +909,68 @@ save_fixed_argument_area (reg_parm_stack_space, argblock,
      int *low_to_save;
      int *high_to_save;
 {
-  int i;
-  rtx save_area = NULL_RTX;
+  int low;
+  int high;
 
-  /* Compute the boundary of the that needs to be saved, if any.  */
+  /* Compute the boundary of the area that needs to be saved, if any.  */
+  high = reg_parm_stack_space;
 #ifdef ARGS_GROW_DOWNWARD
-  for (i = 0; i < reg_parm_stack_space + 1; i++)
-#else
-  for (i = 0; i < reg_parm_stack_space; i++)
+  high += 1;
 #endif
-    {
-      if (i >= highest_outgoing_arg_in_use
-         || stack_usage_map[i] == 0)
-       continue;
+  if (high > highest_outgoing_arg_in_use)
+    high = highest_outgoing_arg_in_use;
+
+  for (low = 0; low < high; low++)
+    if (stack_usage_map[low] != 0)
+      {
+       int num_to_save;
+       enum machine_mode save_mode;
+       int delta;
+       rtx stack_area;
+       rtx save_area;
 
-      if (*low_to_save == -1)
-       *low_to_save = i;
+       while (stack_usage_map[--high] == 0)
+         ;
 
-      *high_to_save = i;
-    }
+       *low_to_save = low;
+       *high_to_save = high;
 
-  if (*low_to_save >= 0)
-    {
-      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;
+       num_to_save = high - low + 1;
+       save_mode = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
 
-      /* 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 we don't have the required alignment, must do this
+          in BLKmode.  */
+       if ((low & (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)));
+       delta = -high;
 #else
-      stack_area = gen_rtx_MEM (save_mode,
-                               memory_address (save_mode,
-                                               plus_constant (argblock,
-                                                              *low_to_save)));
+       delta = low;
 #endif
+       stack_area = gen_rtx_MEM (save_mode,
+                                 memory_address (save_mode,
+                                                 plus_constant (argblock,
+                                                                delta)));
 
-      set_mem_align (stack_area, PARM_BOUNDARY);
-      if (save_mode == BLKmode)
-       {
-         save_area = assign_stack_temp (BLKmode, num_to_save, 0);
-         emit_block_move (validize_mem (save_area), stack_area,
-                          GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
-       }
-      else
-       {
-         save_area = gen_reg_rtx (save_mode);
-         emit_move_insn (save_area, stack_area);
-       }
-    }
+       set_mem_align (stack_area, PARM_BOUNDARY);
+       if (save_mode == BLKmode)
+         {
+           save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+           emit_block_move (validize_mem (save_area), stack_area,
+                            GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
+         }
+       else
+         {
+           save_area = gen_reg_rtx (save_mode);
+           emit_move_insn (save_area, stack_area);
+         }
 
-  return save_area;
+       return save_area;
+      }
+
+  return NULL_RTX;
 }
 
 static void
@@ -988,19 +981,18 @@ restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save)
      int low_to_save;
 {
   enum machine_mode save_mode = GET_MODE (save_area);
+  int delta;
+  rtx stack_area;
+
 #ifdef ARGS_GROW_DOWNWARD
-  rtx stack_area
-    = gen_rtx_MEM (save_mode,
-                  memory_address (save_mode,
-                                  plus_constant (argblock,
-                                                 - high_to_save)));
+  delta = -high_to_save;
 #else
-  rtx stack_area
-    = gen_rtx_MEM (save_mode,
-                  memory_address (save_mode,
-                                  plus_constant (argblock,
-                                                 low_to_save)));
+  delta = low_to_save;
 #endif
+  stack_area = gen_rtx_MEM (save_mode,
+                           memory_address (save_mode,
+                                           plus_constant (argblock, delta)));
+  set_mem_align (stack_area, PARM_BOUNDARY);
 
   if (save_mode != BLKmode)
     emit_move_insn (stack_area, save_area);
@@ -1048,7 +1040,6 @@ store_unaligned_arguments_into_pseudos (args, num_actuals)
           this means we must skip the empty high order bytes when
           calculating the bit offset.  */
        if (BYTES_BIG_ENDIAN
-           && !FUNCTION_ARG_REG_LITTLE_ENDIAN
            && bytes < UNITS_PER_WORD)
          big_endian_correction = (BITS_PER_WORD  - (bytes * BITS_PER_UNIT));
 
@@ -1640,6 +1631,7 @@ compute_argument_addresses (args, argblock, num_actuals)
 
          addr = plus_constant (addr, arg_offset);
          args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+         set_mem_align (args[i].stack, PARM_BOUNDARY);
          set_mem_attributes (args[i].stack,
                              TREE_TYPE (args[i].tree_value), 1);
 
@@ -1650,6 +1642,7 @@ compute_argument_addresses (args, argblock, num_actuals)
 
          addr = plus_constant (addr, arg_offset);
          args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+         set_mem_align (args[i].stack_slot, PARM_BOUNDARY);
          set_mem_attributes (args[i].stack_slot,
                              TREE_TYPE (args[i].tree_value), 1);
 
@@ -1669,12 +1662,12 @@ compute_argument_addresses (args, argblock, num_actuals)
    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.  */
+   ADDR is the operand 0 of CALL_EXPR for this call.  */
 
 static rtx
-rtx_for_function_call (fndecl, exp)
+rtx_for_function_call (fndecl, addr)
      tree fndecl;
-     tree exp;
+     tree addr;
 {
   rtx funexp;
 
@@ -1695,10 +1688,8 @@ rtx_for_function_call (fndecl, exp)
   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);
+      funexp = expand_expr (addr, NULL_RTX, VOIDmode, 0);
       pop_temp_slots ();       /* FUNEXP can't be BLKmode.  */
       emit_queue ();
     }
@@ -1710,14 +1701,20 @@ rtx_for_function_call (fndecl, exp)
    expressions were already evaluated.
 
    Mark all register-parms as living through the call, putting these USE
-   insns in the CALL_INSN_FUNCTION_USAGE field.  */
+   insns in the CALL_INSN_FUNCTION_USAGE field.  
+   When IS_SIBCALL, perform the check_sibcall_overlap_argument_overlap
+   checking, setting *SIBCALL_FAILURE if appropriate.  */
 
 static void
-load_register_parameters (args, num_actuals, call_fusage, flags)
+load_register_parameters (args, num_actuals, call_fusage, flags, 
+                           is_sibcall, sibcall_failure)
      struct arg_data *args;
      int num_actuals;
      rtx *call_fusage;
      int flags;
+     int is_sibcall;
+     int *sibcall_failure;
 {
   int i, j;
 
@@ -1734,6 +1731,7 @@ load_register_parameters (args, num_actuals, call_fusage, flags)
 
       if (reg)
        {
+         rtx before_arg = get_last_insn ();
          /* 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
@@ -1771,6 +1769,13 @@ load_register_parameters (args, num_actuals, call_fusage, flags)
                               validize_mem (args[i].value), nregs,
                               args[i].mode);
 
+         /* When a parameter is a block, and perhaps in other cases, it is
+            possible that it did a load from an argument slot that was
+            already clobbered.  */
+         if (is_sibcall
+             && check_sibcall_argument_overlap (before_arg, &args[i], 0))
+           *sibcall_failure = 1;
+
          /* Handle calls that pass values in multiple non-contiguous
             locations.  The Irix 6 ABI has examples of this.  */
          if (GET_CODE (reg) == PARALLEL)
@@ -1828,9 +1833,11 @@ try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
             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;
+         i = reg_parm_stack_space;
+         if (i > highest_outgoing_arg_in_use)
+           i = highest_outgoing_arg_in_use;
+         while (--i >= 0 && stack_usage_map[i] == 0)
+           ;
 
          if (stack_arg_under_construction || i >= 0)
            {
@@ -1965,7 +1972,7 @@ combine_pending_stack_adjustment_and_call (unadjusted_args_size,
 /* Scan X expression if it does not dereference any argument slots
    we already clobbered by tail call arguments (as noted in stored_args_map
    bitmap).
-   Return non-zero if X expression dereferences such argument slots,
+   Return nonzero if X expression dereferences such argument slots,
    zero otherwise.  */
 
 static int
@@ -2027,14 +2034,16 @@ check_sibcall_argument_overlap_1 (x)
 
 /* Scan sequence after INSN if it does not dereference any argument slots
    we already clobbered by tail call arguments (as noted in stored_args_map
-   bitmap).  Add stack slots for ARG to stored_args_map bitmap afterwards.
-   Return non-zero if sequence after INSN dereferences such argument slots,
-   zero otherwise.  */
+   bitmap).  If MARK_STORED_ARGS_MAP, add stack slots for ARG to
+   stored_args_map bitmap afterwards (when ARG is a register MARK_STORED_ARGS_MAP
+   should be 0).  Return nonzero if sequence after INSN dereferences such argument
+   slots, zero otherwise.  */
 
 static int
-check_sibcall_argument_overlap (insn, arg)
+check_sibcall_argument_overlap (insn, arg, mark_stored_args_map)
      rtx insn;
      struct arg_data *arg;
+     int mark_stored_args_map;
 {
   int low, high;
 
@@ -2048,17 +2057,49 @@ check_sibcall_argument_overlap (insn, arg)
        && check_sibcall_argument_overlap_1 (PATTERN (insn)))
       break;
 
+  if (mark_stored_args_map)
+    {
 #ifdef ARGS_GROW_DOWNWARD
-  low = -arg->slot_offset.constant - arg->size.constant;
+      low = -arg->slot_offset.constant - arg->size.constant;
 #else
-  low = arg->slot_offset.constant;
+      low = arg->slot_offset.constant;
 #endif
 
-  for (high = low + arg->size.constant; low < high; low++)
-    SET_BIT (stored_args_map, low);
+      for (high = low + arg->size.constant; low < high; low++)
+       SET_BIT (stored_args_map, low);
+    }
   return insn != NULL_RTX;
 }
 
+static tree
+fix_unsafe_tree (t)
+     tree t;
+{
+  switch (unsafe_for_reeval (t))
+    {
+    case 0: /* Safe.  */
+      break;
+
+    case 1: /* Mildly unsafe.  */
+      t = unsave_expr (t);
+      break;
+
+    case 2: /* Wildly unsafe.  */
+      {
+       tree var = build_decl (VAR_DECL, NULL_TREE,
+                              TREE_TYPE (t));
+       SET_DECL_RTL (var,
+                     expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL));
+       t = var;
+      }
+      break;
+
+    default:
+      abort ();
+    }
+  return t;
+}
+
 /* 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.
@@ -2155,21 +2196,30 @@ expand_call (exp, target, ignore)
   int is_integrable = 0;
 #ifdef 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;
+     saved, if any.  */
+  int low_to_save, high_to_save;
   rtx save_area = 0;           /* Place that it is saved */
 #endif
 
   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 = 0;
 
+  int old_stack_allocated;
+
+  /* State variables to track stack modifications.  */
   rtx old_stack_level = 0;
+  int old_stack_arg_under_construction = 0;
   int old_pending_adj = 0;
   int old_inhibit_defer_pop = inhibit_defer_pop;
-  int old_stack_allocated;
+
+  /* Some stack pointer alterations we make are performed via
+     allocate_dynamic_stack_space. This modifies the stack_pointer_delta,
+     which we then also need to save/restore along the way.  */
+  int old_stack_pointer_delta = 0;
+
   rtx call_fusage;
   tree p = TREE_OPERAND (exp, 0);
+  tree addr = TREE_OPERAND (exp, 0);
   int i;
   /* The alignment of the stack, in bits.  */
   HOST_WIDE_INT preferred_stack_boundary;
@@ -2259,7 +2309,17 @@ expand_call (exp, target, ignore)
       {
        struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
 
-       if (target && GET_CODE (target) == MEM)
+       if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (exp))
+         {
+           /* The structure value address arg is already in actparms.
+              Pull it out.  It might be nice to just leave it there, but
+              we need to set structure_value_addr.  */
+           tree return_arg = TREE_VALUE (actparms);
+           actparms = TREE_CHAIN (actparms);
+           structure_value_addr = expand_expr (return_arg, NULL_RTX,
+                                               VOIDmode, EXPAND_NORMAL);
+         }
+       else if (target && GET_CODE (target) == MEM)
          structure_value_addr = XEXP (target, 0);
        else
          {
@@ -2289,9 +2349,15 @@ expand_call (exp, target, ignore)
 
   /* Figure out the amount to which the stack should be aligned.  */
   preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+  if (fndecl)
+    {
+      struct cgraph_rtl_info *i = cgraph_rtl_info (fndecl);
+      if (i && i->preferred_incoming_stack_boundary)
+       preferred_stack_boundary = i->preferred_incoming_stack_boundary;
+    }
 
   /* Operand 0 is a pointer-to-function; get the type of the function.  */
-  funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+  funtype = TREE_TYPE (addr);
   if (! POINTER_TYPE_P (funtype))
     abort ();
   funtype = TREE_TYPE (funtype);
@@ -2366,7 +2432,7 @@ expand_call (exp, target, ignore)
      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));
+  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, fndecl);
 
   /* Make a vector to hold all the information about each arg.  */
   args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
@@ -2428,8 +2494,8 @@ expand_call (exp, target, ignore)
 
   /* Tail recursion fails, when we are not dealing with recursive calls.  */
   if (!try_tail_recursion
-      || TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR
-      || TREE_OPERAND (TREE_OPERAND (exp, 0), 0) != current_function_decl)
+      || TREE_CODE (addr) != ADDR_EXPR
+      || TREE_OPERAND (addr, 0) != current_function_decl)
     try_tail_recursion = 0;
 
   /*  Rest of purposes for tail call optimizations to fail.  */
@@ -2445,17 +2511,17 @@ expand_call (exp, target, ignore)
         It does not seem worth the effort since few optimizable
         sibling calls will return a structure.  */
       || structure_value_addr != NULL_RTX
-      /* If the register holding the address is a callee saved
-        register, then we lose.  We have no way to prevent that,
-        so we only allow calls to named functions.  */
-      /* ??? This could be done by having the insn constraints
-        use a register class that is all call-clobbered.  Any
-        reload insns generated to fix things up would appear
-        before the sibcall_epilogue.  */
-      || fndecl == NULL_TREE
-      || (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP))
-      || TREE_THIS_VOLATILE (fndecl)
-      || !FUNCTION_OK_FOR_SIBCALL (fndecl)
+      /* Check whether the target is able to optimize the call
+        into a sibcall.  */
+      || !(*targetm.function_ok_for_sibcall) (fndecl, exp)
+      /* Functions that do not return exactly once may not be sibcall
+         optimized.  */
+      || (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP | ECF_NORETURN))
+      || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))
+      /* If the called function is nested in the current one, it might access
+         some of the caller's arguments, but could clobber them beforehand if
+         the argument areas are shared.  */
+      || (fndecl && decl_function_context (fndecl) == current_function_decl)
       /* If this function requires more stack slots than the current
         function, we cannot change it into a sibling call.  */
       || args_size.constant > current_function_args_size
@@ -2501,35 +2567,16 @@ expand_call (exp, target, ignore)
 
       for (; i != end; i += inc)
        {
-         switch (unsafe_for_reeval (args[i].tree_value))
-           {
-           case 0: /* Safe.  */
-             break;
-
-           case 1: /* Mildly unsafe.  */
-             args[i].tree_value = unsave_expr (args[i].tree_value);
-             break;
-
-           case 2: /* Wildly unsafe.  */
-             {
-               tree var = build_decl (VAR_DECL, NULL_TREE,
-                                      TREE_TYPE (args[i].tree_value));
-               SET_DECL_RTL (var,
-                             expand_expr (args[i].tree_value, NULL_RTX,
-                                          VOIDmode, EXPAND_NORMAL));
-               args[i].tree_value = var;
-             }
-             break;
-
-           default:
-             abort ();
-           }
+          args[i].tree_value = fix_unsafe_tree (args[i].tree_value);
          /* We need to build actparms for optimize_tail_recursion.  We can
             safely trash away TREE_PURPOSE, since it is unused by this
             function.  */
          if (try_tail_recursion)
            actparms = tree_cons (NULL_TREE, args[i].tree_value, actparms);
        }
+      /* Do the same for the function address if it is an expression.  */
+      if (!fndecl)
+        addr = fix_unsafe_tree (addr);
       /* Expanding one of those dangerous arguments could have added
         cleanups, but otherwise give it a whirl.  */
       if (any_pending_cleanups (1))
@@ -2587,9 +2634,7 @@ expand_call (exp, target, ignore)
         is subject to race conditions, just as with multithreaded
         programs.  */
 
-      emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"),
-                        LCT_ALWAYS_RETURN,
-                        VOIDmode, 0);
+      emit_library_call (gcov_flush_libfunc, LCT_ALWAYS_RETURN, VOIDmode, 0);
     }
 
   /* Ensure current function's preferred stack boundary is at least
@@ -2598,6 +2643,8 @@ expand_call (exp, target, ignore)
   if (cfun->preferred_stack_boundary < preferred_stack_boundary
       && fndecl != current_function_decl)
     cfun->preferred_stack_boundary = preferred_stack_boundary;
+  if (fndecl == current_function_decl)
+    cfun->recursive_call_emit = true;
 
   preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
 
@@ -2606,7 +2653,7 @@ expand_call (exp, target, ignore)
   /* We want to make two insn chains; one for a sibling call, the other
      for a normal call.  We will select one of the two chains after
      initial RTL generation is complete.  */
-  for (pass = 0; pass < 2; pass++)
+  for (pass = try_tail_call ? 0 : 1; pass < 2; pass++)
     {
       int sibcall_failure = 0;
       /* We want to emit any pending stack adjustments before the tail
@@ -2620,9 +2667,6 @@ expand_call (exp, target, ignore)
 
       if (pass == 0)
        {
-         if (! try_tail_call)
-           continue;
-
          /* Emit any queued insns now; otherwise they would end up in
              only one of the alternates.  */
          emit_queue ();
@@ -2722,6 +2766,7 @@ expand_call (exp, target, ignore)
          if (old_stack_level == 0)
            {
              emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+             old_stack_pointer_delta = stack_pointer_delta;
              old_pending_adj = pending_stack_adjust;
              pending_stack_adjust = 0;
              /* stack_arg_under_construction says whether a stack arg is
@@ -2848,53 +2893,58 @@ expand_call (exp, target, ignore)
                     VIRTUAL_OUTGOING_ARGS_RTX changes as well.  But might
                     as well always do it.  */
                  argblock = copy_to_reg (argblock);
+               }
+           }
+       }
 
-                 /* The save/restore code in store_one_arg handles all
-                    cases except one: a constructor call (including a C
-                    function returning a BLKmode struct) to initialize
-                    an argument.  */
-                 if (stack_arg_under_construction)
-                   {
+      if (ACCUMULATE_OUTGOING_ARGS)
+       {
+         /* The save/restore code in store_one_arg handles all
+            cases except one: a constructor call (including a C
+            function returning a BLKmode struct) to initialize
+            an argument.  */
+         if (stack_arg_under_construction)
+           {
 #ifndef OUTGOING_REG_PARM_STACK_SPACE
-                     rtx push_size = GEN_INT (reg_parm_stack_space
-                                              + adjusted_args_size.constant);
+             rtx push_size = GEN_INT (reg_parm_stack_space
+                                      + adjusted_args_size.constant);
 #else
-                     rtx push_size = GEN_INT (adjusted_args_size.constant);
+             rtx push_size = GEN_INT (adjusted_args_size.constant);
 #endif
-                     if (old_stack_level == 0)
-                       {
-                         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);
-                         memset (stack_usage_map, 0, 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;
-                     }
+             if (old_stack_level == 0)
+               {
+                 emit_stack_save (SAVE_BLOCK, &old_stack_level,
+                                  NULL_RTX);
+                 old_stack_pointer_delta = stack_pointer_delta;
+                 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);
+                 memset (stack_usage_map, 0, 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;
+             }
+       }
+      
       compute_argument_addresses (args, argblock, num_actuals);
 
       /* If we push args individually in reverse order, perform stack alignment
@@ -2923,7 +2973,7 @@ expand_call (exp, target, ignore)
         be deferred during the evaluation of the arguments.  */
       NO_DEFER_POP;
 
-      funexp = rtx_for_function_call (fndecl, exp);
+      funexp = rtx_for_function_call (fndecl, addr);
 
       /* Figure out the register where the value, if any, will come back.  */
       valreg = 0;
@@ -2965,7 +3015,7 @@ expand_call (exp, target, ignore)
                               reg_parm_stack_space)
                || (pass == 0
                    && check_sibcall_argument_overlap (before_arg,
-                                                      &args[i])))
+                                                      &args[i], 1)))
              sibcall_failure = 1;
          }
 
@@ -2989,7 +3039,7 @@ expand_call (exp, target, ignore)
                                 reg_parm_stack_space)
                  || (pass == 0
                      && check_sibcall_argument_overlap (before_arg,
-                                                        &args[i])))
+                                                        &args[i], 1)))
                sibcall_failure = 1;
            }
 
@@ -3024,7 +3074,8 @@ expand_call (exp, target, ignore)
       funexp = prepare_call_address (funexp, fndecl, &call_fusage,
                                     reg_parm_seen, pass == 0);
 
-      load_register_parameters (args, num_actuals, &call_fusage, flags);
+      load_register_parameters (args, num_actuals, &call_fusage, flags,
+                               pass == 0, &sibcall_failure);
 
       /* Perform postincrements before actually calling the function.  */
       emit_queue ();
@@ -3057,11 +3108,6 @@ expand_call (exp, target, ignore)
                   next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
                   flags, & args_so_far);
 
-      /* Verify that we've deallocated all the stack we used.  */
-      if (pass
-         && old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
-       abort ();
-
       /* If call is cse'able, make appropriate pair of reg-notes around it.
         Test valreg so we don't crash; may safely ignore `const'
         if return type is void.  Disable for PARALLEL return values, because
@@ -3233,6 +3279,12 @@ expand_call (exp, target, ignore)
             If they refer to the same register, this move will be a no-op,
             except when function inlining is being done.  */
          emit_move_insn (target, valreg);
+
+         /* If we are setting a MEM, this code must be executed.  Since it is
+            emitted after the call insn, sibcall optimization cannot be
+            performed in that case.  */
+         if (GET_CODE (target) == MEM)
+           sibcall_failure = 1;
        }
       else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
        {
@@ -3283,6 +3335,7 @@ expand_call (exp, target, ignore)
       if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
        {
          emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+         stack_pointer_delta = old_stack_pointer_delta;
          pending_stack_adjust = old_pending_adj;
          stack_arg_under_construction = old_stack_arg_under_construction;
          highest_outgoing_arg_in_use = initial_highest_arg_in_use;
@@ -3293,10 +3346,8 @@ expand_call (exp, target, ignore)
        {
 #ifdef REG_PARM_STACK_SPACE
          if (save_area)
-           {
-             restore_fixed_argument_area (save_area, argblock,
-                                          high_to_save, low_to_save);
-           }
+           restore_fixed_argument_area (save_area, argblock,
+                                        high_to_save, low_to_save);
 #endif
 
          /* If we saved any argument areas, restore them.  */
@@ -3365,7 +3416,14 @@ expand_call (exp, target, ignore)
          sbitmap_free (stored_args_map);
        }
       else
-       normal_call_insns = insns;
+       {
+         normal_call_insns = insns;
+
+         /* Verify that we've deallocated all the stack we used.  */
+         if (old_stack_allocated !=
+             stack_pointer_delta - pending_stack_adjust)
+           abort ();
+       }
 
       /* If something prevents making this a sibling call,
         zero out the sequence.  */
@@ -3484,7 +3542,7 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
 #ifdef 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 = 0;
+  int low_to_save, high_to_save;
   rtx save_area = 0;            /* Place that it is saved.  */
 #endif
 
@@ -3684,6 +3742,14 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
            }
          flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
 
+         /* If this was a CONST function, it is now PURE since
+            it now reads memory.  */
+         if (flags & ECF_CONST)
+           {
+             flags &= ~ECF_CONST;
+             flags |= ECF_PURE;
+           }
+
          if (GET_MODE (val) == MEM && ! must_copy)
            slot = val;
          else if (must_copy)
@@ -3696,12 +3762,12 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
            {
              tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
 
-             slot = gen_rtx_MEM (mode,
-                                 expand_expr (build1 (ADDR_EXPR,
-                                                      build_pointer_type
-                                                      (type),
-                                                      make_tree (type, val)),
-                                              NULL_RTX, VOIDmode, 0));
+             slot
+               = gen_rtx_MEM (mode,
+                              expand_expr (build1 (ADDR_EXPR,
+                                                   build_pointer_type (type),
+                                                   make_tree (type, val)),
+                                           NULL_RTX, VOIDmode, 0));
            }
 
          call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
@@ -3857,62 +3923,9 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
     {
       /* 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.  */
-
-#ifdef ARGS_GROW_DOWNWARD
-      for (count = 0; count < reg_parm_stack_space + 1; count++)
-#else
-      for (count = 0; count < reg_parm_stack_space; count++)
-#endif
-       {
-         if (count >= highest_outgoing_arg_in_use
-             || stack_usage_map[count] == 0)
-           continue;
-
-         if (low_to_save == -1)
-           low_to_save = count;
-
-         high_to_save = count;
-       }
-
-      if (low_to_save >= 0)
-       {
-         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);
-             set_mem_align (save_area, PARM_BOUNDARY);
-             emit_block_move (save_area, stack_area, GEN_INT (num_to_save),
-                              BLOCK_OP_CALL_PARM);
-           }
-         else
-           {
-             save_area = gen_reg_rtx (save_mode);
-             emit_move_insn (save_area, stack_area);
-           }
-       }
+        parameters, we must save and restore it.  */
+      save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+                                           &low_to_save, &high_to_save);
     }
 #endif
 
@@ -3945,14 +3958,15 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
              upper_bound = lower_bound + argvec[argnum].size.constant;
 #endif
 
-             for (i = lower_bound; i < upper_bound; i++)
-               if (stack_usage_map[i]
-                   /* Don't store things in the fixed argument area at this
-                      point; it has already been saved.  */
-                   && i > reg_parm_stack_space)
-                 break;
+             i = lower_bound;
+             /* Don't worry about things in the fixed argument area;
+                it has already been saved.  */
+             if (i < reg_parm_stack_space)
+               i = reg_parm_stack_space;
+             while (i < upper_bound && stack_usage_map[i] == 0)
+               i++;
 
-             if (i != upper_bound)
+             if (i < upper_bound)
                {
                  /* We need to make a save area.  See what mode we can make
                     it.  */
@@ -4102,7 +4116,7 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
     {
       rtx insns;
 
-      if (valreg == 0 || GET_CODE (valreg) == PARALLEL)
+      if (valreg == 0)
        {
          insns = get_insns ();
          end_sequence ();
@@ -4111,9 +4125,18 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
       else
        {
          rtx note = 0;
-         rtx temp = gen_reg_rtx (GET_MODE (valreg));
+         rtx temp;
          int i;
 
+         if (GET_CODE (valreg) == PARALLEL)
+           {
+             temp = gen_reg_rtx (outmode);
+             emit_group_store (temp, valreg, outmode);
+             valreg = temp;
+           }
+
+         temp = gen_reg_rtx (GET_MODE (valreg));
+
          /* Construct an "equal form" for the value which mentions all the
             arguments in order as well as the function name.  */
          for (i = 0; i < nargs; i++)
@@ -4147,39 +4170,24 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
          if (value != mem_value)
            emit_move_insn (value, mem_value);
        }
+      else if (GET_CODE (valreg) == PARALLEL)
+       {
+         if (value == 0)
+           value = gen_reg_rtx (outmode);
+         emit_group_store (value, valreg, outmode);
+       }
       else if (value != 0)
-       emit_move_insn (value, hard_libcall_value (outmode));
+       emit_move_insn (value, valreg);
       else
-       value = hard_libcall_value (outmode);
+       value = valreg;
     }
 
   if (ACCUMULATE_OUTGOING_ARGS)
     {
 #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)));
-#endif
-
-         set_mem_align (stack_area, PARM_BOUNDARY);
-         if (save_mode != BLKmode)
-           emit_move_insn (stack_area, save_area);
-         else
-           emit_block_move (stack_area, save_area,
-                            GEN_INT (high_to_save - low_to_save + 1),
-                            BLOCK_OP_CALL_PARM);
-       }
+       restore_fixed_argument_area (save_area, argblock,
+                                    high_to_save, low_to_save);
 #endif
 
       /* If we saved any argument areas, restore them.  */
@@ -4280,7 +4288,7 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value,
 
    FNDECL is the declaration of the function we are calling.
 
-   Return non-zero if this arg should cause sibcall failure,
+   Return nonzero if this arg should cause sibcall failure,
    zero otherwise.  */
 
 static int
@@ -4329,14 +4337,15 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
          upper_bound = lower_bound + arg->size.constant;
 #endif
 
-         for (i = lower_bound; i < upper_bound; i++)
-           if (stack_usage_map[i]
-               /* Don't store things in the fixed argument area at this point;
-                  it has already been saved.  */
-               && i > reg_parm_stack_space)
-             break;
+         i = lower_bound;
+         /* Don't worry about things in the fixed argument area;
+            it has already been saved.  */
+         if (i < reg_parm_stack_space)
+           i = reg_parm_stack_space;
+         while (i < upper_bound && stack_usage_map[i] == 0)
+           i++;
 
-         if (i != upper_bound)
+         if (i < upper_bound)
            {
              /* We need to make a save area.  See what mode we can make it.  */
              enum machine_mode save_mode
@@ -4365,13 +4374,6 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
                }
            }
        }
-      /* 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;
     }
 
   /* If this isn't going to be placed on both the stack and in registers,
@@ -4424,7 +4426,7 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
                                (partial
                                 || TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
                                ? NULL_RTX : arg->stack,
-                               VOIDmode, 0);
+                               VOIDmode, EXPAND_STACK_PARM);
 
       /* If we are promoting object (or for any other reason) the mode
         doesn't agree, convert the mode.  */
@@ -4491,6 +4493,7 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
     {
       /* BLKmode, at least partly to be pushed.  */
 
+      unsigned int parm_align;
       int excess;
       rtx size_rtx;
 
@@ -4516,6 +4519,23 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
                                  NULL_RTX, TYPE_MODE (sizetype), 0);
        }
 
+      /* Some types will require stricter alignment, which will be
+        provided for elsewhere in argument layout.  */
+      parm_align = MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval)));
+
+      /* When an argument is padded down, the block is aligned to
+        PARM_BOUNDARY, but the actual argument isn't.  */
+      if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
+       {
+         if (arg->size.var)
+           parm_align = BITS_PER_UNIT;
+         else if (excess)
+           {
+             unsigned int excess_align = (excess & -excess) * BITS_PER_UNIT;
+             parm_align = MIN (parm_align, excess_align);
+           }
+       }
+
       if ((flags & ECF_SIBCALL) && GET_CODE (arg->value) == MEM)
        {
          /* emit_push_insn might not work properly if arg->value and
@@ -4549,41 +4569,8 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
            }
        }
 
-      /* Special handling is required if part of the parameter lies in the
-        register parameter area.  The argument may be copied into the stack
-        slot using memcpy(), but the original contents of the register
-        parameter area will be restored after the memcpy() call.
-
-        To ensure that the part that lies in the register parameter area
-        is copied correctly, we emit a separate push for that part.  This
-        push should be small enough to avoid a call to memcpy().  */
-#ifndef STACK_PARMS_IN_REG_PARM_AREA
-      if (arg->reg && arg->pass_on_stack)
-#else
-      if (1)
-#endif
-       {
-         if (arg->offset.constant < reg_parm_stack_space && arg->offset.var)
-           error ("variable offset is passed partially in stack and in reg");
-         else if (arg->offset.constant < reg_parm_stack_space && arg->size.var)
-           error ("variable size is passed partially in stack and in reg");
-         else if (arg->offset.constant < reg_parm_stack_space 
-             && ((arg->offset.constant + arg->size.constant) 
-                  > reg_parm_stack_space))
-          {
-           rtx size_rtx1 = GEN_INT (reg_parm_stack_space - arg->offset.constant);
-           emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx1,
-                           MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval))),
-                           partial, reg, excess, argblock,
-                           ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
-                           ARGS_SIZE_RTX (arg->alignment_pad));
-         }
-       }
-       
-
       emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-                     MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval))),
-                     partial, reg, excess, argblock,
+                     parm_align, partial, reg, excess, argblock,
                      ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
                      ARGS_SIZE_RTX (arg->alignment_pad));
 
@@ -4599,6 +4586,12 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
        arg->value = arg->stack_slot;
     }
 
+  /* Mark all slots this store used.  */
+  if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
+      && argblock && ! variable_size && arg->stack)
+    for (i = lower_bound; i < upper_bound; i++)
+      stack_usage_map[i] = 1;
+
   /* Once we have pushed something, pops can't safely
      be deferred during the rest of the arguments.  */
   NO_DEFER_POP;
@@ -4616,3 +4609,47 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
 
   return sibcall_failure;
 }
+
+/* Nonzero if we do not know how to pass TYPE solely in registers.
+   We cannot do so in the following cases:
+
+   - if the type has variable size
+   - if the type is marked as addressable (it is required to be constructed
+     into the stack)
+   - if the padding and mode of the type is such that a copy into a register
+     would put it into the wrong part of the register.
+
+   Which padding can't be supported depends on the byte endianness.
+
+   A value in a register is implicitly padded at the most significant end.
+   On a big-endian machine, that is the lower end in memory.
+   So a value padded in memory at the upper end can't go in a register.
+   For a little-endian machine, the reverse is true.  */
+
+bool
+default_must_pass_in_stack (mode, type)
+     enum machine_mode mode;
+     tree type;
+{
+  if (!type)
+    return false;
+
+  /* If the type has variable size...  */
+  if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+    return true;
+
+  /* If the type is marked as addressable (it is required
+     to be constructed into the stack)...  */
+  if (TREE_ADDRESSABLE (type))
+    return true;
+
+  /* If the padding and mode of the type is such that a copy into
+     a register would put it into the wrong part of the register.  */
+  if (mode == BLKmode
+      && int_size_in_bytes (type) % (PARM_BOUNDARY / BITS_PER_UNIT)
+      && (FUNCTION_ARG_PADDING (mode, type)
+         == (BYTES_BIG_ENDIAN ? upward : downward)))
+    return true;
+
+  return false;
+}