OSDN Git Service

* config/mips/abi64.h (SETUP_INCOMING_VARARGS): Undefine.
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 22 Mar 2002 09:55:03 +0000 (09:55 +0000)
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 22 Mar 2002 09:55:03 +0000 (09:55 +0000)
* config/mips/mips-protos.h (mips_setup_incoming_varargs): Declare.
(function_arg): Constify CUMULATIVE_ARGS.
(function_arg_partial_nregs, function_arg_pass_by_reference): Likewise.
* config/mips/mips.h (UNITS_PER_FPVALUE): Zero when TARGET_SOFT_FLOAT.
(UNITS_PER_DOUBLE): New macro.
(SETUP_INCOMING_VARARGS): Define.  Use mips_setup_incoming_varargs.
(CUMULATIVE_ARGS): Reformat.  Remove num_adjusts workaround and
last_arg_fp field.  Replace arg_words and fp_arg_words with gp_regs,
fp_regs and stack_words.
(EABI_FLOAT_VARARGS_P): New macro.
* config/mips/mips.c (struct mips_arg_info): New.
(mips_arg_info): New function.
(function_arg_advance): Use it.  Add adjustment instructions here
rather than in function_arg.
(function_arg): Constify CUMULATIVE_ARGS.  Use mips_arg_info.  Check
for VOIDmode at the beginning of the function.
(function_partial_nregs): Constify CUMULATIVE_ARGS.  Use mips_arg_info.
(function_arg_pass_by_reference): Likewise.
(mips_setup_incoming_varags): New, largely based on old abi64.h code.
(mips_build_va_list): Test EABI_FLOAT_VARARGS_P.
(mips_va_start): Likewise.  Use the new stack_words field of
CUMULATIVE_ARGS to set up overflow area.  Reformat.
(mips_va_arg): Test EABI_FLOAT_VARARGS_P.  Unify EABI handling of
doubles and other types, aligning the overflow pointer for non-doubles
too.  Remove some code duplication.  Replace hard-coded constants.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@51167 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/mips/abi64.h
gcc/config/mips/mips-protos.h
gcc/config/mips/mips.c
gcc/config/mips/mips.h

index 74fe74a..eadd607 100644 (file)
@@ -1,5 +1,34 @@
 2002-03-22  Richard Sandiford  <rsandifo@redhat.com>
 
+       * config/mips/abi64.h (SETUP_INCOMING_VARARGS): Undefine.
+       * config/mips/mips-protos.h (mips_setup_incoming_varargs): Declare.
+       (function_arg): Constify CUMULATIVE_ARGS.
+       (function_arg_partial_nregs, function_arg_pass_by_reference): Likewise.
+       * config/mips/mips.h (UNITS_PER_FPVALUE): Zero when TARGET_SOFT_FLOAT.
+       (UNITS_PER_DOUBLE): New macro.
+       (SETUP_INCOMING_VARARGS): Define.  Use mips_setup_incoming_varargs.
+       (CUMULATIVE_ARGS): Reformat.  Remove num_adjusts workaround and
+       last_arg_fp field.  Replace arg_words and fp_arg_words with gp_regs,
+       fp_regs and stack_words.
+       (EABI_FLOAT_VARARGS_P): New macro.
+       * config/mips/mips.c (struct mips_arg_info): New.
+       (mips_arg_info): New function.
+       (function_arg_advance): Use it.  Add adjustment instructions here
+       rather than in function_arg.
+       (function_arg): Constify CUMULATIVE_ARGS.  Use mips_arg_info.  Check
+       for VOIDmode at the beginning of the function.
+       (function_partial_nregs): Constify CUMULATIVE_ARGS.  Use mips_arg_info.
+       (function_arg_pass_by_reference): Likewise.
+       (mips_setup_incoming_varags): New, largely based on old abi64.h code.
+       (mips_build_va_list): Test EABI_FLOAT_VARARGS_P.
+       (mips_va_start): Likewise.  Use the new stack_words field of
+       CUMULATIVE_ARGS to set up overflow area.  Reformat.
+       (mips_va_arg): Test EABI_FLOAT_VARARGS_P.  Unify EABI handling of
+       doubles and other types, aligning the overflow pointer for non-doubles
+       too.  Remove some code duplication.  Replace hard-coded constants.
+
+2002-03-22  Richard Sandiford  <rsandifo@redhat.com>
+
        * config/mips/mips.h (FUNCTION_ARG_REGNO_P): Simplify.
        (CLASS_UNITS): Undefine.
        (CLASS_MAX_NREGS): Use FP_INC.
index 42ea208..5ba856f 100644 (file)
@@ -102,96 +102,6 @@ Boston, MA 02111-1307, USA.  */
 #undef FUNCTION_VALUE
 #define FUNCTION_VALUE(VALTYPE, FUNC)  mips_function_value (VALTYPE, FUNC)
 
-/* For varargs, we must save the current argument, because it is the fake
-   argument va_alist, and will need to be converted to the real argument.
-   For stdarg, we do not need to save the current argument, because it
-   is a real argument.  */
-#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL)      \
-{ unsigned int mips_off                                                        \
-    = (! current_function_varargs) && (! (CUM).last_arg_fp);           \
-    unsigned int mips_fp_off                                           \
-    = (! current_function_varargs) && ((CUM).last_arg_fp);             \
-  if (((mips_abi != ABI_32 && mips_abi != ABI_O64)                     \
-       && (CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off)          \
-      || (mips_abi == ABI_EABI                                         \
-         && ! TARGET_SOFT_FLOAT                                        \
-         && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off)) \
-    {                                                                  \
-      int mips_save_gp_regs                                            \
-        = MAX_ARGS_IN_REGISTERS - (CUM).arg_words - mips_off;          \
-      int mips_save_fp_regs                                            \
-        = (mips_abi != ABI_EABI ? 0                                    \
-          : MAX_ARGS_IN_REGISTERS - (CUM).fp_arg_words - mips_fp_off); \
-                                                                       \
-      if (mips_save_gp_regs < 0)                                       \
-       mips_save_gp_regs = 0;                                          \
-      if (mips_save_fp_regs < 0)                                       \
-       mips_save_fp_regs = 0;                                          \
-      PRETEND_SIZE = ((mips_save_gp_regs * UNITS_PER_WORD)             \
-                     + (mips_save_fp_regs * UNITS_PER_FPREG));         \
-                                                                       \
-      if (! (NO_RTL))                                                  \
-       {                                                               \
-         if ((CUM).arg_words < MAX_ARGS_IN_REGISTERS - mips_off)       \
-           {                                                           \
-             rtx ptr, mem;                                             \
-             if (mips_abi != ABI_EABI)                                 \
-               ptr = virtual_incoming_args_rtx;                        \
-             else                                                      \
-               ptr = plus_constant (virtual_incoming_args_rtx,         \
-                                    - (mips_save_gp_regs               \
-                                       * UNITS_PER_WORD));             \
-             mem = gen_rtx_MEM (BLKmode, ptr);                 \
-             /* va_arg is an array access in this case, which causes   \
-                it to get MEM_IN_STRUCT_P set.  We must set it here    \
-                so that the insn scheduler won't assume that these     \
-                stores can't possibly overlap with the va_arg loads.  */ \
-             if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN)             \
-               MEM_SET_IN_STRUCT_P (mem, 1);                           \
-             move_block_from_reg                                       \
-               ((CUM).arg_words + GP_ARG_FIRST + mips_off,             \
-                mem,                                                   \
-                mips_save_gp_regs,                                     \
-                mips_save_gp_regs * UNITS_PER_WORD);                   \
-           }                                                           \
-         if (mips_abi == ABI_EABI                                      \
-             && ! TARGET_SOFT_FLOAT                                    \
-             && (CUM).fp_arg_words < MAX_ARGS_IN_REGISTERS - mips_fp_off) \
-           {                                                           \
-             enum machine_mode mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode; \
-             int size = GET_MODE_SIZE (mode);                          \
-             int off;                                                  \
-             int i;                                                    \
-             /* We can't use move_block_from_reg, because it will use  \
-                 the wrong mode.  */                                   \
-             off = - (mips_save_gp_regs * UNITS_PER_WORD);             \
-             if (! TARGET_SINGLE_FLOAT)                                \
-               off &= ~ 7;                                             \
-             if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)              \
-               off -= (mips_save_fp_regs / 2) * size;                  \
-             else                                                      \
-               off -= mips_save_fp_regs * size;                        \
-             for (i = 0; i < mips_save_fp_regs; i++)                   \
-               {                                                       \
-                 rtx tem =                                             \
-                   gen_rtx_MEM (mode,                                  \
-                                plus_constant (virtual_incoming_args_rtx, \
-                                               off));                  \
-                 emit_move_insn (tem,                                  \
-                                 gen_rtx_REG (mode,                    \
-                                              ((CUM).fp_arg_words      \
-                                               + FP_ARG_FIRST          \
-                                               + i                     \
-                                               + mips_fp_off)));       \
-                 off += size;                                          \
-                 if (! TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)          \
-                   ++i;                                                \
-               }                                                       \
-           }                                                           \
-       }                                                               \
-    }                                                                  \
-}
-
 #define STRICT_ARGUMENT_NAMING (mips_abi != ABI_32 && mips_abi != ABI_O64)
 
 /* A C expression that indicates when an argument must be passed by
index 8c4cec2..8332782 100644 (file)
@@ -56,16 +56,21 @@ extern unsigned int mips_hard_regno_nregs PARAMS ((int,
                                                       enum machine_mode));
 extern int              mips_return_in_memory PARAMS ((tree));
 
-extern struct rtx_def  *function_arg PARAMS ((CUMULATIVE_ARGS *,
+extern struct rtx_def  *function_arg PARAMS ((const CUMULATIVE_ARGS *,
                                              enum machine_mode, tree, int));
 extern void            function_arg_advance PARAMS ((CUMULATIVE_ARGS *,
                                                      enum machine_mode,
                                                      tree, int));
-extern int             function_arg_partial_nregs PARAMS ((CUMULATIVE_ARGS *,
-                                                           enum machine_mode,
-                                                           tree, int));
+extern int             function_arg_partial_nregs
+                               PARAMS ((const CUMULATIVE_ARGS *,
+                                        enum machine_mode,
+                                        tree, int));
+extern int             mips_setup_incoming_varargs
+                               PARAMS ((const CUMULATIVE_ARGS *,
+                                        enum machine_mode,
+                                        tree, int));
 extern int             function_arg_pass_by_reference
-                               PARAMS ((CUMULATIVE_ARGS *,
+                               PARAMS ((const CUMULATIVE_ARGS *,
                                         enum machine_mode, tree, int));
 extern int             mips16_constant_after_function_p PARAMS ((tree));
 extern int             mips_output_external PARAMS ((FILE *, tree,
index 535012c..f5bf777 100644 (file)
@@ -86,6 +86,7 @@ enum internal_test {
 
 
 struct constant;
+struct mips_arg_info;
 static enum internal_test map_test_to_internal_test    PARAMS ((enum rtx_code));
 static int mips16_simple_memory_operand                PARAMS ((rtx, rtx,
                                                        enum machine_mode));
@@ -95,6 +96,10 @@ static void block_move_loop                  PARAMS ((rtx, rtx,
                                                         int,
                                                         rtx, rtx));
 static void block_move_call                    PARAMS ((rtx, rtx, rtx));
+static void mips_arg_info              PARAMS ((const CUMULATIVE_ARGS *,
+                                                enum machine_mode,
+                                                tree, int,
+                                                struct mips_arg_info *));
 static rtx mips_add_large_offset_to_sp         PARAMS ((HOST_WIDE_INT,
                                                         FILE *));
 static void mips_annotate_frame_insn           PARAMS ((rtx, rtx));
@@ -150,6 +155,34 @@ struct machine_function {
   rtx mips16_gp_pseudo_rtx;
 };
 
+/* Information about a single argument.  */
+struct mips_arg_info
+{
+  /* True if the argument is a record or union type.  */
+  bool struct_p;
+
+  /* True if the argument is passed in a floating-point register, or
+     would have been if we hadn't run out of registers.  */
+  bool fpr_p;
+
+  /* The argument's size, in bytes.  */
+  unsigned int num_bytes;
+
+  /* The number of words passed in registers, rounded up.  */
+  unsigned int reg_words;
+
+  /* The offset of the first register from GP_ARG_FIRST or FP_ARG_FIRST,
+     or MAX_ARGS_IN_REGISTERS if the argument is passed entirely
+     on the stack.  */
+  unsigned int reg_offset;
+
+  /* The number of words that must be passed on the stack, rounded up.  */
+  unsigned int stack_words;
+
+  /* The offset from the start of the stack overflow area of the argument's
+     first stack word.  Only meaningful when STACK_WORDS is non-zero.  */
+  unsigned int stack_offset;
+};
 
 /* Global variables for machine-dependent things.  */
 
@@ -3918,6 +3951,103 @@ init_cumulative_args (cum, fntype, libname)
     }
 }
 
+static void
+mips_arg_info (cum, mode, type, named, info)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+     struct mips_arg_info *info;
+{
+  bool even_reg_p;
+  unsigned int num_words, max_regs;
+
+  info->struct_p = (type != 0
+                   && (TREE_CODE (type) == RECORD_TYPE
+                       || TREE_CODE (type) == UNION_TYPE
+                       || TREE_CODE (type) == QUAL_UNION_TYPE));
+
+  /* Decide whether this argument should go in a floating-point register,
+     assuming one is free.  Later code checks for availablity.  */
+
+  info->fpr_p = false;
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE)
+    {
+      switch (mips_abi)
+       {
+       case ABI_32:
+       case ABI_O64:
+         info->fpr_p = (!cum->gp_reg_found && cum->arg_number < 2);
+         break;
+
+       case ABI_EABI:
+         info->fpr_p = true;
+         break;
+
+       case ABI_MEABI:
+         /* The MIPS eabi says only structures containing doubles get
+            passed in a fp register, so force a structure containing
+            a float to be passed in the integer registers.  */
+         info->fpr_p = (named && !(mode == SFmode && info->struct_p));
+         break;
+
+       default:
+         info->fpr_p = named;
+         break;
+       }
+    }
+
+  /* Now decide whether the argument must go in an even-numbered register.  */
+
+  even_reg_p = false;
+  if (info->fpr_p)
+    {
+      /* Under the O64 ABI, the second float argument goes in $f13 if it
+        is a double, but $f14 if it is a single.  Otherwise, on a
+        32-bit double-float machine, each FP argument must start in a
+        new register pair.  */
+      even_reg_p = ((mips_abi == ABI_O64 && mode == SFmode) || FP_INC > 1);
+    }
+  else if (!TARGET_64BIT)
+    {
+      if (GET_MODE_CLASS (mode) == MODE_INT
+         || GET_MODE_CLASS (mode) == MODE_FLOAT)
+       even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
+
+      else if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD)
+       even_reg_p = true;
+    }
+
+  /* Set REG_OFFSET to the register count we're interested in.
+     The EABI allocates the floating-point registers separately,
+     but the other ABIs allocate them like integer registers.  */
+  info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
+                     ? cum->fp_regs
+                     : cum->gp_regs);
+
+  if (even_reg_p)
+    info->reg_offset += info->reg_offset & 1;
+
+  /* The alignment applied to registers is also applied to stack arguments.  */
+  info->stack_offset = cum->stack_words;
+  if (even_reg_p)
+    info->stack_offset += info->stack_offset & 1;
+
+  if (mode == BLKmode)
+    info->num_bytes = int_size_in_bytes (type);
+  else
+    info->num_bytes = GET_MODE_SIZE (mode);
+
+  num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+
+  /* Partition the argument between registers and stack.  */
+  info->reg_words = MIN (num_words, max_regs);
+  info->stack_words = num_words - info->reg_words;
+}
+
+
 /* Advance the argument to the next argument position.  */
 
 void
@@ -3927,69 +4057,59 @@ function_arg_advance (cum, mode, type, named)
      tree type;                        /* type of the argument or 0 if lib support */
      int named;                        /* whether or not the argument was named */
 {
-  if (TARGET_DEBUG_E_MODE)
+  struct mips_arg_info info;
+
+  mips_arg_info (cum, mode, type, named, &info);
+
+  /* The following is a hack in order to pass 1 byte structures
+     the same way that the MIPS compiler does (namely by passing
+     the structure in the high byte or half word of the register).
+     This also makes varargs work.  If we have such a structure,
+     we save the adjustment RTL, and the call define expands will
+     emit them.  For the VOIDmode argument (argument after the
+     last real argument), pass back a parallel vector holding each
+     of the adjustments.  */
+
+  /* ??? This scheme requires everything smaller than the word size to
+     shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
+     that would mean every int needs to be shifted left, which is very
+     inefficient.  Let's not carry this compatibility to the 64 bit
+     calling convention for now.  */
+
+  if (info.struct_p
+      && info.reg_words == 1
+      && info.num_bytes < UNITS_PER_WORD
+      && !TARGET_64BIT
+      && mips_abi != ABI_EABI
+      && mips_abi != ABI_MEABI)
     {
-      fprintf (stderr,
-              "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d )\n\n", named);
-    }
+      rtx amount = GEN_INT (BITS_PER_WORD - info.num_bytes * BITS_PER_UNIT);
+      rtx reg = gen_rtx_REG (word_mode, GP_ARG_FIRST + info.reg_offset);
 
-  cum->arg_number++;
-  switch (mode)
-    {
-    case VOIDmode:
-      break;
-
-    default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
-
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+      if (TARGET_64BIT)
+       cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
+      else
+       cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
+    }
 
-    case BLKmode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+  if (!info.fpr_p)
+    cum->gp_reg_found = true;
 
-    case SFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       cum->fp_arg_words++;
-      else
-       cum->arg_words++;
-      if (! cum->gp_reg_found && cum->arg_number <= 2)
-       cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
-      break;
+  /* See the comment above the cumulative args structure in mips.h
+     for an explanation of what this code does.  It assumes the O32
+     ABI, which passes at most 2 arguments in float registers.  */
+  if (cum->arg_number < 2 && info.fpr_p)
+    cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
 
-    case DFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
-      else
-       cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2)
-       cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
-      break;
+  if (mips_abi != ABI_EABI || !info.fpr_p)
+    cum->gp_regs = info.reg_offset + info.reg_words;
+  else if (info.reg_words > 0)
+    cum->fp_regs += FP_INC;
 
-    case DImode:
-    case TImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      break;
+  if (info.stack_words > 0)
+    cum->stack_words = info.stack_offset + info.stack_words;
 
-    case QImode:
-    case HImode:
-    case SImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words++;
-      break;
-    }
+  cum->arg_number++;
 }
 
 /* Return an RTL expression containing the register for the given mode,
@@ -3997,329 +4117,222 @@ function_arg_advance (cum, mode, type, named)
 
 struct rtx_def *
 function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
+     const CUMULATIVE_ARGS *cum; /* current arg information */
      enum machine_mode mode;   /* current arg mode */
      tree type;                        /* type of the argument or 0 if lib support */
      int named;                        /* != 0 for normal args, == 0 for ... args */
 {
-  rtx ret;
-  int regbase = -1;
-  int bias = 0;
-  unsigned int *arg_words = &cum->arg_words;
-  int struct_p = (type != 0
-                 && (TREE_CODE (type) == RECORD_TYPE
-                     || TREE_CODE (type) == UNION_TYPE
-                     || TREE_CODE (type) == QUAL_UNION_TYPE));
-
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d ) = ", named);
-    }
-
+  struct mips_arg_info info;
 
-  cum->last_arg_fp = 0;
-  switch (mode)
+  /* We will be called with a mode of VOIDmode after the last argument
+     has been seen.  Whatever we return will be passed to the call
+     insn.  If we need any shifts for small structures, return them in
+     a PARALLEL; in that case, stuff the mips16 fp_code in as the
+     mode.  Otherwise, if we need a mips16 fp_code, return a REG
+     with the code stored as the mode.  */
+  if (mode == VOIDmode)
     {
-    case SFmode:
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       {
-         if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
-           regbase = GP_ARG_FIRST;
-         else
-           {
-             regbase = FP_ARG_FIRST;
-
-             /* If the first arg was a float in a floating point register,
-                then set bias to align this float arg properly.  */
-             if (cum->arg_words == 1)
-               bias = 1;
-           }
-       }
-      else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       {
-         if (! TARGET_64BIT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
-      /* The MIPS eabi says only structures containing doubles get passed in a
-         fp register, so force a structure containing a float to be passed in
-         the integer registers.  */
-      else if (mips_abi == ABI_MEABI && struct_p)
-       regbase = GP_ARG_FIRST;
-      else
-       regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
+      if (cum->num_adjusts > 0)
+       return gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
+                                gen_rtvec_v (cum->num_adjusts,
+                                             (rtx *) cum->adjust));
 
-    case DFmode:
-      if (! TARGET_64BIT)
-       {
-         if (mips_abi == ABI_EABI
-             && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         else
-           cum->arg_words += cum->arg_words & 1;
-       }
+      else if (TARGET_MIPS16 && cum->fp_code != 0)
+       return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
 
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       regbase = ((cum->gp_reg_found
-                   || TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT
-                   || cum->arg_number >= 2)
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      else if (mips_abi == ABI_EABI
-              && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       {
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
       else
-       regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
-
-    default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
-
-      /* Drops through.  */
-    case BLKmode:
-      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD
-         && ! TARGET_64BIT && mips_abi != ABI_EABI)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
-      break;
-
-    case VOIDmode:
-    case QImode:
-    case HImode:
-    case SImode:
-      regbase = GP_ARG_FIRST;
-      break;
-
-    case DImode:
-    case TImode:
-      if (! TARGET_64BIT)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
+       return 0;
     }
 
-  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
-    {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
+  mips_arg_info (cum, mode, type, named, &info);
 
-      ret = 0;
-    }
-  else
-    {
-      if (regbase == -1)
-       abort ();
+  /* Return straight away if the whole argument is passed on the stack.  */
+  if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+    return 0;
 
-      if (! type || TREE_CODE (type) != RECORD_TYPE
-         || mips_abi == ABI_32  || mips_abi == ABI_EABI
-         || mips_abi == ABI_O64 || mips_abi == ABI_MEABI
-         || ! named
-         || ! TYPE_SIZE_UNIT (type)
-         || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
-       {
+  if (type != 0
+      && TREE_CODE (type) == RECORD_TYPE
+      && (mips_abi == ABI_N32 || mips_abi == ABI_64)
+      && TYPE_SIZE_UNIT (type)
+      && host_integerp (TYPE_SIZE_UNIT (type), 1)
+      && named
+      && mode != DFmode)
+    {
+      /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
+        structure contains a double in its entirety, then that 64 bit
+        chunk is passed in a floating point register.  */
+      tree field;
+
+      /* First check to see if there is any such field.  */
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+           && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+           && host_integerp (bit_position (field), 0)
+           && int_bit_position (field) % BITS_PER_WORD == 0)
+         break;
 
-         unsigned int arg_reg = (regbase + *arg_words + bias);
-         ret = gen_rtx_REG (mode, arg_reg);
-         if (mips_abi == ABI_MEABI
-             && regbase == FP_ARG_FIRST
-             && ! cum->prototype)
-           {
-              /* To make K&R varargs work we need to pass floating
-                 point arguments in both integer and FP registers.  */
-              ret = gen_rtx_PARALLEL (mode,
-                                     gen_rtvec (2,
-                                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                                   gen_rtx_REG (mode,
-                                                                                arg_reg + GP_ARG_FIRST - FP_ARG_FIRST),
-                                                                   const0_rtx),                                                gen_rtx_EXPR_LIST (VOIDmode, ret, const0_rtx)));
-            }
-       }
-      else
+      if (field != 0)
        {
-         /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
-            structure contains a double in its entirety, then that 64 bit
-            chunk is passed in a floating point register.  */
-         tree field;
-
-         /* First check to see if there is any such field.  */
-         for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
-           if (TREE_CODE (field) == FIELD_DECL
-               && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-               && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
-               && host_integerp (bit_position (field), 0)
-               && int_bit_position (field) % BITS_PER_WORD == 0)
-             break;
-
-         /* If the whole struct fits a DFmode register,
-            we don't need the PARALLEL.  */
-         if (! field || mode == DFmode)
-           ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
-         else
-           {
-             /* Now handle the special case by returning a PARALLEL
-                indicating where each 64 bit chunk goes.  */
-             unsigned int chunks;
-             HOST_WIDE_INT bitpos;
-             unsigned int regno;
-             unsigned int i;
-
-             /* ??? If this is a packed structure, then the last hunk won't
-                be 64 bits.  */
-
-             chunks
-               = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
-             if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
-               chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
-
-             /* assign_parms checks the mode of ENTRY_PARM, so we must
-                use the actual mode here.  */
-             ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
-
-             bitpos = 0;
-             regno = regbase + *arg_words + bias;
-             field = TYPE_FIELDS (type);
-             for (i = 0; i < chunks; i++)
-               {
-                 rtx reg;
-
-                 for (; field; field = TREE_CHAIN (field))
-                   if (TREE_CODE (field) == FIELD_DECL
-                       && int_bit_position (field) >= bitpos)
-                     break;
-
-                 if (field
-                     && int_bit_position (field) == bitpos
-                     && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-                     && !TARGET_SOFT_FLOAT
-                     && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
-                   reg = gen_rtx_REG (DFmode,
-                                      regno + FP_ARG_FIRST - GP_ARG_FIRST);
-                 else
-                   reg = gen_rtx_REG (word_mode, regno);
-
-                 XVECEXP (ret, 0, i)
-                   = gen_rtx_EXPR_LIST (VOIDmode, reg,
-                                        GEN_INT (bitpos / BITS_PER_UNIT));
+         /* Now handle the special case by returning a PARALLEL
+            indicating where each 64 bit chunk goes.  INFO.REG_WORDS
+            chunks are passed in registers.  */
+         unsigned int i;
+         HOST_WIDE_INT bitpos;
+         rtx ret;
 
-                 bitpos += 64;
-                 regno++;
-               }
-           }
-       }
-
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
-                struct_p ? ", [struct]" : "");
+         /* assign_parms checks the mode of ENTRY_PARM, so we must
+            use the actual mode here.  */
+         ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
 
-      /* The following is a hack in order to pass 1 byte structures
-        the same way that the MIPS compiler does (namely by passing
-        the structure in the high byte or half word of the register).
-        This also makes varargs work.  If we have such a structure,
-        we save the adjustment RTL, and the call define expands will
-        emit them.  For the VOIDmode argument (argument after the
-        last real argument), pass back a parallel vector holding each
-        of the adjustments.  */
+         bitpos = 0;
+         field = TYPE_FIELDS (type);
+         for (i = 0; i < info.reg_words; i++)
+           {
+             rtx reg;
 
-      /* ??? function_arg can be called more than once for each argument.
-        As a result, we compute more adjustments than we need here.
-        See the CUMULATIVE_ARGS definition in mips.h.  */
+             for (; field; field = TREE_CHAIN (field))
+               if (TREE_CODE (field) == FIELD_DECL
+                   && int_bit_position (field) >= bitpos)
+                 break;
 
-      /* ??? This scheme requires everything smaller than the word size to
-        shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
-        that would mean every int needs to be shifted left, which is very
-        inefficient.  Let's not carry this compatibility to the 64 bit
-        calling convention for now.  */
+             if (field
+                 && int_bit_position (field) == bitpos
+                 && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                 && !TARGET_SOFT_FLOAT
+                 && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+               reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+             else
+               reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
 
-      if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
-         && ! TARGET_64BIT
-         && mips_abi != ABI_EABI
-         && mips_abi != ABI_MEABI)
-       {
-         rtx amount = GEN_INT (BITS_PER_WORD
-                               - int_size_in_bytes (type) * BITS_PER_UNIT);
-         rtx reg = gen_rtx_REG (word_mode, regbase + *arg_words + bias);
+             XVECEXP (ret, 0, i)
+               = gen_rtx_EXPR_LIST (VOIDmode, reg,
+                                    GEN_INT (bitpos / BITS_PER_UNIT));
 
-         if (TARGET_64BIT)
-           cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
-         else
-           cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
+             bitpos += BITS_PER_WORD;
+           }
+         return ret;
        }
     }
 
-  /* We will be called with a mode of VOIDmode after the last argument
-     has been seen.  Whatever we return will be passed to the call
-     insn.  If we need any shifts for small structures, return them in
-     a PARALLEL; in that case, stuff the mips16 fp_code in as the
-     mode.  Otherwise, if we have need a mips16 fp_code, return a REG
-     with the code stored as the mode.  */
-  if (mode == VOIDmode)
+  if (mips_abi == ABI_MEABI && info.fpr_p && !cum->prototype)
     {
-      if (cum->num_adjusts > 0)
-       ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
-                      gen_rtvec_v (cum->num_adjusts, cum->adjust));
-      else if (TARGET_MIPS16 && cum->fp_code != 0)
-       ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
+      /* To make K&R varargs work we need to pass floating
+        point arguments in both integer and FP registers.  */
+      return gen_rtx_PARALLEL
+       (mode,
+        gen_rtvec (2,
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (mode,
+                                                   GP_ARG_FIRST
+                                                   + info.reg_offset),
+                                      const0_rtx),
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (mode,
+                                                   FP_ARG_FIRST
+                                                   + info.reg_offset),
+                                      const0_rtx)));
     }
 
-  return ret;
+  if (info.fpr_p)
+    return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
+  else
+    return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
 }
 
 int
 function_arg_partial_nregs (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
+     const CUMULATIVE_ARGS *cum; /* current arg information */
      enum machine_mode mode;   /* current arg mode */
      tree type;                        /* type of the argument or 0 if lib support */
-     int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
-{
-  if ((mode == BLKmode
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-      && cum->arg_words < (unsigned) MAX_ARGS_IN_REGISTERS
-      && mips_abi != ABI_EABI)
-    {
-      int words;
-      if (mode == BLKmode)
-       words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                / UNITS_PER_WORD);
-      else
-       words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+     int named;                        /* != 0 for normal args, == 0 for ... args */
+{
+  struct mips_arg_info info;
 
-      if (words + cum->arg_words <= (unsigned) MAX_ARGS_IN_REGISTERS)
-       return 0;               /* structure fits in registers */
+  mips_arg_info (cum, mode, type, named, &info);
+  return info.stack_words > 0 ? info.reg_words : 0;
+}
+\f
+int
+mips_setup_incoming_varargs (cum, mode, type, no_rtl)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int no_rtl;
+{
+  CUMULATIVE_ARGS local_cum;
+  int gp_saved, fp_saved;
 
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = %d\n",
-                MAX_ARGS_IN_REGISTERS - cum->arg_words);
+  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+    return 0;
 
-      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
-    }
+  /* The caller has advanced CUM up to, but not beyond, the last named
+     argument.  Advance a local copy of CUM past the last "real" named
+     argument, to find out how many registers are left over.
 
-  else if (mode == DImode
-          && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1
-          && ! TARGET_64BIT && mips_abi != ABI_EABI)
+     For K&R varargs, the last named argument is a dummy word-sized one,
+     so CUM already contains the information we need.  For stdarg, it is
+     a real argument (such as the format in printf()) and we need to
+     step over it.  */
+  local_cum = *cum;
+  if (!current_function_varargs)
+    FUNCTION_ARG_ADVANCE (local_cum, mode, type, 1);
+
+  /* Found out how many registers we need to save.  */
+  gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.gp_regs;
+  fp_saved = (EABI_FLOAT_VARARGS_P
+             ? MAX_ARGS_IN_REGISTERS - local_cum.fp_regs
+             : 0);
+
+  if (!no_rtl)
     {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = 1\n");
+      if (gp_saved > 0)
+       {
+         rtx ptr, mem;
 
-      return 1;
-    }
+         ptr = virtual_incoming_args_rtx;
+         if (mips_abi == ABI_EABI)
+           ptr = plus_constant (ptr, -gp_saved * UNITS_PER_WORD);
+         mem = gen_rtx_MEM (BLKmode, ptr);
 
-  return 0;
+         /* va_arg is an array access in this case, which causes
+            it to get MEM_IN_STRUCT_P set.  We must set it here
+            so that the insn scheduler won't assume that these
+            stores can't possibly overlap with the va_arg loads.  */
+         if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN)
+           MEM_SET_IN_STRUCT_P (mem, 1);
+
+         move_block_from_reg (local_cum.gp_regs + GP_ARG_FIRST, mem,
+                              gp_saved, gp_saved * UNITS_PER_WORD);
+       }
+      if (fp_saved > 0)
+       {
+         /* We can't use move_block_from_reg, because it will use
+            the wrong mode. */
+         enum machine_mode mode;
+         int off, i;
+
+         /* Set OFF to the offset from virtual_incoming_args_rtx of
+            the first float register.   The FP save area lies below
+            the integer one, and is aligned to UNITS_PER_FPVALUE bytes.  */
+         off = -gp_saved * UNITS_PER_WORD;
+         off &= ~(UNITS_PER_FPVALUE - 1);
+         off -= fp_saved * UNITS_PER_FPREG;
+
+         mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
+
+         for (i = local_cum.fp_regs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC)
+           {
+             rtx ptr = plus_constant (virtual_incoming_args_rtx, off);
+             emit_move_insn (gen_rtx_MEM (mode, ptr),
+                             gen_rtx_REG (mode, FP_ARG_FIRST + i));
+             off += UNITS_PER_FPVALUE;
+           }
+       }
+    }
+  return (gp_saved * UNITS_PER_WORD) + (fp_saved * UNITS_PER_FPREG);
 }
-\f
+
 /* Create the va_list data type.
    We keep 3 pointers, and two offsets.
    Two pointers are to the overflow area, which starts at the CFA.
@@ -4332,9 +4345,9 @@ function_arg_partial_nregs (cum, mode, type, named)
      These are downcounted as float or non-float arguments are used,
      and when they get to zero, the argument must be obtained from the
      overflow region.
-   If TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT, then no FPR save area exists,
-     and a single pointer is enough.  It's started at the GPR save area,
-     and is advanced, period.
+   If !EABI_FLOAT_VARARGS_P, then no FPR save area exists, and a single
+     pointer is enough.  It's started at the GPR save area, and is
+     advanced, period.
    Note that the GPR save area is not constant size, due to optimization
      in the prologue.  Hence, we can't use a design with two pointers
      and two offsets, although we could have designed this with two pointers
@@ -4344,7 +4357,7 @@ function_arg_partial_nregs (cum, mode, type, named)
 tree
 mips_build_va_list ()
 {
-  if (mips_abi == ABI_EABI && !TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
+  if (EABI_FLOAT_VARARGS_P)
     {
       tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, record;
 
@@ -4392,37 +4405,24 @@ mips_va_start (stdarg_p, valist, nextarg)
      tree valist;
      rtx nextarg;
 {
-  int int_arg_words;
-  tree t;
-
-  /* Find out how many non-float named formals */
-  int_arg_words = current_function_args_info.arg_words;
+  const CUMULATIVE_ARGS *cum = &current_function_args_info;
 
   if (mips_abi == ABI_EABI)
     {
       int gpr_save_area_size;
-      /* Note UNITS_PER_WORD is 4 bytes or 8, depending on TARGET_64BIT.  */
-      if (int_arg_words < 8 )
-       /* Adjust for the prologue's economy measure */
-       gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
-      else
-       gpr_save_area_size = 0;
 
-      if (!TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
+      gpr_save_area_size
+       = (MAX_ARGS_IN_REGISTERS - cum->gp_regs) * UNITS_PER_WORD;
+
+      if (EABI_FLOAT_VARARGS_P)
        {
          tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
          tree ovfl, gtop, ftop, goff, foff;
-         tree gprv;
-         int float_formals, fpr_offset, size_excess, floats_passed_in_regs;
-         int fpr_save_offset;
-
-         float_formals = current_function_args_info.fp_arg_words;
-         /* If mips2, the number of formals is half the reported # of words */
-         if (!TARGET_64BIT)
-           float_formals /= 2;
-         floats_passed_in_regs = (TARGET_64BIT ? 8 : 4);
+         tree t;
+         int fpr_offset;
+         int fpr_save_area_size;
 
-         f_ovfl  = TYPE_FIELDS (va_list_type_node);
+         f_ovfl = TYPE_FIELDS (va_list_type_node);
          f_gtop = TREE_CHAIN (f_ovfl);
          f_ftop = TREE_CHAIN (f_gtop);
          f_goff = TREE_CHAIN (f_ftop);
@@ -4434,84 +4434,49 @@ mips_va_start (stdarg_p, valist, nextarg)
          goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
          foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
 
-         /* Emit code setting a pointer into the overflow (shared-stack) area.
-            If there were more than 8 non-float formals, or more than 8
-            float formals, then this pointer isn't to the base of the area.
-            In that case, it must point to where the first vararg is.  */
-         size_excess = 0;
-         if (float_formals > floats_passed_in_regs)
-           size_excess += (float_formals-floats_passed_in_regs) * 8;
-         if (int_arg_words > 8)
-           size_excess += (int_arg_words-8) * UNITS_PER_WORD;
-
-         /* FIXME: for mips2, the above size_excess can be wrong.  Because the
-            overflow stack holds mixed size items, there can be alignments,
-            so that an 8 byte double following a 4 byte int will be on an
-            8 byte boundary.  This means that the above calculation should
-            take into account the exact sequence of floats and non-floats
-            which make up the excess.  That calculation should be rolled
-            into the code which sets the current_function_args_info struct.
-            The above then reduces to a fetch from that struct.  */
-
-
+         /* Emit code to initialize OVFL, which points to the next varargs
+            stack argument.  CUM->STACK_WORDS gives the number of stack
+            words used by named arguments.  */
          t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
-         if (size_excess)
+         if (cum->stack_words > 0)
            t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
-               build_int_2 (size_excess, 0));
+                      build_int_2 (cum->stack_words * UNITS_PER_WORD, 0));
          t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code setting a ptr to the base of the overflow area.  */
+         /* Emit code to initialize GTOP, the top of the GPR save area.  */
          t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
          t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code setting a pointer to the GPR save area.
-            More precisely, a pointer to off-the-end of the FPR save area.
-            If mips4, this is gpr_save_area_size below the overflow area.
-            If mips2, also round down to an 8-byte boundary, since the FPR
-            save area is 8-byte aligned, and GPR is 4-byte-aligned.
-            Therefore there can be a 4-byte gap between the save areas.  */
-         gprv = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
-         fpr_save_offset = gpr_save_area_size;
-         if (!TARGET_64BIT)
-           {
-             if (fpr_save_offset & 7)
-               fpr_save_offset += 4;
-           }
-         if (fpr_save_offset)
-           gprv = build (PLUS_EXPR, TREE_TYPE (ftop), gprv,
-               build_int_2 (-fpr_save_offset,-1));
-         t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, gprv);
+         /* Emit code to initialize FTOP, the top of the FPR save area.
+            This address is gpr_save_area_bytes below GTOP, rounded
+            down to the next fp-aligned boundary.  */
+         t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+         fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+         fpr_offset &= ~(UNITS_PER_FPVALUE - 1);
+         if (fpr_offset)
+           t = build (PLUS_EXPR, TREE_TYPE (ftop), t,
+                      build_int_2 (-fpr_offset, -1));
+         t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code initting an offset to the size of the GPR save area */
+         /* Emit code to initialize GOFF, the offset from GTOP of the
+            next GPR argument.  */
          t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (gpr_save_area_size,0));
+                    build_int_2 (gpr_save_area_size, 0));
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code initting an offset from ftop to the first float
-            vararg.  This varies in size, since any float
-            varargs are put in the FPR save area after the formals.
-            Note it's 8 bytes/formal regardless of TARGET_64BIT.
-            However, mips2 stores 4 GPRs, mips4 stores 8 GPRs.
-            If there are 8 or more float formals, init to zero.
-            (In fact, the formals aren't stored in the bottom of the
-            FPR save area: they are elsewhere, and the size of the FPR
-            save area is economized by the prologue.  But this code doesn't
-            care.  This design is unaffected by that fact.) */
-         if (float_formals >= floats_passed_in_regs)
-           fpr_offset = 0;
-         else
-           fpr_offset = (floats_passed_in_regs - float_formals) * 8;
+         /* Likewise emit code to initialize FOFF, the offset from FTOP
+            of the next FPR argument.  */
+         fpr_save_area_size
+           = (MAX_ARGS_IN_REGISTERS - cum->fp_regs) * UNITS_PER_FPREG;
          t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
-                    build_int_2 (fpr_offset,0));
+                    build_int_2 (fpr_save_area_size, 0));
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
        }
       else
        {
-         /* TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT */
-
          /* Everything is in the GPR save area, or in the overflow
             area which is contiguous with it.  */
 
@@ -4534,10 +4499,10 @@ mips_va_start (stdarg_p, valist, nextarg)
          /* ??? This had been conditional on
               _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
             and both iris5.h and iris6.h define _MIPS_SIM.  */
-         if (mips_abi == ABI_N32 || mips_abi == ABI_64)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
-         else if (mips_abi == ABI_MEABI)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
+         if (mips_abi == ABI_N32
+             || mips_abi == ABI_64
+             || mips_abi == ABI_MEABI)
+           ofs = (cum->gp_regs < MAX_ARGS_IN_REGISTERS ? 0 : -UNITS_PER_WORD);
          else
            ofs = -UNITS_PER_WORD;
        }
@@ -4562,13 +4527,12 @@ mips_va_arg (valist, type)
 
   if (mips_abi == ABI_EABI)
     {
-      int indirect;
-      rtx r, lab_over = NULL_RTX, lab_false;
-      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
-      tree ovfl, gtop, ftop, goff, foff;
+      bool indirect;
+      rtx r;
 
       indirect
        = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+
       if (indirect)
        {
          size = POINTER_SIZE / BITS_PER_UNIT;
@@ -4577,187 +4541,166 @@ mips_va_arg (valist, type)
 
       addr_rtx = gen_reg_rtx (Pmode);
 
-      if (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
+      if (!EABI_FLOAT_VARARGS_P)
        {
-         /* Case of all args in a merged stack. No need to check bounds,
+         /* Case of all args in a merged stack.  No need to check bounds,
             just advance valist along the stack.  */
 
          tree gpr = valist;
-         if (! indirect
-             && ! TARGET_64BIT
+         if (!indirect
+             && !TARGET_64BIT
              && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
            {
+             /* Align the pointer using: ap = (ap + align - 1) & -align,
+                where align is 2 * UNITS_PER_WORD.  */
              t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
-                    build_int_2 (2*UNITS_PER_WORD - 1, 0));
+                        build_int_2 (2 * UNITS_PER_WORD - 1, 0));
              t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
-                    build_int_2 (-2*UNITS_PER_WORD, -1));
+                        build_int_2 (-2 * UNITS_PER_WORD, -1));
              t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
              expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
            }
 
-         t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
-               size_int (rsize));
-         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-         if (r != addr_rtx)
+         /* Emit code to set addr_rtx to the valist, and postincrement
+            the valist by the size of the argument, rounded up to the
+            next word.  */
+         t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
+                    size_int (rsize));
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
            emit_move_insn (addr_rtx, r);
 
-         /* flush the POSTINCREMENT */
-         emit_queue();
-
-         if (indirect)
-           {
-             r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
-           }
-         else
-           {
-             if (BYTES_BIG_ENDIAN && rsize != size)
-             addr_rtx = plus_constant (addr_rtx, rsize - size);
-           }
-         return addr_rtx;
+         /* Flush the POSTINCREMENT.  */
+         emit_queue();
        }
+      else
+       {
+         /* Not a simple merged stack.  */
 
-      /* Not a simple merged stack.  Need ptrs and indexes left by va_start.  */
+         tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+         tree ovfl, top, off;
+         rtx lab_over = NULL_RTX, lab_false;
 
-      f_ovfl  = TYPE_FIELDS (va_list_type_node);
-      f_gtop = TREE_CHAIN (f_ovfl);
-      f_ftop = TREE_CHAIN (f_gtop);
-      f_goff = TREE_CHAIN (f_ftop);
-      f_foff = TREE_CHAIN (f_goff);
+         f_ovfl = TYPE_FIELDS (va_list_type_node);
+         f_gtop = TREE_CHAIN (f_ovfl);
+         f_ftop = TREE_CHAIN (f_gtop);
+         f_goff = TREE_CHAIN (f_ftop);
+         f_foff = TREE_CHAIN (f_goff);
 
-      ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
-      gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
-      ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
-      goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
-      foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+         /* We maintain separate pointers and offsets for floating-point
+            and integer arguments, but we need similar code in both cases.
+            Let:
 
-      lab_false = gen_label_rtx ();
-      lab_over = gen_label_rtx ();
+                TOP be the top of the register save area;
+                OFF be the offset from TOP of the next register;
+                ADDR_RTX be the address of the argument; and
+                RSIZE be the number of bytes used to store the argument.
 
-      if (TREE_CODE (type) == REAL_TYPE)
-        {
+            The code we want is:
 
-         /* Emit code to branch if foff == 0.  */
-          r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
-                                  1, lab_false);
+                 1: off &= -rsize;       // round down
+                 2: if (off != 0)
+                 3:   {
+                 4:     addr_rtx = top - off;
+                 5:     off -= rsize;
+                 6:   }
+                 7: else
+                 8:   {
+                 9:     ovfl += ((intptr_t) ovfl + rsize - 1) & -rsize;
+                10:     addr_rtx = ovfl;
+                11:     ovfl += rsize;
+                12:   }
 
-          /* Emit code for addr_rtx = ftop - foff */
-          t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
+            [1] and [9] can sometimes be optimized away.  */
 
-          /* Emit code for foff-=8.
-            Advances the offset up FPR save area by one double */
-          t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         lab_false = gen_label_rtx ();
+         lab_over = gen_label_rtx ();
 
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
+         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
 
-         if (!TARGET_64BIT)
+         if (TREE_CODE (type) == REAL_TYPE)
            {
-             /* For mips2, the overflow area contains mixed size items.
-                If a 4-byte int is followed by an 8-byte float, then
-                natural alignment causes a 4 byte gap.
-                So, dynamically adjust ovfl up to a multiple of 8.  */
-             t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
-                       build_int_2 (7, 0));
-             t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
-             t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
-             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
-
-          /* Emit code for addr_rtx = the ovfl pointer into overflow area.
-            Regardless of mips2, postincrement the ovfl pointer by 8.  */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (8));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
-                 return addr_rtx;
-        }
-      else
-        {
-          /* not REAL_TYPE */
-         int step_size;
+             top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
+             off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
 
-         if (! TARGET_64BIT
-             && TREE_CODE (type) == INTEGER_TYPE
-             && TYPE_PRECISION (type) == 64)
+             /* When floating-point registers are saved to the stack,
+                each one will take up UNITS_PER_FPVALUE bytes, regardless
+                of the float's precision.  */
+             rsize = UNITS_PER_FPVALUE;
+           }
+         else
            {
-             /* In mips2, int takes 32 bits of the GPR save area, but
-                longlong takes an aligned 64 bits.  So, emit code
-                to zero the low order bits of goff, thus aligning
-                the later calculation of (gtop-goff) upwards.  */
-              t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
-                       build_int_2 (-8, -1));
-              t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-              expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+             top = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
+             off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
+             if (rsize > UNITS_PER_WORD)
+               {
+                 /* [1] Emit code for: off &= -rsize.  */
+                 t = build (BIT_AND_EXPR, TREE_TYPE (off), off,
+                            build_int_2 (-rsize, -1));
+                 t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+                 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+               }
            }
 
-         /* Emit code to branch if goff == 0.  */
-          r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
+         /* [2] Emit code to branch if off == 0.  */
+         r = expand_expr (off, NULL_RTX, TYPE_MODE (TREE_TYPE (off)),
+                          EXPAND_NORMAL);
+         emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
                                   1, lab_false);
 
-          /* Emit code for addr_rtx = gtop - goff.  */
-          t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
+         /* [4] Emit code for: addr_rtx = top - off.  */
+         t = build (MINUS_EXPR, TREE_TYPE (top), top, off);
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
+           emit_move_insn (addr_rtx, r);
 
-         /* Note that mips2 int is 32 bit, but mips2 longlong is 64.  */
-         if (! TARGET_64BIT && TYPE_PRECISION (type) == 64)
-           step_size = 8;
-         else
-           step_size = UNITS_PER_WORD;
-
-          /* Emit code for goff = goff - step_size.
-            Advances the offset up GPR save area over the item.  */
-          t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (step_size, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
-
-          /* Emit code for addr_rtx -> overflow area, postinc by step_size */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (step_size));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
+         /* [5] Emit code for: off -= rsize.  */
+         t = build (MINUS_EXPR, TREE_TYPE (off), off, build_int_2 (rsize, 0));
+         t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         if (BYTES_BIG_ENDIAN && rsize != size)
-           addr_rtx = plus_constant (addr_rtx, rsize - size);
+         /* [7] Emit code to jump over the else clause, then the label
+            that starts it.  */
+         emit_queue();
+         emit_jump (lab_over);
+         emit_barrier ();
+         emit_label (lab_false);
 
-          if (indirect)
-           {
-             addr_rtx = force_reg (Pmode, addr_rtx);
-             r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
+         if (rsize > UNITS_PER_WORD)
+           {
+             /* [9] Emit: ovfl += ((intptr_t) ovfl + rsize - 1) & -rsize.  */
+             t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl,
+                        build_int_2 (rsize - 1, 0));
+             t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), t,
+                        build_int_2 (-rsize, -1));
+             t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
            }
 
-         return addr_rtx;
+         /* [10, 11].  Emit code to store ovfl in addr_rtx, then
+            post-increment ovfl by rsize.  */
+         t = build (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl,
+                    size_int (rsize));
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
+           emit_move_insn (addr_rtx, r);
+
+         emit_queue();
+         emit_label (lab_over);
        }
+      if (indirect)
+       {
+         addr_rtx = force_reg (Pmode, addr_rtx);
+         r = gen_rtx_MEM (Pmode, addr_rtx);
+         set_mem_alias_set (r, get_varargs_alias_set ());
+         emit_move_insn (addr_rtx, r);
+       }
+      else
+       {
+         if (BYTES_BIG_ENDIAN && rsize != size)
+           addr_rtx = plus_constant (addr_rtx, rsize - size);
+       }
+      return addr_rtx;
     }
   else
     {
@@ -8083,7 +8026,7 @@ mips_function_value (valtype, func)
 
 int
 function_arg_pass_by_reference (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
+     const CUMULATIVE_ARGS *cum;
      enum machine_mode mode;
      tree type;
      int named ATTRIBUTE_UNUSED;
@@ -8103,16 +8046,9 @@ function_arg_pass_by_reference (cum, mode, type, named)
   /* ??? cum can be NULL when called from mips_va_arg.  The problem handled
      here hopefully is not relevant to mips_va_arg.  */
   if (cum && MUST_PASS_IN_STACK (mode, type)
-      && mips_abi != ABI_MEABI)
-     {
-       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
-         get double copies of any offsets generated for small structs
-         passed in registers.  */
-       CUMULATIVE_ARGS temp;
-       temp = *cum;
-       if (FUNCTION_ARG (temp, mode, type, named) != 0)
-        return 1;
-     }
+      && mips_abi != ABI_MEABI
+      && FUNCTION_ARG (*cum, mode, type, named) != 0)
+    return 1;
 
   /* Otherwise, we only do this if EABI is selected.  */
   if (mips_abi != ABI_EABI)
index 0a8238d..7b01130 100644 (file)
@@ -1595,7 +1595,10 @@ do {                                                     \
 #define FP_INC (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT ? 1 : 2)
 
 /* The largest size of value that can be held in floating-point registers.  */
-#define UNITS_PER_FPVALUE (FP_INC * UNITS_PER_FPREG)
+#define UNITS_PER_FPVALUE (TARGET_SOFT_FLOAT ? 0 : FP_INC * UNITS_PER_FPREG)
+
+/* The number of bytes in a double.  */
+#define UNITS_PER_DOUBLE (TYPE_PRECISION (double_type_node) / BITS_PER_UNIT)
 
 /* A C expression for the size in bits of the type `int' on the
    target machine.  If you don't define this, the default is one
@@ -2737,6 +2740,10 @@ extern struct mips_frame_info current_frame_info;
 
 #define RETURN_IN_MEMORY(TYPE) \
        mips_return_in_memory (TYPE)
+
+#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL)      \
+       (PRETEND_SIZE) = mips_setup_incoming_varargs (&(CUM), (MODE),   \
+                                                     (TYPE), (NO_RTL))
 \f
 
 #define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
@@ -2748,32 +2755,75 @@ extern struct mips_frame_info current_frame_info;
    and about the args processed so far, enough to enable macros
    such as FUNCTION_ARG to determine where the next arg should go.
 
-   On the mips16, we need to keep track of which floating point
-   arguments were passed in general registers, but would have been
-   passed in the FP regs if this were a 32 bit function, so that we
-   can move them to the FP regs if we wind up calling a 32 bit
-   function.  We record this information in fp_code, encoded in base
-   four.  A zero digit means no floating point argument, a one digit
-   means an SFmode argument, and a two digit means a DFmode argument,
-   and a three digit is not used.  The low order digit is the first
-   argument.  Thus 6 == 1 * 4 + 2 means a DFmode argument followed by
-   an SFmode argument.  ??? A more sophisticated approach will be
-   needed if MIPS_ABI != ABI_32.  */
+   This structure has to cope with two different argument allocation
+   schemes.  Most MIPS ABIs view the arguments as a struct, of which the
+   first N words go in registers and the rest go on the stack.  If I < N,
+   the Ith word might go in Ith integer argument register or the
+   Ith floating-point one.  In some cases, it has to go in both (see
+   function_arg).  For these ABIs, we only need to remember the number
+   of words passed so far.
+
+   The EABI instead allocates the integer and floating-point arguments
+   separately.  The first N words of FP arguments go in FP registers,
+   the rest go on the stack.  Likewise, the first N words of the other
+   arguments go in integer registers, and the rest go on the stack.  We
+   need to maintain three counts: the number of integer registers used,
+   the number of floating-point registers used, and the number of words
+   passed on the stack.
+
+   We could keep separate information for the two ABIs (a word count for
+   the standard ABIs, and three separate counts for the EABI).  But it
+   seems simpler to view the standard ABIs as forms of EABI that do not
+   allocate floating-point registers.
+
+   So for the standard ABIs, the first N words are allocated to integer
+   registers, and function_arg decides on an argument-by-argument basis
+   whether that argument should really go in an integer register, or in
+   a floating-point one.  */
 
 typedef struct mips_args {
-  int gp_reg_found;            /* whether a gp register was found yet */
-  unsigned int arg_number;     /* argument number */
-  unsigned int arg_words;      /* # total words the arguments take */
-  unsigned int fp_arg_words;   /* # words for FP args (MIPS_EABI only) */
-  int last_arg_fp;             /* nonzero if last arg was FP (EABI only) */
-  int fp_code;                 /* Mode of FP arguments (mips16) */
-  unsigned int num_adjusts;    /* number of adjustments made */
-                               /* Adjustments made to args pass in regs.  */
-                               /* ??? The size is doubled to work around a
-                                  bug in the code that sets the adjustments
-                                  in function_arg.  */
-  int prototype;                /* True if the function has a prototype.  */
-  struct rtx_def *adjust[MAX_ARGS_IN_REGISTERS*2];
+  /* Always true for varargs functions.  Otherwise true if at least
+     one argument has been passed in an integer register.  */
+  int gp_reg_found;
+
+  /* The number of arguments seen so far.  */
+  unsigned int arg_number;
+
+  /* For EABI, the number of integer registers used so far.  For other
+     ABIs, the number of words passed in registers (whether integer
+     or floating-point).  */
+  unsigned int gp_regs;
+
+  /* For EABI, the number of floating-point registers used so far.  */
+  unsigned int fp_regs;
+
+  /* The number of words passed on the stack.  */
+  unsigned int stack_words;
+
+  /* On the mips16, we need to keep track of which floating point
+     arguments were passed in general registers, but would have been
+     passed in the FP regs if this were a 32 bit function, so that we
+     can move them to the FP regs if we wind up calling a 32 bit
+     function.  We record this information in fp_code, encoded in base
+     four.  A zero digit means no floating point argument, a one digit
+     means an SFmode argument, and a two digit means a DFmode argument,
+     and a three digit is not used.  The low order digit is the first
+     argument.  Thus 6 == 1 * 4 + 2 means a DFmode argument followed by
+     an SFmode argument.  ??? A more sophisticated approach will be
+     needed if MIPS_ABI != ABI_32.  */
+  int fp_code;
+
+  /* True if the function has a prototype.  */
+  int prototype;
+
+  /* When a structure does not take up a full register, the argument
+     should sometimes be shifted left so that it occupies the high part
+     of the register.  These two fields describe an array of ashl
+     patterns for doing this.  See function_arg_advance, which creates
+     the shift patterns, and function_arg, which returns them when given
+     a VOIDmode argument.  */
+  unsigned int num_adjusts;
+  struct rtx_def *adjust[MAX_ARGS_IN_REGISTERS];
 } CUMULATIVE_ARGS;
 
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
@@ -2828,6 +2878,12 @@ typedef struct mips_args {
                ? PARM_BOUNDARY                                         \
                : GET_MODE_ALIGNMENT(MODE)))
 
+/* True if using EABI and varargs can be passed in floating-point
+   registers.  Under these conditions, we need a more complex form
+   of va_list, which tracks GPR, FPR and stack arguments separately.  */
+#define EABI_FLOAT_VARARGS_P \
+       (mips_abi == ABI_EABI && UNITS_PER_FPVALUE >= UNITS_PER_DOUBLE)
+
 \f
 /* Tell prologue and epilogue if register REGNO should be saved / restored.  */