From: Doug Evans Date: Fri, 17 Oct 1997 20:39:37 +0000 (+0000) Subject: sp64-elf.h (TARGET_DEFAULT): Delete MASK_STACK_BIAS. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4fb4e4b8be6e8c27eb777469727555fc1db3ca01;p=gcc.git sp64-elf.h (TARGET_DEFAULT): Delete MASK_STACK_BIAS. * sparc/sp64-elf.h (TARGET_DEFAULT): Delete MASK_STACK_BIAS. * 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. From-SVN: r15968 --- diff --git a/gcc/config/sparc/sp64-elf.h b/gcc/config/sparc/sp64-elf.h index f0a36e6a1d1..8ff9650f910 100644 --- a/gcc/config/sparc/sp64-elf.h +++ b/gcc/config/sparc/sp64-elf.h @@ -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 diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 91d731f80dd..de4ee75c825 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -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) } /* 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"; } - + +/* 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); } - + 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; } } + +/* 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, + ®no, &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, ®no, &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, ®no, &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)); } + +/* 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 */ /* Return the string to output a conditional branch to LABEL, which is the operand number of the label. OP is the conditional expression. diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h index 46e83b1c0fd..205f59b8782 100644 --- a/gcc/config/sparc/sparc.h +++ b/gcc/config/sparc/sparc.h @@ -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)) /* 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) /* 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 (); /* 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 ();