OSDN Git Service

* calls.c: Revert last two changes.
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 16 Mar 2000 17:42:06 +0000 (17:42 +0000)
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 16 Mar 2000 17:42:06 +0000 (17:42 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@32592 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/calls.c

index c9f5687..264a0bc 100644 (file)
@@ -1,3 +1,7 @@
+2000-03-16  Richard Henderson  <rth@cygnus.com>
+
+       * calls.c: Revert last two changes.
+
 2000-03-16  Bernd Schmidt  <bernds@cygnus.co.uk>
 
        * fp-bit.c (_unpack_d): If NO_DENORMALS is defined, anything that
index 473fe0c..7ae7b3d 100644 (file)
@@ -163,9 +163,6 @@ 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 rtx emit_library_call_value_1           PARAMS ((int, rtx, rtx, int,
-                                                        enum machine_mode,
-                                                        int, va_list));
 static int libfunc_nothrow                     PARAMS ((rtx));
 
 #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
@@ -1710,7 +1707,6 @@ expand_call (exp, target, ignore)
   rtx old_stack_level = 0;
   int old_pending_adj = 0;
   int old_inhibit_defer_pop = inhibit_defer_pop;
-  int old_arg_space_so_far = arg_space_so_far;
   rtx call_fusage = 0;
   register tree p;
   register int i;
@@ -2384,10 +2380,7 @@ expand_call (exp, target, ignore)
   /* 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));
-      arg_space_so_far += args_size.constant - unadjusted_args_size;
-    }
+    anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
 #endif
 #endif
 
@@ -2439,10 +2432,6 @@ expand_call (exp, target, ignore)
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
               valreg, old_inhibit_defer_pop, call_fusage, is_const, nothrow);
 
-  /* Stack pointer ought to be restored to the value before call.  */
-  if (old_arg_space_so_far != arg_space_so_far)
-    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
@@ -2704,21 +2693,38 @@ libfunc_nothrow (fun)
 
   return 1;
 }
-\f
-/* Emit 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, no_queue, outmode, nargs, p)
-     int retval;
-     rtx orgfun;
-     rtx value;
-     int no_queue;
-     enum machine_mode outmode;
-     int nargs;
-     va_list p;
+/* 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 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).  */
@@ -2734,18 +2740,9 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
               struct args_size offset; struct args_size size; rtx save_area; };
   struct arg *argvec;
   int old_inhibit_defer_pop = inhibit_defer_pop;
-  int old_arg_space_so_far = arg_space_so_far;
   rtx call_fusage = 0;
-  rtx mem_value = 0;
-  int pcc_struct_value = 0;
-  int struct_value_size = 0;
-  int is_const;
   int reg_parm_stack_space = 0;
   int nothrow;
-#ifdef ACCUMULATE_OUTGOING_ARGS
-  int needed;
-#endif
-
 #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
   /* Define the boundary of the register parm stack space that needs to be
      save, if any.  */
@@ -2754,12 +2751,13 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
 #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;
+  int needed;
 #endif
 
 #ifdef REG_PARM_STACK_SPACE
+  /* Size of the stack reserved for parameter registers.  */
 #ifdef MAYBE_REG_PARM_STACK_SPACE
   reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
 #else
@@ -2767,43 +2765,18 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
 #endif
 #endif
 
-  is_const = no_queue;
-  fun = orgfun;
-
-  nothrow = libfunc_nothrow (fun);
-
-#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
+  VA_START (p, nargs);
 
-  /* If this kind of value comes back in memory,
-     decide where in memory it should come back.  */
-  if (outmode != VOIDmode && aggregate_value_p (type_for_mode (outmode, 0)))
-    {
-#ifdef PCC_STATIC_STRUCT_RETURN
-      rtx pointer_reg
-       = hard_function_value (build_pointer_type (type_for_mode (outmode, 0)),
-                              0, 0);
-      mem_value = gen_rtx_MEM (outmode, pointer_reg);
-      pcc_struct_value = 1;
-      if (value == 0)
-       value = gen_reg_rtx (outmode);
-#else /* not PCC_STATIC_STRUCT_RETURN */
-      struct_value_size = GET_MODE_SIZE (outmode);
-      if (value != 0 && GET_CODE (value) == MEM)
-       mem_value = value;
-      else
-       mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0);
+#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
 
-      /* This call returns a big structure.  */
-      is_const = 0;
-    }
+  fun = orgfun;
 
-  /* ??? Unfinished: must pass the memory address as an argument.  */
+  nothrow = libfunc_nothrow (fun);
 
   /* Copy all the libcall-arguments out of the varargs data
      and into a vector ARGVEC.
@@ -2812,56 +2785,25 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
      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));
-  bzero ((char *) argvec, (nargs + 1) * sizeof (struct arg));
+  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;
 
-  count = 0;
-
   push_temp_slots ();
 
-  /* 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 && struct_value_rtx == 0 && ! pcc_struct_value)
-    {
-      rtx addr = XEXP (mem_value, 0);
-      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 = Pmode;
-      argvec[count].partial = 0;
-
-      argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
-#ifdef FUNCTION_ARG_PARTIAL_NREGS
-      if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, Pmode, NULL_TREE, 1))
-       abort ();
+#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
 
-      locate_and_pad_parm (Pmode, NULL_TREE,
-                          argvec[count].reg && argvec[count].partial == 0,
-                          NULL_TREE, &args_size, &argvec[count].offset,
-                          &argvec[count].size, &alignment_pad);
-
-
-      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, Pmode, (tree) 0, 1);
-
-      count++;
-    }
-
-  for (; count < nargs; count++)
+  for (count = 0; count < nargs; count++)
     {
       rtx val = va_arg (p, rtx);
       enum machine_mode mode = va_arg (p, enum machine_mode);
@@ -2928,25 +2870,21 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
 
       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,
                                                     args_size.var);
 #endif
+      
   /* If this machine requires an external definition for library
      functions, write one out.  */
   assemble_external_libcall (fun);
 
   original_args_size = args_size;
 #ifdef PREFERRED_STACK_BOUNDARY
-  args_size.constant = (((args_size.constant
-                         + arg_space_so_far
-                         + pending_stack_adjust
-                         + STACK_BYTES - 1)
-                        / STACK_BYTES
-                        * STACK_BYTES)
-                       - arg_space_so_far
-                       - pending_stack_adjust);
+  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+                        / STACK_BYTES) * STACK_BYTES);
 #endif
 
   args_size.constant = MAX (args_size.constant,
@@ -3016,11 +2954,8 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
   /* 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));
-      arg_space_so_far += args_size.constant - original_args_size.constant;
-    }
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
 #endif
 #endif
 
@@ -3143,15 +3078,14 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
                   (save_mode,
                    plus_constant (argblock,
                                   argvec[argnum].offset.constant)));
-             argvec[argnum].save_area = gen_reg_rtx (save_mode);
 
+             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));
-         arg_space_so_far += argvec[argnum].size.constant;
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
          /* Now mark the segment we just used.  */
@@ -3168,11 +3102,8 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
   /* 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));
-      arg_space_so_far += args_size.constant - unadjusted_args_size;
-    }
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
 #endif
 #endif
 
@@ -3206,79 +3137,45 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
       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++)
     {
       rtx reg = argvec[count].reg;
-      if (reg != 0 && GET_CODE (reg) == PARALLEL)
+      if (reg != 0 && GET_CODE (argvec[count].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)
-    {
-      emit_move_insn (struct_value_rtx,
-                     force_reg (Pmode,
-                                force_operand (XEXP (mem_value, 0),
-                                               NULL_RTX)));
-      if (GET_CODE (struct_value_rtx) == REG)
-         use_reg (&call_fusage, struct_value_rtx);
-    }
-
   /* 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.  */
+
   /* 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)),
+               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,
-              struct_value_size,
+              original_args_size.constant, args_size.constant, 0,
               FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
-              (mem_value == 0 && outmode != VOIDmode
-               ? hard_libcall_value (outmode) : NULL_RTX),
-              old_inhibit_defer_pop + 1, call_fusage, is_const, nothrow);
-
-  /* Now restore inhibit_defer_pop to its actual original value.  */
-  OK_DEFER_POP;
+              outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
+              old_inhibit_defer_pop + 1, call_fusage, no_queue, nothrow);
 
   pop_temp_slots ();
 
-  /* Stack pointer ought to be restored to the value before call.  */
-  if (old_arg_space_so_far != arg_space_so_far)
-    abort();
-
-  /* Copy the value to the right place.  */
-  if (outmode != VOIDmode && retval)
-    {
-      if (mem_value)
-       {
-         if (value == 0)
-           value = mem_value;
-         if (value != mem_value)
-           emit_move_insn (value, mem_value);
-       }
-      else if (value != 0)
-       emit_move_insn (value, hard_libcall_value (outmode));
-      else
-       value = hard_libcall_value (outmode);
-    }
+  /* Now restore inhibit_defer_pop to its actual original value.  */
+  OK_DEFER_POP;
 
 #ifdef ACCUMULATE_OUTGOING_ARGS
 #ifdef REG_PARM_STACK_SPACE
@@ -3290,19 +3187,20 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
        = gen_rtx_MEM (save_mode,
                       memory_address (save_mode,
                                       plus_constant (argblock,
-                                                     - high_to_save)));
+                                                      - high_to_save)));
 #else
       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);
+                        PARM_BOUNDARY / BITS_PER_UNIT);
     }
 #endif
          
@@ -3324,55 +3222,6 @@ emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
   highest_outgoing_arg_in_use = initial_highest_arg_in_use;
   stack_usage_map = initial_stack_usage_map;
 #endif
-
-  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.
-
-   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 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;
-
-  VA_START (p, nargs);
-
-#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
-
-  emit_library_call_value_1 (0, orgfun, NULL_RTX, no_queue, outmode, nargs, p);
-
-  va_end (p);
 }
 \f
 /* Like emit_library_call except that an extra argument, VALUE,
@@ -3395,20 +3244,600 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
   int nargs;
 #endif
   va_list p;
-
-  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
-
-  value = emit_library_call_value_1 (1, orgfun, value, no_queue, outmode, nargs, p);
-
-  va_end (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;
+  rtx mem_value = 0;
+  int pcc_struct_value = 0;
+  int struct_value_size = 0;
+  int is_const;
+  int reg_parm_stack_space = 0;
+  int nothrow;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+  int needed;
+#endif
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+  /* Define the boundary of the register parm stack space that needs to be
+     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
+  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+  reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
+#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;
+  fun = orgfun;
+
+  nothrow = libfunc_nothrow (fun);
+
+#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
+
+  /* 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)))
+    {
+#ifdef PCC_STATIC_STRUCT_RETURN
+      rtx pointer_reg
+       = hard_function_value (build_pointer_type (type_for_mode (outmode, 0)),
+                              0, 0);
+      mem_value = gen_rtx_MEM (outmode, pointer_reg);
+      pcc_struct_value = 1;
+      if (value == 0)
+       value = gen_reg_rtx (outmode);
+#else /* not PCC_STATIC_STRUCT_RETURN */
+      struct_value_size = GET_MODE_SIZE (outmode);
+      if (value != 0 && GET_CODE (value) == MEM)
+       mem_value = value;
+      else
+       mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0);
+#endif
+
+      /* This call returns a big structure.  */
+      is_const = 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));
+  bzero ((char *) argvec, (nargs + 1) * sizeof (struct arg));
+
+  INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
+
+  args_size.constant = 0;
+  args_size.var = 0;
+
+  count = 0;
+
+  push_temp_slots ();
+
+  /* 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 && struct_value_rtx == 0 && ! pcc_struct_value)
+    {
+      rtx addr = XEXP (mem_value, 0);
+      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 = Pmode;
+      argvec[count].partial = 0;
+
+      argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+      if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, Pmode, NULL_TREE, 1))
+       abort ();
+#endif
+
+      locate_and_pad_parm (Pmode, NULL_TREE,
+                          argvec[count].reg && argvec[count].partial == 0,
+                          NULL_TREE, &args_size, &argvec[count].offset,
+                          &argvec[count].size, &alignment_pad);
+
+
+      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, Pmode, (tree) 0, 1);
+
+      count++;
+    }
+
+  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_modes (DFmode, SFmode, 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);
+
+#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 = XEXP (slot, 0);
+         mode = Pmode;
+       }
+#endif
+
+      argvec[count].value = val;
+      argvec[count].mode = mode;
+
+      argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+
+#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, &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);
+
+#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);
+
+  original_args_size = args_size;
+#ifdef PREFERRED_STACK_BOUNDARY
+  args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+                        / STACK_BYTES) * STACK_BYTES);
+#endif
+
+  args_size.constant = MAX (args_size.constant,
+                           reg_parm_stack_space);
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+  args_size.constant -= reg_parm_stack_space;
+#endif
+
+  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.
+
+     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.  */
+
+  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;
+#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);
+#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;
+
+  /* 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
+
+#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
+
+#ifdef PUSH_ARGS_REVERSED
+  inc = -1;
+  argnum = nargs - 1;
+#else
+  inc = 1;
+  argnum = 0;
+#endif
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+  /* The argument list is the property of the called routine and it
+     may clobber it.  If the fixed area has been used for previous
+     parameters, we must save and restore it.
+
+     Here we compute the boundary of the that needs to be saved, if any.  */
+
+#ifdef ARGS_GROW_DOWNWARD
+  for (count = 0; count < reg_parm_stack_space + 1; count++)
+#else
+  for (count = 0; count < reg_parm_stack_space; count++)
+#endif
+    {
+      if (count >=  highest_outgoing_arg_in_use
+         || stack_usage_map[count] == 0)
+       continue;
+
+      if (low_to_save == -1)
+       low_to_save = count;
+
+      high_to_save = count;
+    }
+
+  if (low_to_save >= 0)
+    {
+      int num_to_save = high_to_save - low_to_save + 1;
+      enum machine_mode save_mode
+       = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+      rtx stack_area;
+
+      /* If we don't have the required alignment, must do this in BLKmode.  */
+      if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+                              BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+       save_mode = BLKmode;
+
+#ifdef ARGS_GROW_DOWNWARD
+      stack_area = gen_rtx_MEM (save_mode,
+                               memory_address (save_mode,
+                                               plus_constant (argblock,
+                                                              - high_to_save)));
+#else
+      stack_area = gen_rtx_MEM (save_mode,
+                               memory_address (save_mode,
+                                               plus_constant (argblock,
+                                                              low_to_save)));
+#endif
+      if (save_mode == BLKmode)
+       {
+         save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+         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);
+       }
+    }
+#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))
+       {
+#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 (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
+
+         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)
+    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
+
+  fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
+
+  /* Now load any reg parms into their regs.  */
+
+  /* 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;
+
+      /* 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++)
+    {
+      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)
+    {
+      emit_move_insn (struct_value_rtx,
+                     force_reg (Pmode,
+                                force_operand (XEXP (mem_value, 0),
+                                               NULL_RTX)));
+      if (GET_CODE (struct_value_rtx) == REG)
+         use_reg (&call_fusage, struct_value_rtx);
+    }
+
+  /* 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.  */
+  /* See the comment in emit_library_call about the function type we build
+     and pass here.  */
+
+  emit_call_1 (fun, 
+               get_identifier (XSTR (orgfun, 0)),
+              build_function_type (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, nothrow);
+
+  /* Now restore inhibit_defer_pop to its actual original value.  */
+  OK_DEFER_POP;
+
+  pop_temp_slots ();
+
+  /* Copy the value to the right place.  */
+  if (outmode != VOIDmode)
+    {
+      if (mem_value)
+       {
+         if (value == 0)
+           value = mem_value;
+         if (value != mem_value)
+           emit_move_insn (value, mem_value);
+       }
+      else if (value != 0)
+       emit_move_insn (value, hard_libcall_value (outmode));
+      else
+       value = hard_libcall_value (outmode);
+    }
+
+#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 (save_mode != BLKmode)
+       emit_move_insn (stack_area, save_area);
+      else
+       emit_block_move (stack_area, validize_mem (save_area),
+                        GEN_INT (high_to_save - low_to_save + 1),
+                            PARM_BOUNDARY / BITS_PER_UNIT);
+    }
+#endif
+         
+  /* If we saved any argument areas, restore them.  */
+  for (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
 
   return value;
 }