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