OSDN Git Service

PR target/9700
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 14 Mar 2003 17:58:48 +0000 (17:58 +0000)
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 14 Mar 2003 17:58:48 +0000 (17:58 +0000)
        * 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@64367 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/alpha/alpha-protos.h
gcc/config/alpha/alpha.c
gcc/config/alpha/alpha.h
gcc/testsuite/gcc.c-torture/execute/va-arg-23.c [new file with mode: 0644]

index fced2cb..c3c9ada 100644 (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.
index 3bd852c..da3294d 100644 (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,
index 28b9873..4feb218 100644 (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 ()
 {
index 27f2822..e20d3c8 100644 (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
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 (file)
index 0000000..89a11cf
--- /dev/null
@@ -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;
+}