OSDN Git Service

* sparc/sp64-elf.h (TARGET_DEFAULT): Delete MASK_STACK_BIAS.
authordevans <devans@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 17 Oct 1997 20:39:37 +0000 (20:39 +0000)
committerdevans <devans@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 17 Oct 1997 20:39:37 +0000 (20:39 +0000)
* sparc/sparc.h (PROMOTE_MODE): Promote small ints if arch64.
(PROMOTE_FUNCTION_ARGS,PROMOTE_FUNCTION_RETURN): Define.
(SPARC_FIRST_FP_REG, SPARC_FP_REG_P): New macros.
(SPARC_{OUTGOING,INCOMING}_INT_ARG_FIRST): New macros.
(SPARC_FP_ARG_FIRST): New macro.
(CONDITIONAL_REGISTER_USAGE): All v9 fp regs are volatile now.
(REG_ALLOC_ORDER,REG_LEAF_ALLOC_ORDER): Reorganize fp regs.
(NPARM_REGS): There are 32 fp argument registers now.
(FUNCTION_ARG_REGNO_P): Likewise.
(FIRST_PARM_OFFSET): Update to new v9 abi.
(REG_PARM_STACK_SPACE): Define for arch64.
(enum sparc_arg_class): Delete.
(sparc_arg_count,sparc_n_named_args): Delete.
(struct sparc_args): Redefine and use for arch32 as well as arch64.
(GET_SPARC_ARG_CLASS,ROUND_REG,ROUND_ADVANCE): Delete.
(FUNCTION_ARG_ADVANCE): Rewrite.
(FUNCTION_ARG,FUNCTION_INCOMING_ARG): Rewrite.
(FUNCTION_ARG_{PARTIAL_NREGS,PASS_BY_REFERENCE}): Rewrite.
(FUNCTION_ARG_CALLEE_COPIES): Delete.
(FUNCTION_ARG_{PADDING,BOUNDARY}): Define.
(STRICT_ARGUMENT_NAMING): Define.
(doublemove_string): Declare.
* sparc/sparc.c (sparc_arg_count,sparc_n_named_args): Delete.
(single_move_string): Use GEN_INT, and HOST_WIDE_INT.
(doublemove_string): New function.
(output_move_quad): Clean up some of the arch64 support.
(compute_frame_size): Add REG_PARM_STACK_SPACE if arch64.
Don't add 8 bytes of reserved space if arch64.
(sparc_builtin_saveregs): Combine arch32/arch64 versions.
(init_cumulative_args): New function.
(function_arg_slotno): New static function.
(function_arg,function_arg_partial_nregs): New functions.
(function_arg_{pass_by_reference,advance}): New functions.
(function_arg_padding): New function.
First pass at updating to current v9 abi.

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

gcc/config/sparc/sp64-elf.h
gcc/config/sparc/sparc.c
gcc/config/sparc/sparc.h

index f0a36e6..8ff9650 100644 (file)
@@ -1,5 +1,5 @@
 /* Definitions of target machine for GNU compiler, for SPARC64, ELF.
-   Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
    Contributed by Doug Evans, dje@cygnus.com.
 
 This file is part of GNU CC.
@@ -19,7 +19,7 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-/* This is a v9 only compiler.  -mv8 is not expected to work.  If you want
+/* This is a v9 only compiler.  -mcpu=v8 is not expected to work.  If you want
    a v8/v9 compiler, this isn't the place to do it.  */
 
 #define SPARC_V9 1     /* See sparc.h.  */
@@ -35,13 +35,15 @@ Boston, MA 02111-1307, USA.  */
 #undef TARGET_VERSION
 #define TARGET_VERSION fprintf (stderr, " (sparc64-elf)")
 
-/* A v9 compiler with stack-bias, 32 bit integers and 64 bit pointers,
-   in a Medium/Anywhere code model environment.  */
+/* A v9 compiler without stack-bias, lp64 sizes,
+   in a Medium/Anywhere code model environment.
+   There is no stack bias as this configuration is intended for
+   embedded systems.  */
 
 #undef TARGET_DEFAULT
 #define TARGET_DEFAULT \
-  (MASK_V9 + MASK_ARCH64 + MASK_PTR64 + MASK_HARD_QUAD \
-   + MASK_STACK_BIAS + MASK_MEDANY + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU)
+  (MASK_V9 + MASK_ARCH64 + MASK_PTR64 + MASK_LONG64 + MASK_HARD_QUAD \
+   MASK_MEDANY + MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU)
 
 /* __svr4__ is used by the C library */
 /* ??? __arch64__ is subject to change.  */
@@ -120,6 +122,7 @@ crtbegin.o%s \
 /* The medium/anywhere code model practically requires us to put jump tables
    in the text section as gcc is unable to distinguish LABEL_REF's of jump
    tables from other label refs (when we need to).  */
+/* ??? Revisit this.  */
 #undef JUMP_TABLES_IN_TEXT_SECTION
 #define JUMP_TABLES_IN_TEXT_SECTION
 
index 91d731f..de4ee75 100644 (file)
@@ -64,17 +64,6 @@ static int actual_fsize;
 
 rtx sparc_compare_op0, sparc_compare_op1;
 
-/* Count of named arguments (v9 only).
-   ??? INIT_CUMULATIVE_ARGS initializes these, and FUNCTION_ARG_ADVANCE
-   increments SPARC_ARG_COUNT. They are then used by
-   FUNCTION_ARG_CALLEE_COPIES to determine if the argument is really a named
-   argument or not.  This hack is necessary because the NAMED argument to the
-   FUNCTION_ARG_XXX macros is not what it says it is: it does not include the
-   last named argument.  */
-
-int sparc_arg_count;
-int sparc_n_named_args;
-
 /* We may need an epilogue if we spill too many registers.
    If this is non-zero, then we branch here for the epilogue.  */
 static rtx leaf_label;
@@ -1647,7 +1636,12 @@ emit_move_sequence (operands, mode)
 }
 \f
 /* Return the best assembler insn template
-   for moving operands[1] into operands[0] as a fullword.  */
+   for moving operands[1] into operands[0] as a 4 byte quantity.
+
+   This isn't intended to be very smart.  It is up to the caller to
+   choose the best way to do things.
+
+   Note that OPERANDS may be modified to suit the returned string.  */
 
 char *
 singlemove_string (operands)
@@ -1673,7 +1667,7 @@ singlemove_string (operands)
 
       REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
       REAL_VALUE_TO_TARGET_SINGLE (r, i);
-      operands[1] = gen_rtx (CONST_INT, VOIDmode, i);
+      operands[1] = GEN_INT (i);
 
       if (CONST_OK_FOR_LETTER_P (i, 'I'))
        return "mov %1,%0";
@@ -1685,10 +1679,11 @@ singlemove_string (operands)
   else if (GET_CODE (operands[1]) == CONST_INT
           && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I'))
     {
-      int i = INTVAL (operands[1]);
+      HOST_WIDE_INT i = INTVAL (operands[1]);
 
       /* If all low order 10 bits are clear, then we only need a single
         sethi insn to load the constant.  */
+      /* FIXME: Use SETHI_P.  */
       if ((i & 0x000003FF) != 0)
        return "sethi %%hi(%a1),%0\n\tor %0,%%lo(%a1),%0";
       else
@@ -1697,7 +1692,59 @@ singlemove_string (operands)
   /* Operand 1 must be a register, or a 'I' type CONST_INT.  */
   return "mov %1,%0";
 }
-\f
+
+/* Return the best assembler insn template
+   for moving operands[1] into operands[0] as an 8 byte quantity.
+
+   This isn't intended to be very smart.  It is up to the caller to
+   choose the best way to do things.
+
+   Note that OPERANDS may be modified to suit the returned string.  */
+
+char *
+doublemove_string (operands)
+     rtx *operands;
+{
+  rtx op0 = operands[0], op1 = operands[1];
+
+  if (GET_CODE (op0) == MEM)
+    {
+      if (GET_CODE (op1) == REG)
+       {
+         if (FP_REG_P (op1))
+           return "std %1,%0";
+         return TARGET_ARCH64 ? "stx %1,%0" : "std %1,%0";
+       }
+      if (TARGET_ARCH64
+         && (op1 == const0_rtx
+             || (GET_MODE (op1) != VOIDmode
+                 && op1 == CONST0_RTX (GET_MODE (op1)))))
+       return "stx %r1,%0";
+      abort ();
+    }
+  else if (GET_CODE (op1) == MEM)
+    {
+      if (GET_CODE (op0) != REG)
+       abort ();
+      if (FP_REG_P (op0))
+       return "ldd %1,%0";
+      return TARGET_ARCH64 ? "ldx %1,%0" : "ldd %1,%0";
+    }
+  else if (GET_CODE (operands[1]) == CONST_DOUBLE)
+    {
+      /* ??? Unfinished, and maybe not needed.  */
+      abort ();
+    }
+  else if (GET_CODE (operands[1]) == CONST_INT
+          && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I'))
+    {
+      /* ??? Unfinished, and maybe not needed.  */
+      abort ();
+    }
+  /* Operand 1 must be a register, or a 'I' type CONST_INT.  */
+  return "mov %1,%0";
+}
+
 /* Return non-zero if it is OK to assume that the given memory operand is
    aligned at least to a 8-byte boundary.  This should only be called
    for memory accesses whose size is 8 bytes or larger.  */
@@ -2038,17 +2085,25 @@ output_move_quad (operands)
 
   if (optype0 == REGOP)
     {
-      wordpart[0][0] = gen_rtx (REG, SImode, REGNO (op0) + 0);
-      wordpart[1][0] = gen_rtx (REG, SImode, REGNO (op0) + 1);
-      wordpart[2][0] = gen_rtx (REG, SImode, REGNO (op0) + 2);
-      wordpart[3][0] = gen_rtx (REG, SImode, REGNO (op0) + 3);
+      wordpart[0][0] = gen_rtx (REG, word_mode, REGNO (op0) + 0);
+      wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 1);
+      if (TARGET_ARCH32)
+       {
+         wordpart[2][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2);
+         wordpart[3][0] = gen_rtx (REG, word_mode, REGNO (op0) + 3);
+       }
     }
   else if (optype0 == OFFSOP)
     {
       wordpart[0][0] = adj_offsettable_operand (op0, 0);
-      wordpart[1][0] = adj_offsettable_operand (op0, 4);
-      wordpart[2][0] = adj_offsettable_operand (op0, 8);
-      wordpart[3][0] = adj_offsettable_operand (op0, 12);
+      if (TARGET_ARCH32)
+       {
+         wordpart[1][0] = adj_offsettable_operand (op0, 4);
+         wordpart[2][0] = adj_offsettable_operand (op0, 8);
+         wordpart[3][0] = adj_offsettable_operand (op0, 12);
+       }
+      else
+       wordpart[1][0] = adj_offsettable_operand (op0, 8);
     }
   else
     {
@@ -2060,17 +2115,25 @@ output_move_quad (operands)
 
   if (optype1 == REGOP)
     {
-      wordpart[0][1] = gen_rtx (REG, SImode, REGNO (op1) + 0);
-      wordpart[1][1] = gen_rtx (REG, SImode, REGNO (op1) + 1);
-      wordpart[2][1] = gen_rtx (REG, SImode, REGNO (op1) + 2);
-      wordpart[3][1] = gen_rtx (REG, SImode, REGNO (op1) + 3);
+      wordpart[0][1] = gen_rtx (REG, word_mode, REGNO (op1) + 0);
+      wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 1);
+      if (TARGET_ARCH32)
+       {
+         wordpart[2][1] = gen_rtx (REG, word_mode, REGNO (op1) + 2);
+         wordpart[3][1] = gen_rtx (REG, word_mode, REGNO (op1) + 3);
+       }
     }
   else if (optype1 == OFFSOP)
     {
       wordpart[0][1] = adj_offsettable_operand (op1, 0);
-      wordpart[1][1] = adj_offsettable_operand (op1, 4);
-      wordpart[2][1] = adj_offsettable_operand (op1, 8);
-      wordpart[3][1] = adj_offsettable_operand (op1, 12);
+      if (TARGET_ARCH32)
+       {
+         wordpart[1][1] = adj_offsettable_operand (op1, 4);
+         wordpart[2][1] = adj_offsettable_operand (op1, 8);
+         wordpart[3][1] = adj_offsettable_operand (op1, 12);
+       }
+      else
+       wordpart[1][1] = adj_offsettable_operand (op1, 8);
     }
   else if (optype1 == CNSTOP)
     {
@@ -2117,12 +2180,13 @@ output_move_quad (operands)
          /* If this is a floating point register higher than %f31,
             then we *must* use an aligned load, since `ld' will not accept
             the register number.  */
-         || (TARGET_V9 && REGNO (reg) >= 64))
+         || (TARGET_V9 && REGNO (reg) >= SPARC_FIRST_V9_FP_REG))
        {
          if (TARGET_V9 && FP_REG_P (reg) && TARGET_HARD_QUAD)
            {
              if ((REGNO (reg) & 3) != 0)
                abort ();
+             /* ??? Can `mem' have an inappropriate alignment here?  */
              return (mem == op1 ? "ldq %1,%0" : "stq %1,%0");
            }
          operands[2] = adj_offsettable_operand (mem, 8);
@@ -2137,21 +2201,36 @@ output_move_quad (operands)
   /* If the first move would clobber the source of the second one,
      do them in the other order.  */
 
-  /* Overlapping registers.  */
-  if (optype0 == REGOP && optype1 == REGOP
-      && (REGNO (op0) == REGNO (wordpart[1][3])
-         || REGNO (op0) == REGNO (wordpart[1][2])
-         || REGNO (op0) == REGNO (wordpart[1][1])))
+  /* Overlapping registers?  */
+  if (TARGET_ARCH32)
     {
-      /* Do fourth word.  */
-      output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
-      /* Do the third word.  */
-      output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
-      /* Do the second word.  */
-      output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
-      /* Do lowest-numbered word.  */
-      return singlemove_string (wordpart[0]);
+      if (optype0 == REGOP && optype1 == REGOP
+         && (REGNO (op0) == REGNO (wordpart[1][3])
+             || REGNO (op0) == REGNO (wordpart[1][2])
+             || REGNO (op0) == REGNO (wordpart[1][1])))
+       {
+         /* Do fourth word.  */
+         output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
+         /* Do the third word.  */
+         output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
+         /* Do the second word.  */
+         output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
+         /* Do lowest-numbered word.  */
+         output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]);
+         return "";
+       }
+    }
+  else /* TARGET_ARCH64 */
+    {
+      if (optype0 == REGOP && optype1 == REGOP
+         && REGNO (op0) == REGNO (wordpart[1][1]))
+       {
+         output_asm_insn ("mov %1,%0", wordpart[1]);
+         output_asm_insn ("mov %1,%0", wordpart[0]);
+         return "";
+       }
     }
+
   /* Loading into a register which overlaps a register used in the address.  */
   if (optype0 == REGOP && optype1 != REGOP
       && reg_overlap_mentioned_p (op0, op1))
@@ -2164,42 +2243,64 @@ output_move_quad (operands)
       abort ();
     }
 
-  /* Normal case: move the four words in lowest to highest address order.  */
+  /* Normal case: move the words in lowest to highest address order.  */
 
-  output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]);
+  if (TARGET_ARCH32)
+    {
+      output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]);
 
-  /* Make any unoffsettable addresses point at the second word.  */
-  if (addreg0)
-    output_asm_insn ("add %0,0x4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add %0,0x4,%0", &addreg1);
+      /* Make any unoffsettable addresses point at the second word.  */
+      if (addreg0)
+       output_asm_insn ("add %0,0x4,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,0x4,%0", &addreg1);
+
+      /* Do the second word.  */
+      output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
 
-  /* Do the second word.  */
-  output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
+      /* Make any unoffsettable addresses point at the third word.  */
+      if (addreg0)
+       output_asm_insn ("add %0,0x4,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,0x4,%0", &addreg1);
 
-  /* Make any unoffsettable addresses point at the third word.  */
-  if (addreg0)
-    output_asm_insn ("add %0,0x4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add %0,0x4,%0", &addreg1);
+      /* Do the third word.  */
+      output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
 
-  /* Do the third word.  */
-  output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
+      /* Make any unoffsettable addresses point at the fourth word.  */
+      if (addreg0)
+       output_asm_insn ("add %0,0x4,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,0x4,%0", &addreg1);
 
-  /* Make any unoffsettable addresses point at the fourth word.  */
-  if (addreg0)
-    output_asm_insn ("add %0,0x4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add %0,0x4,%0", &addreg1);
+      /* Do the fourth word.  */
+      output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
 
-  /* Do the fourth word.  */
-  output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
+      /* Undo the adds we just did.  */
+      if (addreg0)
+       output_asm_insn ("add %0,-0xc,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,-0xc,%0", &addreg1);
+    }
+  else /* TARGET_ARCH64 */
+    {
+      output_asm_insn (doublemove_string (wordpart[0]), wordpart[0]);
 
-  /* Undo the adds we just did.  */
-  if (addreg0)
-    output_asm_insn ("add %0,-0xc,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add %0,-0xc,%0", &addreg1);
+      /* Make any unoffsettable addresses point at the second word.  */
+      if (addreg0)
+       output_asm_insn ("add %0,0x8,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,0x8,%0", &addreg1);
+
+      /* Do the second word.  */
+      output_asm_insn (doublemove_string (wordpart[1]), wordpart[1]);
+
+      /* Undo the adds we just did.  */
+      if (addreg0)
+       output_asm_insn ("add %0,-0x8,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("add %0,-0x8,%0", &addreg1);
+    }
 
   return "";
 }
@@ -2324,7 +2425,7 @@ output_sized_memop (opname, mode, signedp)
 
   fprintf (asm_out_file, "\t%s%s", opname, modename);
 }
-\f
+
 void
 output_move_with_extension (operands)
      rtx *operands;
@@ -3056,10 +3157,7 @@ compute_frame_size (size, leaf_function)
 {
   int n_regs = 0, i;
   int outgoing_args_size = (current_function_outgoing_args_size
-#if ! SPARC_ARCH64
-                           + REG_PARM_STACK_SPACE (current_function_decl)
-#endif
-                           );
+                           + REG_PARM_STACK_SPACE (current_function_decl));
 
   if (TARGET_EPILOGUE)
     {
@@ -3106,10 +3204,9 @@ compute_frame_size (size, leaf_function)
   /* Make sure nothing can clobber our register windows.
      If a SAVE must be done, or there is a stack-local variable,
      the register window area must be allocated.
-     ??? For v9 we need an additional 8 bytes of reserved space, apparently
-     it's needed by v8 as well.  */
+     ??? For v8 we apparently need an additional 8 bytes of reserved space.  */
   if (leaf_function == 0 || size > 0)
-    actual_fsize += (16 * UNITS_PER_WORD) + 8;
+    actual_fsize += (16 * UNITS_PER_WORD) + (TARGET_ARCH64 ? 0 : 8);
 
   return SPARC_STACK_ALIGN (actual_fsize);
 }
@@ -3405,105 +3502,671 @@ output_function_epilogue (file, size, leaf_function)
       target_flags |= old_target_epilogue;
     }
 }
+\f
+/* Functions for handling argument passing.
+
+   For v8 the first six args are normally in registers and the rest are
+   pushed.  Any arg that starts within the first 6 words is at least
+   partially passed in a register unless its data type forbids.
+
+   For v9, the argument registers are laid out as an array of 16 elements
+   and arguments are added sequentially.  The first 6 int args and up to the
+   first 16 fp args (depending on size) are passed in regs.
+
+   Slot    Stack   Integral   Float   Float in structure   Double   Long Double
+   ----    -----   --------   -----   ------------------   ------   -----------
+    15   [SP+248]              %f31       %f30,%f31         %d30
+    14   [SP+240]              %f29       %f28,%f29         %d28       %q28
+    13   [SP+232]              %f27       %f26,%f27         %d26
+    12   [SP+224]              %f25       %f24,%f25         %d24       %q24
+    11   [SP+216]              %f23       %f22,%f23         %d22
+    10   [SP+208]              %f21       %f20,%f21         %d20       %q20
+     9   [SP+200]              %f19       %f18,%f19         %d18
+     8   [SP+192]              %f17       %f16,%f17         %d16       %q16
+     7   [SP+184]              %f15       %f14,%f15         %d14
+     6   [SP+176]              %f13       %f12,%f13         %d12       %q12
+     5   [SP+168]     %o5      %f11       %f10,%f11         %d10
+     4   [SP+160]     %o4       %f9        %f8,%f9           %d8        %q8
+     3   [SP+152]     %o3       %f7        %f6,%f7           %d6
+     2   [SP+144]     %o2       %f5        %f4,%f5           %d4        %q4
+     1   [SP+136]     %o1       %f3        %f2,%f3           %d2
+     0   [SP+128]     %o0       %f1        %f0,%f1           %d0        %q0
+
+   Here SP = %sp if -mno-stack-bias or %sp+stack_bias otherwise.
+
+   Integral arguments are always passed as 64 bit quantities appropriately
+   extended.
+
+   Passing of floating point values is handled as follows.
+   If a prototype is in scope:
+     If the value is in a named argument (i.e. not a stdarg function or a
+     value not part of the `...') then the value is passed in the appropriate
+     fp reg.
+     If the value is part of the `...' and is passed in one of the first 6
+     slots then the value is passed in the appropriate int reg.
+     If the value is part of the `...' and is not passed in one of the first 6
+     slots then the value is passed in memory.
+   If a prototype is not in scope:
+     If the value is one of the first 6 arguments the value is passed in the
+     appropriate integer reg and the appropriate fp reg.
+     If the value is not one of the first 6 arguments the value is passed in
+     the appropriate fp reg and in memory.
+   */
+
+/* Maximum number of int regs for args.  */
+#define SPARC_INT_ARG_MAX 6
+/* Maximum number of fp regs for args.  */
+#define SPARC_FP_ARG_MAX 16
+
+#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* Handle the INIT_CUMULATIVE_ARGS macro.
+   Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
 
-/* Do what is necessary for `va_start'.  The argument is ignored.
-   !v9: We look at the current function to determine if stdarg or varargs
-   is used and return the address of the first unnamed parameter.
-   v9: We save the argument integer and floating point regs in a buffer, and
-   return the address of this buffer.  The rest is handled in va-sparc.h.  */
-/* ??? This is currently conditioned on SPARC_ARCH64 because
-   current_function_args_info is different in each compiler.  */
+void
+init_cumulative_args (cum, fntype, libname, indirect)
+     CUMULATIVE_ARGS *cum;
+     tree fntype, libname;
+     int indirect;
+{
+  cum->words = 0;
+  cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype);
+  cum->libcall_p = fntype == 0;
+}
 
-#if SPARC_ARCH64
+/* Compute the slot number to pass an argument in.
+   Returns the slot number or -1 if passing on the stack.
+
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).
+   INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
+   *PREGNO records the register number to use if scalar type.
+   *PPADDING records the amount of padding needed in words.  */
+
+static int
+function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+     int incoming_p;
+     int *pregno;
+     int *ppadding;
+{
+  int regbase = (incoming_p
+                ? SPARC_INCOMING_INT_ARG_FIRST
+                : SPARC_OUTGOING_INT_ARG_FIRST);
+  int slotno = cum->words;
+  int regno;
+
+  *ppadding = 0;
+
+  if (type != 0 && TREE_ADDRESSABLE (type))
+    return -1;
+  if (TARGET_ARCH32
+      && type != 0 && mode == BLKmode
+      && TYPE_ALIGN (type) % PARM_BOUNDARY != 0)
+    return -1;
+
+  switch (mode)
+    {
+    case VOIDmode :
+      /* MODE is VOIDmode when generating the actual call.
+        See emit_call_1.  */
+      return -1;
+
+    case QImode : case CQImode :
+    case HImode : case CHImode :
+    case SImode : case CSImode :
+    case DImode : case CDImode :
+      if (slotno >= SPARC_INT_ARG_MAX)
+       return -1;
+      regno = regbase + slotno;
+      break;
+
+    case SFmode : case SCmode :
+    case DFmode : case DCmode :
+    case TFmode : case TCmode :
+      if (TARGET_ARCH32)
+       {
+         if (slotno >= SPARC_INT_ARG_MAX)
+           return -1;
+         regno = regbase + slotno;
+       }
+      else
+       {
+         if ((mode == TFmode || mode == TCmode)
+             && (slotno & 1) != 0)
+           slotno++, *ppadding = 1;
+         if (TARGET_FPU && named)
+           {
+             if (slotno >= SPARC_FP_ARG_MAX)
+               return 0;
+             regno = SPARC_FP_ARG_FIRST + slotno * 2;
+             if (mode == SFmode)
+               regno++;
+           }
+         else
+           {
+             if (slotno >= SPARC_INT_ARG_MAX)
+               return -1;
+             regno = regbase + slotno;
+           }
+       }
+      break;
+
+    case BLKmode :
+      /* For sparc64, objects requiring 16 byte alignment get it.  */
+      if (TARGET_ARCH64)
+       {
+         if (type && TYPE_ALIGN (type) == 128 && (slotno & 1) != 0)
+           slotno++, *ppadding = 1;
+       }
+
+      if (TARGET_ARCH32
+         || type && TREE_CODE (type) == UNION_TYPE)
+       {
+         if (slotno >= SPARC_INT_ARG_MAX)
+           return -1;
+         regno = regbase + slotno;
+       }
+      else
+       {
+         tree field;
+         int intregs_p = 0, fpregs_p = 0;
+         /* The ABI obviously doesn't specify how packed
+            structures are passed.  These are defined to be passed
+            in int regs if possible, otherwise memory.  */
+         int packed_p = 0;
+
+         /* First see what kinds of registers we need.  */
+         for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+           {
+             if (TREE_CODE (field) == FIELD_DECL)
+               {
+                 if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                     && TARGET_FPU)
+                   fpregs_p = 1;
+                 else
+                   intregs_p = 1;
+                 if (DECL_PACKED (field))
+                   packed_p = 1;
+               }
+           }
+         if (packed_p || !named)
+           fpregs_p = 0, intregs_p = 1;
+
+         /* If all arg slots are filled, then must pass on stack.  */
+         if (fpregs_p && slotno >= SPARC_FP_ARG_MAX)
+           return -1;
+         /* If there are only int args and all int arg slots are filled,
+            then must pass on stack.  */
+         if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX)
+           return -1;
+         /* Note that even if all int arg slots are filled, fp members may
+            still be passed in regs if such regs are available.
+            *PREGNO isn't set because there may be more than one, it's up
+            to the caller to compute them.  */
+         return slotno;
+       }
+      break;
+
+    default :
+      abort ();
+    }
+
+  *pregno = regno;
+  return slotno;
+}
+
+/* Handle the FUNCTION_ARG macro.
+   Determine where to put an argument to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).
+   INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.  */
 
 rtx
-sparc_builtin_saveregs (arglist)
-     tree arglist;
+function_arg (cum, mode, type, named, incoming_p)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+     int incoming_p;
 {
-  tree fntype = TREE_TYPE (current_function_decl);
-  /* First unnamed integer register.  */
-  int first_intreg = current_function_args_info.arg_count[(int) SPARC_ARG_INT];
-  /* Number of integer registers we need to save.  */
-  int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
-  /* First unnamed SFmode float reg (no, you can't pass SFmode floats as
-     unnamed arguments, we just number them that way).  We must round up to
-     the next double word float reg - that is the first one to save.  */
-  int first_floatreg = current_function_args_info.arg_count[(int) SPARC_ARG_FLOAT] + 1 & ~1;
-  /* Number of SFmode float regs to save.  */
-  int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
-  int ptrsize = GET_MODE_SIZE (Pmode);
-  rtx valist, regbuf, fpregs;
-  int bufsize, adjust, regno;
-
-  /* Allocate block of memory for the regs.
-     We only allocate as much as we need, but we must ensure quadword float
-     regs are stored with the appropriate alignment.  */
-  /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
-     Or can assign_stack_local accept a 0 SIZE argument?  */
-
-  bufsize = (n_intregs * UNITS_PER_WORD) + 
-           (TARGET_FPU ? (n_floatregs * (UNITS_PER_WORD / 2)) : 0);
-  /* Add space in front of the int regs to ensure proper alignment of quadword
-     fp regs.  We must add the space in front because va_start assumes this.  */
-  if (TARGET_FPU && n_floatregs >= 4)
-    adjust = ((n_intregs + first_floatreg / 2) % 2) * UNITS_PER_WORD;
+  int regbase = (incoming_p
+                ? SPARC_INCOMING_INT_ARG_FIRST
+                : SPARC_OUTGOING_INT_ARG_FIRST);
+  int slotno, regno, padding;
+  rtx reg;
+
+  slotno = function_arg_slotno (cum, mode, type, named, incoming_p,
+                               &regno, &padding);
+
+  if (slotno == -1)
+    return 0;
+
+  if (TARGET_ARCH32)
+    {
+      reg = gen_rtx (REG, mode, regno);
+      return reg;
+    }
+
+  /* v9 fp args in reg slots beyond the int reg slots get passed in regs
+     but also have the slot allocated for them.
+     If no prototype is in scope fp values in register slots get passed
+     in two places, either fp regs and int regs or fp regs and memory.  */
+  if ((GET_MODE_CLASS (mode) == MODE_FLOAT
+       || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+      && SPARC_FP_REG_P (regno))
+    {
+      reg = gen_rtx (REG, mode, regno);
+      if (cum->prototype_p || cum->libcall_p)
+       {
+         /* "* 2" because fp reg numbers are recorded in 4 byte
+            quantities.  */
+         /* ??? This will cause the value to be passed in the fp reg and
+            in the stack.  When a prototype exists we want to pass the
+            value in the reg but reserve space on the stack.  That's an
+            optimization, and is defered [for a bit].  */
+         if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
+           return gen_rtx (PARALLEL, mode,
+                           gen_rtvec (2,
+                                      gen_rtx (EXPR_LIST, VOIDmode,
+                                               NULL_RTX, const0_rtx),
+                                      gen_rtx (EXPR_LIST, VOIDmode,
+                                               reg, const0_rtx)));
+         else
+           return reg;
+       }
+      else
+       {
+         if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
+           {
+             int regbase = (incoming_p
+                            ? SPARC_INCOMING_INT_ARG_FIRST
+                            : SPARC_OUTGOING_INT_ARG_FIRST);
+             int intreg = regbase + (regno - SPARC_FP_ARG_FIRST) / 2;
+             return gen_rtx (PARALLEL, mode,
+                             gen_rtvec (2,
+                                        gen_rtx (EXPR_LIST, VOIDmode,
+                                                 gen_rtx (REG, mode, intreg),
+                                                 const0_rtx),
+                                        gen_rtx (EXPR_LIST, VOIDmode,
+                                                 reg, const0_rtx)));
+           }
+         else
+           return gen_rtx (PARALLEL, mode,
+                           gen_rtvec (2,
+                                      gen_rtx (EXPR_LIST, VOIDmode,
+                                               NULL_RTX, const0_rtx),
+                                      gen_rtx (EXPR_LIST, VOIDmode,
+                                               reg, const0_rtx)));
+       }
+    }
+  else if (type && TREE_CODE (type) == RECORD_TYPE)
+    {
+      /* Structures up to 16 bytes in size are passed in arg slots on the
+        stack and are promoted to registers where possible.  */
+      tree field;
+      rtx ret;
+      int i;
+      int nregs;
+      /* Starting bit position of a sequence of integer fields, counted from
+        msb of left most byte, -1 if last field wasn't an int.  */
+      /* ??? This isn't entirely necessary, some simplification
+        may be possible.  */
+      int start_int_bitpos;
+      /* Current bitpos in struct, counted from msb of left most byte.  */
+      int bitpos, this_slotno;
+      /* The ABI obviously doesn't specify how packed
+        structures are passed.  These are defined to be passed
+        in int regs if possible, otherwise memory.  */
+      int packed_p = 0;
+
+      if (int_size_in_bytes (type) > 16)
+       abort (); /* shouldn't get here */
+
+      /* We need to compute how many registers are needed so we can allocate
+        the PARALLEL but before we can do that we need to know whether there
+        are any packed fields.  If there are, int regs are used regardless of
+        whether there are fp values present.  */
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       {
+         if (TREE_CODE (field) == FIELD_DECL
+             && DECL_PACKED (field))
+           {
+             packed_p = 1;
+             break;
+           }
+       }
+
+      /* Compute how many registers we need.  */
+      nregs = 0;
+      start_int_bitpos = -1;
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       {
+         bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+         this_slotno = slotno + bitpos / BITS_PER_WORD;
+         if (TREE_CODE (field) == FIELD_DECL)
+           {
+             if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                 && TARGET_FPU
+                 && ! packed_p
+                 && named)
+               {
+                 /* There's no need to check this_slotno < SPARC_FP_ARG MAX.
+                    If it wasn't true we wouldn't be here.  */
+                 nregs++;
+                 start_int_bitpos = -1;
+               }
+             else if (this_slotno < SPARC_INT_ARG_MAX)
+               {
+                 if (start_int_bitpos == -1)
+                   {
+                     nregs++;
+                     start_int_bitpos = bitpos;
+                   }
+                 else
+                   {
+                     if (bitpos % BITS_PER_WORD == 0)
+                       nregs++;
+                   }
+               }
+           }
+       }
+      if (nregs == 0)
+       abort ();
+
+      ret = gen_rtx (PARALLEL, BLKmode, rtvec_alloc (nregs + 1));
+
+      /* ??? This causes the entire struct to be passed in memory.
+        This isn't necessary, but is left for later.  */
+      XVECEXP (ret, 0, 0) = gen_rtx (EXPR_LIST, VOIDmode, NULL_RTX,
+                                    const0_rtx);
+
+      /* Fill in the entries.  */
+      start_int_bitpos = -1;
+      for (i = 1, field = TYPE_FIELDS (type);
+          field;
+          field = TREE_CHAIN (field))
+       {
+         bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+         this_slotno = slotno + bitpos / BITS_PER_WORD;
+         if (TREE_CODE (field) == FIELD_DECL)
+           {
+             if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                 && TARGET_FPU
+                 && ! packed_p
+                 && named)
+               {
+                 reg = gen_rtx (REG, DECL_MODE (field),
+                                (SPARC_FP_ARG_FIRST + this_slotno * 2
+                                 + (DECL_MODE (field) == SFmode
+                                    && (bitpos & 32) != 0)));
+                 XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
+                                                GEN_INT (bitpos / BITS_PER_UNIT));
+                 i++;
+                 start_int_bitpos = -1;
+               }
+             else
+               {
+                 if (this_slotno < SPARC_INT_ARG_MAX
+                     && (start_int_bitpos == -1
+                         || bitpos % BITS_PER_WORD == 0))
+                   {
+                     enum machine_mode mode;
+
+                     /* If this is the trailing part of a word, only load
+                        that much into the register.  Otherwise load the
+                        whole register.  Note that in the latter case we may
+                        pick up unwanted bits.  It's not a problem at the
+                        moment but may wish to revisit.  */
+                     if (bitpos % BITS_PER_WORD != 0)
+                       mode = mode_for_size (BITS_PER_WORD - bitpos % BITS_PER_WORD,
+                                             MODE_INT, 0);
+                     else
+                       mode = word_mode;
+
+                     regno = regbase + this_slotno;
+                     reg = gen_rtx (REG, mode, regno);
+                     XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
+                                                    GEN_INT (bitpos / BITS_PER_UNIT));
+                     i++;
+                     if (start_int_bitpos == -1)
+                       start_int_bitpos = bitpos;
+                   }
+               }
+           }
+       }
+      if (i != nregs + 1)
+       abort ();
+
+      return ret;
+    }
+  else if (type && TREE_CODE (type) == UNION_TYPE)
+    {
+      enum machine_mode mode;
+      int bytes = int_size_in_bytes (type);
+
+      if (bytes > 16)
+       abort ();
+
+      mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
+      reg = gen_rtx (REG, mode, regno);
+    }
   else
-    adjust = 0;
+    {
+      /* Scalar or complex int.  */
+      reg = gen_rtx (REG, mode, regno);
+    }
+
+  return reg;
+}
 
-  regbuf = assign_stack_local (BLKmode, bufsize + adjust,
-                              GET_MODE_BITSIZE (TFmode));
-  regbuf = gen_rtx (MEM, BLKmode, plus_constant (XEXP (regbuf, 0), adjust));
-  MEM_IN_STRUCT_P (regbuf) = 1;
+/* Handle the FUNCTION_ARG_PARTIAL_NREGS macro.
+   For an arg passed partly in registers and partly in memory,
+   this is the number of registers used.
+   For args passed entirely in registers or entirely in memory, zero.
+
+   Any arg that starts in the first 6 regs but won't entirely fit in them
+   needs partial registers on v8.  On v9, structures with integer
+   values in arg slots 5,6 will be passed in %o5 and SP+176, and complex fp
+   values that begin in the last fp reg [where "last fp reg" varies with the
+   mode] will be split between that reg and memory.  */
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int slotno, regno, padding;
 
-  /* Save int args.
-     This is optimized to only save the regs that are necessary.  Explicitly
-     named args need not be saved.  */
+  /* We pass 0 for incoming_p here, it doesn't matter.  */
+  slotno = function_arg_slotno (cum, mode, type, named, 0, &regno, &padding);
 
-  if (n_intregs > 0)
-    move_block_from_reg (BASE_INCOMING_ARG_REG (SImode) + first_intreg,
-                        regbuf, n_intregs, n_intregs * UNITS_PER_WORD);
+  if (slotno == -1)
+    return 0;
 
-  if (TARGET_FPU) 
+  if (TARGET_ARCH32)
     {
-      /* Save float args.
-         This is optimized to only save the regs that are necessary.
-        Explicitly named args need not be saved.
-         We explicitly build a pointer to the buffer because it halves the insn
-         count when not optimizing (otherwise the pointer is built for each reg
-         saved).  */
+      if ((slotno + (mode == BLKmode
+                    ? ROUND_ADVANCE (int_size_in_bytes (type))
+                    : ROUND_ADVANCE (GET_MODE_SIZE (mode))))
+         > NPARM_REGS (SImode))
+       return NPARM_REGS (SImode) - slotno;
+      return 0;
+    }
+  else
+    {
+      if (type && AGGREGATE_TYPE_P (type))
+       {
+         int size = int_size_in_bytes (type);
+         int align = TYPE_ALIGN (type);
 
-      fpregs = gen_reg_rtx (Pmode);
-      emit_move_insn (fpregs, plus_constant (XEXP (regbuf, 0),
-                                            n_intregs * UNITS_PER_WORD));
-      for (regno = first_floatreg; regno < NPARM_REGS (SFmode); regno += 2)
-        emit_move_insn (gen_rtx (MEM, DFmode,
-                                plus_constant (fpregs,
-                                               GET_MODE_SIZE (SFmode)
-                                               * (regno - first_floatreg))),
-                       gen_rtx (REG, DFmode,
-                                BASE_INCOMING_ARG_REG (DFmode) + regno));
+         if (align == 16)
+           slotno += slotno & 1;
+         if (size > 8 && size <= 16
+             && slotno == SPARC_INT_ARG_MAX - 1)
+           return 1;
+       }
+      else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
+              || (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+                  && ! TARGET_FPU))
+       {
+         if (GET_MODE_ALIGNMENT (mode) == 128)
+           {
+             slotno += slotno & 1;
+             if (slotno == SPARC_INT_ARG_MAX - 2)
+               return 1;
+           }
+         else
+           {
+             if (slotno == SPARC_INT_ARG_MAX - 1)
+               return 1;
+           }
+       }
+      else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+       {
+         if (GET_MODE_ALIGNMENT (mode) == 128)
+           slotno += slotno & 1;
+         if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD)
+             > SPARC_FP_ARG_MAX)
+           return 1;
+       }
+      return 0;
     }
+}
+
+/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
+   !v9: The SPARC ABI stipulates passing struct arguments (of any size) and
+   quad-precision floats by invisible reference.
+   v9: aggregates greater than 16 bytes are passed by reference.
+   For Pascal, also pass arrays by reference.  */
 
-  if (flag_check_memory_usage)
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  if (TARGET_ARCH32)
+    {
+      return (type && AGGREGATE_TYPE_P (type)
+             || mode == TFmode || mode == TCmode);
+    }
+  else
     {
-      emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, regbuf,
-                ptr_mode, GEN_INT (n_intregs * UNITS_PER_WORD),
-                        TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), QImode);
+      return ((type && TREE_CODE (type) == ARRAY_TYPE)
+             || (type && AGGREGATE_TYPE_P (type)
+                 && int_size_in_bytes (type) > 16));
+    }
+}
+
+/* Handle the FUNCTION_ARG_ADVANCE macro.
+   Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   TYPE is null for libcalls where that information may not be available.  */
+
+void
+function_arg_advance (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int slotno, regno, padding;
 
-      emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
-                        fpregs, ptr_mode,
-                        GEN_INT (UNITS_PER_WORD 
-                                 * GET_MODE_SIZE (SFmode)
-                                 * (NPARM_REGS (SFmode) - first_floatreg)),
-                        TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), QImode);
+  /* We pass 0 for incoming_p here, it doesn't matter.  */
+  slotno = function_arg_slotno (cum, mode, type, named, 0, &regno, &padding);
+
+  /* If register required leading padding, add it.  */
+  if (slotno != -1)
+    cum->words += padding;
+
+  if (TARGET_ARCH32)
+    {
+      cum->words += (mode != BLKmode
+                    ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
+                    : ROUND_ADVANCE (int_size_in_bytes (type)));
     }
+  else
+    {
+      if (type && AGGREGATE_TYPE_P (type))
+       {
+         int size = int_size_in_bytes (type);
+
+         if (size <= 8)
+           ++cum->words;
+         else if (size <= 16)
+           cum->words += 2;
+         else /* passed by reference */
+           ++cum->words;
+       }
+      else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT)
+       {
+         cum->words += 2;
+       }
+      else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+       {
+         cum->words += GET_MODE_SIZE (mode) / UNITS_PER_WORD;
+       }
+      else
+       {
+         cum->words += (mode != BLKmode
+                        ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
+                        : ROUND_ADVANCE (int_size_in_bytes (type)));
+       }
+    }
+}
 
-  /* Return the address of the regbuf.  */
+/* Handle the FUNCTION_ARG_PADDING macro.
+   For the 64 bit ABI structs are always stored left shifted in their
+   argument slot.  */
 
-  return XEXP (regbuf, 0);
+enum direction
+function_arg_padding (mode, type)
+     enum machine_mode mode;
+     tree type;
+{
+  if (TARGET_ARCH64 && type && TREE_CODE (type) == RECORD_TYPE)
+    {
+      return upward;
+    }
+
+  /* This is the default definition.  */
+  return (! BYTES_BIG_ENDIAN
+         ? upward
+         : ((mode == BLKmode
+             ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+                && int_size_in_bytes (type) < (PARM_BOUNDARY / BITS_PER_UNIT))
+             : GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
+            ? downward : upward));
 }
+\f
+/* Do what is necessary for `va_start'.  The argument is ignored.
 
-#else /* ! SPARC_ARCH64 */
+   We look at the current function to determine if stdarg or varargs
+   is used and return the address of the first unnamed parameter.  */
 
 rtx
 sparc_builtin_saveregs (arglist)
@@ -3513,41 +4176,34 @@ sparc_builtin_saveregs (arglist)
   int stdarg = (TYPE_ARG_TYPES (fntype) != 0
                && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
                    != void_type_node));
-  int first_reg = current_function_args_info;
+  int first_reg = current_function_args_info.words;
   rtx address;
   int regno;
 
-#if 0 /* This code seemed to have no effect except to make
-        varargs not work right when va_list wasn't the first arg.  */
-  if (! stdarg)
-    first_reg = 0;
-#endif
-
-  for (regno = first_reg; regno < NPARM_REGS (SImode); regno++)
+  for (regno = first_reg; regno < NPARM_REGS (word_mode); regno++)
     emit_move_insn (gen_rtx (MEM, word_mode,
                             gen_rtx (PLUS, Pmode,
                                      frame_pointer_rtx,
                                      GEN_INT (STACK_POINTER_OFFSET
                                               + UNITS_PER_WORD * regno))),
-                   gen_rtx (REG, word_mode, BASE_INCOMING_ARG_REG (word_mode)
-                            + regno));
+                   gen_rtx (REG, word_mode,
+                            BASE_INCOMING_ARG_REG (word_mode) + regno));
 
   address = gen_rtx (PLUS, Pmode,
                     frame_pointer_rtx,
                     GEN_INT (STACK_POINTER_OFFSET
                              + UNITS_PER_WORD * first_reg));
 
-  if (flag_check_memory_usage)
+  if (flag_check_memory_usage
+      && first_reg < NPARM_REGS (word_mode))
     emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
                       address, ptr_mode,
                       GEN_INT (UNITS_PER_WORD 
-                               * (NPARM_REGS (SImode) - first_reg)),
+                               * (NPARM_REGS (word_mode) - first_reg)),
                       TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), QImode);
 
   return address;
 }
-
-#endif /* ! SPARC_ARCH64 */
 \f
 /* Return the string to output a conditional branch to LABEL, which is
    the operand number of the label.  OP is the conditional expression.
index 46e83b1..205f59b 100644 (file)
@@ -612,6 +612,34 @@ extern int sparc_align_funcs;
    See also the macro `Pmode' defined below.  */
 #define POINTER_SIZE (TARGET_PTR64 ? 64 : 32)
 
+/* A macro to update MODE and UNSIGNEDP when an object whose type
+   is TYPE and which has the specified mode and signedness is to be
+   stored in a register.  This macro is only called when TYPE is a
+   scalar type.  */
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+if (TARGET_ARCH64                              \
+    && GET_MODE_CLASS (MODE) == MODE_INT       \
+    && GET_MODE_SIZE (MODE) < UNITS_PER_WORD)  \
+{                                              \
+  (MODE) = DImode;                             \
+}
+
+/* Define this macro if the promotion described by PROMOTE_MODE
+   should also be done for outgoing function arguments.  */
+/* This is only needed for TARGET_ARCH64, but since PROMOTE_MODE is a no-op
+   for TARGET_ARCH32 this is ok.  Otherwise we'd need to add a runtime test
+   for this value.  */
+#define PROMOTE_FUNCTION_ARGS
+
+/* Define this macro if the promotion described by PROMOTE_MODE
+   should also be done for the return value of functions.
+   If this macro is defined, FUNCTION_VALUE must perform the same
+   promotions done by PROMOTE_MODE.  */
+/* This is only needed for TARGET_ARCH64, but since PROMOTE_MODE is a no-op
+   for TARGET_ARCH32 this is ok.  Otherwise we'd need to add a runtime test
+   for this value.  */
+#define PROMOTE_FUNCTION_RETURN
+
 /* Allocation boundary (in *bits*) for storing arguments in argument list.  */
 #define PARM_BOUNDARY (TARGET_ARCH64 ? 64 : 32)
 
@@ -732,9 +760,10 @@ extern int sparc_align_funcs;
 
 #define FIRST_PSEUDO_REGISTER 101
 
+#define SPARC_FIRST_FP_REG     32
 /* Additional V9 fp regs.  */
-#define SPARC_FIRST_V9_FP_REG 64
-#define SPARC_LAST_V9_FP_REG  95
+#define SPARC_FIRST_V9_FP_REG  64
+#define SPARC_LAST_V9_FP_REG   95
 /* V9 %fcc[0123].  V8 uses (figuratively) %fcc0.  */
 #define SPARC_FIRST_V9_FCC_REG 96
 #define SPARC_LAST_V9_FCC_REG  99
@@ -743,16 +772,28 @@ extern int sparc_align_funcs;
 /* Integer CC reg.  We don't distinguish %icc from %xcc.  */
 #define SPARC_ICC_REG 100
 
+/* Nonzero if REGNO is an fp reg.  */
+#define SPARC_FP_REG_P(REGNO) \
+((REGNO) >= SPARC_FIRST_FP_REG && (REGNO) <= SPARC_LAST_V9_FP_REG)
+
+/* Argument passing regs.  */
+#define SPARC_OUTGOING_INT_ARG_FIRST 8
+#define SPARC_INCOMING_INT_ARG_FIRST 24
+#define SPARC_FP_ARG_FIRST           32
+
 /* 1 for registers that have pervasive standard uses
    and are not available for the register allocator.
+
    On non-v9 systems:
    g1 is free to use as temporary.
    g2-g4 are reserved for applications.  Gcc normally uses them as
    temporaries, but this can be disabled via the -mno-app-regs option.
    g5 through g7 are reserved for the operating system.
+
    On v9 systems:
-   g1 and g5 are free to use as temporaries.
-   g2-g4 are reserved for applications.  Gcc normally uses them as
+   g1,g4,g5 are free to use as temporaries.
+   g1,g5 are free to use between calls if call is to external function via PLT.
+   g2-g3 are reserved for applications.  Gcc normally uses them as
    temporaries, but this can be disabled via the -mno-app-regs option.
    g6-g7 are reserved for the operating system.
    ??? Register 1 is used as a temporary by the 64 bit sethi pattern, so must
@@ -819,11 +860,7 @@ do                                                         \
       }                                                                \
     if (SPARC_ARCH64)                                          \
       {                                                                \
-       int regno;                                              \
        fixed_regs[1] = 1;                                      \
-       /* ??? We need to scan argv for -fcall-used-.  */       \
-       for (regno = 48; regno < 80; regno++)                   \
-         call_used_regs[regno] = 0;                            \
       }                                                                \
     if (! TARGET_V9)                                           \
       {                                                                \
@@ -916,11 +953,12 @@ extern int sparc_mode_class[];
 /* Register to use for pushing function arguments.  */
 #define STACK_POINTER_REGNUM 14
 
-/* Actual top-of-stack address is 92/136 greater than the contents of the
+/* Actual top-of-stack address is 92/176 greater than the contents of the
    stack pointer register for !v9/v9.  That is:
    - !v9: 64 bytes for the in and local registers, 4 bytes for structure return
-     address, and 24 bytes for the 6 register parameters.
-   - v9: 128 bytes for the in and local registers + 8 bytes reserved.  */
+     address, and 6*4 bytes for the 6 register parameters.
+   - v9: 128 bytes for the in and local registers + 6*8 bytes for the integer
+     parameter regs.  */
 #define STACK_POINTER_OFFSET FIRST_PARM_OFFSET(0)
 
 /* The stack bias (amount by which the hardware register is offset by).  */
@@ -978,11 +1016,16 @@ extern int sparc_mode_class[];
 
 /* Sparc ABI says that quad-precision floats and all structures are returned
    in memory.
-   For v9, all aggregates are returned in memory.  */
+   For v9: unions <= 32 bytes in size are returned in int regs,
+   structures up to 32 bytes are returned in int and fp regs.
+   FIXME: wip */
+
 #define RETURN_IN_MEMORY(TYPE)                         \
-  (TYPE_MODE (TYPE) == BLKmode                         \
-   || (! TARGET_ARCH64 && (TYPE_MODE (TYPE) == TFmode  \
-                           || TYPE_MODE (TYPE) == TCmode)))
+(TARGET_ARCH32                                         \
+ ? (TYPE_MODE (TYPE) == BLKmode                                \
+    || TYPE_MODE (TYPE) == TFmode                      \
+    || TYPE_MODE (TYPE) == TCmode)                     \
+ : TYPE_MODE (TYPE) == BLKmode)
 
 /* Functions which return large structures get the address
    to place the wanted value at offset 64 from the frame.
@@ -1025,7 +1068,9 @@ extern int sparc_mode_class[];
    For any two classes, it is very desirable that there be another
    class that represents their union.  */
 
-/* The SPARC has two kinds of registers, general and floating point.
+/* The SPARC has various kinds of registers: general, floating point,
+   and condition codes [well, it has others as well, but none that we
+   care directly about].
 
    For v9 we must distinguish between the upper and lower floating point
    registers because the upper ones can't hold SFmode values.
@@ -1092,10 +1137,7 @@ extern enum reg_class sparc_regno_reg_class[];
    We put %f0/%f1 last among the float registers, so as to make it more
    likely that a pseudo-register which dies in the float return register
    will get allocated to the float return register, thus saving a move
-   instruction at the end of the function.
-
-   The float registers are ordered a little "funny" because in the 64 bit
-   architecture, some of them (%f16-%f47) are call-preserved.  */
+   instruction at the end of the function.  */
 
 #define REG_ALLOC_ORDER \
 { 8, 9, 10, 11, 12, 13, 2, 3,          \
@@ -1103,21 +1145,19 @@ extern enum reg_class sparc_regno_reg_class[];
   23, 24, 25, 26, 27, 28, 29, 31,      \
   34, 35, 36, 37, 38, 39,              /* %f2-%f7 */   \
   40, 41, 42, 43, 44, 45, 46, 47,      /* %f8-%f15 */  \
-  80, 81, 82, 83, 84, 85, 86, 87,      /* %f48-%f55 */ \
-  88, 89, 90, 91, 92, 93, 94, 95,      /* %f56-%f63 */ \
   48, 49, 50, 51, 52, 53, 54, 55,      /* %f16-%f23 */ \
   56, 57, 58, 59, 60, 61, 62, 63,      /* %f24-%f31 */ \
   64, 65, 66, 67, 68, 69, 70, 71,      /* %f32-%f39 */ \
   72, 73, 74, 75, 76, 77, 78, 79,      /* %f40-%f47 */ \
+  80, 81, 82, 83, 84, 85, 86, 87,      /* %f48-%f55 */ \
+  88, 89, 90, 91, 92, 93, 94, 95,      /* %f56-%f63 */ \
   32, 33,                              /* %f0,%f1 */   \
   96, 97, 98, 99, 100,                 /* %fcc0-3, %icc */ \
   1, 4, 5, 6, 7, 0, 14, 30}
 
 /* This is the order in which to allocate registers for
    leaf functions.  If all registers can fit in the "i" registers,
-   then we have the possibility of having a leaf function.
-   The floating point registers are ordered a little "funny" because in the
-   64 bit architecture some of them (%f16-%f47) are call-preserved.   */
+   then we have the possibility of having a leaf function.  */
 
 #define REG_LEAF_ALLOC_ORDER \
 { 2, 3, 24, 25, 26, 27, 28, 29,                \
@@ -1125,12 +1165,12 @@ extern enum reg_class sparc_regno_reg_class[];
   16, 17, 18, 19, 20, 21, 22, 23,      \
   34, 35, 36, 37, 38, 39,              \
   40, 41, 42, 43, 44, 45, 46, 47,      \
-  80, 81, 82, 83, 84, 85, 86, 87,      \
-  88, 89, 90, 91, 92, 93, 94, 95,      \
   48, 49, 50, 51, 52, 53, 54, 55,      \
   56, 57, 58, 59, 60, 61, 62, 63,      \
   64, 65, 66, 67, 68, 69, 70, 71,      \
   72, 73, 74, 75, 76, 77, 78, 79,      \
+  80, 81, 82, 83, 84, 85, 86, 87,      \
+  88, 89, 90, 91, 92, 93, 94, 95,      \
   32, 33,                              \
   96, 97, 98, 99, 100,                 \
   1, 4, 5, 6, 7, 0, 14, 30, 31}
@@ -1293,13 +1333,15 @@ extern char leaf_reg_remap[];
 /* Stack layout; function entry, exit and calling.  */
 
 /* Define the number of register that can hold parameters.
-   These two macros are used only in other macro definitions below.
+   This macro is only used in other macro definitions below and in sparc.c.
    MODE is the mode of the argument.
    !v9: All args are passed in %o0-%o5.
-   v9: Non-float args are passed in %o0-5 and float args are passed in
-   %f0-%f15.  */
+   v9: %o0-%o5 and %f0-%f31 are cumulatively used to pass values.
+   See the description in sparc.c.  */
 #define NPARM_REGS(MODE) \
-  (TARGET_ARCH64 ? (GET_MODE_CLASS (MODE) == MODE_FLOAT ? 16 : 6) : 6)
+(TARGET_ARCH64 \
+ ? (GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 : 6) \
+ : 6)
 
 /* Define this if pushing a word on the stack
    makes the stack pointer a smaller address.  */
@@ -1328,16 +1370,16 @@ extern char leaf_reg_remap[];
 /* Offset of first parameter from the argument pointer register value.
    !v9: This is 64 for the ins and locals, plus 4 for the struct-return reg
    even if this function isn't going to use it.
-   v9: This is 128 for the ins and locals, plus a reserved space of 8.  */
+   v9: This is 128 for the ins and locals.  */
 #define FIRST_PARM_OFFSET(FNDECL) \
-  (TARGET_ARCH64 ? (SPARC_STACK_BIAS + 136) \
+  (TARGET_ARCH64 ? (SPARC_STACK_BIAS + 16 * UNITS_PER_WORD) \
    : (STRUCT_VALUE_OFFSET + UNITS_PER_WORD))
 
 /* When a parameter is passed in a register, stack space is still
    allocated for it.  */
-#if ! SPARC_ARCH64
-#define REG_PARM_STACK_SPACE(DECL) (NPARM_REGS (SImode) * UNITS_PER_WORD)
-#endif
+/* This only takes into account the int regs.
+   fp regs are handled elsewhere.  */
+#define REG_PARM_STACK_SPACE(DECL) (6 * UNITS_PER_WORD)
 
 /* Keep the stack pointer constant throughout the function.
    This is both an optimization and a necessity: longjmp
@@ -1427,11 +1469,12 @@ extern char leaf_reg_remap[];
 #define APPLY_RESULT_SIZE 16
 
 /* 1 if N is a possible register number for function argument passing.
-   On SPARC, these are the "output" registers.  v9 also uses %f0-%f15.  */
+   On SPARC, these are the "output" registers.  v9 also uses %f0-%f31.  */
 
 #define FUNCTION_ARG_REGNO_P(N) \
-  (TARGET_ARCH64 ? (((N) < 14 && (N) > 7) || (N) > 31 && (N) < 48) \
-   : ((N) < 14 && (N) > 7))
+(TARGET_ARCH64 \
+ ? (((N) >= 8 && (N) <= 13) || ((N) >= 32 && (N) <= 63)) \
+ : ((N) >= 8 && (N) <= 13))
 \f
 /* Define a data type for recording info about an argument list
    during the scan of that argument list.  This data type should
@@ -1444,129 +1487,30 @@ extern char leaf_reg_remap[];
    if any, which holds the structure-value-address).
    Thus 7 or more means all following args should go on the stack.
 
-   For v9, we record how many of each type has been passed.  Different
-   types get passed differently.
-
-       - Float args are passed in %f0-15, after which they go to the stack
-         where floats and doubles are passed 8 byte aligned and long doubles
-         are passed 16 byte aligned.
-       - All aggregates are passed by reference.  The callee copies
-         the structure if necessary, except if stdarg/varargs and the struct
-         matches the ellipse in which case the caller makes a copy.
-       - Any non-float argument might be split between memory and reg %o5.
-         ??? I don't think this can ever happen now that structs are no
-         longer passed in regs.
-
-   For v9 return values:
-
-       - For all aggregates, the caller allocates space for the return value,
-          and passes the pointer as an implicit first argument, which is
-          allocated like all other arguments.
-       - The unimp instruction stuff for structure returns is gone.  */
+   For v9, we also need to know whether a prototype is present.  */
 
-#if SPARC_ARCH64
-enum sparc_arg_class { SPARC_ARG_INT = 0, SPARC_ARG_FLOAT = 1 };
 struct sparc_args {
-    int arg_count[2];  /* must be int! (for __builtin_args_info) */
+  int words;       /* number of words passed so far */
+  int prototype_p; /* non-zero if a prototype is present */
+  int libcall_p;   /* non-zero if a library call */
 };
 #define CUMULATIVE_ARGS struct sparc_args
 
-/* Return index into CUMULATIVE_ARGS.  */
-
-#define GET_SPARC_ARG_CLASS(MODE) \
-  (GET_MODE_CLASS (MODE) == MODE_FLOAT ? SPARC_ARG_FLOAT : SPARC_ARG_INT)
-
-/* Round a register number up to a proper boundary for an arg of mode MODE.
-   This macro is only used in this file.
-
-   The "& (0x10000 - ...)" is used to round up to the next appropriate reg.  */
-
-#define ROUND_REG(CUM, MODE)                           \
-  (GET_MODE_CLASS (MODE) != MODE_FLOAT                 \
-   ? (CUM).arg_count[(int) GET_SPARC_ARG_CLASS (MODE)] \
-   : ((CUM).arg_count[(int) GET_SPARC_ARG_CLASS (MODE)]        \
-      + GET_MODE_UNIT_SIZE (MODE) / 4 - 1)             \
-     & (0x10000 - GET_MODE_UNIT_SIZE (MODE) / 4))
-
-#define ROUND_ADVANCE(SIZE)    \
-  (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
-
-#else /* ! SPARC_ARCH64 */
-
-#define CUMULATIVE_ARGS int
-
-#define ROUND_REG(CUM, MODE) (CUM)
-
-#define ROUND_ADVANCE(SIZE)    \
-  ((SIZE + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
-#endif /* ! SPARC_ARCH64 */
-
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
    for a call to a function whose data type is FNTYPE.
-   For a library call, FNTYPE is 0.
-
-   On SPARC, the offset always starts at 0: the first parm reg is always
-   the same reg.  */
+   For a library call, FNTYPE is 0.  */
 
-#if SPARC_ARCH64
-extern int sparc_arg_count,sparc_n_named_args;
-#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT)      \
-  do {                                                         \
-    (CUM).arg_count[(int) SPARC_ARG_INT] = 0;                  \
-    (CUM).arg_count[(int) SPARC_ARG_FLOAT] = 0;                        \
-    sparc_arg_count = 0;                                       \
-    sparc_n_named_args =                                       \
-      ((FNTYPE) && TYPE_ARG_TYPES (FNTYPE)                     \
-       ? (list_length (TYPE_ARG_TYPES (FNTYPE))                        \
-         + (TREE_CODE (TREE_TYPE (FNTYPE)) == RECORD_TYPE      \
-            || TREE_CODE (TREE_TYPE (FNTYPE)) == QUAL_UNION_TYPE\
-            || TREE_CODE (TREE_TYPE (FNTYPE)) == SET_TYPE      \
-            || TREE_CODE (TREE_TYPE (FNTYPE)) == UNION_TYPE))  \
-       /* Can't tell, treat 'em all as named.  */              \
-       : 10000);                                               \
-  } while (0)
-#else
-#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) ((CUM) = 0)
-#endif
+extern void init_cumulative_args ();
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \
+init_cumulative_args (& (CUM), (FNTYPE), (LIBNAME), (INDIRECT));
 
 /* Update the data in CUM to advance over an argument
    of mode MODE and data type TYPE.
-   (TYPE is null for libcalls where that information may not be available.)  */
-
-#if SPARC_ARCH64
-#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)           \
-  do {                                                         \
-    (CUM).arg_count[(int) GET_SPARC_ARG_CLASS (MODE)] =                \
-      ROUND_REG ((CUM), (MODE))                                        \
-       + (GET_MODE_CLASS (MODE) == MODE_FLOAT                  \
-          ? GET_MODE_SIZE (MODE) / 4                           \
-          : ROUND_ADVANCE ((MODE) == BLKmode                   \
-                           ? GET_MODE_SIZE (Pmode)             \
-                           : GET_MODE_SIZE (MODE)));           \
-    sparc_arg_count++;                                         \
-  } while (0)
-#else
-#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)   \
-  ((CUM) += ((MODE) != BLKmode                         \
-            ? ROUND_ADVANCE (GET_MODE_SIZE (MODE))     \
-            : ROUND_ADVANCE (int_size_in_bytes (TYPE))))
-#endif
+   TYPE is null for libcalls where that information may not be available.  */
 
-/* Return boolean indicating arg of mode MODE will be passed in a reg.
-   This macro is only used in this file.  */
-
-#if SPARC_ARCH64
-#define PASS_IN_REG_P(CUM, MODE, TYPE)                         \
-  (ROUND_REG ((CUM), (MODE)) < NPARM_REGS (MODE)               \
-   && ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE)))         \
-   && ((TYPE)==0 || (MODE) != BLKmode))
-#else
-#define PASS_IN_REG_P(CUM, MODE, TYPE)                         \
-  ((CUM) < NPARM_REGS (SImode)                                 \
-   && ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE)))         \
-   && ((TYPE)==0 || (MODE) != BLKmode                          \
-       || (TYPE_ALIGN (TYPE) % PARM_BOUNDARY == 0)))
-#endif
+extern void function_arg_advance ();
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+function_arg_advance (& (CUM), (MODE), (TYPE), (NAMED))
 
 /* Determine where to put an argument to a function.
    Value is zero to push the argument on the stack,
@@ -1581,64 +1525,52 @@ extern int sparc_arg_count,sparc_n_named_args;
    NAMED is nonzero if this argument is a named parameter
     (otherwise it is an extra parameter matching an ellipsis).  */
 
-/* On SPARC the first six args are normally in registers
-   and the rest are pushed.  Any arg that starts within the first 6 words
-   is at least partially passed in a register unless its data type forbids.
-   For v9, the first 6 int args are passed in regs and the first N
-   float args are passed in regs (where N is such that %f0-15 are filled).
-   The rest are pushed.  Any arg that starts within the first 6 words
-   is at least partially passed in a register unless its data type forbids.  */
-
-#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED)                           \
-  (PASS_IN_REG_P ((CUM), (MODE), (TYPE))                               \
-   ? gen_rtx (REG, (MODE),                                             \
-             (BASE_PASSING_ARG_REG (MODE) + ROUND_REG ((CUM), (MODE))))\
-   : 0)
+extern struct rtx_def *function_arg ();
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+function_arg (& (CUM), (MODE), (TYPE), (NAMED), 0)
 
 /* Define where a function finds its arguments.
    This is different from FUNCTION_ARG because of register windows.  */
 
-#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED)                  \
-  (PASS_IN_REG_P ((CUM), (MODE), (TYPE))                               \
-   ? gen_rtx (REG, (MODE),                                             \
-             (BASE_INCOMING_ARG_REG (MODE) + ROUND_REG ((CUM), (MODE))))\
-   : 0)
+#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \
+function_arg (& (CUM), (MODE), (TYPE), (NAMED), 1)
 
 /* For an arg passed partly in registers and partly in memory,
    this is the number of registers used.
-   For args passed entirely in registers or entirely in memory, zero.
-   Any arg that starts in the first 6 regs but won't entirely fit in them
-   needs partial registers on the Sparc (!v9).  On v9, there are no arguments
-   that are passed partially in registers (??? complex values?).  */
-
-#if ! SPARC_ARCH64
-#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED)             \
-  (PASS_IN_REG_P ((CUM), (MODE), (TYPE))                               \
-   && ((CUM) + ((MODE) == BLKmode                                      \
-               ? ROUND_ADVANCE (int_size_in_bytes (TYPE))              \
-               : ROUND_ADVANCE (GET_MODE_SIZE (MODE))) - NPARM_REGS (SImode) > 0)\
-   ? (NPARM_REGS (SImode) - (CUM))                                     \
-   : 0)
-#endif
-
-/* The SPARC ABI stipulates passing struct arguments (of any size) and
-   (!v9) quad-precision floats by invisible reference.
-   For Pascal, also pass arrays by reference.  */
-#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \
-  ((TYPE && AGGREGATE_TYPE_P (TYPE))                           \
-   || (!TARGET_ARCH64 && MODE == TFmode))
-
-/* A C expression that indicates when it is the called function's
-   responsibility to make copies of arguments passed by reference.
-   If the callee can determine that the argument won't be modified, it can
-   avoid the copy.  */
-/* ??? We'd love to be able to use NAMED here.  Unfortunately, it doesn't
-   include the last named argument so we keep track of the args ourselves.  */
-
-#if SPARC_ARCH64
-#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \
-  (sparc_arg_count < sparc_n_named_args)
-#endif
+   For args passed entirely in registers or entirely in memory, zero.  */
+
+extern int function_arg_partial_nregs ();
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+function_arg_partial_nregs (& (CUM), (MODE), (TYPE), (NAMED))
+
+/* A C expression that indicates when an argument must be passed by reference.
+   If nonzero for an argument, a copy of that argument is made in memory and a
+   pointer to the argument is passed instead of the argument itself.
+   The pointer is passed in whatever way is appropriate for passing a pointer
+   to that type.  */
+
+extern int function_arg_pass_by_reference ();
+#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \
+function_arg_pass_by_reference (& (CUM), (MODE), (TYPE), (NAMED))
+
+/* If defined, a C expression which determines whether, and in which direction,
+   to pad out an argument with extra space.  The value should be of type
+   `enum direction': either `upward' to pad above the argument,
+   `downward' to pad below, or `none' to inhibit padding.  */
+extern enum direction function_arg_padding ();
+#define FUNCTION_ARG_PADDING(MODE, TYPE) \
+function_arg_padding ((MODE), (TYPE))
+
+/* If defined, a C expression that gives the alignment boundary, in bits,
+   of an argument with the specified mode and type.  If it is not defined,
+   PARM_BOUNDARY is used for all arguments.
+   For sparc64, objects requiring 16 byte alignment are passed that way.  */
+
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \
+((TARGET_ARCH64                                        \
+  && (GET_MODE_ALIGNMENT (MODE) == 128         \
+      || ((TYPE) && TYPE_ALIGN (TYPE) == 128)))        \
+ ? 128 : PARM_BOUNDARY)
 \f
 /* Initialize data used by insn expanders.  This is called from
    init_emit, once for each function, before code is generated.
@@ -2133,6 +2065,7 @@ extern union tree_node *current_function_decl;
      nop
      .xword context
      .xword function  */
+/* ??? Stack is execute-protected in v9.  */
 
 #define TRAMPOLINE_TEMPLATE(FILE) \
 do {                                                                   \
@@ -2177,9 +2110,24 @@ void sparc64_initialize_trampoline ();
 \f
 /* Generate necessary RTL for __builtin_saveregs().
    ARGLIST is the argument list; see expr.c.  */
+
 extern struct rtx_def *sparc_builtin_saveregs ();
 #define EXPAND_BUILTIN_SAVEREGS(ARGLIST) sparc_builtin_saveregs (ARGLIST)
 
+/* Define this macro if the location where a function argument is passed
+   depends on whether or not it is a named argument.
+
+   This macro controls how the NAMED argument to FUNCTION_ARG
+   is set for varargs and stdarg functions.  With this macro defined,
+   the NAMED argument is always true for named arguments, and false for
+   unnamed arguments.  If this is not defined, but SETUP_INCOMING_VARARGS
+   is defined, then all arguments are treated as named.  Otherwise, all named
+   arguments except the last are treated as named.
+   For the v9 we want NAMED to mean what it says it means.  */
+/* ??? This needn't be set for v8, but I don't want to make this runtime
+   selectable if I don't have to.  */
+#define STRICT_ARGUMENT_NAMING
+
 /* Generate RTL to flush the register windows so as to make arbitrary frames
    available.  */
 #define SETUP_FRAME_ADDRESSES()                \
@@ -2717,8 +2665,7 @@ extern struct rtx_def *legitimize_pic_address ();
     return 8;
 
 /* Compute the cost of an address.  For the sparc, all valid addresses are
-   the same cost.
-   ??? Is this true for v9?  */
+   the same cost.  */
 
 #define ADDRESS_COST(RTX)  1
 
@@ -3186,6 +3133,7 @@ do {                                                                      \
 /* Declare functions defined in sparc.c and used in templates.  */
 
 extern char *singlemove_string ();
+extern char *doublemove_string ();
 extern char *output_move_double ();
 extern char *output_move_quad ();
 extern char *output_fp_move_double ();