OSDN Git Service

* gcc.texi: Fixes for makeinfo 4.0 --html.
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index 071a89e..eff0042 100644 (file)
@@ -1,5 +1,6 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989, 92-99, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
+   1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -31,6 +32,23 @@ Boston, MA 02111-1307, USA.  */
 #include "output.h"
 #include "tm_p.h"
 
+#ifndef ACCUMULATE_OUTGOING_ARGS
+#define ACCUMULATE_OUTGOING_ARGS 0
+#endif
+
+/* Supply a default definition for PUSH_ARGS.  */
+#ifndef PUSH_ARGS
+#ifdef PUSH_ROUNDING
+#define PUSH_ARGS      !ACCUMULATE_OUTGOING_ARGS
+#else
+#define PUSH_ARGS      0
+#endif
+#endif
+
+#if !defined FUNCTION_OK_FOR_SIBCALL
+#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
+#endif
+
 #if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
 #define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
 #endif
@@ -44,9 +62,13 @@ Boston, MA 02111-1307, USA.  */
 #ifdef PUSH_ROUNDING
 
 #if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
-#define PUSH_ARGS_REVERSED     /* If it's last to first */
+#define PUSH_ARGS_REVERSED  PUSH_ARGS
+#endif
+
 #endif
 
+#ifndef PUSH_ARGS_REVERSED
+#define PUSH_ARGS_REVERSED 0
 #endif
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
@@ -96,10 +118,8 @@ struct arg_data
      differ from STACK if this arg pads downward.  This location is known
      to be aligned to FUNCTION_ARG_BOUNDARY.  */
   rtx stack_slot;
-#ifdef ACCUMULATE_OUTGOING_ARGS
   /* Place that this stack area has been saved, if needed.  */
   rtx save_area;
-#endif
   /* If an argument's alignment does not permit direct copying into registers,
      copy in smaller-sized pieces into pseudos.  These are stored in a
      block pointed to by this field.  The next field says how many
@@ -111,7 +131,6 @@ struct arg_data
   struct args_size alignment_pad;
 };
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
 /* A vector of one char per byte of stack space.  A byte if non-zero if
    the corresponding stack location has been used.
    This vector is used to prevent a function call within an argument from
@@ -127,10 +146,32 @@ static int highest_outgoing_arg_in_use;
    to make sure the object being constructed does not overlap the
    argument list for the constructor call.  */
 int stack_arg_under_construction;
-#endif
 
 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
+
 static void emit_call_1                PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
                                         HOST_WIDE_INT, HOST_WIDE_INT, rtx,
                                         rtx, int, rtx, int));
@@ -144,9 +185,8 @@ static void store_unaligned_arguments_into_pseudos PARAMS ((struct arg_data *,
 static int finalize_must_preallocate           PARAMS ((int, int,
                                                         struct arg_data *,
                                                         struct args_size *));
-static void precompute_arguments               PARAMS ((int, int, int,
-                                                        struct arg_data *,
-                                                        struct args_size *));
+static void precompute_arguments               PARAMS ((int, int,
+                                                        struct arg_data *));
 static int compute_argument_block_size         PARAMS ((int, 
                                                         struct args_size *,
                                                         int));
@@ -162,8 +202,18 @@ static void compute_argument_addresses             PARAMS ((struct arg_data *,
 static rtx rtx_for_function_call               PARAMS ((tree, tree));
 static void load_register_parameters           PARAMS ((struct arg_data *,
                                                         int, rtx *));
+static int libfunc_nothrow                     PARAMS ((rtx));
+static rtx emit_library_call_value_1           PARAMS ((int, rtx, rtx, int,
+                                                        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 combine_pending_stack_adjustment_and_call
+                                                PARAMS ((int, struct args_size *, int));
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
 static rtx save_fixed_argument_area    PARAMS ((int, rtx, int *, int *));
 static void restore_fixed_argument_area        PARAMS ((rtx, rtx, int, int));
 #endif
@@ -351,9 +401,10 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
    says that the pointer to this aggregate is to be popped by the callee.
 
    STACK_SIZE is the number of bytes of arguments on the stack,
-   rounded up to PREFERRED_STACK_BOUNDARY; zero if the size is variable.
-   This is both to put into the call insn and
-   to generate explicit popping code if necessary.
+   ROUNDED_STACK_SIZE is that number rounded up to
+   PREFERRED_STACK_BOUNDARY; zero if the size is variable.  This is
+   both to put into the call insn and to generate explicit popping
+   code if necessary.
 
    STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value.
    It is zero if this call doesn't want a structure value.
@@ -374,14 +425,12 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
    We restore `inhibit_defer_pop' to that value.
 
    CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that
-   denote registers used by the called function.
-
-   IS_CONST is true if this is a `const' call.  */
+   denote registers used by the called function.  */
 
 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, is_const)
+            call_fusage, ecf_flags)
      rtx funexp;
      tree fndecl ATTRIBUTE_UNUSED;
      tree funtype ATTRIBUTE_UNUSED;
@@ -392,17 +441,15 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
      rtx valreg;
      int old_inhibit_defer_pop;
      rtx call_fusage;
-     int is_const;
+     int ecf_flags;
 {
   rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
 #if defined (HAVE_call) && defined (HAVE_call_value)
   rtx struct_value_size_rtx = GEN_INT (struct_value_size);
 #endif
   rtx call_insn;
-#ifndef ACCUMULATE_OUTGOING_ARGS
   int already_popped = 0;
   HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
-#endif
 
   /* Ensure address is valid.  SYMBOL_REF is already valid, so no need,
      and we don't want to load it into a register as an optimization,
@@ -410,7 +457,33 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
   if (GET_CODE (funexp) != SYMBOL_REF)
     funexp = memory_address (FUNCTION_MODE, funexp);
 
-#ifndef ACCUMULATE_OUTGOING_ARGS
+#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop)
+  if ((ecf_flags & ECF_SIBCALL)
+      && HAVE_sibcall_pop && HAVE_sibcall_value_pop
+      && (RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0 
+          || stack_size == 0))
+    {
+      rtx n_pop = GEN_INT (RETURN_POPS_ARGS (fndecl, funtype, stack_size));
+      rtx pat;
+
+      /* If this subroutine pops its own args, record that in the call insn
+        if possible, for the sake of frame pointer elimination.  */
+
+      if (valreg)
+       pat = gen_sibcall_value_pop (valreg,
+                                    gen_rtx_MEM (FUNCTION_MODE, funexp),
+                                    rounded_stack_size_rtx, next_arg_reg,
+                                    n_pop);
+      else
+       pat = gen_sibcall_pop (gen_rtx_MEM (FUNCTION_MODE, funexp),
+                              rounded_stack_size_rtx, next_arg_reg, n_pop);
+
+      emit_call_insn (pat);
+      already_popped = 1;
+    }
+  else
+#endif
+
 #if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
 /* If the target has "call" or "call_value" insns, then prefer them
    if no arguments are actually popped.  If the target does not have
@@ -418,7 +491,7 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
    even if the call has no arguments to pop.  */
 #if defined (HAVE_call) && defined (HAVE_call_value)
   if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
-       && n_popped > 0)
+      && n_popped > 0)
 #else
   if (HAVE_call_pop && HAVE_call_value_pop)
 #endif
@@ -442,6 +515,22 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
     }
   else
 #endif
+
+#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value)
+  if ((ecf_flags & ECF_SIBCALL)
+      && HAVE_sibcall && HAVE_sibcall_value)
+    {
+      if (valreg)
+       emit_call_insn (gen_sibcall_value (valreg,
+                                          gen_rtx_MEM (FUNCTION_MODE, funexp),
+                                          rounded_stack_size_rtx,
+                                          next_arg_reg, NULL_RTX));
+      else
+       emit_call_insn (gen_sibcall (gen_rtx_MEM (FUNCTION_MODE, funexp),
+                                    rounded_stack_size_rtx, next_arg_reg,
+                                    struct_value_size_rtx));
+    }
+  else
 #endif
 
 #if defined (HAVE_call) && defined (HAVE_call_value)
@@ -470,6 +559,15 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
   if (! call_insn)
     abort ();
 
+  /* Mark memory as used for "pure" function call.  */
+  if (ecf_flags & ECF_PURE)
+    {
+      call_fusage =  gen_rtx_EXPR_LIST (VOIDmode,
+       gen_rtx_USE (VOIDmode,
+                    gen_rtx_MEM (BLKmode,
+                                 gen_rtx_SCRATCH (VOIDmode))), call_fusage);
+    }
+
   /* Put the register usage information on the CALL.  If there is already
      some usage information, put ours at the end.  */
   if (CALL_INSN_FUNCTION_USAGE (call_insn))
@@ -486,21 +584,21 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
     CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
 
   /* If this is a const call, then set the insn's unchanging bit.  */
-  if (is_const)
+  if (ecf_flags & (ECF_CONST | ECF_PURE))
     CONST_CALL_P (call_insn) = 1;
 
+  /* If this call can't throw, attach a REG_EH_REGION reg note to that
+     effect.  */
+  if (ecf_flags & ECF_NOTHROW)
+    REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
+                                              REG_NOTES (call_insn));
+
+  SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
+
   /* Restore this now, so that we do defer pops for this call's args
      if the context of the call as a whole permits.  */
   inhibit_defer_pop = old_inhibit_defer_pop;
 
-#ifndef ACCUMULATE_OUTGOING_ARGS
-  /* If returning from the subroutine does not automatically pop the args,
-     we need an instruction to pop them sooner or later.
-     Perhaps do it now; perhaps just record how much space to pop later.
-
-     If returning from the subroutine does pop the args, indicate that the
-     stack pointer will be changed.  */
-
   if (n_popped > 0)
     {
       if (!already_popped)
@@ -510,16 +608,40 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
                               CALL_INSN_FUNCTION_USAGE (call_insn));
       rounded_stack_size -= n_popped;
       rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
+      stack_pointer_delta -= n_popped;
     }
 
-  if (rounded_stack_size != 0)
+  if (!ACCUMULATE_OUTGOING_ARGS)
     {
-      if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const)
-       pending_stack_adjust += rounded_stack_size;
-      else
-       adjust_stack (rounded_stack_size_rtx);
+      /* If returning from the subroutine does not automatically pop the args,
+        we need an instruction to pop them sooner or later.
+        Perhaps do it now; perhaps just record how much space to pop later.
+
+        If returning from the subroutine does pop the args, indicate that the
+        stack pointer will be changed.  */
+
+      if (rounded_stack_size != 0)
+       {
+         if (flag_defer_pop && inhibit_defer_pop == 0
+             && !(ecf_flags & (ECF_CONST | ECF_PURE)))
+           pending_stack_adjust += rounded_stack_size;
+         else
+           adjust_stack (rounded_stack_size_rtx);
+       }
     }
-#endif
+  /* When we accumulate outgoing args, we must avoid any stack manipulations.
+     Restore the stack pointer to its original value now.  Usually
+     ACCUMULATE_OUTGOING_ARGS targets don't get here, but there are exceptions.
+     On  i386 ACCUMULATE_OUTGOING_ARGS can be enabled on demand, and
+     popping variants of functions exist as well.
+
+     ??? We may optimize similar to defer_pop above, but it is
+     probably not worthwhile.
+   
+     ??? It will be worthwhile to enable combine_stack_adjustments even for
+     such machines.  */
+  else if (n_popped)
+    anti_adjust_stack (GEN_INT (n_popped));
 }
 
 /* Determine if the function identified by NAME and FNDECL is one with
@@ -528,33 +650,20 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
    For example, if the function might return more than one time (setjmp), then
    set RETURNS_TWICE to a nonzero value.
 
-   Similarly set IS_LONGJMP for if the function is in the longjmp family.
+   Similarly set LONGJMP for if the function is in the longjmp family.
 
-   Set IS_MALLOC for any of the standard memory allocation functions which
+   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.  */
 
-void
-special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
-                   is_malloc, may_be_alloca)
+static int
+special_function_p (fndecl, flags)
      tree fndecl;
-     int *returns_twice;
-     int *is_longjmp;
-     int *fork_or_exec;
-     int *is_malloc;
-     int *may_be_alloca;
+     int flags;
 {
-  *returns_twice = 0;
-  *is_longjmp = 0;
-  *fork_or_exec = 0;
-  *may_be_alloca = 0;
-
-  /* The function decl may have the `malloc' attribute.  */
-  *is_malloc = fndecl && DECL_IS_MALLOC (fndecl);
-
-  if (! *is_malloc 
+  if (! (flags & ECF_MALLOC)
       && fndecl && DECL_NAME (fndecl)
       && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
       /* Exclude functions not at the file scope, or not `extern',
@@ -568,13 +677,13 @@ special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
       /* We assume that alloca will always be called by name.  It
         makes no sense to pass it as a pointer-to-function to
         anything that does not understand its behavior.  */
-      *may_be_alloca
-       = (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
-            && name[0] == 'a'
-            && ! strcmp (name, "alloca"))
-           || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
-               && name[0] == '_'
-               && ! strcmp (name, "__builtin_alloca"))));
+      if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
+           && name[0] == 'a'
+           && ! strcmp (name, "alloca"))
+          || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
+              && name[0] == '_'
+              && ! strcmp (name, "__builtin_alloca"))))
+       flags |= ECF_MAY_BE_ALLOCA;
 
       /* Disregard prefix _, __ or __x.  */
       if (name[0] == '_')
@@ -589,27 +698,28 @@ special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
 
       if (tname[0] == 's')
        {
-         *returns_twice
-            = ((tname[1] == 'e'
-                && (! strcmp (tname, "setjmp")
-                    || ! strcmp (tname, "setjmp_syscall")))
-               || (tname[1] == 'i'
-                   && ! strcmp (tname, "sigsetjmp"))
-               || (tname[1] == 'a'
-                   && ! strcmp (tname, "savectx")));
+         if ((tname[1] == 'e'
+              && (! strcmp (tname, "setjmp")
+                  || ! strcmp (tname, "setjmp_syscall")))
+             || (tname[1] == 'i'
+                 && ! strcmp (tname, "sigsetjmp"))
+             || (tname[1] == 'a'
+                 && ! strcmp (tname, "savectx")))
+           flags |= ECF_RETURNS_TWICE;
+
          if (tname[1] == 'i'
              && ! strcmp (tname, "siglongjmp"))
-           *is_longjmp = 1;
+           flags |= ECF_LONGJMP;
        }
       else if ((tname[0] == 'q' && tname[1] == 's'
                && ! strcmp (tname, "qsetjmp"))
               || (tname[0] == 'v' && tname[1] == 'f'
                   && ! strcmp (tname, "vfork")))
-       *returns_twice = 1;
+       flags |= ECF_RETURNS_TWICE;
 
       else if (tname[0] == 'l' && tname[1] == 'o'
               && ! strcmp (tname, "longjmp"))
-       *is_longjmp = 1;
+       flags |= ECF_LONGJMP;
 
       else if ((tname[0] == 'f' && tname[1] == 'o'
                && ! strcmp (tname, "fork"))
@@ -623,25 +733,63 @@ special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
                   && (tname[5] == '\0'
                       || ((tname[5] == 'p' || tname[5] == 'e')
                           && tname[6] == '\0'))))
-       *fork_or_exec = 1;
+       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. */
-      else if (! strcmp (tname, "malloc")
-              || ! strcmp (tname, "calloc")
-              || ! strcmp (tname, "strdup")
-              /* Note use of NAME rather than TNAME here.  These functions
-                 are only reserved when preceded with __.  */
-              || ! strcmp (name, "__vn")       /* mangled __builtin_vec_new */
-              || ! strcmp (name, "__nw")       /* mangled __builtin_new */
-              || ! strcmp (name, "__builtin_new")
-              || ! strcmp (name, "__builtin_vec_new"))
-       *is_malloc = 1;
+         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;
+}
+
+/* Return nonzero when tree represent call to longjmp.  */
+int
+setjmp_call_p (fndecl)
+     tree fndecl;
+{
+  return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
+}
+
+/* Detect flags (function attributes) from the function type node.  */
+static int
+flags_from_decl_or_type (exp)
+     tree exp;
+{
+  int flags = 0;
+  /* ??? We can't set IS_MALLOC for function types?  */
+  if (DECL_P (exp))
+    {
+      /* 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;
+
+      if (TREE_NOTHROW (exp))
+       flags |= ECF_NOTHROW;
     }
+
+  if (TREE_READONLY (exp) && !TREE_THIS_VOLATILE (exp))
+    flags |= ECF_CONST;
+
+  if (TREE_THIS_VOLATILE (exp))
+    flags |= ECF_NORETURN;
+
+  return flags;
 }
 
+
 /* Precompute all register parameters as described by ARGS, storing values
    into fields within the ARGS array.
 
@@ -705,7 +853,7 @@ precompute_register_parameters (num_actuals, args, reg_parm_seen)
       }
 }
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
 
   /* The argument list is the property of the called routine and it
      may clobber it.  If the fixed area has been used for previous
@@ -764,11 +912,11 @@ save_fixed_argument_area (reg_parm_stack_space, argblock,
       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.  */
+         /* 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 / BITS_PER_UNIT);
+                         PARM_BOUNDARY);
        }
       else
        {
@@ -808,8 +956,7 @@ restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save)
        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 / BITS_PER_UNIT);
+                   high_to_save - low_to_save + 1, PARM_BOUNDARY);
 }
 #endif
          
@@ -876,12 +1023,10 @@ store_unaligned_arguments_into_pseudos (args, num_actuals)
 
            bytes -= bitsize / BITS_PER_UNIT;
            store_bit_field (reg, bitsize, big_endian_correction, word_mode,
-                            extract_bit_field (word, bitsize, 0, 1,
-                                               NULL_RTX, word_mode,
-                                               word_mode,
-                                               bitalign / BITS_PER_UNIT,
+                            extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
+                                               word_mode, word_mode, bitalign,
                                                BITS_PER_WORD),
-                            bitalign / BITS_PER_UNIT, BITS_PER_WORD);
+                            bitalign, BITS_PER_WORD);
          }
       }
 }
@@ -904,14 +1049,15 @@ store_unaligned_arguments_into_pseudos (args, num_actuals)
    OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level
    and may be modified by this routine.
 
-   OLD_PENDING_ADJ, MUST_PREALLOCATE and IS_CONST are pointers to integer
+   OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer
    flags which may may be modified by this routine.  */
 
 static void
 initialize_argument_information (num_actuals, args, args_size, n_named_args,
                                 actparms, fndecl, args_so_far,
                                 reg_parm_stack_space, old_stack_level,
-                                old_pending_adj, must_preallocate, is_const)
+                                old_pending_adj, must_preallocate,
+                                ecf_flags)
      int num_actuals ATTRIBUTE_UNUSED;
      struct arg_data *args;
      struct args_size *args_size;
@@ -923,7 +1069,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
      rtx *old_stack_level;
      int *old_pending_adj;
      int *must_preallocate;
-     int *is_const;
+     int *ecf_flags;
 {
   /* 1 if scanning parms front to back, -1 if scanning back to front.  */
   int inc;
@@ -942,13 +1088,16 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
      We fill up ARGS from the front or from the back if necessary
      so that in any case the first arg to be pushed ends up at the front.  */
 
-#ifdef PUSH_ARGS_REVERSED
-  i = num_actuals - 1, inc = -1;
-  /* In this case, must reverse order of args
-     so that we compute and push the last arg first.  */
-#else
-  i = 0, inc = 1;
-#endif
+  if (PUSH_ARGS_REVERSED)
+    {
+      i = num_actuals - 1, inc = -1;
+      /* In this case, must reverse order of args
+        so that we compute and push the last arg first.  */
+    }
+  else
+    {
+      i = 0, inc = 1;
+    }
 
   /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
   for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
@@ -960,7 +1109,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
       args[i].tree_value = TREE_VALUE (p);
 
       /* Replace erroneous argument with constant zero.  */
-      if (type == error_mark_node || TYPE_SIZE (type) == 0)
+      if (type == error_mark_node || !COMPLETE_TYPE_P (type))
        args[i].tree_value = integer_zero_node, type = integer_type_node;
 
       /* If TYPE is a transparent union, pass things the way we would
@@ -1017,9 +1166,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
                 make a bitwise copy of the argument. */
                 
              if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
-                 && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND
-                                                 (args[i].tree_value, 1)))
-                     == 'd')
+                 && (DECL_P (TREE_OPERAND (args[i].tree_value, 1)))
                  && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
                args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
 
@@ -1034,12 +1181,11 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
                 function being called.  */
              rtx copy;
 
-             if (TYPE_SIZE (type) == 0
+             if (!COMPLETE_TYPE_P (type)
                  || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
                  || (flag_stack_check && ! STACK_CHECK_BUILTIN
-                     && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0
-                         || (TREE_INT_CST_LOW (TYPE_SIZE (type))
-                             > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT))))
+                     && (0 < compare_tree_int (TYPE_SIZE_UNIT (type),
+                                               STACK_CHECK_MAX_VAR_SIZE))))
                {
                  /* This is a variable-sized object.  Make space on the stack
                     for it.  */
@@ -1066,7 +1212,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
              MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
 
              store_expr (args[i].tree_value, copy, 0);
-             *is_const = 0;
+             *ecf_flags &= ~(ECF_CONST | ECF_PURE);
 
              args[i].tree_value = build1 (ADDR_EXPR,
                                           build_pointer_type (type),
@@ -1084,8 +1230,19 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
 
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
-      args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
-                                 argpos < n_named_args);
+
+#ifdef FUNCTION_INCOMING_ARG
+      /* If this is a sibling call and the machine has register windows, the
+        register window has to be unwinded before calling the routine, so
+        arguments have to go into the incoming registers.  */
+      if (*ecf_flags & ECF_SIBCALL)
+       args[i].reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
+                                            argpos < n_named_args);
+      else
+#endif
+       args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
+                                   argpos < n_named_args);
+
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
       if (args[i].reg)
        args[i].partial
@@ -1114,7 +1271,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))
-       *is_const = 0;
+       *ecf_flags &= ~(ECF_CONST | ECF_PURE);
 
       /* Compute the stack-size of this argument.  */
       if (args[i].reg == 0 || args[i].partial != 0
@@ -1157,9 +1314,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
 
       args[i].slot_offset.constant = -args_size->constant;
       if (args_size->var)
-       {
-         SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
-       }
+       SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
 #endif
 
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
@@ -1185,6 +1340,14 @@ compute_argument_block_size (reg_parm_stack_space, args_size,
 {
   int unadjusted_args_size = args_size->constant;
 
+  /* For accumulate outgoing args mode we don't need to align, since the frame
+     will be already aligned.  Align to STACK_BOUNDARY in order to prevent
+     backends from generating missaligned frame sizes.  */
+#ifdef STACK_BOUNDARY
+  if (ACCUMULATE_OUTGOING_ARGS && preferred_stack_boundary > STACK_BOUNDARY)
+    preferred_stack_boundary = STACK_BOUNDARY;
+#endif
+
   /* Compute the actual size of the argument block required.  The variable
      and constant sizes must be combined, the size may have to be rounded,
      and there may be a minimum required size.  */
@@ -1197,21 +1360,28 @@ compute_argument_block_size (reg_parm_stack_space, args_size,
 #ifdef PREFERRED_STACK_BOUNDARY
       preferred_stack_boundary /= BITS_PER_UNIT;
       if (preferred_stack_boundary > 1)
-       args_size->var = round_up (args_size->var, preferred_stack_boundary);
+       {
+         /* We don't handle this case yet.  To handle it correctly we have
+            to add the delta, round and substract the delta.  
+            Currently no machine description requires this support.  */
+         if (stack_pointer_delta & (preferred_stack_boundary - 1))
+           abort();
+         args_size->var = round_up (args_size->var, preferred_stack_boundary);
+       }
 #endif
 
       if (reg_parm_stack_space > 0)
        {
          args_size->var
            = size_binop (MAX_EXPR, args_size->var,
-                         size_int (reg_parm_stack_space));
+                         ssize_int (reg_parm_stack_space));
 
 #ifndef OUTGOING_REG_PARM_STACK_SPACE
          /* The area corresponding to register parameters is not to count in
             the size of the block we need.  So make the adjustment.  */
          args_size->var
            = size_binop (MINUS_EXPR, args_size->var,
-                         size_int (reg_parm_stack_space));
+                         ssize_int (reg_parm_stack_space));
 #endif
        }
     }
@@ -1219,12 +1389,14 @@ compute_argument_block_size (reg_parm_stack_space, args_size,
     {
 #ifdef PREFERRED_STACK_BOUNDARY
       preferred_stack_boundary /= BITS_PER_UNIT;
+      if (preferred_stack_boundary < 1)
+       preferred_stack_boundary = 1;
       args_size->constant = (((args_size->constant
-                              + pending_stack_adjust
+                              + stack_pointer_delta
                               + preferred_stack_boundary - 1)
                              / preferred_stack_boundary
                              * preferred_stack_boundary)
-                            - pending_stack_adjust);
+                            - stack_pointer_delta);
 #endif
 
       args_size->constant = MAX (args_size->constant,
@@ -1244,25 +1416,19 @@ compute_argument_block_size (reg_parm_stack_space, args_size,
 
 /* Precompute parameters as needed for a function call.
 
-   IS_CONST indicates the target function is a pure function.
-
-   MUST_PREALLOCATE indicates that we must preallocate stack space for
-   any stack arguments.
+   FLAGS is mask of ECF_* constants.
 
    NUM_ACTUALS is the number of arguments.
 
    ARGS is an array containing information for each argument; this routine
-   fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
-
-   ARGS_SIZE contains information about the size of the arg list.  */
+   fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.  
+   */
 
 static void
-precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
-     int is_const;
-     int must_preallocate;
+precompute_arguments (flags, num_actuals, args)
+     int flags;
      int num_actuals;
      struct arg_data *args;
-     struct args_size *args_size;
 {
   int i;
 
@@ -1277,15 +1443,13 @@ precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
      on the stack, then we must precompute any parameter which contains a
      function call which will store arguments on the stack.
      Otherwise, evaluating the parameter may clobber previous parameters
-     which have already been stored into the stack.  */
+     which have already been stored into the stack.  (we have code to avoid
+     such case by saving the ougoing stack arguments, but it results in
+     worse code)  */
 
   for (i = 0; i < num_actuals; i++)
-    if (is_const
-       || ((args_size->var != 0 || args_size->constant != 0)
-           && calls_function (args[i].tree_value, 1))
-       || (must_preallocate
-           && (args_size->var != 0 || args_size->constant != 0)
-           && calls_function (args[i].tree_value, 0)))
+    if ((flags & (ECF_CONST | ECF_PURE))
+       || calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
       {
        /* If this is an addressable type, we cannot pre-evaluate it.  */
        if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
@@ -1541,12 +1705,9 @@ load_register_parameters (args, num_actuals, call_fusage)
             locations.  The Irix 6 ABI has examples of this.  */
 
          if (GET_CODE (reg) == PARALLEL)
-           {
-             emit_group_load (reg, args[i].value,
-                              int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
-                              (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
-                               / BITS_PER_UNIT));
-           }
+           emit_group_load (reg, args[i].value,
+                            int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
+                            TYPE_ALIGN (TREE_TYPE (args[i].tree_value)));
 
          /* If simple case, just do move.  If normal partial, store_one_arg
             has already loaded the register for us.  In all other cases,
@@ -1580,6 +1741,178 @@ load_register_parameters (args, num_actuals, call_fusage)
     }
 }
 
+/* Try to integreate function.  See expand_inline_function for documentation
+   about the parameters.  */
+
+static rtx
+try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
+     tree fndecl;
+     tree actparms;
+     rtx target;
+     int ignore;
+     tree type;
+     rtx structure_value_addr;
+{
+  rtx temp;
+  rtx before_call;
+  int i;
+  rtx old_stack_level = 0;
+  int reg_parm_stack_space = 0;
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
+
+  before_call = get_last_insn ();
+
+  temp = expand_inline_function (fndecl, actparms, target,
+                                ignore, type,
+                                structure_value_addr);
+
+  /* If inlining succeeded, return.  */
+  if (temp != (rtx) (HOST_WIDE_INT) - 1)
+    {
+      if (ACCUMULATE_OUTGOING_ARGS)
+       {
+         /* If the outgoing argument list must be preserved, push
+            the stack before executing the inlined function if it
+            makes any calls.  */
+
+         for (i = reg_parm_stack_space - 1; i >= 0; i--)
+           if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
+             break;
+
+         if (stack_arg_under_construction || i >= 0)
+           {
+             rtx first_insn
+               = before_call ? NEXT_INSN (before_call) : get_insns ();
+             rtx insn = NULL_RTX, seq;
+
+             /* Look for a call in the inline function code.
+                If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
+                nonzero then there is a call and it is not necessary
+                to scan the insns.  */
+
+             if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
+               for (insn = first_insn; insn; insn = NEXT_INSN (insn))
+                 if (GET_CODE (insn) == CALL_INSN)
+                   break;
+
+             if (insn)
+               {
+                 /* Reserve enough stack space so that the largest
+                    argument list of any function call in the inline
+                    function does not overlap the argument list being
+                    evaluated.  This is usually an overestimate because
+                    allocate_dynamic_stack_space reserves space for an
+                    outgoing argument list in addition to the requested
+                    space, but there is no way to ask for stack space such
+                    that an argument list of a certain length can be
+                    safely constructed. 
+
+                    Add the stack space reserved for register arguments, if
+                    any, in the inline function.  What is really needed is the
+                    largest value of reg_parm_stack_space in the inline
+                    function, but that is not available.  Using the current
+                    value of reg_parm_stack_space is wrong, but gives
+                    correct results on all supported machines.  */
+
+                 int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+                               + reg_parm_stack_space);
+
+                 start_sequence ();
+                 emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+                 allocate_dynamic_stack_space (GEN_INT (adjust),
+                                               NULL_RTX, BITS_PER_UNIT);
+                 seq = get_insns ();
+                 end_sequence ();
+                 emit_insns_before (seq, first_insn);
+                 emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+               }
+           }
+       }
+
+      /* If the result is equivalent to TARGET, return TARGET to simplify
+         checks in store_expr.  They can be equivalent but not equal in the
+         case of a function that returns BLKmode.  */
+      if (temp != target && rtx_equal_p (temp, target))
+       return target;
+      return temp;
+    }
+
+  /* If inlining failed, mark FNDECL as needing to be compiled
+     separately after all.  If function was declared inline,
+     give a warning.  */
+  if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+      && optimize > 0 && !TREE_ADDRESSABLE (fndecl))
+    {
+      warning_with_decl (fndecl, "inlining failed in call to `%s'");
+      warning ("called from here");
+    }
+  mark_addressable (fndecl);
+  return (rtx) (HOST_WIDE_INT) - 1;
+}
+
+/* We need to pop PENDING_STACK_ADJUST bytes.  But, if the arguments
+   wouldn't fill up an even multiple of PREFERRED_UNIT_STACK_BOUNDARY
+   bytes, then we would need to push some additional bytes to pad the
+   arguments.  So, we compute an adjust to the stack pointer for an
+   amount that will leave the stack under-aligned by UNADJUSTED_ARGS_SIZE
+   bytes.  Then, when the arguments are pushed the stack will be perfectly
+   aligned.  ARGS_SIZE->CONSTANT is set to the number of bytes that should
+   be popped after the call.  Returns the adjustment.  */
+
+static int
+combine_pending_stack_adjustment_and_call (unadjusted_args_size,
+                                          args_size,
+                                          preferred_unit_stack_boundary)
+     int unadjusted_args_size;
+     struct args_size *args_size;
+     int preferred_unit_stack_boundary;
+{
+  /* The number of bytes to pop so that the stack will be
+     under-aligned by UNADJUSTED_ARGS_SIZE bytes.  */
+  HOST_WIDE_INT adjustment;
+  /* The alignment of the stack after the arguments are pushed, if we
+     just pushed the arguments without adjust the stack here.  */
+  HOST_WIDE_INT unadjusted_alignment;
+
+  unadjusted_alignment 
+    = ((stack_pointer_delta + unadjusted_args_size)
+       % preferred_unit_stack_boundary);
+
+  /* We want to get rid of as many of the PENDING_STACK_ADJUST bytes
+     as possible -- leaving just enough left to cancel out the
+     UNADJUSTED_ALIGNMENT.  In other words, we want to ensure that the
+     PENDING_STACK_ADJUST is non-negative, and congruent to
+     -UNADJUSTED_ALIGNMENT modulo the PREFERRED_UNIT_STACK_BOUNDARY.  */
+
+  /* Begin by trying to pop all the bytes.  */
+  unadjusted_alignment 
+    = (unadjusted_alignment 
+       - (pending_stack_adjust % preferred_unit_stack_boundary));
+  adjustment = pending_stack_adjust;
+  /* Push enough additional bytes that the stack will be aligned
+     after the arguments are pushed.  */
+  if (unadjusted_alignment >= 0)
+    adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
+  else
+    adjustment += unadjusted_alignment;
+  
+  /* Now, sets ARGS_SIZE->CONSTANT so that we pop the right number of
+     bytes after the call.  The right number is the entire
+     PENDING_STACK_ADJUST less our ADJUSTMENT plus the amount required
+     by the arguments in the first place.  */
+  args_size->constant 
+    = pending_stack_adjust - adjustment + unadjusted_args_size;
+
+  return adjustment;
+}
+
 /* 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.
@@ -1592,17 +1925,28 @@ expand_call (exp, target, ignore)
      rtx target;
      int ignore;
 {
+  /* Nonzero if we are currently expanding a call.  */
+  static int currently_expanding_call = 0;
+
   /* List of actual parameters.  */
   tree actparms = TREE_OPERAND (exp, 1);
   /* RTX for the function to be called.  */
   rtx funexp;
+  /* Sequence of insns to perform a tail recursive "call".  */
+  rtx tail_recursion_insns = NULL_RTX;
+  /* Sequence of insns to perform a normal "call".  */
+  rtx normal_call_insns = NULL_RTX;
+  /* Sequence of insns to perform a tail recursive "call".  */
+  rtx tail_call_insns = NULL_RTX;
   /* Data type of the function.  */
   tree funtype;
   /* Declaration of the function being called,
      or 0 if the function is computed (not known by name).  */
   tree fndecl = 0;
   char *name = 0;
-  rtx before_call;
+  rtx insn;
+  int try_tail_call;
+  int pass;
 
   /* Register in which non-BLKmode value will be returned,
      or 0 if no value or if value is BLKmode.  */
@@ -1649,11 +1993,7 @@ expand_call (exp, target, ignore)
      So the entire argument block must then be preallocated (i.e., we
      ignore PUSH_ROUNDING in that case).  */
 
-#ifdef PUSH_ROUNDING
-  int must_preallocate = 0;
-#else
-  int must_preallocate = 1;
-#endif
+  int must_preallocate = !PUSH_ARGS;
 
   /* Size of the stack reserved for parameter registers.  */
   int reg_parm_stack_space = 0;
@@ -1662,51 +2002,32 @@ expand_call (exp, target, ignore)
      (on machines that lack push insns), or 0 if space not preallocated.  */
   rtx argblock = 0;
 
-  /* Nonzero if it is plausible that this is a call to alloca.  */
-  int may_be_alloca;
-  /* Nonzero if this is a call to malloc or a related function. */
-  int is_malloc;
-  /* Nonzero if this is a call to setjmp or a related function.  */
-  int returns_twice;
-  /* Nonzero if this is a call to `longjmp'.  */
-  int is_longjmp;
-  /* Nonzero if this is a syscall that makes a new process in the image of
-     the current one.  */
-  int fork_or_exec;
+  /* Mask of ECF_ flags.  */
+  int flags = 0;
   /* Nonzero if this is a call to an inline function.  */
   int is_integrable = 0;
-  /* Nonzero if this is a call to a `const' function.
-     Note that only explicitly named functions are handled as `const' here.  */
-  int is_const = 0;
-  /* Nonzero if this is a call to a `volatile' function.  */
-  int is_volatile = 0;
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#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;
   rtx save_area = 0;           /* Place that it is saved */
 #endif
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
   int old_stack_arg_under_construction = 0;
-#endif
 
   rtx old_stack_level = 0;
   int old_pending_adj = 0;
   int old_inhibit_defer_pop = inhibit_defer_pop;
-  rtx call_fusage = 0;
+  int old_stack_allocated;
+  rtx call_fusage;
   register tree p;
   register int i;
-#ifdef PREFERRED_STACK_BOUNDARY
-  int preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
-#else
-  /* In this case preferred_stack_boundary variable is meaningless.
-     It is used only in order to keep ifdef noise down when calling
-     compute_argument_block_size.  */
-  int preferred_stack_boundary = 0;
-#endif
+  /* The alignment of the stack, in bits.  */
+  HOST_WIDE_INT preferred_stack_boundary;
+  /* The alignment of the stack, in bytes.  */
+  HOST_WIDE_INT preferred_unit_stack_boundary;
 
   /* The value of the function call can be put in a hard register.  But
      if -fcheck-memory-usage, code which invokes functions (and thus
@@ -1715,54 +2036,47 @@ expand_call (exp, target, ignore)
   if (current_function_check_memory_usage)
     target = 0;
 
+  /* See if this is "nothrow" function call.  */
+  if (TREE_NOTHROW (exp))
+    flags |= ECF_NOTHROW;
+
   /* See if we can find a DECL-node for the actual function.
      As a result, decide whether this is a call to an integrable function.  */
 
-  p = TREE_OPERAND (exp, 0);
-  if (TREE_CODE (p) == ADDR_EXPR)
+  fndecl = get_callee_fndecl (exp);
+  if (fndecl)
     {
-      fndecl = TREE_OPERAND (p, 0);
-      if (TREE_CODE (fndecl) != FUNCTION_DECL)
-       fndecl = 0;
-      else
+      if (!flag_no_inline
+         && fndecl != current_function_decl
+         && DECL_INLINE (fndecl)
+         && DECL_SAVED_INSNS (fndecl)
+         && DECL_SAVED_INSNS (fndecl)->inlinable)
+       is_integrable = 1;
+      else if (! TREE_ADDRESSABLE (fndecl))
        {
-         if (!flag_no_inline
-             && fndecl != current_function_decl
-             && DECL_INLINE (fndecl)
-             && DECL_SAVED_INSNS (fndecl)
-             && DECL_SAVED_INSNS (fndecl)->inlinable)
-           is_integrable = 1;
-         else if (! TREE_ADDRESSABLE (fndecl))
-           {
-             /* In case this function later becomes inlinable,
-                record that there was already a non-inline call to it.
+         /* In case this function later becomes inlinable,
+            record that there was already a non-inline call to it.
 
-                Use abstraction instead of setting TREE_ADDRESSABLE
-                directly.  */
-             if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
-                 && optimize > 0)
-               {
-                 warning_with_decl (fndecl, "can't inline call to `%s'");
-                 warning ("called from here");
-               }
-             mark_addressable (fndecl);
+            Use abstraction instead of setting TREE_ADDRESSABLE
+            directly.  */
+         if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+             && optimize > 0)
+           {
+             warning_with_decl (fndecl, "can't inline call to `%s'");
+             warning ("called from here");
            }
-
-         if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
-             && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
-           is_const = 1;
-
-         if (TREE_THIS_VOLATILE (fndecl))
-           is_volatile = 1;
+         mark_addressable (fndecl);
        }
+
+      flags |= flags_from_decl_or_type (fndecl);
     }
 
   /* If we don't have specific function to call, see if we have a 
-     constant or `noreturn' function from the type.  */
-  if (fndecl == 0)
+     attributes set in the type.  */
+  else
     {
-      is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
-      is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
+      p = TREE_OPERAND (exp, 0);
+      flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
     }
 
 #ifdef REG_PARM_STACK_SPACE
@@ -1773,8 +2087,8 @@ expand_call (exp, target, ignore)
 #endif
 #endif
 
-#if defined(PUSH_ROUNDING) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
-  if (reg_parm_stack_space > 0)
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+  if (reg_parm_stack_space > 0 && PUSH_ARGS)
     must_preallocate = 1;
 #endif
 
@@ -1789,7 +2103,7 @@ expand_call (exp, target, ignore)
   if (aggregate_value_p (exp))
     {
       /* This call returns a big structure.  */
-      is_const = 0;
+      flags &= ~(ECF_CONST | ECF_PURE);
 
 #ifdef PCC_STATIC_STRUCT_RETURN
       {
@@ -1840,96 +2154,113 @@ expand_call (exp, target, ignore)
 
   if (is_integrable)
     {
-      rtx temp;
-
-#ifdef ACCUMULATE_OUTGOING_ARGS
-      before_call = get_last_insn ();
-#endif
+      rtx temp = try_to_integrate (fndecl, actparms, target,
+                                  ignore, TREE_TYPE (exp),
+                                  structure_value_addr);
+      if (temp != (rtx) (HOST_WIDE_INT) - 1)
+       return temp;
+    }
 
-      temp = expand_inline_function (fndecl, actparms, target,
-                                    ignore, TREE_TYPE (exp),
-                                    structure_value_addr);
+  currently_expanding_call++;
+
+  /* Tail calls can make things harder to debug, and we're traditionally
+     pushed these optimizations into -O2.  Don't try if we're already
+     expanding a call, as that means we're an argument.  Similarly, if
+     there's pending loops or cleanups we know there's code to follow
+     the call.
+
+     If rtx_equal_function_value_matters is false, that means we've 
+     finished with regular parsing.  Which means that some of the
+     machinery we use to generate tail-calls is no longer in place.
+     This is most often true of sjlj-exceptions, which we couldn't
+     tail-call to anyway.  */
+
+  try_tail_call = 0;
+  if (flag_optimize_sibling_calls
+      && currently_expanding_call == 1
+      && rtx_equal_function_value_matters
+      && stmt_loop_nest_empty ()
+      && ! any_pending_cleanups (1))
+    {
+      tree new_actparms = NULL_TREE;
 
-      /* If inlining succeeded, return.  */
-      if (temp != (rtx) (HOST_WIDE_INT) -1)
-       {
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* If the outgoing argument list must be preserved, push
-            the stack before executing the inlined function if it
-            makes any calls.  */
+      /* Ok, we're going to give the tail call the old college try.
+        This means we're going to evaluate the function arguments
+        up to three times.  There are two degrees of badness we can
+        encounter, those that can be unsaved and those that can't.
+        (See unsafe_for_reeval commentary for details.)
 
-         for (i = reg_parm_stack_space - 1; i >= 0; i--)
-           if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
-             break;
+        Generate a new argument list.  Pass safe arguments through
+        unchanged.  For the easy badness wrap them in UNSAVE_EXPRs.  
+        For hard badness, evaluate them now and put their resulting
+        rtx in a temporary VAR_DECL.  */
 
-         if (stack_arg_under_construction || i >= 0)
+      for (p = actparms; p; p = TREE_CHAIN (p))
+       switch (unsafe_for_reeval (TREE_VALUE (p)))
+         {
+         case 0: /* Safe.  */
+           new_actparms = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p),
+                                     new_actparms);
+           break;
+
+         case 1: /* Mildly unsafe.  */
+           new_actparms = tree_cons (TREE_PURPOSE (p),
+                                     unsave_expr (TREE_VALUE (p)),
+                                     new_actparms);
+           break;
+
+         case 2: /* Wildly unsafe.  */
            {
-             rtx first_insn
-               = before_call ? NEXT_INSN (before_call) : get_insns ();
-             rtx insn = NULL_RTX, seq;
+             tree var = build_decl (VAR_DECL, NULL_TREE,
+                                    TREE_TYPE (TREE_VALUE (p)));
+             DECL_RTL (var) = expand_expr (TREE_VALUE (p), NULL_RTX,
+                                           VOIDmode, EXPAND_NORMAL);
+             new_actparms = tree_cons (TREE_PURPOSE (p), var, new_actparms);
+           }
+           break;
 
-             /* Look for a call in the inline function code.
-                If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
-                nonzero then there is a call and it is not necessary
-                to scan the insns.  */
+         default:
+           abort ();
+         }
 
-             if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
-               for (insn = first_insn; insn; insn = NEXT_INSN (insn))
-                 if (GET_CODE (insn) == CALL_INSN)
-                   break;
+      /* We built the new argument chain backwards.  */
+      actparms = nreverse (new_actparms);
 
-             if (insn)
-               {
-                 /* Reserve enough stack space so that the largest
-                    argument list of any function call in the inline
-                    function does not overlap the argument list being
-                    evaluated.  This is usually an overestimate because
-                    allocate_dynamic_stack_space reserves space for an
-                    outgoing argument list in addition to the requested
-                    space, but there is no way to ask for stack space such
-                    that an argument list of a certain length can be
-                    safely constructed. 
-
-                    Add the stack space reserved for register arguments, if
-                    any, in the inline function.  What is really needed is the
-                    largest value of reg_parm_stack_space in the inline
-                    function, but that is not available.  Using the current
-                    value of reg_parm_stack_space is wrong, but gives
-                    correct results on all supported machines.  */
-
-                 int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
-                               + reg_parm_stack_space);
+      /* Expanding one of those dangerous arguments could have added
+        cleanups, but otherwise give it a whirl.  */
+      try_tail_call = ! any_pending_cleanups (1);
+    }
 
-                 start_sequence ();
-                 emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
-                 allocate_dynamic_stack_space (GEN_INT (adjust),
-                                               NULL_RTX, BITS_PER_UNIT);
-                 seq = get_insns ();
-                 end_sequence ();
-                 emit_insns_before (seq, first_insn);
-                 emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
-               }
-           }
-#endif
+  /* Generate a tail recursion sequence when calling ourselves.  */
 
-         /* If the result is equivalent to TARGET, return TARGET to simplify
-            checks in store_expr.  They can be equivalent but not equal in the
-            case of a function that returns BLKmode.  */
-         if (temp != target && rtx_equal_p (temp, target))
-           return target;
-         return temp;
-       }
+  if (try_tail_call
+      && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+      && TREE_OPERAND (TREE_OPERAND (exp, 0), 0) == current_function_decl)
+    {
+      /* We want to emit any pending stack adjustments before the tail
+        recursion "call".  That way we know any adjustment after the tail
+        recursion call can be ignored if we indeed use the tail recursion
+        call expansion.  */
+      int save_pending_stack_adjust = pending_stack_adjust;
+      int save_stack_pointer_delta = stack_pointer_delta;
+
+      /* Use a new sequence to hold any RTL we generate.  We do not even
+        know if we will use this RTL yet.  The final decision can not be
+        made until after RTL generation for the entire function is
+        complete.  */
+      start_sequence ();
+
+      /* Emit the pending stack adjustments before we expand any arguments.  */
+      do_pending_stack_adjust ();
+
+      if (optimize_tail_recursion (actparms, get_last_insn ()))
+        tail_recursion_insns = get_insns ();
+      end_sequence ();
 
-      /* If inlining failed, mark FNDECL as needing to be compiled
-        separately after all.  If function was declared inline,
-        give a warning.  */
-      if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
-         && optimize > 0 && ! TREE_ADDRESSABLE (fndecl))
-       {
-         warning_with_decl (fndecl, "inlining failed in call to `%s'");
-         warning ("called from here");
-       }
-      mark_addressable (fndecl);
+      /* Restore the original pending stack adjustment for the sibling and
+        normal call cases below.  */
+      pending_stack_adjust = save_pending_stack_adjust;
+      stack_pointer_delta = save_stack_pointer_delta;
     }
 
   function_call_count++;
@@ -1937,6 +2268,14 @@ expand_call (exp, target, ignore)
   if (fndecl && DECL_NAME (fndecl))
     name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
 
+  /* Figure out the amount to which the stack should be aligned.  */
+#ifdef PREFERRED_STACK_BOUNDARY
+  preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+#else
+  preferred_stack_boundary = STACK_BOUNDARY;
+#endif
+  preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
+
   /* Ensure current function's preferred stack boundary is at least
      what we need.  We don't have to increase alignment for recursive
      functions.  */
@@ -1946,10 +2285,9 @@ expand_call (exp, target, ignore)
 
   /* See if this is a call to a function that can return more than once
      or a call to longjmp or malloc.  */
-  special_function_p (fndecl, &returns_twice, &is_longjmp, &fork_or_exec,
-                     &is_malloc, &may_be_alloca);
+  flags |= special_function_p (fndecl, flags);
 
-  if (may_be_alloca)
+  if (flags & ECF_MAY_BE_ALLOCA)
     current_function_calls_alloca = 1;
 
   /* Operand 0 is a pointer-to-function; get the type of the function.  */
@@ -1958,1227 +2296,981 @@ expand_call (exp, target, ignore)
     abort ();
   funtype = TREE_TYPE (funtype);
 
-  /* When calling a const function, we must pop the stack args right away,
-     so that the pop is deleted or moved with the call.  */
-  if (is_const)
-    NO_DEFER_POP;
-
-  /* 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.  */
-
-  if (pending_stack_adjust >= 32
-      || (pending_stack_adjust > 0 && may_be_alloca))
-    do_pending_stack_adjust ();
-
-  if (profile_arc_flag && fork_or_exec)
-    {
-       /* A fork duplicates the profile information, and an exec discards
-          it.  We can't rely on fork/exec to be paired.  So write out the
-          profile information we have gathered so far, and clear it.  */
-      emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"), 0,
-                        VOIDmode, 0);
-
-      /* ??? When __clone is called with CLONE_VM set, profiling is
-         subject to race conditions, just as with multithreaded programs.  */
-    }
-
-  /* Push the temporary stack slot level so that we can free any temporaries
-     we make.  */
-  push_temp_slots ();
-
-  /* Start updating where the next arg would go.
-
-     On some machines (such as the PA) indirect calls have a different
-     calling convention than normal calls.  The last argument in
-     INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
-     or not.  */
-  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
-
-  /* If struct_value_rtx is 0, it means pass the address
-     as if it were an extra parameter.  */
-  if (structure_value_addr && struct_value_rtx == 0)
-    {
-      /* If structure_value_addr is a REG other than
-        virtual_outgoing_args_rtx, we can use always use it.  If it
-        is not a REG, we must always copy it into a register.
-        If it is virtual_outgoing_args_rtx, we must copy it to another
-        register in some cases.  */
-      rtx temp = (GET_CODE (structure_value_addr) != REG
-#ifdef ACCUMULATE_OUTGOING_ARGS
-                 || (stack_arg_under_construction
-                     && structure_value_addr == virtual_outgoing_args_rtx)
-#endif
-                 ? copy_addr_to_reg (structure_value_addr)
-                 : structure_value_addr);
-
-      actparms
-       = tree_cons (error_mark_node,
-                    make_tree (build_pointer_type (TREE_TYPE (funtype)),
-                               temp),
-                    actparms);
-      structure_value_addr_parm = 1;
-    }
-
-  /* Count the arguments and set NUM_ACTUALS.  */
-  for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++;
-  num_actuals = i;
-
-  /* Compute number of named args.
-     Normally, don't include the last named arg if anonymous args follow.
-     We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero.
-     (If no anonymous args follow, the result of list_length is actually
-     one too large.  This is harmless.)
-
-     If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is
-     zero, this machine will be able to place unnamed args that were passed in
-     registers into the stack.  So treat all args as named.  This allows the
-     insns emitting for a specific argument list to be independent of the
-     function declaration.
-
-     If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any reliable
-     way to pass unnamed args in registers, so we must force them into
-     memory.  */
-
-  if ((STRICT_ARGUMENT_NAMING
-       || ! PRETEND_OUTGOING_VARARGS_NAMED)
-      && TYPE_ARG_TYPES (funtype) != 0)
-    n_named_args
-      = (list_length (TYPE_ARG_TYPES (funtype))
-        /* Don't include the last named arg.  */
-        - (STRICT_ARGUMENT_NAMING ? 0 : 1)
-        /* Count the struct value address, if it is passed as a parm.  */
-        + structure_value_addr_parm);
-  else
-    /* If we know nothing, treat all args as named.  */
-    n_named_args = num_actuals;
-
-  /* Make a vector to hold all the information about each arg.  */
-  args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
-  bzero ((char *) args, num_actuals * sizeof (struct arg_data));
-
-  /* Build up entries inthe ARGS array, compute the size of the arguments
-     into ARGS_SIZE, etc.  */
-  initialize_argument_information (num_actuals, args, &args_size, n_named_args,
-                                  actparms, fndecl, &args_so_far,
-                                  reg_parm_stack_space, &old_stack_level,
-                                  &old_pending_adj, &must_preallocate,
-                                  &is_const);
-
-#ifdef FINAL_REG_PARM_STACK_SPACE
-  reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
-                                                    args_size.var);
-#endif
-      
-  if (args_size.var)
-    {
-      /* If this function requires a variable-sized argument list, don't try to
-        make a cse'able block for this call.  We may be able to do this
-        eventually, but it is too complicated to keep track of what insns go
-        in the cse'able block and which don't.  */
-
-      is_const = 0;
-      must_preallocate = 1;
-    }
-
-  /* Compute the actual size of the argument block required.  The variable
-     and constant sizes must be combined, the size may have to be rounded,
-     and there may be a minimum required size.  */
-  unadjusted_args_size
-    = compute_argument_block_size (reg_parm_stack_space, &args_size,
-                                  preferred_stack_boundary);
-
-  /* Now make final decision about preallocating stack space.  */
-  must_preallocate = finalize_must_preallocate (must_preallocate,
-                                               num_actuals, args, &args_size);
-
-  /* If the structure value address will reference the stack pointer, we must
-     stabilize it.  We don't need to do this if we know that we are not going
-     to adjust the stack pointer in processing this call.  */
-
-  if (structure_value_addr
-      && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr)
-       || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr))
-      && (args_size.var
-#ifndef ACCUMULATE_OUTGOING_ARGS
-         || args_size.constant
-#endif
-         ))
-    structure_value_addr = copy_to_reg (structure_value_addr);
-
-  /* Precompute any arguments as needed.  */
-  precompute_arguments (is_const, must_preallocate, num_actuals,
-                        args, &args_size);
-
-  /* Now we are about to start emitting insns that can be deleted
-     if a libcall is deleted.  */
-  if (is_const || is_malloc)
-    start_sequence ();
-
-  /* If we have no actual push instructions, or shouldn't use them,
-     make space for all args right now.  */
-
-  if (args_size.var != 0)
-    {
-      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;
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* 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;
-#endif
-       }
-      argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
-    }
-  else
+  /* 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++)
     {
-      /* Note that we must go through the motions of allocating an argument
-        block even if the size is zero because we may be storing args
-        in the area reserved for register arguments, which may be part of
-        the stack frame.  */
-
-      int needed = args_size.constant;
-
-      /* Store the maximum argument space used.  It will be pushed by
-        the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow
-        checking).  */
-
-      if (needed > current_function_outgoing_args_size)
-       current_function_outgoing_args_size = needed;
+      int sibcall_failure = 0;
+      /* We want to emit ay pending stack adjustments before the tail
+        recursion "call".  That way we know any adjustment after the tail
+        recursion call can be ignored if we indeed use the tail recursion
+        call expansion.  */
+      int save_pending_stack_adjust;
+      int save_stack_pointer_delta;
+      rtx insns;
+      rtx before_call, next_arg_reg;
 
-      if (must_preallocate)
+      if (pass == 0)
        {
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* Since the stack pointer will never be pushed, it is possible for
-            the evaluation of a parm to clobber something we have already
-            written to the stack.  Since most function calls on RISC machines
-            do not use the stack, this is uncommon, but must work correctly.
-
-            Therefore, we save any area of the stack that was already written
-            and that we are using.  Here we set up to do this by making a new
-            stack usage map from the old one.  The actual save will be done
-            by store_one_arg. 
-
-            Another approach might be to try to reorder the argument
-            evaluations to avoid this conflicting stack usage.  */
-
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
-         /* Since we will be writing into the entire argument area, the
-            map must be allocated for its entire size, not just the part that
-            is the responsibility of the caller.  */
-         needed += reg_parm_stack_space;
-#endif
-
-#ifdef ARGS_GROW_DOWNWARD
-         highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                            needed + 1);
+         /* Various reasons we can not use a sibling call.  */
+         if (! try_tail_call 
+#ifdef HAVE_sibcall_epilogue
+             || ! HAVE_sibcall_epilogue
 #else
-         highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                            needed);
-#endif
-         stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
-
-         if (initial_highest_arg_in_use)
-           bcopy (initial_stack_usage_map, stack_usage_map,
-                  initial_highest_arg_in_use);
-
-         if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
-           bzero (&stack_usage_map[initial_highest_arg_in_use],
-                  highest_outgoing_arg_in_use - initial_highest_arg_in_use);
-         needed = 0;
+             || 1
+#endif
+             /* The structure value address is used and modified in the
+                loop below.  It does not seem worth the effort to save and
+                restore it as a state variable 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
+             || ! FUNCTION_OK_FOR_SIBCALL (fndecl))
+           continue;
 
-         /* The address of the outgoing argument list must not be copied to a
-            register here, because argblock would be left pointing to the
-            wrong place after the call to allocate_dynamic_stack_space below.
-            */
+         /* Emit any queued insns now; otherwise they would end up in
+             only one of the alternates.  */
+         emit_queue ();
 
-         argblock = virtual_outgoing_args_rtx;
+         /* We know at this point that there are not currently any
+            pending cleanups.  If, however, in the process of evaluating
+            the arguments we were to create some, we'll need to be
+            able to get rid of them.  */
+         expand_start_target_temps ();
 
-#else /* not ACCUMULATE_OUTGOING_ARGS */
-         if (inhibit_defer_pop == 0)
-           {
-             /* Try to reuse some or all of the pending_stack_adjust
-                to get this space.  Maybe we can avoid any pushing.  */
-             if (needed > pending_stack_adjust)
-               {
-                 needed -= pending_stack_adjust;
-                 pending_stack_adjust = 0;
-               }
-             else
-               {
-                 pending_stack_adjust -= needed;
-                 needed = 0;
-               }
-           }
-         /* Special case this because overhead of `push_block' in this
-            case is non-trivial.  */
-         if (needed == 0)
-           argblock = virtual_outgoing_args_rtx;
-         else
-           argblock = push_block (GEN_INT (needed), 0, 0);
-
-         /* We only really need to call `copy_to_reg' in the case where push
-            insns are going to be used to pass ARGBLOCK to a function
-            call in ARGS.  In that case, the stack pointer changes value
-            from the allocation point to the call point, and hence
-            the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well.
-            But might as well always do it.  */
-         argblock = copy_to_reg (argblock);
-#endif /* not ACCUMULATE_OUTGOING_ARGS */
+         /* State variables we need to save and restore between
+            iterations.  */
+         save_pending_stack_adjust = pending_stack_adjust;
+         save_stack_pointer_delta = stack_pointer_delta;
        }
-    }
-
-#ifdef 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 + args_size.constant);
-#else
-      rtx push_size = GEN_INT (args_size.constant);
-#endif
-      if (old_stack_level == 0)
+      if (pass)
+       flags &= ~ECF_SIBCALL;
+      else
+       flags |= ECF_SIBCALL;
+
+      /* Other state variables that we must reinitialize each time
+        through the loop (that are not initialized by the loop itself).  */
+      argblock = 0;
+      call_fusage = 0;
+
+      /* Start a new sequence for the normal call case. 
+
+        From this point on, if the sibling call fails, we want to set
+        sibcall_failure instead of continuing the loop.  */
+      start_sequence ();
+
+      /* 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))
+       NO_DEFER_POP;
+
+      /* 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.  */
+      if (pending_stack_adjust >= 32
+         || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA))
+         || pass == 0)
+       do_pending_stack_adjust ();
+
+      if (profile_arc_flag && (flags & ECF_FORK_OR_EXEC))
        {
-         emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
-         old_pending_adj = pending_stack_adjust;
-         pending_stack_adjust = 0;
-         /* stack_arg_under_construction says whether a stack arg is
-            being constructed at the old stack level.  Pushing the stack
-            gets a clean outgoing argument block.  */
-         old_stack_arg_under_construction = stack_arg_under_construction;
-         stack_arg_under_construction = 0;
-         /* Make a new map for the new argument list.  */
-         stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use);
-         bzero (stack_usage_map, highest_outgoing_arg_in_use);
-         highest_outgoing_arg_in_use = 0;
+         /* A fork duplicates the profile information, and an exec discards
+            it.  We can't rely on fork/exec to be paired.  So write out the
+            profile information we have gathered so far, and clear it.  */
+         /* ??? When Linux's __clone is called with CLONE_VM set, profiling
+            is subject to race conditions, just as with multithreaded
+            programs.  */
+
+         emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"), 0,
+                            VOIDmode, 0);
        }
-      allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT);
-    }
-  /* If argument evaluation might modify the stack pointer, copy the
-     address of the argument list to a register.  */
-  for (i = 0; i < num_actuals; i++)
-    if (args[i].pass_on_stack)
-      {
-       argblock = copy_addr_to_reg (argblock);
-       break;
-      }
-#endif
 
-  compute_argument_addresses (args, argblock, num_actuals);
+      /* Push the temporary stack slot level so that we can free any
+        temporaries we make.  */
+      push_temp_slots ();
 
-#ifdef PUSH_ARGS_REVERSED
-#ifdef PREFERRED_STACK_BOUNDARY
-  /* If we push args individually in reverse order, perform stack alignment
-     before the first push (the last arg).  */
-  if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
-#endif
-#endif
+      /* Start updating where the next arg would go.
 
-  /* Don't try to defer pops if preallocating, not even from the first arg,
-     since ARGBLOCK probably refers to the SP.  */
-  if (argblock)
-    NO_DEFER_POP;
+        On some machines (such as the PA) indirect calls have a different
+        calling convention than normal calls.  The last argument in
+        INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
+        or not.  */
+      INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
 
-  funexp = rtx_for_function_call (fndecl, exp);
+      /* If struct_value_rtx is 0, it means pass the address
+        as if it were an extra parameter.  */
+      if (structure_value_addr && struct_value_rtx == 0)
+       {
+         /* If structure_value_addr is a REG other than
+            virtual_outgoing_args_rtx, we can use always use it.  If it
+            is not a REG, we must always copy it into a register.
+            If it is virtual_outgoing_args_rtx, we must copy it to another
+            register in some cases.  */
+         rtx temp = (GET_CODE (structure_value_addr) != REG
+                     || (ACCUMULATE_OUTGOING_ARGS
+                         && stack_arg_under_construction
+                         && structure_value_addr == virtual_outgoing_args_rtx)
+                     ? copy_addr_to_reg (structure_value_addr)
+                     : structure_value_addr);
+
+         actparms
+           = tree_cons (error_mark_node,
+                        make_tree (build_pointer_type (TREE_TYPE (funtype)),
+                                   temp),
+                        actparms);
+         structure_value_addr_parm = 1;
+       }
 
-  /* Figure out the register where the value, if any, will come back.  */
-  valreg = 0;
-  if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
-      && ! structure_value_addr)
-    {
-      if (pcc_struct_value)
-       valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
-                                     fndecl, 0);
+      /* Count the arguments and set NUM_ACTUALS.  */
+      for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++;
+      num_actuals = i;
+
+      /* Compute number of named args.
+        Normally, don't include the last named arg if anonymous args follow.
+        We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero.
+        (If no anonymous args follow, the result of list_length is actually
+        one too large.  This is harmless.)
+
+        If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is
+        zero, this machine will be able to place unnamed args that were
+        passed in registers into the stack.  So treat all args as named.
+        This allows the insns emitting for a specific argument list to be
+        independent of the function declaration.
+
+        If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any
+        reliable way to pass unnamed args in registers, so we must force
+        them into memory.  */
+
+      if ((STRICT_ARGUMENT_NAMING
+          || ! PRETEND_OUTGOING_VARARGS_NAMED)
+         && TYPE_ARG_TYPES (funtype) != 0)
+       n_named_args
+         = (list_length (TYPE_ARG_TYPES (funtype))
+            /* Don't include the last named arg.  */
+            - (STRICT_ARGUMENT_NAMING ? 0 : 1)
+            /* Count the struct value address, if it is passed as a parm.  */
+            + structure_value_addr_parm);
       else
-       valreg = hard_function_value (TREE_TYPE (exp), fndecl, 0);
-    }
-
-  /* Precompute all register parameters.  It isn't safe to compute anything
-     once we have started filling any specific hard regs.  */
-  precompute_register_parameters (num_actuals, args, &reg_parm_seen);
-
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-
-  /* Save the fixed argument area if it's part of the caller's frame and
-     is clobbered by argument setup for this call.  */
-  save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
-                                       &low_to_save, &high_to_save);
-#endif
-                       
-
-  /* Now store (and compute if necessary) all non-register parms.
-     These come before register parms, since they can require block-moves,
-     which could clobber the registers used for register parms.
-     Parms which have partial registers are not stored here,
-     but we do preallocate space here if they want that.  */
-
-  for (i = 0; i < num_actuals; i++)
-    if (args[i].reg == 0 || args[i].pass_on_stack)
-      store_one_arg (&args[i], argblock, may_be_alloca,
-                    args_size.var != 0, reg_parm_stack_space);
-
-  /* If we have a parm that is passed in registers but not in memory
-     and whose alignment does not permit a direct copy into registers,
-     make a group of pseudos that correspond to each register that we
-     will later fill.  */
-  if (STRICT_ALIGNMENT)
-    store_unaligned_arguments_into_pseudos (args, num_actuals);
-
-  /* Now store any partially-in-registers parm.
-     This is the last place a block-move can happen.  */
-  if (reg_parm_seen)
-    for (i = 0; i < num_actuals; i++)
-      if (args[i].partial != 0 && ! args[i].pass_on_stack)
-       store_one_arg (&args[i], argblock, may_be_alloca,
-                      args_size.var != 0, reg_parm_stack_space);
+       /* If we know nothing, treat all args as named.  */
+       n_named_args = num_actuals;
+
+      /* Make a vector to hold all the information about each arg.  */
+      args = (struct arg_data *) alloca (num_actuals
+                                        * sizeof (struct arg_data));
+      bzero ((char *) args, num_actuals * sizeof (struct arg_data));
+
+      /* Build up entries inthe ARGS array, compute the size of the arguments
+        into ARGS_SIZE, etc.  */
+      initialize_argument_information (num_actuals, args, &args_size,
+                                      n_named_args, actparms, fndecl,
+                                      &args_so_far, reg_parm_stack_space,
+                                      &old_stack_level, &old_pending_adj,
+                                      &must_preallocate, &flags);
 
-#ifndef PUSH_ARGS_REVERSED
-#ifdef PREFERRED_STACK_BOUNDARY
-  /* If we pushed args in forward order, perform stack alignment
-     after pushing the last arg.  */
-  if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
-#endif
-#endif
-
-  /* If register arguments require space on the stack and stack space
-     was not preallocated, allocate stack space here for arguments
-     passed in registers.  */
-#if ! defined(ACCUMULATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
-  if (must_preallocate == 0 && reg_parm_stack_space > 0)
-    anti_adjust_stack (GEN_INT (reg_parm_stack_space));
+#ifdef FINAL_REG_PARM_STACK_SPACE
+      reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+                                                        args_size.var);
 #endif
-
-  /* Pass the function the address in which to return a structure value.  */
-  if (structure_value_addr && ! structure_value_addr_parm)
-    {
-      emit_move_insn (struct_value_rtx,
-                     force_reg (Pmode,
-                                force_operand (structure_value_addr,
-                                               NULL_RTX)));
-
-      /* Mark the memory for the aggregate as write-only.  */
-      if (current_function_check_memory_usage)
-       emit_library_call (chkr_set_right_libfunc, 1,
-                          VOIDmode, 3,
-                          structure_value_addr, Pmode, 
-                          GEN_INT (struct_value_size), TYPE_MODE (sizetype),
-                          GEN_INT (MEMORY_USE_WO),
-                          TYPE_MODE (integer_type_node));
-
-      if (GET_CODE (struct_value_rtx) == REG)
-         use_reg (&call_fusage, struct_value_rtx);
-    }
-
-  funexp = prepare_call_address (funexp, fndecl, &call_fusage, reg_parm_seen);
-
-  load_register_parameters (args, num_actuals, &call_fusage);
-
-  /* Perform postincrements before actually calling the function.  */
-  emit_queue ();
-
-  /* Save a pointer to the last insn before the call, so that we can
-     later safely search backwards to find the CALL_INSN.  */
-  before_call = get_last_insn ();
-
-  /* All arguments and registers used for the call must be set up by now!  */
-
-  /* Generate the actual call instruction.  */
-  emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
-              args_size.constant, struct_value_size,
-              FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
-              valreg, old_inhibit_defer_pop, call_fusage, is_const);
-
-  /* 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
-     we have no way to move such values into a pseudo register.  */
-  if (is_const && valreg != 0 && GET_CODE (valreg) != PARALLEL)
-    {
-      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)
+      
+      if (args_size.var)
        {
-         tree pointed_to = TREE_TYPE (TREE_TYPE (exp));
-         mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT);
-       }
-
-      /* Construct an "equal form" for the value which mentions all the
-        arguments in order as well as the function name.  */
-#ifdef PUSH_ARGS_REVERSED
-      for (i = 0; i < num_actuals; i++)
-       note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
-#else
-      for (i = num_actuals - 1; i >= 0; i--)
-       note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
-#endif
-      note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
-
-      insns = get_insns ();
-      end_sequence ();
-
-      emit_libcall_block (insns, temp, valreg, note);
-
-      valreg = temp;
-    }
-  else if (is_const)
-    {
-      /* Otherwise, just write out the sequence without a note.  */
-      rtx insns = get_insns ();
-
-      end_sequence ();
-      emit_insns (insns);
-    }
-  else if (is_malloc)
-    {
-      rtx temp = gen_reg_rtx (GET_MODE (valreg));
-      rtx last, insns;
+         /* If this function requires a variable-sized argument list, don't
+            try to make a cse'able block for this call.  We may be able to
+            do this eventually, but it is too complicated to keep track of
+            what insns go in the cse'able block and which don't. 
 
-      /* The return value from a malloc-like function is a pointer. */
-      if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
-       mark_reg_pointer (temp, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+            Also do not make a sibling call.  */
 
-      emit_move_insn (temp, valreg);
-
-      /* The return value from a malloc-like function can not alias
-        anything else.  */
-      last = get_last_insn ();
-      REG_NOTES (last) = 
-       gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
-
-      /* Write out the sequence.  */
-      insns = get_insns ();
-      end_sequence ();
-      emit_insns (insns);
-      valreg = temp;
-    }
-
-  /* For calls to `setjmp', etc., inform flow.c it should complain
-     if nonvolatile values are live.  */
-
-  if (returns_twice)
-    {
-      /* The NOTE_INSN_SETJMP note must be emitted immediately after the
-        CALL_INSN.  Some ports emit more than just a CALL_INSN above, so
-        we must search for it here.  */
-      rtx last = get_last_insn ();
-      while (GET_CODE (last) != CALL_INSN)
-       {
-         last = PREV_INSN (last);
-         /* There was no CALL_INSN?  */
-         if (last == before_call)
-           abort ();
+         flags &= ~(ECF_CONST | ECF_PURE);
+         must_preallocate = 1;
+         sibcall_failure = 1;
        }
-      emit_note_after (NOTE_INSN_SETJMP, last);
-      current_function_calls_setjmp = 1;
-    }
-
-  if (is_longjmp)
-    current_function_calls_longjmp = 1;
 
-  /* Notice functions that cannot return.
-     If optimizing, insns emitted below will be dead.
-     If not optimizing, they will exist, which is useful
-     if the user uses the `return' command in the debugger.  */
-
-  if (is_volatile || is_longjmp)
-    emit_barrier ();
-
-  /* If value type not void, return an rtx for the value.  */
-
-  /* If there are cleanups to be called, don't use a hard reg as target.
-     We need to double check this and see if it matters anymore.  */
-  if (any_pending_cleanups (1)
-      && target && REG_P (target)
-      && REGNO (target) < FIRST_PSEUDO_REGISTER)
-    target = 0;
-
-  if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
-      || ignore)
-    {
-      target = const0_rtx;
-    }
-  else if (structure_value_addr)
-    {
-      if (target == 0 || GET_CODE (target) != MEM)
+      if (args_size.constant > current_function_args_size)
        {
-         target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
-                               memory_address (TYPE_MODE (TREE_TYPE (exp)),
-                                               structure_value_addr));
-         MEM_SET_IN_STRUCT_P (target,
-                              AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+         /* If this function requires more stack slots than the current
+            function, we cannot change it into a sibling call.  */
+         sibcall_failure = 1;
        }
-    }
-  else if (pcc_struct_value)
-    {
-      /* This is the special C++ case where we need to
-        know what the true target was.  We take care to
-        never use this value more than once in one expression.  */
-      target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
-                           copy_to_reg (valreg));
-      MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
-    }
-  /* Handle calls that return values in multiple non-contiguous locations.
-     The Irix 6 ABI has examples of this.  */
-  else if (GET_CODE (valreg) == PARALLEL)
-    {
-      int bytes = int_size_in_bytes (TREE_TYPE (exp));
 
-      if (target == 0)
+      /* If the callee pops its own arguments, then it must pop exactly
+        the same number of arguments as the current function.  */
+      if (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
+         != RETURN_POPS_ARGS (current_function_decl,
+                              TREE_TYPE (current_function_decl),
+                              current_function_args_size))
+       sibcall_failure = 1;
+
+      /* Now make final decision about preallocating stack space.  */
+      must_preallocate = finalize_must_preallocate (must_preallocate,
+                                                   num_actuals, args,
+                                                   &args_size);
+
+      /* If the structure value address will reference the stack pointer, we
+        must stabilize it.  We don't need to do this if we know that we are
+        not going to adjust the stack pointer in processing this call.  */
+
+      if (structure_value_addr
+         && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr)
+             || reg_mentioned_p (virtual_outgoing_args_rtx,
+                                 structure_value_addr))
+         && (args_size.var
+             || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
+       structure_value_addr = copy_to_reg (structure_value_addr);
+
+      /* Precompute any arguments as needed.  */
+      if (pass)
+       precompute_arguments (flags, num_actuals, args);
+
+      /* 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))
+       start_sequence ();
+
+      /* Compute the actual size of the argument block required.  The variable
+        and constant sizes must be combined, the size may have to be rounded,
+        and there may be a minimum required size.  When generating a sibcall
+        pattern, do not round up, since we'll be re-using whatever space our
+        caller provided.  */
+      unadjusted_args_size
+       = compute_argument_block_size (reg_parm_stack_space, &args_size,
+                                      (pass == 0 ? 0
+                                       : preferred_stack_boundary));
+
+      old_stack_allocated =  stack_pointer_delta - pending_stack_adjust;
+
+      /* The argument block when performing a sibling call is the
+         incoming argument block.  */
+      if (pass == 0)
+       argblock = virtual_incoming_args_rtx;
+
+      /* If we have no actual push instructions, or shouldn't use them,
+        make space for all args right now.  */
+      else if (args_size.var != 0)
        {
-         target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0);
-         MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
-         preserve_temp_slots (target);
+         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;
+           }
+         argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
        }
+      else
+       {
+         /* Note that we must go through the motions of allocating an argument
+            block even if the size is zero because we may be storing args
+            in the area reserved for register arguments, which may be part of
+            the stack frame.  */
 
-      if (! rtx_equal_p (target, valreg))
-        emit_group_store (target, valreg, bytes,
-                         TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
-    }
-  else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
-          && GET_MODE (target) == GET_MODE (valreg))
-    /* TARGET and VALREG cannot be equal at this point because the latter
-       would not have REG_FUNCTION_VALUE_P true, while the former would if
-       it were referring to the same register.
-
-       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);
-  else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
-    target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
-  else
-    target = copy_to_reg (valreg);
-
-#ifdef PROMOTE_FUNCTION_RETURN
-  /* If we promoted this return value, make the proper SUBREG.  TARGET
-     might be const0_rtx here, so be careful.  */
-  if (GET_CODE (target) == REG
-      && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
-      && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
-    {
-      tree type = TREE_TYPE (exp);
-      int unsignedp = TREE_UNSIGNED (type);
-
-      /* If we don't promote as expected, something is wrong.  */
-      if (GET_MODE (target)
-         != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
-       abort ();
-
-      target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0);
-      SUBREG_PROMOTED_VAR_P (target) = 1;
-      SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
-    }
-#endif
-
-  /* If size of args is variable or this was a constructor call for a stack
-     argument, restore saved stack-pointer value.  */
-
-  if (old_stack_level)
-    {
-      emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
-      pending_stack_adjust = old_pending_adj;
-#ifdef ACCUMULATE_OUTGOING_ARGS
-      stack_arg_under_construction = old_stack_arg_under_construction;
-      highest_outgoing_arg_in_use = initial_highest_arg_in_use;
-      stack_usage_map = initial_stack_usage_map;
-#endif
-    }
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  else
-    {
-#ifdef REG_PARM_STACK_SPACE
-      if (save_area)
-       restore_fixed_argument_area (save_area, argblock,
-                                    high_to_save, low_to_save);
-#endif
-
-      /* If we saved any argument areas, restore them.  */
-      for (i = 0; i < num_actuals; i++)
-       if (args[i].save_area)
-         {
-           enum machine_mode save_mode = GET_MODE (args[i].save_area);
-           rtx stack_area
-             = gen_rtx_MEM (save_mode,
-                            memory_address (save_mode,
-                                            XEXP (args[i].stack_slot, 0)));
-
-           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),
-                              PARM_BOUNDARY / BITS_PER_UNIT);
-         }
-
-      highest_outgoing_arg_in_use = initial_highest_arg_in_use;
-      stack_usage_map = initial_stack_usage_map;
-    }
-#endif
-
-  /* If this was alloca, record the new stack level for nonlocal gotos.  
-     Check for the handler slots since we might not have a save area
-     for non-local gotos.  */
-
-  if (may_be_alloca && nonlocal_goto_handler_slots != 0)
-    emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
-
-  pop_temp_slots ();
-
-  /* Free up storage we no longer need.  */
-  for (i = 0; i < num_actuals; ++i)
-    if (args[i].aligned_regs)
-      free (args[i].aligned_regs);
+         int needed = args_size.constant;
 
-  return target;
-}
-\f
-/* Output a library call to function FUN (a SYMBOL_REF rtx)
-   (emitting the queue unless NO_QUEUE is nonzero),
-   for a value of mode OUTMODE,
-   with NARGS different arguments, passed as alternating rtx values
-   and machine_modes to convert them to.
-   The rtx values should have been passed through protect_from_queue already.
+         /* Store the maximum argument space used.  It will be pushed by
+            the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow
+            checking).  */
 
-   NO_QUEUE will be true if and only if the library call is a `const' call
-   which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent
-   to the variable is_const in expand_call.
+         if (needed > current_function_outgoing_args_size)
+           current_function_outgoing_args_size = needed;
 
-   NO_QUEUE must be true for const calls, because if it isn't, then
-   any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes,
-   and will be lost if the libcall sequence is optimized away.
+         if (must_preallocate)
+           {
+             if (ACCUMULATE_OUTGOING_ARGS)
+               {
+                 /* Since the stack pointer will never be pushed, it is
+                    possible for the evaluation of a parm to clobber
+                    something we have already written to the stack.
+                    Since most function calls on RISC machines do not use
+                    the stack, this is uncommon, but must work correctly.
 
-   NO_QUEUE must be false for non-const calls, because if it isn't, the
-   call insn will have its CONST_CALL_P bit set, and it will be incorrectly
-   optimized.  For instance, the instruction scheduler may incorrectly
-   move memory references across the non-const call.  */
+                    Therefore, we save any area of the stack that was already
+                    written and that we are using.  Here we set up to do this
+                    by making a new stack usage map from the old one.  The
+                    actual save will be done by store_one_arg. 
 
-void
-emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode,
-                          int nargs, ...))
-{
-#ifndef ANSI_PROTOTYPES
-  rtx orgfun;
-  int no_queue;
-  enum machine_mode outmode;
-  int nargs;
-#endif
-  va_list p;
-  /* Total size in bytes of all the stack-parms scanned so far.  */
-  struct args_size args_size;
-  /* Size of arguments before any adjustments (such as rounding).  */
-  struct args_size original_args_size;
-  register int argnum;
-  rtx fun;
-  int inc;
-  int count;
-  struct args_size alignment_pad;
-  rtx argblock = 0;
-  CUMULATIVE_ARGS args_so_far;
-  struct arg { rtx value; enum machine_mode mode; rtx reg; int partial;
-              struct args_size offset; struct args_size size; rtx save_area; };
-  struct arg *argvec;
-  int old_inhibit_defer_pop = inhibit_defer_pop;
-  rtx call_fusage = 0;
-  int reg_parm_stack_space = 0;
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-  /* Define the boundary of the register parm stack space that needs to be
-     save, if any.  */
-  int low_to_save = -1, high_to_save = 0;
-  rtx save_area = 0;            /* Place that it is saved */
-#endif
+                    Another approach might be to try to reorder the argument
+                    evaluations to avoid this conflicting stack usage.  */
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
-  char *initial_stack_usage_map = stack_usage_map;
-  int needed;
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+                 /* Since we will be writing into the entire argument area,
+                    the map must be allocated for its entire size, not just
+                    the part that is the responsibility of the caller.  */
+                 needed += reg_parm_stack_space;
 #endif
 
-#ifdef REG_PARM_STACK_SPACE
-  /* Size of the stack reserved for parameter registers.  */
-#ifdef MAYBE_REG_PARM_STACK_SPACE
-  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#ifdef ARGS_GROW_DOWNWARD
+                 highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+                                                    needed + 1);
 #else
-  reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
-#endif
+                 highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+                                                    needed);
 #endif
+                 stack_usage_map
+                   = (char *) alloca (highest_outgoing_arg_in_use);
 
-  VA_START (p, nargs);
+                 if (initial_highest_arg_in_use)
+                   bcopy (initial_stack_usage_map, stack_usage_map,
+                          initial_highest_arg_in_use);
 
-#ifndef ANSI_PROTOTYPES
-  orgfun = va_arg (p, rtx);
-  no_queue = va_arg (p, int);
-  outmode = va_arg (p, enum machine_mode);
-  nargs = va_arg (p, int);
-#endif
-
-  fun = orgfun;
-
-  /* Copy all the libcall-arguments out of the varargs data
-     and into a vector ARGVEC.
-
-     Compute how to pass each argument.  We only support a very small subset
-     of the full argument passing conventions to limit complexity here since
-     library functions shouldn't have many args.  */
-
-  argvec = (struct arg *) alloca (nargs * sizeof (struct arg));
-  bzero ((char *) argvec, nargs * sizeof (struct arg));
-
-
-  INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
-
-  args_size.constant = 0;
-  args_size.var = 0;
-
-  push_temp_slots ();
-
-#ifdef PREFERRED_STACK_BOUNDARY
-  /* Ensure current function's preferred stack boundary is at least
-     what we need.  */
-  if (cfun->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
-    cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
-#endif
-
-  for (count = 0; count < nargs; count++)
-    {
-      rtx val = va_arg (p, rtx);
-      enum machine_mode mode = va_arg (p, enum machine_mode);
-
-      /* We cannot convert the arg value to the mode the library wants here;
-        must do it earlier where we know the signedness of the arg.  */
-      if (mode == BLKmode
-         || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode))
-       abort ();
-
-      /* On some machines, there's no way to pass a float to a library fcn.
-        Pass it as a double instead.  */
-#ifdef LIBGCC_NEEDS_DOUBLE
-      if (LIBGCC_NEEDS_DOUBLE && mode == SFmode)
-       val = convert_modes (DFmode, SFmode, val, 0), mode = DFmode;
-#endif
+                 if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
+                   bzero (&stack_usage_map[initial_highest_arg_in_use],
+                          (highest_outgoing_arg_in_use
+                           - initial_highest_arg_in_use));
+                 needed = 0;
 
-      /* There's no need to call protect_from_queue, because
-        either emit_move_insn or emit_push_insn will do that.  */
+                 /* The address of the outgoing argument list must not be
+                    copied to a register here, because argblock would be left
+                    pointing to the wrong place after the call to
+                    allocate_dynamic_stack_space below. */
 
-      /* Make sure it is a reasonable operand for a move or push insn.  */
-      if (GET_CODE (val) != REG && GET_CODE (val) != MEM
-         && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
-       val = force_operand (val, NULL_RTX);
-
-#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
-      if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
-       {
-         /* We do not support FUNCTION_ARG_CALLEE_COPIES here since it can
-            be viewed as just an efficiency improvement.  */
-         rtx slot = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
-         emit_move_insn (slot, val);
-         val = force_operand (XEXP (slot, 0), NULL_RTX);
-         mode = Pmode;
-       }
-#endif
-
-      argvec[count].value = val;
-      argvec[count].mode = mode;
+                 argblock = virtual_outgoing_args_rtx;
+               }
+             else
+               {
+                 if (inhibit_defer_pop == 0)
+                   {
+                     /* Try to reuse some or all of the pending_stack_adjust
+                        to get this space.  */
+                     needed
+                       = (combine_pending_stack_adjustment_and_call 
+                          (unadjusted_args_size,
+                           &args_size,
+                           preferred_unit_stack_boundary));
+
+                     /* combine_pending_stack_adjustment_and_call computes
+                        an adjustment before the arguments are allocated.
+                        Account for them and see whether or not the stack
+                        needs to go up or down.  */
+                     needed = unadjusted_args_size - needed;
+
+                     if (needed < 0)
+                       {
+                         /* We're releasing stack space.  */
+                         /* ??? We can avoid any adjustment at all if we're
+                            already aligned.  FIXME.  */
+                         pending_stack_adjust = -needed;
+                         do_pending_stack_adjust ();
+                         needed = 0;
+                       }
+                     else 
+                       /* We need to allocate space.  We'll do that in
+                          push_block below.  */
+                       pending_stack_adjust = 0;
+                   }
 
-      argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
-      if (argvec[count].reg && GET_CODE (argvec[count].reg) == PARALLEL)
-       abort ();
-#ifdef FUNCTION_ARG_PARTIAL_NREGS
-      argvec[count].partial
-       = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
+                 /* Special case this because overhead of `push_block' in
+                    this case is non-trivial.  */
+                 if (needed == 0)
+                   argblock = virtual_outgoing_args_rtx;
+                 else
+                   argblock = push_block (GEN_INT (needed), 0, 0);
+
+                 /* We only really need to call `copy_to_reg' in the case
+                    where push insns are going to be used to pass ARGBLOCK
+                    to a function call in ARGS.  In that case, the stack
+                    pointer changes value from the allocation point to the
+                    call point, and hence the value of
+                    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)
+                   {
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+                     rtx push_size = GEN_INT (reg_parm_stack_space
+                                              + args_size.constant);
 #else
-      argvec[count].partial = 0;
-#endif
-
-      locate_and_pad_parm (mode, NULL_TREE,
-                          argvec[count].reg && argvec[count].partial == 0,
-                          NULL_TREE, &args_size, &argvec[count].offset,
-                          &argvec[count].size, &alignment_pad);
-
-      if (argvec[count].size.var)
-       abort ();
-
-      if (reg_parm_stack_space == 0 && argvec[count].partial)
-       argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
-
-      if (argvec[count].reg == 0 || argvec[count].partial != 0
-         || reg_parm_stack_space > 0)
-       args_size.constant += argvec[count].size.constant;
-
-      FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
-    }
-  va_end (p);
+                     rtx push_size = GEN_INT (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);
+                         bzero (stack_usage_map, highest_outgoing_arg_in_use);
+                         highest_outgoing_arg_in_use = 0;
+                       }
+                     allocate_dynamic_stack_space (push_size, NULL_RTX,
+                                                   BITS_PER_UNIT);
+                   }
+                 /* If argument evaluation might modify the stack pointer,
+                    copy the address of the argument list to a register.  */
+                 for (i = 0; i < num_actuals; i++)
+                   if (args[i].pass_on_stack)
+                     {
+                       argblock = copy_addr_to_reg (argblock);
+                       break;
+                     }
+               }
+           }
+       }
 
-#ifdef FINAL_REG_PARM_STACK_SPACE
-  reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
-                                                    args_size.var);
-#endif
-      
-  /* If this machine requires an external definition for library
-     functions, write one out.  */
-  assemble_external_libcall (fun);
+      compute_argument_addresses (args, argblock, num_actuals);
 
-  original_args_size = args_size;
 #ifdef PREFERRED_STACK_BOUNDARY
-  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
-                        / STACK_BYTES) * STACK_BYTES);
+      /* If we push args individually in reverse order, perform stack alignment
+        before the first push (the last arg).  */
+      if (PUSH_ARGS_REVERSED && argblock == 0
+         && args_size.constant != unadjusted_args_size)
+       {
+         /* When the stack adjustment is pending, we get better code
+            by combining the adjustments.  */
+         if (pending_stack_adjust 
+             && ! (flags & (ECF_CONST | ECF_PURE))
+             && ! inhibit_defer_pop)
+           {
+             pending_stack_adjust
+               = (combine_pending_stack_adjustment_and_call 
+                  (unadjusted_args_size,
+                   &args_size,
+                   preferred_unit_stack_boundary));
+             do_pending_stack_adjust ();
+           }
+         else if (argblock == 0)
+           anti_adjust_stack (GEN_INT (args_size.constant
+                                       - unadjusted_args_size));
+       }
+      /* Now that the stack is properly aligned, pops can't safely
+        be deferred during the evaluation of the arguments.  */
+      NO_DEFER_POP;
 #endif
 
-  args_size.constant = MAX (args_size.constant,
-                           reg_parm_stack_space);
+      /* Don't try to defer pops if preallocating, not even from the first arg,
+        since ARGBLOCK probably refers to the SP.  */
+      if (argblock)
+       NO_DEFER_POP;
 
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
-  args_size.constant -= reg_parm_stack_space;
-#endif
+      funexp = rtx_for_function_call (fndecl, exp);
 
-  if (args_size.constant > current_function_outgoing_args_size)
-    current_function_outgoing_args_size = args_size.constant;
+      /* Figure out the register where the value, if any, will come back.  */
+      valreg = 0;
+      if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+         && ! structure_value_addr)
+       {
+         if (pcc_struct_value)
+           valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+                                         fndecl, (pass == 0));
+         else
+           valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
+       }
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  /* Since the stack pointer will never be pushed, it is possible for
-     the evaluation of a parm to clobber something we have already
-     written to the stack.  Since most function calls on RISC machines
-     do not use the stack, this is uncommon, but must work correctly.
+      /* Precompute all register parameters.  It isn't safe to compute anything
+        once we have started filling any specific hard regs.  */
+      precompute_register_parameters (num_actuals, args, &reg_parm_seen);
 
-     Therefore, we save any area of the stack that was already written
-     and that we are using.  Here we set up to do this by making a new
-     stack usage map from the old one.
+#ifdef REG_PARM_STACK_SPACE
+      /* Save the fixed argument area if it's part of the caller's frame and
+        is clobbered by argument setup for this call.  */
+      if (ACCUMULATE_OUTGOING_ARGS && pass)
+       save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+                                             &low_to_save, &high_to_save);
+#endif
 
-     Another approach might be to try to reorder the argument
-     evaluations to avoid this conflicting stack usage.  */
+      /* Now store (and compute if necessary) all non-register parms.
+        These come before register parms, since they can require block-moves,
+        which could clobber the registers used for register parms.
+        Parms which have partial registers are not stored here,
+        but we do preallocate space here if they want that.  */
 
-  needed = args_size.constant;
+      for (i = 0; i < num_actuals; i++)
+       if (args[i].reg == 0 || args[i].pass_on_stack)
+         store_one_arg (&args[i], argblock, flags,
+                        args_size.var != 0, reg_parm_stack_space);
+
+      /* If we have a parm that is passed in registers but not in memory
+        and whose alignment does not permit a direct copy into registers,
+        make a group of pseudos that correspond to each register that we
+        will later fill.  */
+      if (STRICT_ALIGNMENT)
+       store_unaligned_arguments_into_pseudos (args, num_actuals);
+
+      /* Now store any partially-in-registers parm.
+        This is the last place a block-move can happen.  */
+      if (reg_parm_seen)
+       for (i = 0; i < num_actuals; i++)
+         if (args[i].partial != 0 && ! args[i].pass_on_stack)
+           store_one_arg (&args[i], argblock, flags,
+                          args_size.var != 0, reg_parm_stack_space);
 
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
-  /* Since we will be writing into the entire argument area, the
-     map must be allocated for its entire size, not just the part that
-     is the responsibility of the caller.  */
-  needed += reg_parm_stack_space;
+#ifdef PREFERRED_STACK_BOUNDARY
+      /* If we pushed args in forward order, perform stack alignment
+        after pushing the last arg.  */
+      if (!PUSH_ARGS_REVERSED && argblock == 0)
+       anti_adjust_stack (GEN_INT (args_size.constant
+                                   - unadjusted_args_size));
 #endif
 
-#ifdef ARGS_GROW_DOWNWARD
-  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                    needed + 1);
-#else
-  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                    needed);
+      /* If register arguments require space on the stack and stack space
+        was not preallocated, allocate stack space here for arguments
+        passed in registers.  */
+#ifdef OUTGOING_REG_PARM_STACK_SPACE
+      if (!ACCUMULATE_OUTGOING_ARGS
+          && must_preallocate == 0 && reg_parm_stack_space > 0)
+       anti_adjust_stack (GEN_INT (reg_parm_stack_space));
 #endif
-  stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
 
-  if (initial_highest_arg_in_use)
-    bcopy (initial_stack_usage_map, stack_usage_map,
-          initial_highest_arg_in_use);
+      /* Pass the function the address in which to return a
+        structure value.  */
+      if (pass != 0 && structure_value_addr && ! structure_value_addr_parm)
+       {
+         emit_move_insn (struct_value_rtx,
+                         force_reg (Pmode,
+                                    force_operand (structure_value_addr,
+                                                   NULL_RTX)));
+
+         /* Mark the memory for the aggregate as write-only.  */
+         if (current_function_check_memory_usage)
+           emit_library_call (chkr_set_right_libfunc, 1,
+                              VOIDmode, 3,
+                              structure_value_addr, ptr_mode, 
+                              GEN_INT (struct_value_size),
+                              TYPE_MODE (sizetype),
+                              GEN_INT (MEMORY_USE_WO),
+                              TYPE_MODE (integer_type_node));
+
+         if (GET_CODE (struct_value_rtx) == REG)
+           use_reg (&call_fusage, struct_value_rtx);
+       }
+
+      funexp = prepare_call_address (funexp, fndecl, &call_fusage,
+                                    reg_parm_seen);
 
-  if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
-    bzero (&stack_usage_map[initial_highest_arg_in_use],
-          highest_outgoing_arg_in_use - initial_highest_arg_in_use);
-  needed = 0;
+      load_register_parameters (args, num_actuals, &call_fusage);
+     
+      /* Perform postincrements before actually calling the function.  */
+      emit_queue ();
 
-  /* The address of the outgoing argument list must not be copied to a
-     register here, because argblock would be left pointing to the
-     wrong place after the call to allocate_dynamic_stack_space below.
-     */
+      /* Save a pointer to the last insn before the call, so that we can
+        later safely search backwards to find the CALL_INSN.  */
+      before_call = get_last_insn ();
 
-  argblock = virtual_outgoing_args_rtx;
-#else /* not ACCUMULATE_OUTGOING_ARGS */
-#ifndef PUSH_ROUNDING
-  argblock = push_block (GEN_INT (args_size.constant), 0, 0);
-#endif
+      /* Set up next argument register.  For sibling calls on machines
+        with register windows this should be the incoming register.  */
+#ifdef FUNCTION_INCOMING_ARG
+      if (pass == 0)
+       next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
+                                             void_type_node, 1);
+      else
 #endif
+       next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
+                                    void_type_node, 1);
+
+      /* All arguments and registers used for the call must be set up by
+        now!  */
 
-#ifdef PUSH_ARGS_REVERSED
 #ifdef PREFERRED_STACK_BOUNDARY
-  /* If we push args individually in reverse order, perform stack alignment
-     before the first push (the last arg).  */
-  if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant
-                               - original_args_size.constant));
-#endif
-#endif
+      /* Stack must be properly aligned now.  */
+      if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
+       abort();
+#endif
+
+      /* Generate the actual call instruction.  */
+      emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
+                  args_size.constant, struct_value_size,
+                  next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
+                  flags);
+
+      /* 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
+        we have no way to move such values into a pseudo register.  */
+      if ((flags & (ECF_CONST | ECF_PURE))
+         && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+       {
+         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 ();
 
-#ifdef PUSH_ARGS_REVERSED
-  inc = -1;
-  argnum = nargs - 1;
-#else
-  inc = 1;
-  argnum = 0;
-#endif
+         end_sequence ();
+         emit_insns (insns);
+       }
+      else if (flags & ECF_MALLOC)
+       {
+         rtx temp = gen_reg_rtx (GET_MODE (valreg));
+         rtx last, insns;
+
+         /* The return value from a malloc-like function is a pointer. */
+         if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
+           mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
+
+         emit_move_insn (temp, valreg);
+
+         /* The return value from a malloc-like function can not alias
+            anything else.  */
+         last = get_last_insn ();
+         REG_NOTES (last) = 
+           gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
+
+         /* Write out the sequence.  */
+         insns = get_insns ();
+         end_sequence ();
+         emit_insns (insns);
+         valreg = temp;
+       }
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-  /* The argument list is the property of the called routine and it
-     may clobber it.  If the fixed area has been used for previous
-     parameters, we must save and restore it.
+      /* For calls to `setjmp', etc., inform flow.c it should complain
+        if nonvolatile values are live.  For functions that cannot return,
+        inform flow that control does not fall through.  */
 
-     Here we compute the boundary of the that needs to be saved, if any.  */
+      if ((flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
+       {
+         /* The barrier or NOTE_INSN_SETJMP note must be emitted
+            immediately after the CALL_INSN.  Some ports emit more
+            than just a CALL_INSN above, so we must search for it here.  */
 
-#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;
+         rtx last = get_last_insn ();
+         while (GET_CODE (last) != CALL_INSN)
+           {
+             last = PREV_INSN (last);
+             /* There was no CALL_INSN?  */
+             if (last == before_call)
+               abort ();
+           }
 
-      if (low_to_save == -1)
-       low_to_save = count;
+         if (flags & ECF_RETURNS_TWICE)
+           {
+             emit_note_after (NOTE_INSN_SETJMP, last);
+             current_function_calls_setjmp = 1;
+             sibcall_failure = 1;
+           }
+         else
+           emit_barrier_after (last);
+       }
 
-      high_to_save = count;
-    }
+      if (flags & ECF_LONGJMP)
+       current_function_calls_longjmp = 1, sibcall_failure = 1;
 
-  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 this function is returning into a memory location marked as
+        readonly, it means it is initializing that location.  But we normally
+        treat functions as not clobbering such locations, so we need to
+        specify that this one does.  */
+      if (target != 0 && GET_CODE (target) == MEM
+         && structure_value_addr != 0 && RTX_UNCHANGING_P (target))
+       emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
 
-      /* 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 value type not void, return an rtx for the value.  */
 
-#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)
+      /* If there are cleanups to be called, don't use a hard reg as target.
+        We need to double check this and see if it matters anymore.  */
+      if (any_pending_cleanups (1))
        {
-         save_area = assign_stack_temp (BLKmode, num_to_save, 0);
-         emit_block_move (validize_mem (save_area), stack_area,
-                          GEN_INT (num_to_save),
-                          PARM_BOUNDARY / BITS_PER_UNIT);
+         if (target && REG_P (target)
+             && REGNO (target) < FIRST_PSEUDO_REGISTER)
+           target = 0;
+         sibcall_failure = 1;
        }
-      else
+
+      if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
+         || ignore)
        {
-         save_area = gen_reg_rtx (save_mode);
-         emit_move_insn (save_area, stack_area);
+         target = const0_rtx;
        }
-    }
-#endif
-         
-  /* Push the args that need to be pushed.  */
-
-  /* ARGNUM indexes the ARGVEC array in the order in which the arguments
-     are to be pushed.  */
-  for (count = 0; count < nargs; count++, argnum += inc)
-    {
-      register enum machine_mode mode = argvec[argnum].mode;
-      register rtx val = argvec[argnum].value;
-      rtx reg = argvec[argnum].reg;
-      int partial = argvec[argnum].partial;
-#ifdef ACCUMULATE_OUTGOING_ARGS
-      int lower_bound, upper_bound, i;
-#endif
-
-      if (! (reg != 0 && partial == 0))
+      else if (structure_value_addr)
        {
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* If this is being stored into a pre-allocated, fixed-size, stack
-            area, save any previous data at that location.  */
-
-#ifdef ARGS_GROW_DOWNWARD
-         /* stack_slot is negative, but we want to index stack_usage_map
-            with positive values.  */
-         upper_bound = -argvec[argnum].offset.constant + 1;
-         lower_bound = upper_bound - argvec[argnum].size.constant;
-#else
-         lower_bound = argvec[argnum].offset.constant;
-         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;
+         if (target == 0 || GET_CODE (target) != MEM)
+           {
+             target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+                                   memory_address (TYPE_MODE (TREE_TYPE (exp)),
+                                                   structure_value_addr));
+             MEM_SET_IN_STRUCT_P (target,
+                                  AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+           }
+       }
+      else if (pcc_struct_value)
+       {
+         /* This is the special C++ case where we need to
+            know what the true target was.  We take care to
+            never use this value more than once in one expression.  */
+         target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+                               copy_to_reg (valreg));
+         MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+       }
+      /* Handle calls that return values in multiple non-contiguous locations.
+        The Irix 6 ABI has examples of this.  */
+      else if (GET_CODE (valreg) == PARALLEL)
+       {
+         int bytes = int_size_in_bytes (TREE_TYPE (exp));
 
-         if (i != upper_bound)
+         if (target == 0)
            {
-             /* We need to make a save area.  See what mode we can make it. */
-             enum machine_mode save_mode
-               = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
-                                MODE_INT, 1);
-             rtx stack_area
-               = gen_rtx_MEM
-                 (save_mode,
-                  memory_address
-                  (save_mode,
-                   plus_constant (argblock,
-                                  argvec[argnum].offset.constant)));
-
-             argvec[argnum].save_area = gen_reg_rtx (save_mode);
-             emit_move_insn (argvec[argnum].save_area, stack_area);
+             target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)),
+                                         bytes, 0);
+             MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+             preserve_temp_slots (target);
            }
-#endif
-         emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
-                         argblock, GEN_INT (argvec[argnum].offset.constant),
-                         reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* Now mark the segment we just used.  */
-         for (i = lower_bound; i < upper_bound; i++)
-           stack_usage_map[i] = 1;
-#endif
+         if (! rtx_equal_p (target, valreg))
+           emit_group_store (target, valreg, bytes,
+                             TYPE_ALIGN (TREE_TYPE (exp)));
 
-         NO_DEFER_POP;
+         /* We can not support sibling calls for this case.  */
+         sibcall_failure = 1;
        }
-    }
+      else if (target
+              && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
+              && GET_MODE (target) == GET_MODE (valreg))
+       {
+         /* TARGET and VALREG cannot be equal at this point because the
+            latter would not have REG_FUNCTION_VALUE_P true, while the
+            former would if it were referring to the same register.
 
-#ifndef PUSH_ARGS_REVERSED
-#ifdef PREFERRED_STACK_BOUNDARY
-  /* If we pushed args in forward order, perform stack alignment
-     after pushing the last arg.  */
-  if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant
-                               - original_args_size.constant));
-#endif
-#endif
+            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);
+       }
+      else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+       target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
+      else
+       target = copy_to_reg (valreg);
 
-#ifdef PUSH_ARGS_REVERSED
-  argnum = nargs - 1;
-#else
-  argnum = 0;
-#endif
+#ifdef PROMOTE_FUNCTION_RETURN
+      /* If we promoted this return value, make the proper SUBREG.  TARGET
+        might be const0_rtx here, so be careful.  */
+      if (GET_CODE (target) == REG
+         && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
+         && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+       {
+         tree type = TREE_TYPE (exp);
+         int unsignedp = TREE_UNSIGNED (type);
 
-  fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
+         /* If we don't promote as expected, something is wrong.  */
+         if (GET_MODE (target)
+             != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
+           abort ();
 
-  /* Now load any reg parms into their regs.  */
+         target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0);
+         SUBREG_PROMOTED_VAR_P (target) = 1;
+         SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
+       }
+#endif
 
-  /* ARGNUM indexes the ARGVEC array in the order in which the arguments
-     are to be pushed.  */
-  for (count = 0; count < nargs; count++, argnum += inc)
-    {
-      register rtx val = argvec[argnum].value;
-      rtx reg = argvec[argnum].reg;
-      int partial = argvec[argnum].partial;
+      /* If size of args is variable or this was a constructor call for a stack
+        argument, restore saved stack-pointer value.  */
 
-      if (reg != 0 && partial == 0)
-       emit_move_insn (reg, val);
-      NO_DEFER_POP;
-    }
+      if (old_stack_level)
+       {
+         emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+         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;
+         stack_usage_map = initial_stack_usage_map;
+         sibcall_failure = 1;
+       }
+      else if (ACCUMULATE_OUTGOING_ARGS && pass)
+       {
+#ifdef REG_PARM_STACK_SPACE
+         if (save_area)
+           {
+             restore_fixed_argument_area (save_area, argblock,
+                                          high_to_save, low_to_save);
+           }
+#endif
 
-  /* For version 1.37, try deleting this entirely.  */
-  if (! no_queue)
-    emit_queue ();
+         /* If we saved any argument areas, restore them.  */
+         for (i = 0; i < num_actuals; i++)
+           if (args[i].save_area)
+             {
+               enum machine_mode save_mode = GET_MODE (args[i].save_area);
+               rtx stack_area
+                 = gen_rtx_MEM (save_mode,
+                                memory_address (save_mode,
+                                                XEXP (args[i].stack_slot, 0)));
+
+               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),
+                                  PARM_BOUNDARY);
+             }
 
-  /* Any regs containing parms remain in use through the call.  */
-  for (count = 0; count < nargs; count++)
-    if (argvec[count].reg != 0)
-       use_reg (&call_fusage, argvec[count].reg);
+         highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+         stack_usage_map = initial_stack_usage_map;
+       }
 
-  /* Don't allow popping to be deferred, since then
-     cse'ing of library calls could delete a call and leave the pop.  */
-  NO_DEFER_POP;
+      /* If this was alloca, record the new stack level for nonlocal gotos.  
+        Check for the handler slots since we might not have a save area
+        for non-local gotos.  */
 
-  /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
-     will set inhibit_defer_pop to that value.  */
+      if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
+       emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
 
-  /* The return type is needed to decide how many bytes the function pops.
-     Signedness plays no role in that, so for simplicity, we pretend it's
-     always signed.  We also assume that the list of arguments passed has
-     no impact, so we pretend it is unknown.  */
+      pop_temp_slots ();
 
-  emit_call_1 (fun, 
-               get_identifier (XSTR (orgfun, 0)), 
-              build_function_type (outmode == VOIDmode ? void_type_node
-                                   : type_for_mode (outmode, 0), NULL_TREE),
-              original_args_size.constant, args_size.constant, 0,
-              FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
-              outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
-              old_inhibit_defer_pop + 1, call_fusage, no_queue);
+      /* Free up storage we no longer need.  */
+      for (i = 0; i < num_actuals; ++i)
+       if (args[i].aligned_regs)
+         free (args[i].aligned_regs);
 
-  pop_temp_slots ();
+      if (pass == 0)
+       {
+         /* Undo the fake expand_start_target_temps we did earlier.  If
+            there had been any cleanups created, we've already set
+            sibcall_failure.  */
+         expand_end_target_temps ();
+       }
 
-  /* Now restore inhibit_defer_pop to its actual original value.  */
-  OK_DEFER_POP;
+      insns = get_insns ();
+      end_sequence ();
 
-#ifdef 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
+      if (pass == 0)
+       {
+         tail_call_insns = insns;
 
-      if (save_mode != BLKmode)
-       emit_move_insn (stack_area, save_area);
+         /* If something prevents making this a sibling call,
+            zero out the sequence.  */
+         if (sibcall_failure)
+           tail_call_insns = NULL_RTX;
+         /* Restore the pending stack adjustment now that we have
+            finished generating the sibling call sequence.  */
+
+         pending_stack_adjust = save_pending_stack_adjust;
+         stack_pointer_delta = save_stack_pointer_delta;
+       }
       else
-       emit_block_move (stack_area, validize_mem (save_area),
-                        GEN_INT (high_to_save - low_to_save + 1),
-                        PARM_BOUNDARY / BITS_PER_UNIT);
+       normal_call_insns = insns;
     }
-#endif
-         
-  /* If we saved any argument areas, restore them.  */
-  for (count = 0; count < nargs; count++)
-    if (argvec[count].save_area)
-      {
-       enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
-       rtx stack_area
-         = gen_rtx_MEM (save_mode,
-                        memory_address
-                        (save_mode,
-                         plus_constant (argblock,
-                                        argvec[count].offset.constant)));
-
-       emit_move_insn (stack_area, argvec[count].save_area);
-      }
 
-  highest_outgoing_arg_in_use = initial_highest_arg_in_use;
-  stack_usage_map = initial_stack_usage_map;
-#endif
+  /* The function optimize_sibling_and_tail_recursive_calls doesn't
+     handle CALL_PLACEHOLDERs inside other CALL_PLACEHOLDERs.  This
+     can happen if the arguments to this function call an inline
+     function who's expansion contains another CALL_PLACEHOLDER.
+
+     If there are any C_Ps in any of these sequences, replace them
+     with their normal call. */
+
+  for (insn = normal_call_insns; insn; insn = NEXT_INSN (insn))
+    if (GET_CODE (insn) == CALL_INSN
+       && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+      replace_call_placeholder (insn, sibcall_use_normal);
+
+  for (insn = tail_call_insns; insn; insn = NEXT_INSN (insn))
+    if (GET_CODE (insn) == CALL_INSN
+       && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+      replace_call_placeholder (insn, sibcall_use_normal);
+
+  for (insn = tail_recursion_insns; insn; insn = NEXT_INSN (insn))
+    if (GET_CODE (insn) == CALL_INSN
+       && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+      replace_call_placeholder (insn, sibcall_use_normal);
+
+  /* If this was a potential tail recursion site, then emit a
+     CALL_PLACEHOLDER with the normal and the tail recursion streams.
+     One of them will be selected later.  */
+  if (tail_recursion_insns || tail_call_insns)
+    {
+      /* The tail recursion label must be kept around.  We could expose
+        its use in the CALL_PLACEHOLDER, but that creates unwanted edges
+        and makes determining true tail recursion sites difficult.
+
+        So we set LABEL_PRESERVE_P here, then clear it when we select
+        one of the call sequences after rtl generation is complete.  */
+      if (tail_recursion_insns)
+       LABEL_PRESERVE_P (tail_recursion_label) = 1;
+      emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns,
+                                               tail_call_insns,
+                                               tail_recursion_insns,
+                                               tail_recursion_label));
+    }
+  else
+    emit_insns (normal_call_insns);
+
+  currently_expanding_call--;
+
+  return target;
 }
 \f
-/* Like emit_library_call except that an extra argument, VALUE,
-   comes second and says where to store the result.
-   (If VALUE is zero, this function chooses a convenient way
-   to return the value.
+/* Returns nonzero if FUN is the symbol for a library function which can
+   not throw.  */
 
-   This function returns an rtx for where the value is to be found.
-   If VALUE is nonzero, VALUE is returned.  */
+static int
+libfunc_nothrow (fun)
+     rtx fun;
+{
+  if (fun == throw_libfunc
+      || fun == rethrow_libfunc
+      || fun == sjthrow_libfunc
+      || fun == sjpopnthrow_libfunc)
+    return 0;
 
-rtx
-emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
-                                enum machine_mode outmode, int nargs, ...))
+  return 1;
+}
+\f
+/* Output a library call to function FUN (a SYMBOL_REF rtx).
+   The RETVAL parameter specifies whether return value needs to be saved, other 
+   parameters are documented in the emit_library_call function bellow.  */
+static rtx
+emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
+     int retval;
+     rtx orgfun;
+     rtx value;
+     int fn_type;
+     enum machine_mode outmode;
+     int nargs;
+     va_list p;
 {
-#ifndef ANSI_PROTOTYPES
-  rtx orgfun;
-  rtx value;
-  int no_queue;
-  enum machine_mode outmode;
-  int nargs;
-#endif
-  va_list p;
   /* Total size in bytes of all the stack-parms scanned so far.  */
   struct args_size args_size;
   /* Size of arguments before any adjustments (such as rounding).  */
@@ -3196,26 +3288,23 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
   int old_inhibit_defer_pop = inhibit_defer_pop;
   rtx call_fusage = 0;
   rtx mem_value = 0;
+  rtx valreg;
   int pcc_struct_value = 0;
   int struct_value_size = 0;
-  int is_const;
+  int flags = 0;
   int reg_parm_stack_space = 0;
-#ifdef ACCUMULATE_OUTGOING_ARGS
   int needed;
-#endif
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#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;
   rtx save_area = 0;            /* Place that it is saved */
 #endif
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
   /* Size of the stack reserved for parameter registers.  */
   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
-#endif
 
 #ifdef REG_PARM_STACK_SPACE
 #ifdef MAYBE_REG_PARM_STACK_SPACE
@@ -3225,19 +3314,15 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 #endif
 #endif
 
-  VA_START (p, nargs);
-
-#ifndef ANSI_PROTOTYPES
-  orgfun = va_arg (p, rtx);
-  value = va_arg (p, rtx);
-  no_queue = va_arg (p, int);
-  outmode = va_arg (p, enum machine_mode);
-  nargs = va_arg (p, int);
-#endif
-
-  is_const = no_queue;
+  if (fn_type == 1)
+    flags |= ECF_CONST;
+  else if (fn_type == 2)
+    flags |= ECF_PURE;
   fun = orgfun;
 
+  if (libfunc_nothrow (fun))
+    flags |= ECF_NOTHROW;
+
 #ifdef PREFERRED_STACK_BOUNDARY
   /* Ensure current function's preferred stack boundary is at least
      what we need.  */
@@ -3247,7 +3332,7 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 
   /* If this kind of value comes back in memory,
      decide where in memory it should come back.  */
-  if (aggregate_value_p (type_for_mode (outmode, 0)))
+  if (outmode != VOIDmode && aggregate_value_p (type_for_mode (outmode, 0)))
     {
 #ifdef PCC_STATIC_STRUCT_RETURN
       rtx pointer_reg
@@ -3266,7 +3351,7 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 #endif
 
       /* This call returns a big structure.  */
-      is_const = 0;
+      flags &= ~(ECF_CONST | ECF_PURE);
     }
 
   /* ??? Unfinished: must pass the memory address as an argument.  */
@@ -3288,6 +3373,11 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 
   count = 0;
 
+  /* Now we are about to start emitting insns that can be deleted
+     if a libcall is deleted.  */
+  if (flags & (ECF_CONST | ECF_PURE))
+    start_sequence ();
+
   push_temp_slots ();
 
   /* If there's a structure value address to be passed,
@@ -3313,7 +3403,11 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 #endif
 
       locate_and_pad_parm (Pmode, NULL_TREE,
-                          argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+                           1,
+#else
+                          argvec[count].reg != 0,
+#endif
                           NULL_TREE, &args_size, &argvec[count].offset,
                           &argvec[count].size, &alignment_pad);
 
@@ -3360,7 +3454,7 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
             be viewed as just an efficiency improvement.  */
          rtx slot = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
          emit_move_insn (slot, val);
-         val = XEXP (slot, 0);
+         val = force_operand (XEXP (slot, 0), NULL_RTX);
          mode = Pmode;
        }
 #endif
@@ -3369,8 +3463,7 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
       argvec[count].mode = mode;
 
       argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
-      if (argvec[count].reg && GET_CODE (argvec[count].reg) == PARALLEL)
-       abort ();
+
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
       argvec[count].partial
        = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
@@ -3379,7 +3472,11 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 #endif
 
       locate_and_pad_parm (mode, NULL_TREE,
-                          argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+                           1,
+#else
+                          argvec[count].reg != 0,
+#endif
                           NULL_TREE, &args_size, &argvec[count].offset,
                           &argvec[count].size, &alignment_pad);
 
@@ -3395,7 +3492,6 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 
       FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
     }
-  va_end (p);
 
 #ifdef FINAL_REG_PARM_STACK_SPACE
   reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
@@ -3407,8 +3503,12 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
 
   original_args_size = args_size;
 #ifdef PREFERRED_STACK_BOUNDARY
-  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
-                        / STACK_BYTES) * STACK_BYTES);
+  args_size.constant = (((args_size.constant
+                         + stack_pointer_delta
+                         + STACK_BYTES - 1)
+                         / STACK_BYTES
+                         * STACK_BYTES)
+                        - stack_pointer_delta);
 #endif
 
   args_size.constant = MAX (args_size.constant,
@@ -3421,133 +3521,138 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
   if (args_size.constant > current_function_outgoing_args_size)
     current_function_outgoing_args_size = args_size.constant;
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  /* Since the stack pointer will never be pushed, it is possible for
-     the evaluation of a parm to clobber something we have already
-     written to the stack.  Since most function calls on RISC machines
-     do not use the stack, this is uncommon, but must work correctly.
+  if (ACCUMULATE_OUTGOING_ARGS)
+    {
+      /* Since the stack pointer will never be pushed, it is possible for
+        the evaluation of a parm to clobber something we have already
+        written to the stack.  Since most function calls on RISC machines
+        do not use the stack, this is uncommon, but must work correctly.
 
-     Therefore, we save any area of the stack that was already written
-     and that we are using.  Here we set up to do this by making a new
-     stack usage map from the old one.
+        Therefore, we save any area of the stack that was already written
+        and that we are using.  Here we set up to do this by making a new
+        stack usage map from the old one.
 
-     Another approach might be to try to reorder the argument
-     evaluations to avoid this conflicting stack usage.  */
+        Another approach might be to try to reorder the argument
+        evaluations to avoid this conflicting stack usage.  */
 
-  needed = args_size.constant;
+      needed = args_size.constant;
 
 #ifndef OUTGOING_REG_PARM_STACK_SPACE
-  /* Since we will be writing into the entire argument area, the
-     map must be allocated for its entire size, not just the part that
-     is the responsibility of the caller.  */
-  needed += reg_parm_stack_space;
+      /* Since we will be writing into the entire argument area, the
+        map must be allocated for its entire size, not just the part that
+        is the responsibility of the caller.  */
+      needed += reg_parm_stack_space;
 #endif
 
 #ifdef ARGS_GROW_DOWNWARD
-  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                    needed + 1);
+      highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+                                        needed + 1);
 #else
-  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
-                                    needed);
+      highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+                                        needed);
 #endif
-  stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
+      stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
 
-  if (initial_highest_arg_in_use)
-    bcopy (initial_stack_usage_map, stack_usage_map,
-          initial_highest_arg_in_use);
+      if (initial_highest_arg_in_use)
+       bcopy (initial_stack_usage_map, stack_usage_map,
+              initial_highest_arg_in_use);
 
-  if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
-    bzero (&stack_usage_map[initial_highest_arg_in_use],
-          highest_outgoing_arg_in_use - initial_highest_arg_in_use);
-  needed = 0;
+      if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
+       bzero (&stack_usage_map[initial_highest_arg_in_use],
+              highest_outgoing_arg_in_use - initial_highest_arg_in_use);
+      needed = 0;
 
-  /* The address of the outgoing argument list must not be copied to a
-     register here, because argblock would be left pointing to the
-     wrong place after the call to allocate_dynamic_stack_space below.
-     */
+      /* The address of the outgoing argument list must not be copied to a
+        register here, because argblock would be left pointing to the
+        wrong place after the call to allocate_dynamic_stack_space below.
+        */
 
-  argblock = virtual_outgoing_args_rtx;
-#else /* not ACCUMULATE_OUTGOING_ARGS */
-#ifndef PUSH_ROUNDING
-  argblock = push_block (GEN_INT (args_size.constant), 0, 0);
-#endif
-#endif
+      argblock = virtual_outgoing_args_rtx;
+    }
+  else
+    {
+      if (!PUSH_ARGS)
+       argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+    }
 
-#ifdef PUSH_ARGS_REVERSED
 #ifdef PREFERRED_STACK_BOUNDARY
   /* If we push args individually in reverse order, perform stack alignment
      before the first push (the last arg).  */
-  if (argblock == 0)
+  if (argblock == 0 && PUSH_ARGS_REVERSED)
     anti_adjust_stack (GEN_INT (args_size.constant
                                - original_args_size.constant));
 #endif
-#endif
 
-#ifdef PUSH_ARGS_REVERSED
-  inc = -1;
-  argnum = nargs - 1;
-#else
-  inc = 1;
-  argnum = 0;
-#endif
+  if (PUSH_ARGS_REVERSED)
+    {
+      inc = -1;
+      argnum = nargs - 1;
+    }
+  else
+    {
+      inc = 1;
+      argnum = 0;
+    }
 
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
-  /* The argument list is the property of the called routine and it
-     may clobber it.  If the fixed area has been used for previous
-     parameters, we must save and restore it.
+#ifdef REG_PARM_STACK_SPACE
+  if (ACCUMULATE_OUTGOING_ARGS)
+    {
+      /* 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.  */
+        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++)
+      for (count = 0; count < reg_parm_stack_space + 1; count++)
 #else
-  for (count = 0; count < reg_parm_stack_space; count++)
+      for (count = 0; count < reg_parm_stack_space; count++)
 #endif
-    {
-      if (count >=  highest_outgoing_arg_in_use
-         || stack_usage_map[count] == 0)
-       continue;
+       {
+         if (count >=  highest_outgoing_arg_in_use
+             || stack_usage_map[count] == 0)
+           continue;
 
-      if (low_to_save == -1)
-       low_to_save = count;
+         if (low_to_save == -1)
+           low_to_save = count;
 
-      high_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 (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;
+         /* 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)));
+         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)));
+         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);
-         emit_block_move (validize_mem (save_area), stack_area,
-                          GEN_INT (num_to_save),
-                          PARM_BOUNDARY / BITS_PER_UNIT);
-       }
-      else
-       {
-         save_area = gen_reg_rtx (save_mode);
-         emit_move_insn (save_area, stack_area);
+         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), PARM_BOUNDARY);
+           }
+         else
+           {
+             save_area = gen_reg_rtx (save_mode);
+             emit_move_insn (save_area, stack_area);
+           }
        }
     }
 #endif
@@ -3562,80 +3667,78 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
       register rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
-#ifdef ACCUMULATE_OUTGOING_ARGS
-      int lower_bound, upper_bound, i;
-#endif
+      int lower_bound = 0, upper_bound = 0, i;
 
       if (! (reg != 0 && partial == 0))
        {
-#ifdef ACCUMULATE_OUTGOING_ARGS
-         /* If this is being stored into a pre-allocated, fixed-size, stack
-            area, save any previous data at that location.  */
+         if (ACCUMULATE_OUTGOING_ARGS)
+           {
+             /* If this is being stored into a pre-allocated, fixed-size,
+                stack area, save any previous data at that location.  */
 
 #ifdef ARGS_GROW_DOWNWARD
-         /* stack_slot is negative, but we want to index stack_usage_map
-            with positive values.  */
-         upper_bound = -argvec[argnum].offset.constant + 1;
-         lower_bound = upper_bound - argvec[argnum].size.constant;
+             /* stack_slot is negative, but we want to index stack_usage_map
+                with positive values.  */
+             upper_bound = -argvec[argnum].offset.constant + 1;
+             lower_bound = upper_bound - argvec[argnum].size.constant;
 #else
-         lower_bound = argvec[argnum].offset.constant;
-         upper_bound = lower_bound + argvec[argnum].size.constant;
+             lower_bound = argvec[argnum].offset.constant;
+             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;
+             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;
 
-         if (i != upper_bound)
-           {
-             /* We need to make a save area.  See what mode we can make it. */
-             enum machine_mode save_mode
-               = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
-                                MODE_INT, 1);
-             rtx stack_area
-               = gen_rtx_MEM
-                 (save_mode,
-                  memory_address
-                  (save_mode,
-                   plus_constant (argblock,
-                                  argvec[argnum].offset.constant)));
-             argvec[argnum].save_area = gen_reg_rtx (save_mode);
-
-             emit_move_insn (argvec[argnum].save_area, stack_area);
+             if (i != upper_bound)
+               {
+                 /* We need to make a save area.  See what mode we can make
+                    it. */
+                 enum machine_mode save_mode
+                   = mode_for_size (argvec[argnum].size.constant
+                                    * BITS_PER_UNIT,
+                                    MODE_INT, 1);
+                 rtx stack_area
+                   = gen_rtx_MEM
+                     (save_mode,
+                      memory_address
+                      (save_mode,
+                       plus_constant (argblock,
+                                      argvec[argnum].offset.constant)));
+                 argvec[argnum].save_area = gen_reg_rtx (save_mode);
+
+                 emit_move_insn (argvec[argnum].save_area, stack_area);
+               }
            }
-#endif
+
          emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
                          argblock, GEN_INT (argvec[argnum].offset.constant),
                          reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
          /* Now mark the segment we just used.  */
-         for (i = lower_bound; i < upper_bound; i++)
-           stack_usage_map[i] = 1;
-#endif
+         if (ACCUMULATE_OUTGOING_ARGS)
+           for (i = lower_bound; i < upper_bound; i++)
+             stack_usage_map[i] = 1;
 
          NO_DEFER_POP;
        }
     }
 
-#ifndef PUSH_ARGS_REVERSED
 #ifdef PREFERRED_STACK_BOUNDARY
   /* If we pushed args in forward order, perform stack alignment
      after pushing the last arg.  */
-  if (argblock == 0)
+  if (argblock == 0 && !PUSH_ARGS_REVERSED)
     anti_adjust_stack (GEN_INT (args_size.constant
                                - original_args_size.constant));
 #endif
-#endif
 
-#ifdef PUSH_ARGS_REVERSED
-  argnum = nargs - 1;
-#else
-  argnum = 0;
-#endif
+  if (PUSH_ARGS_REVERSED)
+    argnum = nargs - 1;
+  else
+    argnum = 0;
 
   fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
 
@@ -3649,21 +3752,27 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
 
-      if (reg != 0 && partial == 0)
+      /* Handle calls that pass values in multiple non-contiguous
+        locations.  The PA64 has examples of this for library calls.  */
+      if (reg != 0 && GET_CODE (reg) == PARALLEL)
+       emit_group_load (reg, val,
+                        GET_MODE_SIZE (GET_MODE (val)),
+                        GET_MODE_ALIGNMENT (GET_MODE (val)));
+      else if (reg != 0 && partial == 0)
        emit_move_insn (reg, val);
+
       NO_DEFER_POP;
     }
 
-#if 0
-  /* For version 1.37, try deleting this entirely.  */
-  if (! no_queue)
-    emit_queue ();
-#endif
-
   /* Any regs containing parms remain in use through the call.  */
   for (count = 0; count < nargs; count++)
-    if (argvec[count].reg != 0)
-       use_reg (&call_fusage, argvec[count].reg);
+    {
+      rtx reg = argvec[count].reg;
+      if (reg != 0 && GET_CODE (reg) == PARALLEL)
+       use_group_regs (&call_fusage, reg);
+      else if (reg != 0)
+       use_reg (&call_fusage, reg);
+    }
 
   /* Pass the function the address in which to return a structure value.  */
   if (mem_value != 0 && struct_value_rtx != 0 && ! pcc_struct_value)
@@ -3679,28 +3788,78 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
   /* Don't allow popping to be deferred, since then
      cse'ing of library calls could delete a call and leave the pop.  */
   NO_DEFER_POP;
+  valreg = (mem_value == 0 && outmode != VOIDmode
+           ? hard_libcall_value (outmode) : NULL_RTX);
+
+#ifdef PREFERRED_STACK_BOUNDARY
+  /* Stack must be properly aligned now.  */
+  if (stack_pointer_delta & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1))
+    abort();
+#endif
 
   /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
      will set inhibit_defer_pop to that value.  */
-  /* See the comment in emit_library_call about the function type we build
-     and pass here.  */
+  /* The return type is needed to decide how many bytes the function pops.
+     Signedness plays no role in that, so for simplicity, we pretend it's
+     always signed.  We also assume that the list of arguments passed has
+     no impact, so we pretend it is unknown.  */
 
   emit_call_1 (fun, 
                get_identifier (XSTR (orgfun, 0)),
-              build_function_type (type_for_mode (outmode, 0), NULL_TREE),
+              build_function_type (outmode == VOIDmode ? void_type_node
+                                   : type_for_mode (outmode, 0), NULL_TREE),
                original_args_size.constant, args_size.constant,
               struct_value_size,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
-              mem_value == 0 ? hard_libcall_value (outmode) : NULL_RTX,
-              old_inhibit_defer_pop + 1, call_fusage, is_const);
+              valreg,
+              old_inhibit_defer_pop + 1, call_fusage, flags);
 
   /* Now restore inhibit_defer_pop to its actual original value.  */
   OK_DEFER_POP;
 
+  /* 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
+     we have no way to move such values into a pseudo register.  */
+  if ((flags & (ECF_CONST | ECF_PURE))
+      && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+    {
+      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);
+
+      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);
+    }
   pop_temp_slots ();
 
   /* Copy the value to the right place.  */
-  if (outmode != VOIDmode)
+  if (outmode != VOIDmode && retval)
     {
       if (mem_value)
        {
@@ -3715,51 +3874,129 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
        value = hard_libcall_value (outmode);
     }
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-#ifdef REG_PARM_STACK_SPACE
-  if (save_area)
+  if (ACCUMULATE_OUTGOING_ARGS)
     {
-      enum machine_mode save_mode = GET_MODE (save_area);
+#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)));
+         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)));
+         rtx stack_area
+           = gen_rtx_MEM (save_mode,
+                          memory_address (save_mode,
+                                          plus_constant (argblock, low_to_save)));
 #endif
-      if (save_mode != BLKmode)
-       emit_move_insn (stack_area, save_area);
-      else
-       emit_block_move (stack_area, validize_mem (save_area),
-                        GEN_INT (high_to_save - low_to_save + 1),
-                            PARM_BOUNDARY / BITS_PER_UNIT);
+         if (save_mode != BLKmode)
+           emit_move_insn (stack_area, save_area);
+         else
+           emit_block_move (stack_area, validize_mem (save_area),
+                            GEN_INT (high_to_save - low_to_save + 1),
+                            PARM_BOUNDARY);
+       }
+#endif
+             
+      /* If we saved any argument areas, restore them.  */
+      for (count = 0; count < nargs; count++)
+       if (argvec[count].save_area)
+         {
+           enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
+           rtx stack_area
+             = gen_rtx_MEM (save_mode,
+                            memory_address
+                            (save_mode,
+                             plus_constant (argblock,
+                                            argvec[count].offset.constant)));
+
+           emit_move_insn (stack_area, argvec[count].save_area);
+         }
+
+      highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+      stack_usage_map = initial_stack_usage_map;
     }
+
+  return value;
+
+}
+\f
+/* Output a library call to function FUN (a SYMBOL_REF rtx)
+   (emitting the queue unless NO_QUEUE is nonzero),
+   for a value of mode OUTMODE,
+   with NARGS different arguments, passed as alternating rtx values
+   and machine_modes to convert them to.
+   The rtx values should have been passed through protect_from_queue already.
+
+   FN_TYPE will is zero for `normal' calls, one for `const' calls, wich
+   which will be enclosed in REG_LIBCALL/REG_RETVAL notes and two for `pure'
+   calls, that are handled like `const' calls with extra
+   (use (memory (scratch)).  */
+
+void
+emit_library_call VPARAMS((rtx orgfun, int fn_type, enum machine_mode outmode,
+                          int nargs, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  rtx orgfun;
+  int fn_type;
+  enum machine_mode outmode;
+  int nargs;
 #endif
-         
-  /* If we saved any argument areas, restore them.  */
-  for (count = 0; count < nargs; count++)
-    if (argvec[count].save_area)
-      {
-       enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
-       rtx stack_area
-         = gen_rtx_MEM (save_mode,
-                        memory_address
-                        (save_mode,
-                         plus_constant (argblock,
-                                        argvec[count].offset.constant)));
-
-       emit_move_insn (stack_area, argvec[count].save_area);
-      }
+  va_list p;
+
+  VA_START (p, nargs);
+
+#ifndef ANSI_PROTOTYPES
+  orgfun = va_arg (p, rtx);
+  fn_type = va_arg (p, int);
+  outmode = va_arg (p, enum machine_mode);
+  nargs = va_arg (p, int);
+#endif
+
+  emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
+
+  va_end (p);
+}
+\f
+/* Like emit_library_call except that an extra argument, VALUE,
+   comes second and says where to store the result.
+   (If VALUE is zero, this function chooses a convenient way
+   to return the value.
+
+   This function returns an rtx for where the value is to be found.
+   If VALUE is nonzero, VALUE is returned.  */
+
+rtx
+emit_library_call_value VPARAMS((rtx orgfun, rtx value, int fn_type,
+                                enum machine_mode outmode, int nargs, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  rtx orgfun;
+  rtx value;
+  int fn_type;
+  enum machine_mode outmode;
+  int nargs;
+#endif
+  va_list p;
+
+  VA_START (p, nargs);
 
-  highest_outgoing_arg_in_use = initial_highest_arg_in_use;
-  stack_usage_map = initial_stack_usage_map;
+#ifndef ANSI_PROTOTYPES
+  orgfun = va_arg (p, rtx);
+  value = va_arg (p, rtx);
+  fn_type = va_arg (p, int);
+  outmode = va_arg (p, enum machine_mode);
+  nargs = va_arg (p, int);
 #endif
 
+  value = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode, nargs, p);
+
+  va_end (p);
+
   return value;
 }
 \f
@@ -3818,11 +4055,11 @@ target_for_arg (type, size, args_addr, offset)
    FNDECL is the declaration of the function we are calling.  */
 
 static void
-store_one_arg (arg, argblock, may_be_alloca, variable_size,
+store_one_arg (arg, argblock, flags, variable_size,
               reg_parm_stack_space)
      struct arg_data *arg;
      rtx argblock;
-     int may_be_alloca;
+     int flags;
      int variable_size ATTRIBUTE_UNUSED;
      int reg_parm_stack_space;
 {
@@ -3830,9 +4067,7 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
   rtx reg = 0;
   int partial = 0;
   int used = 0;
-#ifdef ACCUMULATE_OUTGOING_ARGS
   int i, lower_bound = 0, upper_bound = 0;
-#endif
 
   if (TREE_CODE (pval) == ERROR_MARK)
     return;
@@ -3841,75 +4076,75 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
      this argument.  */
   push_temp_slots ();
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  /* If this is being stored into a pre-allocated, fixed-size, stack area,
-     save any previous data at that location.  */
-  if (argblock && ! variable_size && arg->stack)
+  if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL))
     {
+      /* If this is being stored into a pre-allocated, fixed-size, stack area,
+        save any previous data at that location.  */
+      if (argblock && ! variable_size && arg->stack)
+       {
 #ifdef ARGS_GROW_DOWNWARD
-      /* stack_slot is negative, but we want to index stack_usage_map
-         with positive values.  */
-      if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
-       upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
-      else
-       upper_bound = 0;
+         /* stack_slot is negative, but we want to index stack_usage_map
+            with positive values.  */
+         if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+           upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
+         else
+           upper_bound = 0;
 
-      lower_bound = upper_bound - arg->size.constant;
+         lower_bound = upper_bound - arg->size.constant;
 #else
-      if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
-       lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
-      else
-       lower_bound = 0;
+         if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+           lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+         else
+           lower_bound = 0;
 
-      upper_bound = lower_bound + arg->size.constant;
+         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;
-
-      if (i != upper_bound)
-       {
-         /* We need to make a save area.  See what mode we can make it.  */
-         enum machine_mode save_mode
-           = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
-         rtx stack_area
-           = gen_rtx_MEM (save_mode,
-                          memory_address (save_mode,
-                                          XEXP (arg->stack_slot, 0)));
+         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;
 
-         if (save_mode == BLKmode)
-           {
-             arg->save_area = assign_stack_temp (BLKmode,
-                                                 arg->size.constant, 0);
-             MEM_SET_IN_STRUCT_P (arg->save_area,
-                                  AGGREGATE_TYPE_P (TREE_TYPE
-                                                    (arg->tree_value))); 
-             preserve_temp_slots (arg->save_area);
-             emit_block_move (validize_mem (arg->save_area), stack_area,
-                              GEN_INT (arg->size.constant),
-                              PARM_BOUNDARY / BITS_PER_UNIT);
-           }
-         else
+         if (i != upper_bound)
            {
-             arg->save_area = gen_reg_rtx (save_mode);
-             emit_move_insn (arg->save_area, stack_area);
+             /* We need to make a save area.  See what mode we can make it.  */
+             enum machine_mode save_mode
+               = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
+             rtx stack_area
+               = gen_rtx_MEM (save_mode,
+                              memory_address (save_mode,
+                                              XEXP (arg->stack_slot, 0)));
+
+             if (save_mode == BLKmode)
+               {
+                 arg->save_area = assign_stack_temp (BLKmode,
+                                                     arg->size.constant, 0);
+                 MEM_SET_IN_STRUCT_P (arg->save_area,
+                                      AGGREGATE_TYPE_P (TREE_TYPE
+                                                        (arg->tree_value))); 
+                 preserve_temp_slots (arg->save_area);
+                 emit_block_move (validize_mem (arg->save_area), stack_area,
+                                  GEN_INT (arg->size.constant),
+                                  PARM_BOUNDARY);
+               }
+             else
+               {
+                 arg->save_area = gen_reg_rtx (save_mode);
+                 emit_move_insn (arg->save_area, stack_area);
+               }
            }
        }
+      /* 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;
     }
 
-  /* Now that we have saved any slots that will be overwritten by this
-     store, mark all slots this store will use.  We must do this before
-     we actually expand the argument since the expansion itself may
-     trigger library calls which might need to use the same stack slot.  */
-  if (argblock && ! variable_size && arg->stack)
-    for (i = lower_bound; i < upper_bound; i++)
-      stack_usage_map[i] = 1;
-#endif
-
   /* If this isn't going to be placed on both the stack and in registers,
      set up the register and number of words.  */
   if (! arg->pass_on_stack)
@@ -3929,7 +4164,6 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
      it directly into its stack slot.  Otherwise, we can.  */
   if (arg->value == 0)
     {
-#ifdef ACCUMULATE_OUTGOING_ARGS
       /* stack_arg_under_construction is nonzero if a function argument is
         being evaluated directly into the outgoing argument list and
         expand_call must take special action to preserve the argument list
@@ -3950,7 +4184,7 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
 
       if (arg->pass_on_stack)
        stack_arg_under_construction++;
-#endif
+
       arg->value = expand_expr (pval,
                                (partial
                                 || TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
@@ -3964,15 +4198,13 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
        arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
                                    arg->value, arg->unsignedp);
 
-#ifdef ACCUMULATE_OUTGOING_ARGS
       if (arg->pass_on_stack)
        stack_arg_under_construction--;
-#endif
     }
 
   /* Don't allow anything left on stack from computation
      of argument to alloca.  */
-  if (may_be_alloca)
+  if (flags & ECF_MAY_BE_ALLOCA)
     do_pending_stack_adjust ();
 
   if (arg->value == arg->stack)
@@ -4024,7 +4256,6 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
                      partial, reg, used - size, argblock,
                      ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
                      ARGS_SIZE_RTX (arg->alignment_pad));
-
     }
   else
     {
@@ -4055,8 +4286,8 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size,
        }
 
       emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-                     TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
-                     reg, excess, argblock, ARGS_SIZE_RTX (arg->offset),
+                     TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess,
+                     argblock, ARGS_SIZE_RTX (arg->offset),
                      reg_parm_stack_space,
                      ARGS_SIZE_RTX (arg->alignment_pad));
     }