rs6000.c (rs6000_mixed_function_arg): Rewrite.

* config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite.
	(function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in
	calculating gpr size for altivec.  Simplify and correct
	rs6000_mixed_function_arg calls.  Call rs6000_mixed_function_arg
	for ABI_V4 gpr case too.  Fix off-by-one error in long double
	reg test.  Generate the correct PARALLEL to handle long double
	for ABI_AIX 32-bit.  Use this for -m32 -mpowerpc64 fpr case too.
	(function_arg_partial_nregs): Align before calculating regs left.
	Don't return info on partial fprs when we need info on gprs.
	Correct long double fpr off-by-one error.

From-SVN: r83951
This commit is contained in:
Alan Modra 2004-07-01 03:17:31 +00:00 committed by Alan Modra
parent cde0e59bf2
commit ec6376abd7
2 changed files with 135 additions and 171 deletions

View File

@ -1,3 +1,16 @@
2004-07-01 Alan Modra <amodra@bigpond.net.au>
* config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite.
(function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in
calculating gpr size for altivec. Simplify and correct
rs6000_mixed_function_arg calls. Call rs6000_mixed_function_arg
for ABI_V4 gpr case too. Fix off-by-one error in long double
reg test. Generate the correct PARALLEL to handle long double
for ABI_AIX 32-bit. Use this for -m32 -mpowerpc64 fpr case too.
(function_arg_partial_nregs): Align before calculating regs left.
Don't return info on partial fprs when we need info on gprs.
Correct long double fpr off-by-one error.
2004-06-30 John David Anglin <dave.anglin@nrc-cnrc.gc.ca>
* pa-protos.h (prefetch_operand): Add prototype.

View File

@ -425,8 +425,7 @@ static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
static rtx rs6000_complex_function_value (enum machine_mode);
static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree);
static rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int);
static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
static void setup_incoming_varargs (CUMULATIVE_ARGS *,
enum machine_mode, tree,
@ -4424,124 +4423,49 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
/* Determine where to place an argument in 64-bit mode with 32-bit ABI. */
static rtx
rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int align_words)
rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
{
if (mode == DFmode)
{
/* -mpowerpc64 with 32bit ABI splits up a DFmode argument
in vararg list into zero, one or two GPRs */
if (align_words >= GP_ARG_NUM_REG)
return gen_rtx_PARALLEL (DFmode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode,
cum->fregno),
const0_rtx)));
else if (align_words + rs6000_arg_size (mode, type)
> GP_ARG_NUM_REG)
/* If this is partially on the stack, then we only
include the portion actually in registers here. */
return gen_rtx_PARALLEL (DFmode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words),
const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode,
cum->fregno),
const0_rtx)));
int n_units;
int i, k;
rtx rvec[GP_ARG_NUM_REG + 1];
/* split a DFmode arg into two GPRs */
return gen_rtx_PARALLEL (DFmode,
gen_rtvec (3,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words),
const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words + 1),
GEN_INT (4)),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode, cum->fregno),
const0_rtx)));
}
/* -mpowerpc64 with 32bit ABI splits up a DImode argument into one
or two GPRs */
else if (mode == DImode)
{
if (align_words < GP_ARG_NUM_REG - 1)
return gen_rtx_PARALLEL (DImode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words),
const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words + 1),
GEN_INT (4))));
else if (align_words == GP_ARG_NUM_REG - 1)
return gen_rtx_PARALLEL (DImode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words),
const0_rtx)));
}
else if (ALTIVEC_VECTOR_MODE (mode) && align_words == GP_ARG_NUM_REG - 2)
{
/* Varargs vector regs must be saved in R9-R10. */
return gen_rtx_PARALLEL (mode,
gen_rtvec (3,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words),
const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words + 1),
GEN_INT (4))));
}
else if ((mode == BLKmode || ALTIVEC_VECTOR_MODE (mode))
&& align_words <= (GP_ARG_NUM_REG - 1))
{
/* AltiVec vector regs are saved in R5-R8. */
int k;
int size = int_size_in_bytes (type);
int no_units = ((size - 1) / 4) + 1;
int max_no_words = GP_ARG_NUM_REG - align_words;
int rtlvec_len = no_units < max_no_words ? no_units : max_no_words;
rtx *rtlvec = (rtx *) alloca (rtlvec_len * sizeof (rtx));
if (align_words >= GP_ARG_NUM_REG)
return NULL_RTX;
memset ((char *) rtlvec, 0, rtlvec_len * sizeof (rtx));
n_units = rs6000_arg_size (mode, type);
for (k=0; k < rtlvec_len; k++)
rtlvec[k] = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (SImode,
GP_ARG_MIN_REG
+ align_words + k),
k == 0 ? const0_rtx : GEN_INT (k*4));
/* Optimize the simple case where the arg fits in one gpr, except in
the case of BLKmode due to assign_parms assuming that registers are
BITS_PER_WORD wide. */
if (n_units == 0
|| (n_units == 1 && mode != BLKmode))
return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rtlvec));
k = 0;
if (align_words + n_units > GP_ARG_NUM_REG)
/* Not all of the arg fits in gprs. Say that it goes in memory too,
using a magic NULL_RTX component.
FIXME: This is not strictly correct. Only some of the arg
belongs in memory, not all of it. However, there isn't any way
to do this currently, apart from building rtx descriptions for
the pieces of memory we want stored. Due to bugs in the generic
code we can't use the normal function_arg_partial_nregs scheme
with the PARALLEL arg description we emit here.
In any case, the code to store the whole arg to memory is often
more efficient than code to store pieces, and we know that space
is available in the right place for the whole arg. */
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
i = 0;
do
{
rtx r = gen_rtx_REG (SImode, GP_ARG_MIN_REG + align_words);
rtx off = GEN_INT (i++ * 4);
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
}
return NULL_RTX;
while (++align_words < GP_ARG_NUM_REG && --n_units != 0);
return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
}
/* Determine where to put an argument to a function.
@ -4636,8 +4560,8 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
{
/* Vector parameters to varargs functions under AIX or Darwin
get passed in memory and possibly also in GPRs. */
int align, align_words;
enum machine_mode part_mode = mode;
int align, align_words, n_words;
enum machine_mode part_mode;
/* Vector parameters must be 16-byte aligned. This places them at
2 mod 4 in terms of words in 32-bit mode, since the parameter
@ -4653,20 +4577,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
/* Out of registers? Memory, then. */
if (align_words >= GP_ARG_NUM_REG)
return NULL_RTX;
if (TARGET_32BIT && TARGET_POWERPC64)
return rs6000_mixed_function_arg (mode, type, align_words);
/* The vector value goes in GPRs. Only the part of the
value in GPRs is reported here. */
if (align_words + CLASS_MAX_NREGS (mode, GENERAL_REGS)
> GP_ARG_NUM_REG)
part_mode = mode;
n_words = rs6000_arg_size (mode, type);
if (align_words + n_words > GP_ARG_NUM_REG)
/* Fortunately, there are only two possibilities, the value
is either wholly in GPRs or half in GPRs and half not. */
part_mode = DImode;
if (TARGET_32BIT
&& (TARGET_POWERPC64 || (align_words == GP_ARG_NUM_REG - 2)))
return rs6000_mixed_function_arg (cum, part_mode, type, align_words);
else
return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
}
}
else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode))
@ -4693,10 +4617,13 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
gregno += (1 - gregno) & 1;
/* Multi-reg args are not split between registers and stack. */
if (gregno + n_words - 1 <= GP_ARG_MAX_REG)
return gen_rtx_REG (mode, gregno);
else
if (gregno + n_words - 1 > GP_ARG_MAX_REG)
return NULL_RTX;
if (TARGET_32BIT && TARGET_POWERPC64)
return rs6000_mixed_function_arg (mode, type,
gregno - GP_ARG_MIN_REG);
return gen_rtx_REG (mode, gregno);
}
}
else
@ -4706,25 +4633,23 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
if (USE_FP_FOR_ARG_P (cum, mode, type))
{
rtx fpr[2];
rtx *r;
rtx rvec[GP_ARG_NUM_REG + 1];
rtx r;
int k;
bool needs_psave;
enum machine_mode fmode = mode;
int n;
unsigned long n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3;
if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1)
{
/* Long double split over regs and memory. */
if (fmode == TFmode)
fmode = DFmode;
/* Currently, we only ever need one reg here because complex
doubles are split. */
if (cum->fregno != FP_ARG_MAX_REG - 1)
if (cum->fregno != FP_ARG_MAX_REG || fmode != TFmode)
abort ();
/* Long double split over regs and memory. */
fmode = DFmode;
}
fpr[1] = gen_rtx_REG (fmode, cum->fregno);
/* Do we also need to pass this arg in the parameter save
area? */
@ -4735,46 +4660,55 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
&& align_words >= GP_ARG_NUM_REG)));
if (!needs_psave && mode == fmode)
return fpr[1];
if (TARGET_32BIT && TARGET_POWERPC64
&& mode == DFmode && cum->stdarg)
return rs6000_mixed_function_arg (cum, mode, type, align_words);
/* Describe where this piece goes. */
r = fpr + 1;
*r = gen_rtx_EXPR_LIST (VOIDmode, *r, const0_rtx);
n = 1;
return gen_rtx_REG (fmode, cum->fregno);
k = 0;
if (needs_psave)
{
/* Now describe the part that goes in gprs or the stack.
/* Describe the part that goes in gprs or the stack.
This piece must come first, before the fprs. */
rtx reg = NULL_RTX;
if (align_words < GP_ARG_NUM_REG)
{
unsigned long n_words = rs6000_arg_size (mode, type);
enum machine_mode rmode = mode;
if (align_words + n_words > GP_ARG_NUM_REG)
/* If this is partially on the stack, then we only
include the portion actually in registers here.
We know this can only be one register because
complex doubles are splt. */
rmode = Pmode;
reg = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words);
if (align_words + n_words > GP_ARG_NUM_REG
|| (TARGET_32BIT && TARGET_POWERPC64))
{
/* If this is partially on the stack, then we only
include the portion actually in registers here. */
enum machine_mode rmode = TARGET_32BIT ? SImode : DImode;
rtx off;
do
{
r = gen_rtx_REG (rmode,
GP_ARG_MIN_REG + align_words);
off = GEN_INT (k * GET_MODE_SIZE (rmode));
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
}
while (++align_words < GP_ARG_NUM_REG && --n_words != 0);
}
else
{
/* The whole arg fits in gprs. */
r = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
}
}
*--r = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
++n;
else
/* It's entirely in memory. */
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
}
return gen_rtx_PARALLEL (mode, gen_rtvec_v (n, r));
/* Describe where this piece goes in the fprs. */
r = gen_rtx_REG (fmode, cum->fregno);
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
}
else if (align_words < GP_ARG_NUM_REG)
{
if (TARGET_32BIT && TARGET_POWERPC64
&& (mode == DImode || mode == BLKmode))
return rs6000_mixed_function_arg (cum, mode, type, align_words);
if (TARGET_32BIT && TARGET_POWERPC64)
return rs6000_mixed_function_arg (mode, type, align_words);
return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
}
@ -4783,15 +4717,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
}
}
/* For an arg passed partly in registers and partly in memory,
this is the number of registers used.
For args passed entirely in registers or entirely in memory, zero. */
/* For an arg passed partly in registers and partly in memory, this is
the number of registers used. For args passed entirely in registers
or entirely in memory, zero. When an arg is described by a PARALLEL,
perhaps using more than one register type, this function returns the
number of registers used by the first element of the PARALLEL. */
int
function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
int ret = 0;
int align;
int parm_offset;
int align_words;
if (DEFAULT_ABI == ABI_V4)
return 0;
@ -4800,17 +4739,29 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
&& cum->nargs_prototype >= 0)
return 0;
if (USE_FP_FOR_ARG_P (cum, mode, type))
align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
parm_offset = TARGET_32BIT ? 2 : 0;
align_words = cum->words + ((parm_offset - cum->words) & align);
if (USE_FP_FOR_ARG_P (cum, mode, type)
/* If we are passing this arg in gprs as well, then this function
should return the number of gprs (or memory) partially passed,
*not* the number of fprs. */
&& !(type
&& (cum->nargs_prototype <= 0
|| (DEFAULT_ABI == ABI_AIX
&& TARGET_XL_CALL
&& align_words >= GP_ARG_NUM_REG))))
{
if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) > FP_ARG_MAX_REG + 1)
ret = FP_ARG_MAX_REG - cum->fregno;
ret = FP_ARG_MAX_REG + 1 - cum->fregno;
else if (cum->nargs_prototype >= 0)
return 0;
}
if (cum->words < GP_ARG_NUM_REG
&& GP_ARG_NUM_REG < cum->words + rs6000_arg_size (mode, type))
ret = GP_ARG_NUM_REG - cum->words;
if (align_words < GP_ARG_NUM_REG
&& GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type))
ret = GP_ARG_NUM_REG - align_words;
if (ret != 0 && TARGET_DEBUG_ARG)
fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);