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:
parent
f43f4314a1
commit
35d9c40302
5 changed files with 125 additions and 68 deletions
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
26
gcc/testsuite/gcc.c-torture/execute/va-arg-23.c
Normal file
26
gcc/testsuite/gcc.c-torture/execute/va-arg-23.c
Normal 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;
|
||||
}
|
Loading…
Add table
Reference in a new issue