OSDN Git Service

2003-03-10 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index 6a22820..cab6571 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"
@@ -33,10 +35,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "tm_p.h"
 #include "timevar.h"
 #include "sbitmap.h"
-
-#if !defined FUNCTION_OK_FOR_SIBCALL
-#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
-#endif
+#include "langhooks.h"
+#include "target.h"
+#include "cgraph.h"
 
 /* Decide whether a function's arguments should be processed
    from first to last or from last to first.
@@ -46,9 +47,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
 
@@ -89,7 +92,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.  */
@@ -124,7 +127,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.  */
@@ -150,36 +153,10 @@ 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
-
 static void emit_call_1                PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
                                         HOST_WIDE_INT, HOST_WIDE_INT, rtx,
-                                        rtx, int, rtx, int));
+                                        rtx, int, rtx, int,
+                                        CUMULATIVE_ARGS *));
 static void precompute_register_parameters     PARAMS ((int,
                                                         struct arg_data *,
                                                         int *));
@@ -206,20 +183,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 *));
@@ -442,7 +421,7 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen, sibcallp)
 static void
 emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
             struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop,
-            call_fusage, ecf_flags)
+            call_fusage, ecf_flags, args_so_far)
      rtx funexp;
      tree fndecl ATTRIBUTE_UNUSED;
      tree funtype ATTRIBUTE_UNUSED;
@@ -454,6 +433,7 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
      int old_inhibit_defer_pop;
      rtx call_fusage;
      int ecf_flags;
+     CUMULATIVE_ARGS *args_so_far ATTRIBUTE_UNUSED;
 {
   rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
   rtx call_insn;
@@ -464,6 +444,10 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
   struct_value_size_rtx = GEN_INT (struct_value_size);
 #endif
 
+#ifdef CALL_POPS_ARGS
+  n_popped += CALL_POPS_ARGS (* args_so_far);
+#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,
      because prepare_call_address already did it if it should be done.  */
@@ -789,35 +773,66 @@ setjmp_call_p (fndecl)
   return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
 }
 
-/* Detect flags (function attributes) from the function type node.  */
+/* 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;
+}
 
-static int
+/* Detect flags (function attributes) from the function decl or type node.  */
+
+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 && i->pure_function)
+       flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
+      if (i && 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))
        flags |= ECF_MALLOC;
 
       /* The function exp may have the `pure' attribute.  */
       if (DECL_P (exp) && DECL_IS_PURE (exp))
-       flags |= ECF_PURE;
+       flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
 
       if (TREE_NOTHROW (exp))
        flags |= ECF_NOTHROW;
     }
 
   if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
-    flags |= ECF_CONST;
+    flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
 
   if (TREE_THIS_VOLATILE (exp))
     flags |= ECF_NORETURN;
 
+  /* Mark if the function returns with the stack pointer depressed.   We
+     cannot consider it pure or constant in that case.  */
+  if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
+    {
+      flags |= ECF_SP_DEPRESSED;
+      flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
+    }
+
   return flags;
 }
 
@@ -856,6 +871,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.  */
 
@@ -898,69 +919,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;
 
-      if (*low_to_save == -1)
-       *low_to_save = i;
+  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;
 
-      *high_to_save = i;
-    }
+       while (stack_usage_map[--high] == 0)
+         ;
 
-  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;
+       *low_to_save = low;
+       *high_to_save = high;
+
+       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);
-         /* Cannot use emit_block_move here because it can be done by a
-            library call which in turn gets into this place again and deadly
-            infinite recursion happens.  */
-         move_by_pieces (validize_mem (save_area), stack_area, num_to_save,
-                         PARM_BOUNDARY);
-       }
-      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
@@ -971,28 +991,25 @@ 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);
   else
-    /* Cannot use emit_block_move here because it can be done by a library
-       call which in turn gets into this place again and deadly infinite
-       recursion happens.  */
-    move_by_pieces (stack_area, validize_mem (save_area),
-                   high_to_save - low_to_save + 1, PARM_BOUNDARY);
+    emit_block_move (stack_area, validize_mem (save_area),
+                    GEN_INT (high_to_save - low_to_save + 1),
+                    BLOCK_OP_CALL_PARM);
 }
 #endif /* REG_PARM_STACK_SPACE */
 
@@ -1033,7 +1050,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));
 
@@ -1212,6 +1228,16 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
                                           args[i].tree_value);
              type = build_pointer_type (type);
            }
+         else if (TREE_CODE (args[i].tree_value) == TARGET_EXPR)
+           {
+             /* In the V3 C++ ABI, parameters are destroyed in the caller.
+                We implement this by passing the address of the temporary
+                rather than expanding it into another allocated slot.  */
+             args[i].tree_value = build1 (ADDR_EXPR,
+                                          build_pointer_type (type),
+                                          args[i].tree_value);
+             type = build_pointer_type (type);
+           }
          else
            {
              /* We make a copy of the object and pass the address to the
@@ -1244,7 +1270,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
                copy = assign_temp (type, 0, 1, 0);
 
              store_expr (args[i].tree_value, copy, 0);
-             *ecf_flags &= ~(ECF_CONST | ECF_PURE);
+             *ecf_flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
 
              args[i].tree_value = build1 (ADDR_EXPR,
                                           build_pointer_type (type),
@@ -1303,7 +1329,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
       /* If this is an addressable type, we cannot pre-evaluate it.  Thus,
         we cannot consider this function call constant.  */
       if (TREE_ADDRESSABLE (type))
-       *ecf_flags &= ~(ECF_CONST | ECF_PURE);
+       *ecf_flags &= ~ECF_LIBCALL_BLOCK;
 
       /* Compute the stack-size of this argument.  */
       if (args[i].reg == 0 || args[i].partial != 0
@@ -1474,7 +1500,7 @@ precompute_arguments (flags, num_actuals, args)
      worse code)  */
 
   for (i = 0; i < num_actuals; i++)
-    if ((flags & (ECF_CONST | ECF_PURE))
+    if ((flags & ECF_LIBCALL_BLOCK)
        || calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
       {
        enum machine_mode mode;
@@ -1483,14 +1509,9 @@ precompute_arguments (flags, num_actuals, args)
        if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
          abort ();
 
-       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 ();
@@ -1514,8 +1535,8 @@ precompute_arguments (flags, num_actuals, args)
                args[i].initial_value
                  = gen_lowpart_SUBREG (mode, args[i].value);
                SUBREG_PROMOTED_VAR_P (args[i].initial_value) = 1;
-               SUBREG_PROMOTED_UNSIGNED_P (args[i].initial_value)
-                 = args[i].unsignedp;
+               SUBREG_PROMOTED_UNSIGNED_SET (args[i].initial_value,
+                 args[i].unsignedp);
              }
 #endif
          }
@@ -1620,6 +1641,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);
 
@@ -1630,6 +1652,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);
 
@@ -1649,12 +1672,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;
 
@@ -1675,10 +1698,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 ();
     }
@@ -1690,14 +1711,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;
 
@@ -1714,6 +1741,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
@@ -1751,6 +1779,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)
@@ -1800,7 +1835,7 @@ try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
   timevar_pop (TV_INTEGRATION);
 
   /* If inlining succeeded, return.  */
-  if (temp != (rtx) (HOST_WIDE_INT) - 1)
+  if (temp != (rtx) (size_t) - 1)
     {
       if (ACCUMULATE_OUTGOING_ARGS)
        {
@@ -1808,9 +1843,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)
            {
@@ -1856,7 +1893,7 @@ try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
                                                NULL_RTX, BITS_PER_UNIT);
                  seq = get_insns ();
                  end_sequence ();
-                 emit_insns_before (seq, first_insn);
+                 emit_insn_before (seq, first_insn);
                  emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
                }
            }
@@ -1879,8 +1916,8 @@ try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
       warning_with_decl (fndecl, "inlining failed in call to `%s'");
       warning ("called from here");
     }
-  mark_addressable (fndecl);
-  return (rtx) (HOST_WIDE_INT) - 1;
+  (*lang_hooks.mark_addressable) (fndecl);
+  return (rtx) (size_t) - 1;
 }
 
 /* We need to pop PENDING_STACK_ADJUST bytes.  But, if the arguments
@@ -1945,7 +1982,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
@@ -2007,14 +2044,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;
 
@@ -2028,17 +2067,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.
@@ -2135,8 +2206,8 @@ 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
 
@@ -2150,6 +2221,7 @@ expand_call (exp, target, ignore)
   int old_stack_allocated;
   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;
@@ -2185,7 +2257,7 @@ expand_call (exp, target, ignore)
              warning_with_decl (fndecl, "can't inline call to `%s'");
              warning ("called from here");
            }
-         mark_addressable (fndecl);
+         (*lang_hooks.mark_addressable) (fndecl);
        }
 
       flags |= flags_from_decl_or_type (fndecl);
@@ -2196,14 +2268,6 @@ expand_call (exp, target, ignore)
   else
     flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
 
-  /* Mark if the function returns with the stack pointer depressed.  */
-  if (TREE_CODE (TREE_TYPE (TREE_TYPE (p))) == FUNCTION_TYPE
-      && TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (TREE_TYPE (p))))
-    {
-      flags |= ECF_SP_DEPRESSED;
-      flags &= ~(ECF_PURE | ECF_CONST);
-    }
-
 #ifdef REG_PARM_STACK_SPACE
 #ifdef MAYBE_REG_PARM_STACK_SPACE
   reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
@@ -2228,7 +2292,7 @@ expand_call (exp, target, ignore)
   if (aggregate_value_p (exp))
     {
       /* This call returns a big structure.  */
-      flags &= ~(ECF_CONST | ECF_PURE);
+      flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
 
 #ifdef PCC_STATIC_STRUCT_RETURN
       {
@@ -2239,7 +2303,7 @@ expand_call (exp, target, ignore)
            /* In case this is a static function, note that it has been
               used.  */
            if (! TREE_ADDRESSABLE (fndecl))
-             mark_addressable (fndecl);
+             (*lang_hooks.mark_addressable) (fndecl);
            is_integrable = 0;
          }
       }
@@ -2247,7 +2311,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
          {
@@ -2271,15 +2345,21 @@ expand_call (exp, target, ignore)
       rtx temp = try_to_integrate (fndecl, actparms, target,
                                   ignore, TREE_TYPE (exp),
                                   structure_value_addr);
-      if (temp != (rtx) (HOST_WIDE_INT) - 1)
+      if (temp != (rtx) (size_t) - 1)
        return temp;
     }
 
   /* 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);
@@ -2354,7 +2434,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));
@@ -2375,7 +2455,7 @@ expand_call (exp, target, ignore)
         do this eventually, but it is too complicated to keep track of
         what insns go in the cse'able block and which don't.  */
 
-      flags &= ~(ECF_CONST | ECF_PURE);
+      flags &= ~ECF_LIBCALL_BLOCK;
       must_preallocate = 1;
     }
 
@@ -2416,8 +2496,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.  */
@@ -2433,17 +2513,13 @@ 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 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
@@ -2453,7 +2529,7 @@ expand_call (exp, target, ignore)
         != RETURN_POPS_ARGS (current_function_decl,
                              TREE_TYPE (current_function_decl),
                              current_function_args_size))
-  try_tail_call = 0;
+    try_tail_call = 0;
 
   if (try_tail_call || try_tail_recursion)
     {
@@ -2489,35 +2565,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))
@@ -2575,7 +2632,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"),
+      emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__gcov_flush"),
                         LCT_ALWAYS_RETURN,
                         VOIDmode, 0);
     }
@@ -2586,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;
 
@@ -2594,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
@@ -2608,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 ();
@@ -2648,21 +2704,19 @@ expand_call (exp, target, ignore)
       /* 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 or if we are expanding a sibling
-        call sequence.  */
+        call sequence or if we are calling a function that is to return
+        with stack pointer depressed.  */
       if (pending_stack_adjust >= 32
-         || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA))
+         || (pending_stack_adjust > 0
+             && (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
          || pass == 0)
        do_pending_stack_adjust ();
 
       /* 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 (flags & (ECF_CONST | ECF_PURE))
+      if (pass && (flags & ECF_LIBCALL_BLOCK))
        NO_DEFER_POP;
 
-      /* Push the temporary stack slot level so that we can free any
-        temporaries we make.  */
-      push_temp_slots ();
-
 #ifdef FINAL_REG_PARM_STACK_SPACE
       reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
                                                         args_size.var);
@@ -2673,7 +2727,7 @@ expand_call (exp, target, ignore)
 
       /* Now we are about to start emitting insns that can be deleted
         if a libcall is deleted.  */
-      if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC))
+      if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
        start_sequence ();
 
       adjusted_args_size = args_size;
@@ -2695,6 +2749,12 @@ expand_call (exp, target, ignore)
       if (pass == 0)
        {
          argblock = virtual_incoming_args_rtx;
+         argblock
+#ifdef STACK_GROWS_DOWNWARD
+           = plus_constant (argblock, current_function_pretend_args_size);
+#else
+           = plus_constant (argblock, -current_function_pretend_args_size);
+#endif
          stored_args_map = sbitmap_alloc (args_size.constant);
          sbitmap_zero (stored_args_map);
        }
@@ -2889,7 +2949,7 @@ expand_call (exp, target, ignore)
          /* When the stack adjustment is pending, we get better code
             by combining the adjustments.  */
          if (pending_stack_adjust
-             && ! (flags & (ECF_CONST | ECF_PURE))
+             && ! (flags & ECF_LIBCALL_BLOCK)
              && ! inhibit_defer_pop)
            {
              pending_stack_adjust
@@ -2907,7 +2967,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;
@@ -2949,7 +3009,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;
          }
 
@@ -2973,7 +3033,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;
            }
 
@@ -3008,7 +3068,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 ();
@@ -3039,7 +3100,7 @@ expand_call (exp, target, ignore)
       emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
                   adjusted_args_size.constant, struct_value_size,
                   next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
-                  flags);
+                  flags, & args_so_far);
 
       /* Verify that we've deallocated all the stack we used.  */
       if (pass
@@ -3050,46 +3111,49 @@ expand_call (exp, target, ignore)
         Test valreg so we don't crash; may safely ignore `const'
         if return type is void.  Disable for PARALLEL return values, because
         we have no way to move such values into a pseudo register.  */
-      if (pass
-         && (flags & (ECF_CONST | ECF_PURE))
-         && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+      if (pass && (flags & ECF_LIBCALL_BLOCK))
        {
-         rtx note = 0;
-         rtx temp = gen_reg_rtx (GET_MODE (valreg));
          rtx insns;
 
-         /* Mark the return value as a pointer if needed.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
-           mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
-
-         /* Construct an "equal form" for the value which mentions all the
-            arguments in order as well as the function name.  */
-         for (i = 0; i < num_actuals; i++)
-           note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
-         note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
-
-         insns = get_insns ();
-         end_sequence ();
-
-         if (flags & ECF_PURE)
-           note = gen_rtx_EXPR_LIST (VOIDmode,
-              gen_rtx_USE (VOIDmode,
-                           gen_rtx_MEM (BLKmode,
-                                        gen_rtx_SCRATCH (VOIDmode))), note);
-
-         emit_libcall_block (insns, temp, valreg, note);
-
-         valreg = temp;
-       }
-      else if (flags & (ECF_CONST | ECF_PURE))
-       {
-         /* Otherwise, just write out the sequence without a note.  */
-         rtx insns = get_insns ();
-
-         end_sequence ();
-         emit_insns (insns);
+         if (valreg == 0 || GET_CODE (valreg) == PARALLEL)
+           {
+             insns = get_insns ();
+             end_sequence ();
+             emit_insn (insns);
+           }
+         else
+           {
+             rtx note = 0;
+             rtx temp = gen_reg_rtx (GET_MODE (valreg));
+
+             /* Mark the return value as a pointer if needed.  */
+             if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
+               mark_reg_pointer (temp,
+                                 TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
+
+             /* Construct an "equal form" for the value which mentions all the
+                arguments in order as well as the function name.  */
+             for (i = 0; i < num_actuals; i++)
+               note = gen_rtx_EXPR_LIST (VOIDmode,
+                                         args[i].initial_value, note);
+             note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
+
+             insns = get_insns ();
+             end_sequence ();
+
+             if (flags & ECF_PURE)
+               note = gen_rtx_EXPR_LIST (VOIDmode,
+                       gen_rtx_USE (VOIDmode,
+                                    gen_rtx_MEM (BLKmode,
+                                                 gen_rtx_SCRATCH (VOIDmode))),
+                       note);
+
+             emit_libcall_block (insns, temp, valreg, note);
+
+             valreg = temp;
+           }
        }
-      else if (flags & ECF_MALLOC)
+      else if (pass && (flags & ECF_MALLOC))
        {
          rtx temp = gen_reg_rtx (GET_MODE (valreg));
          rtx last, insns;
@@ -3109,7 +3173,7 @@ expand_call (exp, target, ignore)
          /* Write out the sequence.  */
          insns = get_insns ();
          end_sequence ();
-         emit_insns (insns);
+         emit_insn (insns);
          valreg = temp;
        }
 
@@ -3160,9 +3224,7 @@ expand_call (exp, target, ignore)
 
       if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
          || ignore)
-       {
-         target = const0_rtx;
-       }
+       target = const0_rtx;
       else if (structure_value_addr)
        {
          if (target == 0 || GET_CODE (target) != MEM)
@@ -3256,7 +3318,7 @@ expand_call (exp, target, ignore)
          }
          target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
          SUBREG_PROMOTED_VAR_P (target) = 1;
-         SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
+         SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
        }
 #endif
 
@@ -3276,10 +3338,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.  */
@@ -3295,9 +3355,9 @@ expand_call (exp, target, ignore)
                if (save_mode != BLKmode)
                  emit_move_insn (stack_area, args[i].save_area);
                else
-                 emit_block_move (stack_area,
-                                  validize_mem (args[i].save_area),
-                                  GEN_INT (args[i].size.constant));
+                 emit_block_move (stack_area, args[i].save_area,
+                                  GEN_INT (args[i].size.constant),
+                                  BLOCK_OP_CALL_PARM);
              }
 
          highest_outgoing_arg_in_use = initial_highest_arg_in_use;
@@ -3311,8 +3371,6 @@ expand_call (exp, target, ignore)
       if ((flags & ECF_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)
@@ -3400,7 +3458,7 @@ expand_call (exp, target, ignore)
                                                tail_recursion_label));
     }
   else
-    emit_insns (normal_call_insns);
+    emit_insn (normal_call_insns);
 
   currently_expanding_call--;
 
@@ -3464,11 +3522,12 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
   int reg_parm_stack_space = 0;
   int needed;
   rtx before_call;
+  tree tfom;                   /* type_for_mode (outmode, 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 = 0;
+  int low_to_save, high_to_save;
   rtx save_area = 0;            /* Place that it is saved.  */
 #endif
 
@@ -3490,15 +3549,18 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
   switch (fn_type)
     {
     case LCT_NORMAL:
+      break;
     case LCT_CONST:
+      flags |= ECF_CONST;
+      break;
     case LCT_PURE:
-      /* Nothing to do here.  */
+      flags |= ECF_PURE;
       break;
     case LCT_CONST_MAKE_BLOCK:
-      flags |= ECF_CONST;
+      flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
       break;
     case LCT_PURE_MAKE_BLOCK:
-      flags |= ECF_PURE;
+      flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
       break;
     case LCT_NORETURN:
       flags |= ECF_NORETURN;
@@ -3522,27 +3584,31 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
 
   /* If this kind of value comes back in memory,
      decide where in memory it should come back.  */
-  if (outmode != VOIDmode && aggregate_value_p (type_for_mode (outmode, 0)))
+  if (outmode != VOIDmode)
     {
+      tfom = (*lang_hooks.types.type_for_mode) (outmode, 0);
+      if (aggregate_value_p (tfom))
+       {
 #ifdef PCC_STATIC_STRUCT_RETURN
-      rtx pointer_reg
-       = hard_function_value (build_pointer_type (type_for_mode (outmode, 0)),
-                              0, 0);
-      mem_value = gen_rtx_MEM (outmode, pointer_reg);
-      pcc_struct_value = 1;
-      if (value == 0)
-       value = gen_reg_rtx (outmode);
+         rtx pointer_reg
+           = hard_function_value (build_pointer_type (tfom), 0, 0);
+         mem_value = gen_rtx_MEM (outmode, pointer_reg);
+         pcc_struct_value = 1;
+         if (value == 0)
+           value = gen_reg_rtx (outmode);
 #else /* not PCC_STATIC_STRUCT_RETURN */
-      struct_value_size = GET_MODE_SIZE (outmode);
-      if (value != 0 && GET_CODE (value) == MEM)
-       mem_value = value;
-      else
-       mem_value = assign_temp (type_for_mode (outmode, 0), 0, 1, 1);
+         struct_value_size = GET_MODE_SIZE (outmode);
+         if (value != 0 && GET_CODE (value) == MEM)
+           mem_value = value;
+         else
+           mem_value = assign_temp (tfom, 0, 1, 1);
 #endif
-
-      /* This call returns a big structure.  */
-      flags &= ~(ECF_CONST | ECF_PURE);
+         /* This call returns a big structure.  */
+         flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
+       }
     }
+  else
+    tfom = void_type_node;
 
   /* ??? Unfinished: must pass the memory address as an argument.  */
 
@@ -3569,7 +3635,7 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
 
   /* Now we are about to start emitting insns that can be deleted
      if a libcall is deleted.  */
-  if (flags & (ECF_CONST | ECF_PURE))
+  if (flags & ECF_LIBCALL_BLOCK)
     start_sequence ();
 
   push_temp_slots ();
@@ -3651,23 +3717,42 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
 #endif
            ;
 
+         /* loop.c won't look at CALL_INSN_FUNCTION_USAGE of const/pure
+            functions, so we have to pretend this isn't such a function.  */
+         if (flags & ECF_LIBCALL_BLOCK)
+           {
+             rtx insns = get_insns ();
+             end_sequence ();
+             emit_insn (insns);
+           }
+         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)
            {
-             slot = assign_temp (type_for_mode (mode, 0), 0, 1, 1);
+             slot = assign_temp ((*lang_hooks.types.type_for_mode) (mode, 0),
+                                 0, 1, 1);
              emit_move_insn (slot, val);
            }
          else
            {
-             tree type = 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));
+             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));
            }
 
          call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
@@ -3823,62 +3908,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 (validize_mem (save_area), stack_area,
-                              GEN_INT (num_to_save));
-           }
-         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
 
@@ -3911,14 +3943,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.  */
@@ -3939,8 +3972,9 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
                }
            }
 
-         emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
-                         argblock, GEN_INT (argvec[argnum].offset.constant),
+         emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
+                         partial, reg, 0, argblock,
+                         GEN_INT (argvec[argnum].offset.constant),
                          reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
 
          /* Now mark the segment we just used.  */
@@ -4027,13 +4061,12 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
 
   emit_call_1 (fun,
               get_identifier (XSTR (orgfun, 0)),
-              build_function_type (outmode == VOIDmode ? void_type_node
-                                   : type_for_mode (outmode, 0), NULL_TREE),
+              build_function_type (tfom, NULL_TREE),
               original_args_size.constant, args_size.constant,
               struct_value_size,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
               valreg,
-              old_inhibit_defer_pop + 1, call_fusage, flags);
+              old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far);
 
   /* For calls to `setjmp', etc., inform flow.c it should complain
      if nonvolatile values are live.  For functions that cannot return,
@@ -4064,40 +4097,51 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
      Test valreg so we don't crash; may safely ignore `const'
      if return type is void.  Disable for PARALLEL return values, because
      we have no way to move such values into a pseudo register.  */
-  if ((flags & (ECF_CONST | ECF_PURE))
-      && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+  if (flags & ECF_LIBCALL_BLOCK)
     {
-      rtx note = 0;
-      rtx temp = gen_reg_rtx (GET_MODE (valreg));
       rtx insns;
-      int i;
 
-      /* 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++)
-       note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
-      note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
+      if (valreg == 0)
+       {
+         insns = get_insns ();
+         end_sequence ();
+         emit_insn (insns);
+       }
+      else
+       {
+         rtx note = 0;
+         rtx temp;
+         int i;
+
+         if (GET_CODE (valreg) == PARALLEL)
+           {
+             temp = gen_reg_rtx (outmode);
+             emit_group_store (temp, valreg, outmode);
+             valreg = temp;
+           }
 
-      insns = get_insns ();
-      end_sequence ();
+         temp = gen_reg_rtx (GET_MODE (valreg));
 
-      if (flags & ECF_PURE)
-       note = gen_rtx_EXPR_LIST (VOIDmode,
-          gen_rtx_USE (VOIDmode,
-                       gen_rtx_MEM (BLKmode,
-                                    gen_rtx_SCRATCH (VOIDmode))), note);
+         /* 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++)
+           note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
+         note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
 
-      emit_libcall_block (insns, temp, valreg, note);
+         insns = get_insns ();
+         end_sequence ();
 
-      valreg = temp;
-    }
-  else if (flags & (ECF_CONST | ECF_PURE))
-    {
-      /* Otherwise, just write out the sequence without a note.  */
-      rtx insns = get_insns ();
+         if (flags & ECF_PURE)
+           note = gen_rtx_EXPR_LIST (VOIDmode,
+                       gen_rtx_USE (VOIDmode,
+                                    gen_rtx_MEM (BLKmode,
+                                                 gen_rtx_SCRATCH (VOIDmode))),
+                       note);
 
-      end_sequence ();
-      emit_insns (insns);
+         emit_libcall_block (insns, temp, valreg, note);
+
+         valreg = temp;
+       }
     }
   pop_temp_slots ();
 
@@ -4111,38 +4155,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, validize_mem (save_area),
-                            GEN_INT (high_to_save - low_to_save + 1));
-       }
+       restore_fixed_argument_area (save_area, argblock,
+                                    high_to_save, low_to_save);
 #endif
 
       /* If we saved any argument areas, restore them.  */
@@ -4243,7 +4273,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
@@ -4292,14 +4322,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
@@ -4318,7 +4349,8 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
                  arg->save_area = assign_temp (nt, 0, 1, 1);
                  preserve_temp_slots (arg->save_area);
                  emit_block_move (validize_mem (arg->save_area), stack_area,
-                                  expr_size (arg->tree_value));
+                                  expr_size (arg->tree_value),
+                                  BLOCK_OP_CALL_PARM);
                }
              else
                {
@@ -4327,19 +4359,18 @@ 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,
      set up the register and number of words.  */
   if (! arg->pass_on_stack)
-    reg = arg->reg, partial = arg->partial;
+    {
+      if (flags & ECF_SIBCALL)
+       reg = arg->tail_call_reg;
+      else
+       reg = arg->reg;
+      partial = arg->partial;
+    }
 
   if (reg != 0 && partial == 0)
     /* Being passed entirely in a register.  We shouldn't be called in
@@ -4380,7 +4411,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.  */
@@ -4433,15 +4464,21 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
 
       /* 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,
+      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, 
+                     PARM_BOUNDARY, partial, reg, used - size, argblock,
                      ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
                      ARGS_SIZE_RTX (arg->alignment_pad));
+
+      /* Unless this is a partially-in-register argument, the argument is now
+        in the stack.  */
+      if (partial == 0)
+       arg->value = arg->stack;
     }
   else
     {
       /* BLKmode, at least partly to be pushed.  */
 
+      unsigned int parm_align;
       int excess;
       rtx size_rtx;
 
@@ -4463,7 +4500,25 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
             emit_push_insn for BLKmode is careful to avoid it.  */
          excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
                    + partial * UNITS_PER_WORD);
-         size_rtx = expr_size (pval);
+         size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+                                 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)
@@ -4499,53 +4554,28 @@ 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,
-                           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,
-                     TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess,
-                     argblock, ARGS_SIZE_RTX (arg->offset),
-                     reg_parm_stack_space,
+                     parm_align, partial, reg, excess, argblock,
+                     ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
                      ARGS_SIZE_RTX (arg->alignment_pad));
-    }
 
-  /* Unless this is a partially-in-register argument, the argument is now
-     in the stack.
+      /* Unless this is a partially-in-register argument, the argument is now
+        in the stack.
 
-     ??? Note that this can change arg->value from arg->stack to
-     arg->stack_slot and it matters when they are not the same.
-     It isn't totally clear that this is correct in all cases.  */
-  if (partial == 0)
-    arg->value = arg->stack_slot;
+        ??? Unlike the case above, in which we want the actual
+        address of the data, so that we can load it directly into a
+        register, here we want the address of the stack slot, so that
+        it's properly aligned for word-by-word copying or something
+        like that.  It's not clear that this is always correct.  */
+      if (partial == 0)
+       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.  */
@@ -4564,3 +4594,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;
+}