From 35d9c4030288e9b9d99e16ec3213fb5e91ce6113 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 14 Mar 2003 09:58:48 -0800 Subject: [PATCH] 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 --- gcc/ChangeLog | 15 +++- gcc/config/alpha/alpha-protos.h | 4 + gcc/config/alpha/alpha.c | 78 ++++++++++++++++++- gcc/config/alpha/alpha.h | 70 ++--------------- .../gcc.c-torture/execute/va-arg-23.c | 26 +++++++ 5 files changed, 125 insertions(+), 68 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/va-arg-23.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fced2cbbf01..c3c9ada7381 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,8 +1,21 @@ +2003-03-14 Richard Henderson + + 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 * stmt.c (expand_start_case): Call emit_queue (). -2003-03-14 Chris Demetriou , Alexandre Oliva +2003-03-14 Chris Demetriou + Alexandre Oliva * config/mips/mips.h (FUNCTION_PROFILER): _mcount() doesn't pop 2 words in new abis. diff --git a/gcc/config/alpha/alpha-protos.h b/gcc/config/alpha/alpha-protos.h index 3bd852c59a1..da3294dc2b4 100644 --- a/gcc/config/alpha/alpha-protos.h +++ b/gcc/config/alpha/alpha-protos.h @@ -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, diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index 28b98735bf2..4feb2186bab 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -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 () { diff --git a/gcc/config/alpha/alpha.h b/gcc/config/alpha/alpha.h index 27f28228fcb..e20d3c80736 100644 --- a/gcc/config/alpha/alpha.h +++ b/gcc/config/alpha/alpha.h @@ -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 diff --git a/gcc/testsuite/gcc.c-torture/execute/va-arg-23.c b/gcc/testsuite/gcc.c-torture/execute/va-arg-23.c new file mode 100644 index 00000000000..89a11cf9f73 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/va-arg-23.c @@ -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 + +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; +}