OSDN Git Service

Handle nested functions which take variable arguments
authornickc <nickc@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 14 Aug 2001 20:49:22 +0000 (20:49 +0000)
committernickc <nickc@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 14 Aug 2001 20:49:22 +0000 (20:49 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@44898 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/arm/arm-protos.h
gcc/config/arm/arm.c
gcc/config/arm/arm.h

index f705a11..2854206 100644 (file)
@@ -1,3 +1,15 @@
+2001-08-14  Nick Clifton  <nickc@cambridge.redhat.com>
+
+       * config/arm/arm.c (arm_compute_initial_elimination_offset): New
+       function.
+       (arm_expand_prologue): Handled nested functions which take a
+       variable argument list.
+       * config/arm/arm.h (ARM_INITIAL_ELIMINATION_OFFSET): Replace
+       macro with an invocation of
+       arm_compute_initial_elimination_offset.
+       * config/arm/arm-protos.h: Prototype
+       arm_compute_initial_elimination_offset. 
+
 2001-08-14  Gerald Pfeifer  <pfeifer@dbai.tuwien.ac.at>
 
        * doc/install.texi (Specific, avr): Fix markup.
index 7078058..1a603f2 100644 (file)
@@ -34,6 +34,7 @@ extern void   arm_expand_prologue     PARAMS ((void));
 extern void   assemble_align           PARAMS ((int)); 
 extern const char * arm_strip_name_encoding    PARAMS ((const char *));
 extern unsigned long arm_current_func_type     PARAMS ((void));
+extern unsigned int  arm_compute_initial_elimination_offset PARAMS ((unsigned int, unsigned int));
 
 #ifdef TREE_CODE
 extern int    arm_return_in_memory     PARAMS ((tree));
index 265a79b..7f89a3d 100644 (file)
@@ -7875,6 +7875,170 @@ emit_sfm (base_reg, count)
   return par;
 }
 
+/* Compute the distance from register FROM to register TO.
+   These can be the arg pointer (26), the soft frame pointer (25),
+   the stack pointer (13) or the hard frame pointer (11).
+   Typical stack layout looks like this:
+
+       old stack pointer -> |    |
+                             ----
+                            |    | \
+                            |    |   saved arguments for
+                            |    |   vararg functions
+                           |    | /
+                              --
+   hard FP & arg pointer -> |    | \
+                            |    |   stack
+                            |    |   frame
+                            |    | /
+                              --
+                            |    | \
+                            |    |   call saved
+                            |    |   registers
+      soft frame pointer -> |    | /
+                              --
+                            |    | \
+                            |    |   local
+                            |    |   variables
+                            |    | /
+                              --
+                            |    | \
+                            |    |   outgoing
+                            |    |   arguments
+   current stack pointer -> |    | /
+                              --
+
+  For a given funciton some or all of these stack compomnents
+  may not be needed, giving rise to the possibility of
+  eliminating some of the registers.
+
+  The values returned by this function must reflect the behaviour
+  of arm_expand_prologue() and arm_compute_save_reg_mask().
+
+  The sign of the number returned reflects the direction of stack
+  growth, so the values are positive for all eliminations except
+  from the soft frame pointer to the hard frame pointer.  */
+                           
+unsigned int
+arm_compute_initial_elimination_offset (from, to)
+     unsigned int from;
+     unsigned int to;
+{
+  unsigned int local_vars    = (get_frame_size () + 3) & ~3;
+  unsigned int outgoing_args = current_function_outgoing_args_size;
+  unsigned int stack_frame;
+  unsigned int call_saved_registers;
+  unsigned long func_type;
+  
+  func_type = arm_current_func_type ();
+
+  /* Volatile functions never return, so there is
+     no need to save call saved registers.  */
+  call_saved_registers = 0;
+  if (! IS_VOLATILE (func_type))
+    {
+      unsigned int reg;
+
+      for (reg = 0; reg <= 10; reg ++)
+       if (regs_ever_live[reg] && ! call_used_regs[reg])
+         call_saved_registers += 4;
+
+      if (! TARGET_APCS_FRAME
+         && ! frame_pointer_needed
+         && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
+         && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
+       call_saved_registers += 4;
+
+      if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+       call_saved_registers += 4;
+
+      if (regs_ever_live[LR_REGNUM]
+         /* If a stack frame is going to be created, the LR will
+            be saved as part of that, so we do not need to allow
+            for it here.  */
+         && ! frame_pointer_needed)
+       call_saved_registers += 4;
+    }
+
+  /* The stack frame contains 4 registers - the old frame pointer,
+     the old stack pointer, the return address and PC of the start
+     of the function.  */
+  stack_frame = frame_pointer_needed ? 16 : 0;
+
+  /* FIXME: we should allow for saved floating point registers.  */
+
+  /* OK, now we have enough information to compute the distances.
+     There must be an entry in these switch tables for each pair
+     of registers in ELIMINABLE_REGS, even if some of the entries
+     seem to be redundant or useless.  */
+  switch (from)
+    {
+    case ARG_POINTER_REGNUM:
+      switch (to)
+       {
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+         return 0;
+
+       case FRAME_POINTER_REGNUM:
+         /* This is the reverse of the soft frame pointer
+            to hard frame pointer elimination below.  */
+         if (call_saved_registers == 0 && stack_frame == 0)
+           return 0;
+         return (call_saved_registers + stack_frame - 4);
+
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         /* If there is no stack frame then the hard
+            frame pointer and the arg pointer coincide.  */
+         if (stack_frame == 0 && call_saved_registers != 0)
+           return 0;
+         /* FIXME:  Not sure about this.  Maybe we should always return 0 ?  */
+         return (frame_pointer_needed
+                 && current_function_needs_context
+                 && ! current_function_anonymous_args) ? 4 : 0;
+
+       case STACK_POINTER_REGNUM:
+         /* If nothing has been pushed on the stack at all
+            then this will return -4.  This *is* correct!  */
+         return call_saved_registers + stack_frame + local_vars + outgoing_args - 4;
+
+       default:
+         abort ();
+       }
+      break;
+
+    case FRAME_POINTER_REGNUM:
+      switch (to)
+       {
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+         return 0;
+
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         /* The hard frame pointer points to the top entry in the
+            stack frame.  The soft frame pointer to the bottom entry
+            in the stack frame.  If there is no stack frame at all,
+            then they are identical.  */
+         if (call_saved_registers == 0 && stack_frame == 0)
+           return 0;
+         return - (call_saved_registers + stack_frame - 4);
+
+       case STACK_POINTER_REGNUM:
+         return local_vars + outgoing_args;
+
+       default:
+         abort ();
+       }
+      break;
+
+    default:
+      /* You cannot eliminate from the stack pointer.
+        In theory you could eliminate from the hard frame
+        pointer to the stack pointer, but this will never
+        happen, since if a stack frame is not needed the
+        hard frame pointer will never be used.  */
+      abort ();
+    }
+}
+
 /* Generate the prologue instructions for entry into an ARM function.  */
 
 void
@@ -7887,6 +8051,8 @@ arm_expand_prologue ()
   unsigned long live_regs_mask;
   unsigned long func_type;
   int fp_offset = 0;
+  int saved_pretend_args = 0;
+  unsigned int args_to_push;
 
   func_type = arm_current_func_type ();
 
@@ -7894,6 +8060,9 @@ arm_expand_prologue ()
   if (IS_NAKED (func_type))
     return;
 
+  /* Make a copy of c_f_p_a_s as we may need to modify it locally.  */
+  args_to_push = current_function_pretend_args_size;
+  
   /* Compute which register we will have to save onto the stack.  */
   live_regs_mask = arm_compute_save_reg_mask ();
 
@@ -7920,8 +8089,8 @@ arm_expand_prologue ()
               1. The last argument register.
               2. A slot on the stack above the frame.  (This only
                  works if the function is not a varargs function).
-                 
-            If neither of these places is available, we abort (for now).
+              3. Register r3, after pushing the argument registers
+                 onto the stack.
 
             Note - we only need to tell the dwarf2 backend about the SP
             adjustment in the second variant; the static chain register
@@ -7934,7 +8103,7 @@ arm_expand_prologue ()
              insn = gen_rtx_SET (SImode, insn, ip_rtx);
              insn = emit_insn (insn);
            }
-         else if (current_function_pretend_args_size == 0)
+         else if (args_to_push == 0)
            {
              rtx dwarf;
              insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx);
@@ -7953,13 +8122,27 @@ arm_expand_prologue ()
                                                    dwarf, REG_NOTES (insn));
            }
          else
-           /* FIXME - the way to handle this situation is to allow
-              the pretend args to be dumped onto the stack, then
-              reuse r3 to save IP.  This would involve moving the
-              copying of SP into IP until after the pretend args
-              have been dumped, but this is not too hard.  */
-           /* [See e.g. gcc.c-torture/execute/nest-stdar-1.c.]  */
-           error ("Unable to find a temporary location for static chain register");
+           {
+             /* Store the args on the stack.  */
+             if (current_function_anonymous_args)
+               insn = emit_multi_reg_push
+                 ((0xf0 >> (args_to_push / 4)) & 0xf);
+             else
+               insn = emit_insn
+                 (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 
+                              GEN_INT (- args_to_push)));
+
+             RTX_FRAME_RELATED_P (insn) = 1;
+
+             saved_pretend_args = 1;
+             fp_offset = args_to_push;
+             args_to_push = 0;
+
+             /* Now reuse r3 to preserve IP.  */
+             insn = gen_rtx_REG (SImode, 3);
+             insn = gen_rtx_SET (SImode, insn, ip_rtx);
+             (void) emit_insn (insn);
+           }
        }
 
       if (fp_offset)
@@ -7974,16 +8157,16 @@ arm_expand_prologue ()
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
-  if (current_function_pretend_args_size)
+  if (args_to_push)
     {
       /* Push the argument registers, or reserve space for them.  */
       if (current_function_anonymous_args)
        insn = emit_multi_reg_push
-         ((0xf0 >> (current_function_pretend_args_size / 4)) & 0xf);
+         ((0xf0 >> (args_to_push / 4)) & 0xf);
       else
        insn = emit_insn
          (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, 
-                      GEN_INT (-current_function_pretend_args_size)));
+                      GEN_INT (- args_to_push)));
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
@@ -8045,25 +8228,26 @@ arm_expand_prologue ()
   if (frame_pointer_needed)
     {
       /* Create the new frame pointer.  */
-      insn = GEN_INT (-(4 + current_function_pretend_args_size + fp_offset));
+      insn = GEN_INT (-(4 + args_to_push + fp_offset));
       insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
       RTX_FRAME_RELATED_P (insn) = 1;
       
       if (IS_NESTED (func_type))
        {
          /* Recover the static chain register.  */
-         if (regs_ever_live [3] == 0)
+         if (regs_ever_live [3] == 0
+             || saved_pretend_args)
            {
              insn = gen_rtx_REG (SImode, 3);
              insn = gen_rtx_SET (SImode, ip_rtx, insn);
-             insn = emit_insn (insn);
+             (void) emit_insn (insn);
            }
          else /* if (current_function_pretend_args_size == 0) */
            {
              insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, GEN_INT (4));
              insn = gen_rtx_MEM (SImode, insn);
              insn = gen_rtx_SET (SImode, ip_rtx, insn);
-             insn = emit_insn (insn);
+             (void) emit_insn (insn);
            }
        }
     }
index 143e1d4..28f49ea 100644 (file)
@@ -1639,57 +1639,11 @@ typedef struct
 /* Define the offset between two registers, one to be eliminated, and the
    other its replacement, at the start of a routine.  */
 #define ARM_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)               \
-{                                                                      \
-  int volatile_func = IS_VOLATILE (arm_current_func_type ());          \
-  if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
-    {                                                                  \
-      if (! current_function_needs_context || ! frame_pointer_needed)  \
-        (OFFSET) = 0;                                                  \
-      else                                                             \
-        (OFFSET) = 4;                                                  \
-    }                                                                  \
-  else if ((FROM) == FRAME_POINTER_REGNUM                              \
-          && (TO) == STACK_POINTER_REGNUM)                             \
-    (OFFSET) = current_function_outgoing_args_size                     \
-               + ROUND_UP (get_frame_size ());                         \
-  else                                                                 \
+  do                                                                   \
     {                                                                  \
-      int regno;                                                       \
-      int offset = 12;                                                 \
-      int saved_hard_reg = 0;                                          \
-                                                                       \
-      if (! volatile_func)                                             \
-        {                                                              \
-          for (regno = 0; regno <= 10; regno++)                                \
-           if (regs_ever_live[regno] && ! call_used_regs[regno])       \
-             saved_hard_reg = 1, offset += 4;                          \
-         if (! TARGET_APCS_FRAME                                       \
-             && ! frame_pointer_needed                                 \
-             && regs_ever_live[HARD_FRAME_POINTER_REGNUM]              \
-             && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])           \
-           saved_hard_reg = 1, offset += 4;                            \
-         /* PIC register is a fixed reg, so call_used_regs set.  */    \
-         if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])      \
-           saved_hard_reg = 1, offset += 4;                            \
-          for (regno = FIRST_ARM_FP_REGNUM;                            \
-              regno <= LAST_ARM_FP_REGNUM; regno++)                    \
-           if (regs_ever_live[regno] && ! call_used_regs[regno])       \
-             offset += 12;                                             \
-       }                                                               \
-      if ((FROM) == FRAME_POINTER_REGNUM)                              \
-       (OFFSET) = - offset;                                            \
-      else                                                             \
-       {                                                               \
-          if (! frame_pointer_needed)                                  \
-            offset -= 16;                                              \
-          if (! volatile_func                                          \
-              && (regs_ever_live[LR_REGNUM] /*|| saved_hard_reg */))   \
-            offset += 4;                                               \
-          offset += current_function_outgoing_args_size;               \
-          (OFFSET) = ROUND_UP (get_frame_size ()) + offset;            \
-         }                                                             \
+      (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO);    \
     }                                                                  \
-}
+  while (0)
 
 /* Note:  This macro must match the code in thumb_function_prologue().  */
 #define THUMB_INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)             \
@@ -1727,7 +1681,7 @@ typedef struct
 
 #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                   \
   if (TARGET_ARM)                                                      \
-    ARM_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET)                  \
+    ARM_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET);                 \
   else                                                                 \
     THUMB_INITIAL_ELIMINATION_OFFSET (FROM, TO, OFFSET)