OSDN Git Service

(call patterns): Use %. in cror.
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index a4b8747..edb0dd0 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1992, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -22,6 +22,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "tree.h"
 #include "flags.h"
 #include "expr.h"
+#include "gvarargs.h"
 #include "insn-flags.h"
 
 /* Decide whether a function's arguments should be processed
@@ -117,8 +118,12 @@ static int highest_outgoing_arg_in_use;
 int stack_arg_under_construction;
 #endif
 
-static void store_one_arg ();
-extern enum machine_mode mode_for_size ();
+static int calls_function      PROTO((tree, int));
+static int calls_function_1    PROTO((tree, int));
+static void emit_call_1                PROTO((rtx, tree, int, int, rtx, rtx, int,
+                                      rtx, int));
+static void store_one_arg      PROTO ((struct arg_data *, rtx, int, int,
+                                       tree, int));
 \f
 /* If WHICH is 1, return 1 if EXP contains a call to the built-in function
    `alloca'.
@@ -128,11 +133,25 @@ extern enum machine_mode mode_for_size ();
    arguments on the stack, but that is too difficult to compute, so we just
    assume any function call might require the stack.  */
 
+static tree calls_function_save_exprs;
+
 static int
 calls_function (exp, which)
      tree exp;
      int which;
 {
+  int val;
+  calls_function_save_exprs = 0;
+  val = calls_function_1 (exp, which);
+  calls_function_save_exprs = 0;
+  return val;
+}
+
+static int
+calls_function_1 (exp, which)
+     tree exp;
+     int which;
+{
   register int i;
   int type = TREE_CODE_CLASS (TREE_CODE (exp));
   int length = tree_code_length[(int) TREE_CODE (exp)];
@@ -163,7 +182,12 @@ calls_function (exp, which)
     case SAVE_EXPR:
       if (SAVE_EXPR_RTL (exp) != 0)
        return 0;
-      break;
+      if (value_member (exp, calls_function_save_exprs))
+       return 0;
+      calls_function_save_exprs = tree_cons (NULL_TREE, exp,
+                                            calls_function_save_exprs);
+      return (TREE_OPERAND (exp, 0) != 0
+             && calls_function_1 (TREE_OPERAND (exp, 0), which));
 
     case BLOCK:
       {
@@ -171,7 +195,7 @@ calls_function (exp, which)
 
        for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
          if (DECL_INITIAL (local) != 0
-             && calls_function (DECL_INITIAL (local), which))
+             && calls_function_1 (DECL_INITIAL (local), which))
            return 1;
       }
       {
@@ -180,7 +204,7 @@ calls_function (exp, which)
        for (subblock = BLOCK_SUBBLOCKS (exp);
             subblock;
             subblock = TREE_CHAIN (subblock))
-         if (calls_function (subblock, which))
+         if (calls_function_1 (subblock, which))
            return 1;
       }
       return 0;
@@ -199,7 +223,7 @@ calls_function (exp, which)
 
   for (i = 0; i < length; i++)
     if (TREE_OPERAND (exp, i) != 0
-       && calls_function (TREE_OPERAND (exp, i), which))
+       && calls_function_1 (TREE_OPERAND (exp, i), which))
       return 1;
 
   return 0;
@@ -293,7 +317,7 @@ prepare_call_address (funexp, fndecl, use_insns)
 
    IS_CONST is true if this is a `const' call.  */
 
-void
+static void
 emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg,
             valreg, old_inhibit_defer_pop, use_insns, is_const)
      rtx funexp;
@@ -469,6 +493,8 @@ expand_call (exp, target, ignore)
   CUMULATIVE_ARGS args_so_far;
   /* Nonzero if a reg parm has been scanned.  */
   int reg_parm_seen;
+  /* Nonzero if this is an indirect function call.  */
+  int current_call_is_indirect = 0;
 
   /* Nonzero if we must avoid push-insns in the args for this call. 
      If stack space is allocated for register parameters, but not by the
@@ -558,7 +584,8 @@ expand_call (exp, target, ignore)
 
                 Use abstraction instead of setting TREE_ADDRESSABLE
                 directly.  */
-             if (DECL_INLINE (fndecl) && extra_warnings && !flag_no_inline)
+             if (DECL_INLINE (fndecl) && extra_warnings && warn_inline
+                 && !flag_no_inline)
                warning_with_decl (fndecl, "can't inline call to `%s' which was declared inline");
              mark_addressable (fndecl);
            }
@@ -584,6 +611,7 @@ expand_call (exp, target, ignore)
   if (warn_aggregate_return
       && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
          || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE
          || TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE))
     warning ("function call has aggregate value");
 
@@ -596,34 +624,33 @@ expand_call (exp, target, ignore)
       is_const = 0;
 
 #ifdef PCC_STATIC_STRUCT_RETURN
-      if (flag_pcc_struct_return)
-       {
-         pcc_struct_value = 1;
-         is_integrable = 0;  /* Easier than making that case work right.  */
-       }
-      else
-#endif
-       {
-         struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
+      {
+       pcc_struct_value = 1;
+       is_integrable = 0;  /* Easier than making that case work right.  */
+      }
+#else /* not PCC_STATIC_STRUCT_RETURN */
+      {
+       struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
 
-         if (struct_value_size < 0)
-           abort ();
+       if (struct_value_size < 0)
+         abort ();
 
-         if (target && GET_CODE (target) == MEM)
-           structure_value_addr = XEXP (target, 0);
-         else
-           {
-             /* Assign a temporary on the stack to hold the value.  */
+       if (target && GET_CODE (target) == MEM)
+         structure_value_addr = XEXP (target, 0);
+       else
+         {
+           /* Assign a temporary on the stack to hold the value.  */
 
-             /* For variable-sized objects, we must be called with a target
-                specified.  If we were to allocate space on the stack here,
-                we would have no way of knowing when to free it.  */
+           /* For variable-sized objects, we must be called with a target
+              specified.  If we were to allocate space on the stack here,
+              we would have no way of knowing when to free it.  */
 
-             structure_value_addr
-               = XEXP (assign_stack_temp (BLKmode, struct_value_size, 1), 0);
-             target = 0;
-           }
-       }
+           structure_value_addr
+             = XEXP (assign_stack_temp (BLKmode, struct_value_size, 1), 0);
+           target = 0;
+         }
+      }
+#endif /* not PCC_STATIC_STRUCT_RETURN */
     }
 
   /* If called function is inline, try to integrate it.  */
@@ -693,7 +720,7 @@ expand_call (exp, target, ignore)
                  adjust += reg_parm_stack_space;
 #endif
                  start_sequence ();
-                 emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
+                 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 ();
@@ -727,6 +754,15 @@ expand_call (exp, target, ignore)
   if (fndecl && DECL_NAME (fndecl))
     name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
 
+  /* On some machines (such as the PA) indirect calls have a different
+     calling convention than normal calls.  FUNCTION_ARG in the target
+     description can look at current_call_is_indirect to determine which
+     calling convention to use.  */
+  current_call_is_indirect = (fndecl == 0);
+#if 0
+    = TREE_CODE (TREE_OPERAND (exp, 0)) == NON_LVALUE_EXPR ? 1 : 0;
+#endif
+
 #if 0
   /* Unless it's a call to a specific function that isn't alloca,
      if it has one argument, we must assume it might be alloca.  */
@@ -910,41 +946,60 @@ expand_call (exp, target, ignore)
       if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), type,
                                          argpos < n_named_args))
        {
-         /* We make a copy of the object and pass the address to the function
-            being called.  */
-         rtx copy;
-
-         if (TYPE_SIZE (type) == 0
-             || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+#ifdef FUNCTION_ARG_CALLEE_COPIES
+         if (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), type,
+                                         argpos < n_named_args)
+             /* If it's in a register, we must make a copy of it too.  */
+             /* ??? Is this a sufficient test?  Is there a better one? */
+             && !(TREE_CODE (args[i].tree_value) == VAR_DECL
+                  && REG_P (DECL_RTL (args[i].tree_value))))
            {
-             /* This is a variable-sized object.  Make space on the stack
-                for it.  */
-             rtx size_rtx = expr_size (TREE_VALUE (p));
-
-             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;
-               }
-
-             copy = gen_rtx (MEM, BLKmode,
-                             allocate_dynamic_stack_space (size_rtx, NULL_RTX,
-                                                           TYPE_ALIGN (type)));
+             args[i].tree_value = build1 (ADDR_EXPR,
+                                          build_pointer_type (type),
+                                          args[i].tree_value);
+             type = build_pointer_type (type);
            }
          else
+#endif
            {
-             int size = int_size_in_bytes (type);
-             copy = assign_stack_temp (TYPE_MODE (type), size, 1);
-           }
+             /* We make a copy of the object and pass the address to the
+                function being called.  */
+             rtx copy;
+
+             if (TYPE_SIZE (type) == 0
+                 || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+               {
+                 /* This is a variable-sized object.  Make space on the stack
+                    for it.  */
+                 rtx size_rtx = expr_size (TREE_VALUE (p));
+
+                 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;
+                   }
+
+                 copy = gen_rtx (MEM, BLKmode,
+                                 allocate_dynamic_stack_space (size_rtx,
+                                                               NULL_RTX,
+                                                               TYPE_ALIGN (type)));
+               }
+             else
+               {
+                 int size = int_size_in_bytes (type);
+                 copy = assign_stack_temp (TYPE_MODE (type), size, 1);
+               }
 
-         store_expr (args[i].tree_value, copy, 0);
+             store_expr (args[i].tree_value, copy, 0);
 
-         args[i].tree_value = build1 (ADDR_EXPR, build_pointer_type (type),
-                                      make_tree (type, copy));
-         type = build_pointer_type (type);
+             args[i].tree_value = build1 (ADDR_EXPR,
+                                          build_pointer_type (type),
+                                          make_tree (type, copy));
+             type = build_pointer_type (type);
+           }
        }
-#endif
+#endif /* FUNCTION_ARG_PASS_BY_REFERENCE */
 
       mode = TYPE_MODE (type);
 
@@ -1421,8 +1476,18 @@ expand_call (exp, target, ignore)
 
   /* Get the function to call, in the form of RTL.  */
   if (fndecl)
-    /* Get a SYMBOL_REF rtx for the function address.  */
-    funexp = XEXP (DECL_RTL (fndecl), 0);
+    {
+      /* If this is the first use of the function, see if we need to
+        make an external definition for it.  */
+      if (! TREE_USED (fndecl))
+       {
+         assemble_external (fndecl);
+         TREE_USED (fndecl) = 1;
+       }
+
+      /* Get a SYMBOL_REF rtx for the function address.  */
+      funexp = XEXP (DECL_RTL (fndecl), 0);
+    }
   else
     /* Generate an rtx (probably a pseudo-register) for the address.  */
     {
@@ -1623,7 +1688,7 @@ expand_call (exp, target, ignore)
   /* 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(ALLOCATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
+#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));
 #endif
@@ -1795,21 +1860,34 @@ expand_call (exp, target, ignore)
          MEM_IN_STRUCT_P (target)
            = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE
               || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
-              || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE);
+              || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE
+              || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE);
        }
     }
   else if (pcc_struct_value)
     {
       if (target == 0)
        {
-         target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
-                           copy_to_reg (valreg));
-         MEM_IN_STRUCT_P (target)
-           = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE
-              || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
-              || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE);
+         /* We used leave the value in the location that it is
+            returned in, but that causes problems if it is used more
+            than once in one expression.  Rather than trying to track
+            when a copy is required, we always copy when TARGET is
+            not specified.  This calling sequence is only used on
+            a few machines and TARGET is usually nonzero.  */
+         if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+           {
+             target = assign_stack_temp (BLKmode,
+                                         int_size_in_bytes (TREE_TYPE (exp)),
+                                         0);
+
+             /* Save this temp slot around the pop below.  */
+             preserve_temp_slots (target);
+           }
+         else
+           target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
        }
-      else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
+
+      if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
        emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
                                         copy_to_reg (valreg)));
       else
@@ -1830,10 +1908,12 @@ expand_call (exp, target, ignore)
     target = copy_to_reg (valreg);
 
 #ifdef PROMOTE_FUNCTION_RETURN
-  /* If we promoted this return value, make the proper SUBREG.  */
-  if (GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+  /* If we promoted this return value, make the proper SUBREG.  TARGET
+     might be const0_rtx here, so be careful.  */
+  if (GET_CODE (target) == REG
+      && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
     {
-      enum machine_mode mode = GET_MODE (target);
+      enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
 
       if (TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE
@@ -1847,6 +1927,10 @@ expand_call (exp, target, ignore)
          PROMOTE_MODE (mode, unsignedp, TREE_TYPE (exp));
        }
 
+      /* If we didn't promote as expected, something is wrong.  */
+      if (mode != GET_MODE (target))
+       abort ();
+
       target = gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (exp)), target, 0);
       SUBREG_PROMOTED_VAR_P (target) = 1;
       SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
@@ -1931,6 +2015,608 @@ expand_call (exp, target, ignore)
   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.
+
+   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.
+
+   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.
+
+   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.  */
+
+void
+emit_library_call (va_alist)
+     va_dcl
+{
+  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;
+  enum machine_mode outmode;
+  int nargs;
+  rtx fun;
+  rtx orgfun;
+  int inc;
+  int count;
+  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; };
+  struct arg *argvec;
+  int old_inhibit_defer_pop = inhibit_defer_pop;
+  int no_queue = 0;
+  rtx use_insns;
+  /* library calls are never indirect calls.  */
+  int current_call_is_indirect = 0;
+
+  va_start (p);
+  orgfun = fun = va_arg (p, rtx);
+  no_queue = va_arg (p, int);
+  outmode = va_arg (p, enum machine_mode);
+  nargs = va_arg (p, int);
+
+  /* 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));
+
+  INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun);
+
+  args_size.constant = 0;
+  args_size.var = 0;
+
+  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_to_mode (DFmode, val, 0), mode = DFmode;
+#endif
+
+      /* There's no need to call protect_from_queue, because
+        either emit_move_insn or emit_push_insn will do that.  */
+
+      /* 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);
+
+      argvec[count].value = val;
+      argvec[count].mode = mode;
+
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+      if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
+       abort ();
+#endif
+
+      argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+      if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST)
+       abort ();
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+      argvec[count].partial
+       = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
+#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);
+
+      if (argvec[count].size.var)
+       abort ();
+
+#ifndef REG_PARM_STACK_SPACE
+      if (argvec[count].partial)
+       argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
+#endif
+
+      if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+         || 1
+#endif
+         )
+       args_size.constant += argvec[count].size.constant;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      /* If this arg is actually passed on the stack, it might be
+        clobbering something we already put there (this library call might
+        be inside the evaluation of an argument to a function whose call
+        requires the stack).  This will only occur when the library call
+        has sufficient args to run out of argument registers.  Abort in
+        this case; if this ever occurs, code must be added to save and
+        restore the arg slot.  */
+
+      if (argvec[count].reg == 0 || argvec[count].partial != 0)
+       abort ();
+#endif
+
+      FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1);
+    }
+  va_end (p);
+
+  /* If this machine requires an external definition for library
+     functions, write one out.  */
+  assemble_external_libcall (fun);
+
+  original_args_size = args_size;
+#ifdef STACK_BOUNDARY
+  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+                        / STACK_BYTES) * STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+  args_size.constant = MAX (args_size.constant,
+                           REG_PARM_STACK_SPACE (NULL_TREE));
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+  args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+#endif
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+  if (args_size.constant > current_function_outgoing_args_size)
+    current_function_outgoing_args_size = args_size.constant;
+  args_size.constant = 0;
+#endif
+
+#ifndef PUSH_ROUNDING
+  argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+  /* If we push args individually in reverse order, perform stack alignment
+     before the first push (the last arg).  */
+  if (argblock == 0)
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+  inc = -1;
+  argnum = nargs - 1;
+#else
+  inc = 1;
+  argnum = 0;
+#endif
+
+  /* Push the args that need 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;
+
+      if (! (reg != 0 && partial == 0))
+       emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+                       argblock, GEN_INT (argvec[count].offset.constant));
+      NO_DEFER_POP;
+    }
+
+#ifndef PUSH_ARGS_REVERSED
+#ifdef 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
+
+#ifdef PUSH_ARGS_REVERSED
+  argnum = nargs - 1;
+#else
+  argnum = 0;
+#endif
+
+  /* Now load any reg parms into their regs.  */
+
+  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;
+
+      if (reg != 0 && partial == 0)
+       emit_move_insn (reg, val);
+      NO_DEFER_POP;
+    }
+
+  /* For version 1.37, try deleting this entirely.  */
+  if (! no_queue)
+    emit_queue ();
+
+  /* Any regs containing parms remain in use through the call.  */
+  start_sequence ();
+  for (count = 0; count < nargs; count++)
+    if (argvec[count].reg != 0)
+      emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg));
+
+  use_insns = get_insns ();
+  end_sequence ();
+
+  fun = prepare_call_address (fun, NULL_TREE, &use_insns);
+
+  /* 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;
+
+  /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
+     will set inhibit_defer_pop to that value.  */
+
+  emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), 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, use_insns, no_queue);
+
+  /* Now restore inhibit_defer_pop to its actual original value.  */
+  OK_DEFER_POP;
+}
+\f
+/* Like emit_library_call except that an extra argument, VALUE,
+   comes second and says where to store the result.
+   (If VALUE is zero, the result comes in the function value register.)  */
+
+void
+emit_library_call_value (va_alist)
+     va_dcl
+{
+  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;
+  enum machine_mode outmode;
+  int nargs;
+  rtx fun;
+  rtx orgfun;
+  int inc;
+  int count;
+  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; };
+  struct arg *argvec;
+  int old_inhibit_defer_pop = inhibit_defer_pop;
+  int no_queue = 0;
+  rtx use_insns;
+  rtx value;
+  rtx mem_value = 0;
+  /* library calls are never indirect calls.  */
+  int current_call_is_indirect = 0;
+
+  va_start (p);
+  orgfun = fun = 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);
+
+  /* If this kind of value comes back in memory,
+     decide where in memory it should come back.  */
+  if (RETURN_IN_MEMORY (type_for_mode (outmode, 0)))
+    {
+      if (GET_CODE (value) == MEM)
+       mem_value = value;
+      else
+       mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0);
+    }
+
+  /* ??? Unfinished: must pass the memory address as an argument.  */
+
+  /* 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 + 1) * sizeof (struct arg));
+
+  INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun);
+
+  args_size.constant = 0;
+  args_size.var = 0;
+
+  count = 0;
+
+  /* If there's a structure value address to be passed,
+     either pass it in the special place, or pass it as an extra argument.  */
+  if (mem_value)
+    {
+      rtx addr = XEXP (mem_value, 0);
+
+      if (! struct_value_rtx)
+       {
+         nargs++;
+
+         /* Make sure it is a reasonable operand for a move or push insn.  */
+         if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM
+             && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr)))
+           addr = force_operand (addr, NULL_RTX);
+
+         argvec[count].value = addr;
+         argvec[count].mode = outmode;
+         argvec[count].partial = 0;
+
+         argvec[count].reg = FUNCTION_ARG (args_so_far, outmode, NULL_TREE, 1);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+         if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, outmode, NULL_TREE, 1))
+           abort ();
+#endif
+
+         locate_and_pad_parm (outmode, NULL_TREE,
+                              argvec[count].reg && argvec[count].partial == 0,
+                              NULL_TREE, &args_size, &argvec[count].offset,
+                              &argvec[count].size);
+
+
+         if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+             || 1
+#endif
+             )
+           args_size.constant += argvec[count].size.constant;
+
+         FUNCTION_ARG_ADVANCE (args_so_far, outmode, (tree)0, 1);
+       }
+    }
+
+  for (; 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_to_mode (DFmode, val, 0), mode = DFmode;
+#endif
+
+      /* There's no need to call protect_from_queue, because
+        either emit_move_insn or emit_push_insn will do that.  */
+
+      /* 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);
+
+      argvec[count].value = val;
+      argvec[count].mode = mode;
+
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+      if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
+       abort ();
+#endif
+
+      argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+      if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST)
+       abort ();
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+      argvec[count].partial
+       = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
+#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);
+
+      if (argvec[count].size.var)
+       abort ();
+
+#ifndef REG_PARM_STACK_SPACE
+      if (argvec[count].partial)
+       argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
+#endif
+
+      if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+         || 1
+#endif
+         )
+       args_size.constant += argvec[count].size.constant;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      /* If this arg is actually passed on the stack, it might be
+        clobbering something we already put there (this library call might
+        be inside the evaluation of an argument to a function whose call
+        requires the stack).  This will only occur when the library call
+        has sufficient args to run out of argument registers.  Abort in
+        this case; if this ever occurs, code must be added to save and
+        restore the arg slot.  */
+
+      if (argvec[count].reg == 0 || argvec[count].partial != 0)
+       abort ();
+#endif
+
+      FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1);
+    }
+  va_end (p);
+
+  /* If this machine requires an external definition for library
+     functions, write one out.  */
+  assemble_external_libcall (fun);
+
+  original_args_size = args_size;
+#ifdef STACK_BOUNDARY
+  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+                        / STACK_BYTES) * STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+  args_size.constant = MAX (args_size.constant,
+                           REG_PARM_STACK_SPACE (NULL_TREE));
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+  args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+#endif
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+  if (args_size.constant > current_function_outgoing_args_size)
+    current_function_outgoing_args_size = args_size.constant;
+  args_size.constant = 0;
+#endif
+
+#ifndef PUSH_ROUNDING
+  argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+  /* If we push args individually in reverse order, perform stack alignment
+     before the first push (the last arg).  */
+  if (argblock == 0)
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+  inc = -1;
+  argnum = nargs - 1;
+#else
+  inc = 1;
+  argnum = 0;
+#endif
+
+  /* Push the args that need 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;
+
+      if (! (reg != 0 && partial == 0))
+       emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+                       argblock, GEN_INT (argvec[count].offset.constant));
+      NO_DEFER_POP;
+    }
+
+#ifndef PUSH_ARGS_REVERSED
+#ifdef 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
+
+#ifdef PUSH_ARGS_REVERSED
+  argnum = nargs - 1;
+#else
+  argnum = 0;
+#endif
+
+  /* Now load any reg parms into their regs.  */
+
+  if (mem_value != 0 && struct_value_rtx != 0)
+    emit_move_insn (struct_value_rtx, XEXP (mem_value, 0));
+
+  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;
+
+      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.  */
+  start_sequence ();
+  for (count = 0; count < nargs; count++)
+    if (argvec[count].reg != 0)
+      emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg));
+
+  use_insns = get_insns ();
+  end_sequence ();
+
+  fun = prepare_call_address (fun, NULL_TREE, &use_insns);
+
+  /* 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;
+
+  /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
+     will set inhibit_defer_pop to that value.  */
+
+  emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), 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, use_insns, no_queue);
+
+  /* Now restore inhibit_defer_pop to its actual original value.  */
+  OK_DEFER_POP;
+
+  /* Copy the value to the right place.  */
+  if (outmode != VOIDmode)
+    {
+      if (mem_value)
+       {
+         if (value == 0)
+           value = hard_libcall_value (outmode);
+         if (value != mem_value)
+           emit_move_insn (value, mem_value);
+       }
+      else if (value != 0)
+       emit_move_insn (value, hard_libcall_value (outmode));
+    }
+}
+\f
 #if 0
 /* Return an rtx which represents a suitable home on the stack
    given TYPE, the type of the argument looking for a home.
@@ -2168,8 +2854,9 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
 
       /* This isn't already where we want it on the stack, so put it there.
         This can either be done with push or copy insns.  */
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), 0, 0, partial,
-                     reg, used - size, argblock, ARGS_SIZE_RTX (arg->offset));
+      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
+                     0, partial, reg, used - size,
+                     argblock, ARGS_SIZE_RTX (arg->offset));
     }
   else
     {
@@ -2192,12 +2879,11 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
        }
       else
        {
-         register tree size = size_in_bytes (TREE_TYPE (pval));
          /* PUSH_ROUNDING has no effect on us, because
             emit_push_insn for BLKmode is careful to avoid it.  */
-         excess = (arg->size.constant - TREE_INT_CST_LOW (size)
+         excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
                    + partial * UNITS_PER_WORD);
-         size_rtx = expand_expr (size, NULL_RTX, VOIDmode, 0);
+         size_rtx = expr_size (pval);
        }
 
       emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,