Commit 4d72536e by Richard Sandiford Committed by Richard Sandiford

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
parent e6f884cd
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.
......
......@@ -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
......
......@@ -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,
......
......@@ -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)
......
......@@ -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. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment