diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index f705a11dcb1d02780c944830e59ad08fc3571cbe..2854206e62335295e775536b83b0805d397955ab 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 70780582022cd9765fb678c535c6b2e6133aabf2..1a603f263138d58b36ef1089b064f045ebd71955 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -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));
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 265a79b034a40931132e8220bbf1c11b3eb65672..7f89a3d61ff3c0a67d78e5f7251c42a3d6678fb8 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -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);
 	    }
 	}
     }
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 143e1d4614b40b7771e6da407c8178b1a7b14667..28f49eacbc45397cc03fb7d60f6183aaa7c175cf 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -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)