aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog15
-rw-r--r--gcc/config/alpha/alpha-protos.h4
-rw-r--r--gcc/config/alpha/alpha.c78
-rw-r--r--gcc/config/alpha/alpha.h70
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/va-arg-23.c26
5 files changed, 125 insertions, 68 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index fced2cb..c3c9ada 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/config/alpha/alpha-protos.h b/gcc/config/alpha/alpha-protos.h
index 3bd852c..da3294d 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 28b9873..4feb218 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 27f2822..e20d3c8 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 0000000..89a11cf
--- /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 <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;
+}