re PR target/9700 ([alpha linux] va_start gets __offset wrong in some situations)

PR target/9700
        * config/alpha/alpha.c (alpha_va_start): Account for
        current_function_pretend_args_size in the AP offset.

        * config/alpha/alpha.h (SETUP_INCOMING_VARARGS): Move out of line.
        (INITIAL_ELIMINATION_OFFSET): Move out of line.
        * config/alpha/alpha.c (alpha_setup_incoming_varargs): New.
        (alpha_initial_elimination_offset) New.
        * config/alpha/alpha-protos.h: Update.

	* gcc.c-torture/execute/va-arg-23.c: New.

From-SVN: r64367
This commit is contained in:
Richard Henderson 2003-03-14 09:58:48 -08:00
parent f43f4314a1
commit 35d9c40302
5 changed files with 125 additions and 68 deletions

View file

@ -1,8 +1,21 @@
2003-03-14 Richard Henderson <rth@redhat.com>
PR target/9700
* config/alpha/alpha.c (alpha_va_start): Account for
current_function_pretend_args_size in the AP offset.
* config/alpha/alpha.h (SETUP_INCOMING_VARARGS): Move out of line.
(INITIAL_ELIMINATION_OFFSET): Move out of line.
* config/alpha/alpha.c (alpha_setup_incoming_varargs): New.
(alpha_initial_elimination_offset) New.
* config/alpha/alpha-protos.h: Update.
2003-03-14 Jakub Jelinek <jakub@redhat.com>
* stmt.c (expand_start_case): Call emit_queue ().
2003-03-14 Chris Demetriou <cgd@broadcom.com>, Alexandre Oliva <aoliva@redhat.com>
2003-03-14 Chris Demetriou <cgd@broadcom.com>
Alexandre Oliva <aoliva@redhat.com>
* config/mips/mips.h (FUNCTION_PROFILER): _mcount() doesn't pop 2
words in new abis.

View file

@ -26,6 +26,8 @@ extern int zap_mask PARAMS ((HOST_WIDE_INT));
extern int direct_return PARAMS ((void));
extern int alpha_sa_size PARAMS ((void));
extern HOST_WIDE_INT alpha_initial_elimination_offset PARAMS ((unsigned int,
unsigned int));
extern int alpha_pv_save_size PARAMS ((void));
extern int alpha_using_fp PARAMS ((void));
extern void alpha_write_verstamp PARAMS ((FILE *));
@ -135,6 +137,8 @@ extern void alpha_initialize_trampoline PARAMS ((rtx, rtx, rtx, int, int, int));
extern void alpha_reorg PARAMS ((rtx));
extern tree alpha_build_va_list PARAMS ((void));
extern void alpha_setup_incoming_varargs
PARAMS ((CUMULATIVE_ARGS, enum machine_mode, tree, int *, int));
extern void alpha_va_start PARAMS ((tree, rtx));
extern rtx alpha_va_arg PARAMS ((tree, tree));
extern rtx function_arg PARAMS ((CUMULATIVE_ARGS, enum machine_mode,

View file

@ -6558,6 +6558,53 @@ alpha_build_va_list ()
return record;
}
/* Perform any needed actions needed for a function that is receiving a
variable number of arguments.
On the Alpha, we allocate space for all 12 arg registers, but only
push those that are remaining. However, if NO registers need to be
saved, don't allocate any space. This is not only because we won't
need the space, but because AP includes the current_pretend_args_size
and we don't want to mess up any ap-relative addresses already made.
If we are not to use the floating-point registers, save the integer
registers where we would put the floating-point registers. This is
not the most efficient way to implement varargs with just one register
class, but it isn't worth doing anything more efficient in this rare
case. */
void
alpha_setup_incoming_varargs(cum, mode, type, pretend_size, no_rtl)
CUMULATIVE_ARGS cum;
enum machine_mode mode;
tree type;
int *pretend_size;
int no_rtl;
{
if (cum >= 6)
return;
if (!no_rtl)
{
int set = get_varargs_alias_set ();
rtx tmp;
tmp = gen_rtx_MEM (BLKmode,
plus_constant (virtual_incoming_args_rtx,
(cum + 6) * UNITS_PER_WORD));
set_mem_alias_set (tmp, set);
move_block_from_reg (16 + cum, tmp, 6 - cum, (6 - cum) * UNITS_PER_WORD);
tmp = gen_rtx_MEM (BLKmode,
plus_constant (virtual_incoming_args_rtx,
cum * UNITS_PER_WORD));
set_mem_alias_set (tmp, set);
move_block_from_reg (16 + (TARGET_FPREGS ? 32 : 0) + cum, tmp,
6 - cum, (6 - cum) * UNITS_PER_WORD);
}
*pretend_size = 12 * UNITS_PER_WORD;
}
void
alpha_va_start (valist, nextarg)
tree valist;
@ -6579,12 +6626,15 @@ alpha_va_start (valist, nextarg)
If no integer registers need be stored, then we must subtract 48
in order to account for the integer arg registers which are counted
in argsize above, but which are not actually stored on the stack. */
in argsize above, but which are not actually stored on the stack.
Must further be careful here about structures straddling the last
integer argument register; that futzes with pretend_args_size,
which changes the meaning of AP. */
if (NUM_ARGS <= 6)
offset = TARGET_ABI_OPEN_VMS ? UNITS_PER_WORD : 6 * UNITS_PER_WORD;
else
offset = -6 * UNITS_PER_WORD;
offset = -6 * UNITS_PER_WORD + current_function_pretend_args_size;
if (TARGET_ABI_OPEN_VMS)
{
@ -7200,6 +7250,30 @@ alpha_sa_size ()
return sa_size * 8;
}
/* Define the offset between two registers, one to be eliminated,
and the other its replacement, at the start of a routine. */
HOST_WIDE_INT
alpha_initial_elimination_offset(from, to)
unsigned int from, to;
{
HOST_WIDE_INT ret;
ret = alpha_sa_size ();
ret += ALPHA_ROUND (current_function_outgoing_args_size);
if (from == FRAME_POINTER_REGNUM)
;
else if (from == ARG_POINTER_REGNUM)
ret += (ALPHA_ROUND (get_frame_size ()
+ current_function_pretend_args_size)
- current_function_pretend_args_size);
else
abort ();
return ret;
}
int
alpha_pv_save_size ()
{

View file

@ -963,19 +963,8 @@ extern int alpha_memory_latency;
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
{ if ((FROM) == FRAME_POINTER_REGNUM) \
(OFFSET) = (ALPHA_ROUND (current_function_outgoing_args_size) \
+ alpha_sa_size ()); \
else if ((FROM) == ARG_POINTER_REGNUM) \
(OFFSET) = (ALPHA_ROUND (current_function_outgoing_args_size) \
+ alpha_sa_size () \
+ (ALPHA_ROUND (get_frame_size () \
+ current_function_pretend_args_size) \
- current_function_pretend_args_size)); \
else \
abort (); \
}
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
((OFFSET) = alpha_initial_elimination_offset(FROM, TO))
/* Define this if stack space is still allocated for a parameter passed
in a register. */
@ -1122,58 +1111,9 @@ extern int alpha_memory_latency;
? 6 - (CUM) : 0)
/* Perform any needed actions needed for a function that is receiving a
variable number of arguments.
CUM is as above.
MODE and TYPE are the mode and type of the current parameter.
PRETEND_SIZE is a variable that should be set to the amount of stack
that must be pushed by the prolog to pretend that our caller pushed
it.
Normally, this macro will push all remaining incoming registers on the
stack and set PRETEND_SIZE to the length of the registers pushed.
On the Alpha, we allocate space for all 12 arg registers, but only
push those that are remaining.
However, if NO registers need to be saved, don't allocate any space.
This is not only because we won't need the space, but because AP includes
the current_pretend_args_size and we don't want to mess up any
ap-relative addresses already made.
If we are not to use the floating-point registers, save the integer
registers where we would put the floating-point registers. This is
not the most efficient way to implement varargs with just one register
class, but it isn't worth doing anything more efficient in this rare
case. */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
{ if ((CUM) < 6) \
{ \
if (! (NO_RTL)) \
{ \
rtx tmp; int set = get_varargs_alias_set (); \
tmp = gen_rtx_MEM (BLKmode, \
plus_constant (virtual_incoming_args_rtx, \
((CUM) + 6)* UNITS_PER_WORD)); \
set_mem_alias_set (tmp, set); \
move_block_from_reg \
(16 + CUM, tmp, \
6 - (CUM), (6 - (CUM)) * UNITS_PER_WORD); \
\
tmp = gen_rtx_MEM (BLKmode, \
plus_constant (virtual_incoming_args_rtx, \
(CUM) * UNITS_PER_WORD)); \
set_mem_alias_set (tmp, set); \
move_block_from_reg \
(16 + (TARGET_FPREGS ? 32 : 0) + CUM, tmp, \
6 - (CUM), (6 - (CUM)) * UNITS_PER_WORD); \
} \
PRETEND_SIZE = 12 * UNITS_PER_WORD; \
} \
}
variable number of arguments. */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
alpha_setup_incoming_varargs(CUM,MODE,TYPE,&(PRETEND_SIZE),NO_RTL)
/* Try to output insns to set TARGET equal to the constant C if it can be
done in less than N insns. Do all computations in MODE. Returns the place

View file

@ -0,0 +1,26 @@
/* PR 9700 */
/* Alpha got the base address for the va_list incorrect when there was
a structure that was passed partially in registers and partially on
the stack. */
#include <stdarg.h>
struct two { long x, y; };
void foo(int a, int b, int c, int d, int e, struct two f, int g, ...)
{
va_list args;
int h;
va_start(args, g);
h = va_arg(args, int);
if (g != 1 || h != 2)
abort ();
}
int main()
{
struct two t = { 0, 0 };
foo(0, 0, 0, 0, 0, t, 1, 2);
return 0;
}