aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog29
-rw-r--r--gcc/config/mips/abi64.h90
-rw-r--r--gcc/config/mips/mips-protos.h15
-rw-r--r--gcc/config/mips/mips.c1106
-rw-r--r--gcc/config/mips/mips.h106
5 files changed, 641 insertions, 705 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 74fe74a..eadd607 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,34 @@
2002-03-22 Richard Sandiford <rsandifo@redhat.com>
+ * config/mips/abi64.h (SETUP_INCOMING_VARARGS): Undefine.
+ * config/mips/mips-protos.h (mips_setup_incoming_varargs): Declare.
+ (function_arg): Constify CUMULATIVE_ARGS.
+ (function_arg_partial_nregs, function_arg_pass_by_reference): Likewise.
+ * config/mips/mips.h (UNITS_PER_FPVALUE): Zero when TARGET_SOFT_FLOAT.
+ (UNITS_PER_DOUBLE): New macro.
+ (SETUP_INCOMING_VARARGS): Define. Use mips_setup_incoming_varargs.
+ (CUMULATIVE_ARGS): Reformat. Remove num_adjusts workaround and
+ last_arg_fp field. Replace arg_words and fp_arg_words with gp_regs,
+ fp_regs and stack_words.
+ (EABI_FLOAT_VARARGS_P): New macro.
+ * config/mips/mips.c (struct mips_arg_info): New.
+ (mips_arg_info): New function.
+ (function_arg_advance): Use it. Add adjustment instructions here
+ rather than in function_arg.
+ (function_arg): Constify CUMULATIVE_ARGS. Use mips_arg_info. Check
+ for VOIDmode at the beginning of the function.
+ (function_partial_nregs): Constify CUMULATIVE_ARGS. Use mips_arg_info.
+ (function_arg_pass_by_reference): Likewise.
+ (mips_setup_incoming_varags): New, largely based on old abi64.h code.
+ (mips_build_va_list): Test EABI_FLOAT_VARARGS_P.
+ (mips_va_start): Likewise. Use the new stack_words field of
+ CUMULATIVE_ARGS to set up overflow area. Reformat.
+ (mips_va_arg): Test EABI_FLOAT_VARARGS_P. Unify EABI handling of
+ doubles and other types, aligning the overflow pointer for non-doubles
+ too. Remove some code duplication. Replace hard-coded constants.
+
+2002-03-22 Richard Sandiford <rsandifo@redhat.com>
+
* config/mips/mips.h (FUNCTION_ARG_REGNO_P): Simplify.
(CLASS_UNITS): Undefine.
(CLASS_MAX_NREGS): Use FP_INC.
diff --git a/gcc/config/mips/abi64.h b/gcc/config/mips/abi64.h
index 42ea208..5ba856f 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 8c4cec2..8332782 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 535012c..f5bf777 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, "<stack>%s\n", struct_p ? ", [struct]" : "");
+ mips_arg_info (cum, mode, type, named, &info);
- ret = 0;
- }
- else
- {
- if (regbase == -1)
- abort ();
+ /* Return straight away if the whole argument is passed on the stack. */
+ if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+ return 0;
- if (! type || TREE_CODE (type) != RECORD_TYPE
- || mips_abi == ABI_32 || mips_abi == ABI_EABI
- || mips_abi == ABI_O64 || mips_abi == ABI_MEABI
- || ! named
- || ! TYPE_SIZE_UNIT (type)
- || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
- {
+ if (type != 0
+ && TREE_CODE (type) == RECORD_TYPE
+ && (mips_abi == ABI_N32 || mips_abi == ABI_64)
+ && TYPE_SIZE_UNIT (type)
+ && host_integerp (TYPE_SIZE_UNIT (type), 1)
+ && named
+ && mode != DFmode)
+ {
+ /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
+ structure contains a double in its entirety, then that 64 bit
+ chunk is passed in a floating point register. */
+ tree field;
+
+ /* First check to see if there is any such field. */
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+ && host_integerp (bit_position (field), 0)
+ && int_bit_position (field) % BITS_PER_WORD == 0)
+ break;
- unsigned int arg_reg = (regbase + *arg_words + bias);
- ret = gen_rtx_REG (mode, arg_reg);
- if (mips_abi == ABI_MEABI
- && regbase == FP_ARG_FIRST
- && ! cum->prototype)
- {
- /* To make K&R varargs work we need to pass floating
- point arguments in both integer and FP registers. */
- ret = gen_rtx_PARALLEL (mode,
- gen_rtvec (2,
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (mode,
- arg_reg + GP_ARG_FIRST - FP_ARG_FIRST),
- const0_rtx), gen_rtx_EXPR_LIST (VOIDmode, ret, const0_rtx)));
- }
- }
- else
+ if (field != 0)
{
- /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
- structure contains a double in its entirety, then that 64 bit
- chunk is passed in a floating point register. */
- tree field;
-
- /* First check to see if there is any such field. */
- for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
- && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
- && host_integerp (bit_position (field), 0)
- && int_bit_position (field) % BITS_PER_WORD == 0)
- break;
-
- /* If the whole struct fits a DFmode register,
- we don't need the PARALLEL. */
- if (! field || mode == DFmode)
- ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
- else
- {
- /* Now handle the special case by returning a PARALLEL
- indicating where each 64 bit chunk goes. */
- unsigned int chunks;
- HOST_WIDE_INT bitpos;
- unsigned int regno;
- unsigned int i;
-
- /* ??? If this is a packed structure, then the last hunk won't
- be 64 bits. */
-
- chunks
- = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
- if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
- chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
-
- /* assign_parms checks the mode of ENTRY_PARM, so we must
- use the actual mode here. */
- ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
-
- bitpos = 0;
- regno = regbase + *arg_words + bias;
- field = TYPE_FIELDS (type);
- for (i = 0; i < chunks; i++)
- {
- rtx reg;
-
- for (; field; field = TREE_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && int_bit_position (field) >= bitpos)
- break;
-
- if (field
- && int_bit_position (field) == bitpos
- && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
- && !TARGET_SOFT_FLOAT
- && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
- reg = gen_rtx_REG (DFmode,
- regno + FP_ARG_FIRST - GP_ARG_FIRST);
- else
- reg = gen_rtx_REG (word_mode, regno);
-
- XVECEXP (ret, 0, i)
- = gen_rtx_EXPR_LIST (VOIDmode, reg,
- GEN_INT (bitpos / BITS_PER_UNIT));
+ /* Now handle the special case by returning a PARALLEL
+ indicating where each 64 bit chunk goes. INFO.REG_WORDS
+ chunks are passed in registers. */
+ unsigned int i;
+ HOST_WIDE_INT bitpos;
+ rtx ret;
- bitpos += 64;
- regno++;
- }
- }
- }
-
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
- struct_p ? ", [struct]" : "");
+ /* assign_parms checks the mode of ENTRY_PARM, so we must
+ use the actual mode here. */
+ ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
- /* The following is a hack in order to pass 1 byte structures
- the same way that the MIPS compiler does (namely by passing
- the structure in the high byte or half word of the register).
- This also makes varargs work. If we have such a structure,
- we save the adjustment RTL, and the call define expands will
- emit them. For the VOIDmode argument (argument after the
- last real argument), pass back a parallel vector holding each
- of the adjustments. */
+ bitpos = 0;
+ field = TYPE_FIELDS (type);
+ for (i = 0; i < info.reg_words; i++)
+ {
+ rtx reg;
- /* ??? function_arg can be called more than once for each argument.
- As a result, we compute more adjustments than we need here.
- See the CUMULATIVE_ARGS definition in mips.h. */
+ for (; field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && int_bit_position (field) >= bitpos)
+ break;
- /* ??? This scheme requires everything smaller than the word size to
- shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
- that would mean every int needs to be shifted left, which is very
- inefficient. Let's not carry this compatibility to the 64 bit
- calling convention for now. */
+ if (field
+ && int_bit_position (field) == bitpos
+ && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+ && !TARGET_SOFT_FLOAT
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+ reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+ else
+ reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
- if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
- && ! TARGET_64BIT
- && mips_abi != ABI_EABI
- && mips_abi != ABI_MEABI)
- {
- rtx amount = GEN_INT (BITS_PER_WORD
- - int_size_in_bytes (type) * BITS_PER_UNIT);
- rtx reg = gen_rtx_REG (word_mode, regbase + *arg_words + bias);
+ XVECEXP (ret, 0, i)
+ = gen_rtx_EXPR_LIST (VOIDmode, reg,
+ GEN_INT (bitpos / BITS_PER_UNIT));
- if (TARGET_64BIT)
- cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
- else
- cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
+ bitpos += BITS_PER_WORD;
+ }
+ return ret;
}
}
- /* We will be called with a mode of VOIDmode after the last argument
- has been seen. Whatever we return will be passed to the call
- insn. If we need any shifts for small structures, return them in
- a PARALLEL; in that case, stuff the mips16 fp_code in as the
- mode. Otherwise, if we have need a mips16 fp_code, return a REG
- with the code stored as the mode. */
- if (mode == VOIDmode)
+ if (mips_abi == ABI_MEABI && info.fpr_p && !cum->prototype)
{
- if (cum->num_adjusts > 0)
- ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
- gen_rtvec_v (cum->num_adjusts, cum->adjust));
- else if (TARGET_MIPS16 && cum->fp_code != 0)
- ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
+ /* To make K&R varargs work we need to pass floating
+ point arguments in both integer and FP registers. */
+ return gen_rtx_PARALLEL
+ (mode,
+ gen_rtvec (2,
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode,
+ GP_ARG_FIRST
+ + info.reg_offset),
+ const0_rtx),
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode,
+ FP_ARG_FIRST
+ + info.reg_offset),
+ const0_rtx)));
}
- return ret;
+ if (info.fpr_p)
+ return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
+ else
+ return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
}
int
function_arg_partial_nregs (cum, mode, type, named)
- CUMULATIVE_ARGS *cum; /* current arg information */
+ const CUMULATIVE_ARGS *cum; /* current arg information */
enum machine_mode mode; /* current arg mode */
tree type; /* type of the argument or 0 if lib support */
- int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
-{
- if ((mode == BLKmode
- || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
- || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
- && cum->arg_words < (unsigned) MAX_ARGS_IN_REGISTERS
- && mips_abi != ABI_EABI)
- {
- int words;
- if (mode == BLKmode)
- words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
- / UNITS_PER_WORD);
- else
- words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ int named; /* != 0 for normal args, == 0 for ... args */
+{
+ struct mips_arg_info info;
- if (words + cum->arg_words <= (unsigned) MAX_ARGS_IN_REGISTERS)
- return 0; /* structure fits in registers */
+ mips_arg_info (cum, mode, type, named, &info);
+ return info.stack_words > 0 ? info.reg_words : 0;
+}
+
+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 = &current_function_args_info;
if (mips_abi == ABI_EABI)
{
int gpr_save_area_size;
- /* Note UNITS_PER_WORD is 4 bytes or 8, depending on TARGET_64BIT. */
- if (int_arg_words < 8 )
- /* Adjust for the prologue's economy measure */
- gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
- else
- gpr_save_area_size = 0;
- if (!TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
+ gpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->gp_regs) * UNITS_PER_WORD;
+
+ if (EABI_FLOAT_VARARGS_P)
{
tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
tree ovfl, gtop, ftop, goff, foff;
- tree gprv;
- int float_formals, fpr_offset, size_excess, floats_passed_in_regs;
- int fpr_save_offset;
-
- float_formals = current_function_args_info.fp_arg_words;
- /* If mips2, the number of formals is half the reported # of words */
- if (!TARGET_64BIT)
- float_formals /= 2;
- floats_passed_in_regs = (TARGET_64BIT ? 8 : 4);
+ tree t;
+ int fpr_offset;
+ int fpr_save_area_size;
- f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
f_gtop = TREE_CHAIN (f_ovfl);
f_ftop = TREE_CHAIN (f_gtop);
f_goff = TREE_CHAIN (f_ftop);
@@ -4434,84 +4434,49 @@ mips_va_start (stdarg_p, valist, nextarg)
goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
- /* Emit code setting a pointer into the overflow (shared-stack) area.
- If there were more than 8 non-float formals, or more than 8
- float formals, then this pointer isn't to the base of the area.
- In that case, it must point to where the first vararg is. */
- size_excess = 0;
- if (float_formals > floats_passed_in_regs)
- size_excess += (float_formals-floats_passed_in_regs) * 8;
- if (int_arg_words > 8)
- size_excess += (int_arg_words-8) * UNITS_PER_WORD;
-
- /* FIXME: for mips2, the above size_excess can be wrong. Because the
- overflow stack holds mixed size items, there can be alignments,
- so that an 8 byte double following a 4 byte int will be on an
- 8 byte boundary. This means that the above calculation should
- take into account the exact sequence of floats and non-floats
- which make up the excess. That calculation should be rolled
- into the code which sets the current_function_args_info struct.
- The above then reduces to a fetch from that struct. */
-
-
+ /* Emit code to initialize OVFL, which points to the next varargs
+ stack argument. CUM->STACK_WORDS gives the number of stack
+ words used by named arguments. */
t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
- if (size_excess)
+ if (cum->stack_words > 0)
t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
- build_int_2 (size_excess, 0));
+ build_int_2 (cum->stack_words * UNITS_PER_WORD, 0));
t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Emit code setting a ptr to the base of the overflow area. */
+ /* Emit code to initialize GTOP, the top of the GPR save area. */
t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Emit code setting a pointer to the GPR save area.
- More precisely, a pointer to off-the-end of the FPR save area.
- If mips4, this is gpr_save_area_size below the overflow area.
- If mips2, also round down to an 8-byte boundary, since the FPR
- save area is 8-byte aligned, and GPR is 4-byte-aligned.
- Therefore there can be a 4-byte gap between the save areas. */
- gprv = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
- fpr_save_offset = gpr_save_area_size;
- if (!TARGET_64BIT)
- {
- if (fpr_save_offset & 7)
- fpr_save_offset += 4;
- }
- if (fpr_save_offset)
- gprv = build (PLUS_EXPR, TREE_TYPE (ftop), gprv,
- build_int_2 (-fpr_save_offset,-1));
- t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, gprv);
+ /* Emit code to initialize FTOP, the top of the FPR save area.
+ This address is gpr_save_area_bytes below GTOP, rounded
+ down to the next fp-aligned boundary. */
+ t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+ fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+ fpr_offset &= ~(UNITS_PER_FPVALUE - 1);
+ if (fpr_offset)
+ t = build (PLUS_EXPR, TREE_TYPE (ftop), t,
+ build_int_2 (-fpr_offset, -1));
+ t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Emit code initting an offset to the size of the GPR save area */
+ /* Emit code to initialize GOFF, the offset from GTOP of the
+ next GPR argument. */
t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (gpr_save_area_size,0));
+ build_int_2 (gpr_save_area_size, 0));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Emit code initting an offset from ftop to the first float
- vararg. This varies in size, since any float
- varargs are put in the FPR save area after the formals.
- Note it's 8 bytes/formal regardless of TARGET_64BIT.
- However, mips2 stores 4 GPRs, mips4 stores 8 GPRs.
- If there are 8 or more float formals, init to zero.
- (In fact, the formals aren't stored in the bottom of the
- FPR save area: they are elsewhere, and the size of the FPR
- save area is economized by the prologue. But this code doesn't
- care. This design is unaffected by that fact.) */
- if (float_formals >= floats_passed_in_regs)
- fpr_offset = 0;
- else
- fpr_offset = (floats_passed_in_regs - float_formals) * 8;
+ /* Likewise emit code to initialize FOFF, the offset from FTOP
+ of the next FPR argument. */
+ fpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->fp_regs) * UNITS_PER_FPREG;
t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
- build_int_2 (fpr_offset,0));
+ build_int_2 (fpr_save_area_size, 0));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
else
{
- /* TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT */
-
/* Everything is in the GPR save area, or in the overflow
area which is contiguous with it. */
@@ -4534,10 +4499,10 @@ mips_va_start (stdarg_p, valist, nextarg)
/* ??? This had been conditional on
_MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
and both iris5.h and iris6.h define _MIPS_SIM. */
- if (mips_abi == ABI_N32 || mips_abi == ABI_64)
- ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
- else if (mips_abi == ABI_MEABI)
- ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
+ if (mips_abi == ABI_N32
+ || mips_abi == ABI_64
+ || mips_abi == ABI_MEABI)
+ ofs = (cum->gp_regs < MAX_ARGS_IN_REGISTERS ? 0 : -UNITS_PER_WORD);
else
ofs = -UNITS_PER_WORD;
}
@@ -4562,13 +4527,12 @@ mips_va_arg (valist, type)
if (mips_abi == ABI_EABI)
{
- int indirect;
- rtx r, lab_over = NULL_RTX, lab_false;
- tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
- tree ovfl, gtop, ftop, goff, foff;
+ bool indirect;
+ rtx r;
indirect
= function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+
if (indirect)
{
size = POINTER_SIZE / BITS_PER_UNIT;
@@ -4577,187 +4541,166 @@ mips_va_arg (valist, type)
addr_rtx = gen_reg_rtx (Pmode);
- if (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
+ if (!EABI_FLOAT_VARARGS_P)
{
- /* Case of all args in a merged stack. No need to check bounds,
+ /* Case of all args in a merged stack. No need to check bounds,
just advance valist along the stack. */
tree gpr = valist;
- if (! indirect
- && ! TARGET_64BIT
+ if (!indirect
+ && !TARGET_64BIT
&& TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
{
+ /* Align the pointer using: ap = (ap + align - 1) & -align,
+ where align is 2 * UNITS_PER_WORD. */
t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
- build_int_2 (2*UNITS_PER_WORD - 1, 0));
+ build_int_2 (2 * UNITS_PER_WORD - 1, 0));
t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
- build_int_2 (-2*UNITS_PER_WORD, -1));
+ build_int_2 (-2 * UNITS_PER_WORD, -1));
t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
- t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
- size_int (rsize));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
+ /* Emit code to set addr_rtx to the valist, and postincrement
+ the valist by the size of the argument, rounded up to the
+ next word. */
+ t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
+ size_int (rsize));
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
emit_move_insn (addr_rtx, r);
- /* flush the POSTINCREMENT */
- emit_queue();
-
- if (indirect)
- {
- r = gen_rtx_MEM (Pmode, addr_rtx);
- set_mem_alias_set (r, get_varargs_alias_set ());
- emit_move_insn (addr_rtx, r);
- }
- else
- {
- if (BYTES_BIG_ENDIAN && rsize != size)
- addr_rtx = plus_constant (addr_rtx, rsize - size);
- }
- return addr_rtx;
+ /* Flush the POSTINCREMENT. */
+ emit_queue();
}
+ else
+ {
+ /* Not a simple merged stack. */
- /* Not a simple merged stack. Need ptrs and indexes left by va_start. */
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+ tree ovfl, top, off;
+ rtx lab_over = NULL_RTX, lab_false;
- f_ovfl = TYPE_FIELDS (va_list_type_node);
- f_gtop = TREE_CHAIN (f_ovfl);
- f_ftop = TREE_CHAIN (f_gtop);
- f_goff = TREE_CHAIN (f_ftop);
- f_foff = TREE_CHAIN (f_goff);
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_gtop = TREE_CHAIN (f_ovfl);
+ f_ftop = TREE_CHAIN (f_gtop);
+ f_goff = TREE_CHAIN (f_ftop);
+ f_foff = TREE_CHAIN (f_goff);
- ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
- gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
- ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
- goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
- foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+ /* We maintain separate pointers and offsets for floating-point
+ and integer arguments, but we need similar code in both cases.
+ Let:
- lab_false = gen_label_rtx ();
- lab_over = gen_label_rtx ();
+ TOP be the top of the register save area;
+ OFF be the offset from TOP of the next register;
+ ADDR_RTX be the address of the argument; and
+ RSIZE be the number of bytes used to store the argument.
- if (TREE_CODE (type) == REAL_TYPE)
- {
+ The code we want is:
- /* Emit code to branch if foff == 0. */
- r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
- EXPAND_NORMAL);
- emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
- 1, lab_false);
+ 1: off &= -rsize; // round down
+ 2: if (off != 0)
+ 3: {
+ 4: addr_rtx = top - off;
+ 5: off -= rsize;
+ 6: }
+ 7: else
+ 8: {
+ 9: ovfl += ((intptr_t) ovfl + rsize - 1) & -rsize;
+ 10: addr_rtx = ovfl;
+ 11: ovfl += rsize;
+ 12: }
- /* Emit code for addr_rtx = ftop - foff */
- t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
+ [1] and [9] can sometimes be optimized away. */
- /* Emit code for foff-=8.
- Advances the offset up FPR save area by one double */
- t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ lab_false = gen_label_rtx ();
+ lab_over = gen_label_rtx ();
- emit_queue();
- emit_jump (lab_over);
- emit_barrier ();
- emit_label (lab_false);
+ ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
- if (!TARGET_64BIT)
+ if (TREE_CODE (type) == REAL_TYPE)
{
- /* For mips2, the overflow area contains mixed size items.
- If a 4-byte int is followed by an 8-byte float, then
- natural alignment causes a 4 byte gap.
- So, dynamically adjust ovfl up to a multiple of 8. */
- t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
- build_int_2 (7, 0));
- t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
- t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
-
- /* Emit code for addr_rtx = the ovfl pointer into overflow area.
- Regardless of mips2, postincrement the ovfl pointer by 8. */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
- size_int (8));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- emit_queue();
- emit_label (lab_over);
- return addr_rtx;
- }
- else
- {
- /* not REAL_TYPE */
- int step_size;
+ top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
+ off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
- if (! TARGET_64BIT
- && TREE_CODE (type) == INTEGER_TYPE
- && TYPE_PRECISION (type) == 64)
+ /* When floating-point registers are saved to the stack,
+ each one will take up UNITS_PER_FPVALUE bytes, regardless
+ of the float's precision. */
+ rsize = UNITS_PER_FPVALUE;
+ }
+ else
{
- /* In mips2, int takes 32 bits of the GPR save area, but
- longlong takes an aligned 64 bits. So, emit code
- to zero the low order bits of goff, thus aligning
- the later calculation of (gtop-goff) upwards. */
- t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (-8, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ top = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
+ off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
+ if (rsize > UNITS_PER_WORD)
+ {
+ /* [1] Emit code for: off &= -rsize. */
+ t = build (BIT_AND_EXPR, TREE_TYPE (off), off,
+ build_int_2 (-rsize, -1));
+ t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
}
- /* Emit code to branch if goff == 0. */
- r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
- EXPAND_NORMAL);
- emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
+ /* [2] Emit code to branch if off == 0. */
+ r = expand_expr (off, NULL_RTX, TYPE_MODE (TREE_TYPE (off)),
+ EXPAND_NORMAL);
+ emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
1, lab_false);
- /* Emit code for addr_rtx = gtop - goff. */
- t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
+ /* [4] Emit code for: addr_rtx = top - off. */
+ t = build (MINUS_EXPR, TREE_TYPE (top), top, off);
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
- /* Note that mips2 int is 32 bit, but mips2 longlong is 64. */
- if (! TARGET_64BIT && TYPE_PRECISION (type) == 64)
- step_size = 8;
- else
- step_size = UNITS_PER_WORD;
-
- /* Emit code for goff = goff - step_size.
- Advances the offset up GPR save area over the item. */
- t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (step_size, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- emit_queue();
- emit_jump (lab_over);
- emit_barrier ();
- emit_label (lab_false);
-
- /* Emit code for addr_rtx -> overflow area, postinc by step_size */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
- size_int (step_size));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- emit_queue();
- emit_label (lab_over);
+ /* [5] Emit code for: off -= rsize. */
+ t = build (MINUS_EXPR, TREE_TYPE (off), off, build_int_2 (rsize, 0));
+ t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- if (BYTES_BIG_ENDIAN && rsize != size)
- addr_rtx = plus_constant (addr_rtx, rsize - size);
+ /* [7] Emit code to jump over the else clause, then the label
+ that starts it. */
+ emit_queue();
+ emit_jump (lab_over);
+ emit_barrier ();
+ emit_label (lab_false);
- if (indirect)
- {
- addr_rtx = force_reg (Pmode, addr_rtx);
- r = gen_rtx_MEM (Pmode, addr_rtx);
- set_mem_alias_set (r, get_varargs_alias_set ());
- emit_move_insn (addr_rtx, r);
+ if (rsize > UNITS_PER_WORD)
+ {
+ /* [9] Emit: ovfl += ((intptr_t) ovfl + rsize - 1) & -rsize. */
+ t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl,
+ build_int_2 (rsize - 1, 0));
+ t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), t,
+ build_int_2 (-rsize, -1));
+ t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
- return addr_rtx;
+ /* [10, 11]. Emit code to store ovfl in addr_rtx, then
+ post-increment ovfl by rsize. */
+ t = build (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl,
+ size_int (rsize));
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
+
+ emit_queue();
+ emit_label (lab_over);
}
+ if (indirect)
+ {
+ addr_rtx = force_reg (Pmode, addr_rtx);
+ r = gen_rtx_MEM (Pmode, addr_rtx);
+ set_mem_alias_set (r, get_varargs_alias_set ());
+ emit_move_insn (addr_rtx, r);
+ }
+ else
+ {
+ if (BYTES_BIG_ENDIAN && rsize != size)
+ addr_rtx = plus_constant (addr_rtx, rsize - size);
+ }
+ return addr_rtx;
}
else
{
@@ -8083,7 +8026,7 @@ mips_function_value (valtype, func)
int
function_arg_pass_by_reference (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
+ const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
@@ -8103,16 +8046,9 @@ function_arg_pass_by_reference (cum, mode, type, named)
/* ??? cum can be NULL when called from mips_va_arg. The problem handled
here hopefully is not relevant to mips_va_arg. */
if (cum && MUST_PASS_IN_STACK (mode, type)
- && mips_abi != ABI_MEABI)
- {
- /* Don't pass the actual CUM to FUNCTION_ARG, because we would
- get double copies of any offsets generated for small structs
- passed in registers. */
- CUMULATIVE_ARGS temp;
- temp = *cum;
- if (FUNCTION_ARG (temp, mode, type, named) != 0)
- return 1;
- }
+ && mips_abi != ABI_MEABI
+ && FUNCTION_ARG (*cum, mode, type, named) != 0)
+ return 1;
/* Otherwise, we only do this if EABI is selected. */
if (mips_abi != ABI_EABI)
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 0a8238d..7b01130 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. */