This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Nested functions on ARM (mk III)


Hi Richards,

: > 	str	ip, [sp, #-4]!
: > 	add	ip, sp, #4
: > 	stmfd	sp!, {r4, fp, ip, lr, pc}
: > 	sub	fp, ip, #4
: > 	ldr	ip, [fp, #4] 
: 
: This sequence is wrong.  You can't leave a hole in the stack like that.
: 
: 1) As richard has pointed out, it upsets the elimination offsets.

True, I had forgotten about this.

: 2) The return sequence won't adjust for it when the frame pointer is 
: eliminated.

This is not a problem.  If the frame pointer is not being used, then a
stack frame is not created, and so IP will not be corrupted by the
function's prologue.  In this case the new code does nothing at all,
and just leaves the static chain in IP.

: Finally, have you checked that this compatible with dwarf unwind
: tables (ok, so those still aren't working properly, after the last
: round of changes to dwarf2out.c).

No, I have not checked.  I am not too worried about this because as
you say they are still not working properly.


So, here is a tweaked patch, that fixes the initial elimination offset
and also the computation of the new frame pointer (which was broken in
the previous version).  I have several test files now, which I will
also check into the testsuite once this patch is ready.

Here is an example code sequence for a nested function that takes 7
arguments and adds most of them together. (7 arguments are used so
that some of them are in the caller's stack space).  I added the
comments to show what is happening:

	str	ip, [sp, #-4]!		; Save the static chain on the stack
	add	ip, sp, #4		; Make IP point at end of old stack
	stmfd	sp!, {fp, ip, lr, pc}	; Create a stack frame
	sub	fp, ip, #8		; Point FP at the top of the frame
	ldr	ip, [fp, #4]		; Recover the static chain
	sub	sp, sp, #4		; Allocate a real stack slot for the static chain
					; End of prologue
	add	r3, r2, r3		; Add args 2 and 3
	add	r2, fp, #8		; Point at args in caller's stack space
	ldmia	r2, {r2, lr}		; Get args 5 & 6
	add	r3, r3, r2		; Add them
	ldr	r2, [fp, #16]		; Get arg 7
	...etc

I have actually compiled and run this test code and it does work, both
with and without the frame pointer being eliminated.

Cheers
	Nick

2000-11-15  Nick Clifton  <nickc@redhat.com>

	* config/arm/arm.c (output_arm_prologue): Note nested functions.
	(arm_expand_prologue): For nested functions preserve the
	static chain register during stack frame creation.

	* config/arm/arm.h (STATIC_CHAIN_REGNUM): Change to 12.
	(ARM_INITIAL_FRAME_ELIMINATION_OFFSET): For a nested function
	with a stack frame there is a 4 byte gap between the arg
	pointer and the hard frame pointer (used to preserve the
	static chain register during stack frame creation).

Index: config/arm/arm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.c,v
retrieving revision 1.113
diff -p -r1.113 arm.c
*** arm.c	2000/11/10 16:01:21	1.113
--- arm.c	2000/11/15 18:56:29
*************** output_arm_prologue (f, frame_size)
*** 6776,6781 ****
--- 6776,6784 ----
    if (volatile_func)
      asm_fprintf (f, "\t%@ Volatile function.\n");
  
+   if (current_function_needs_context)
+     asm_fprintf (f, "\t%@ Nested function.\n");
+ 
    if (current_function_anonymous_args && current_function_pretend_args_size)
      store_arg_regs = 1;
  
*************** arm_expand_prologue ()
*** 7318,7323 ****
--- 7321,7329 ----
       the call-saved regs.  */
    int volatile_func = arm_volatile_func ();
    rtx insn;
+   rtx ip_rtx;
+   int fp_offset = 0;
+       
  
    /* Naked functions don't have prologues.  */
    if (arm_naked_function_p (current_function_decl))
*************** arm_expand_prologue ()
*** 7345,7355 ****
  	live_regs_mask |= 1 << LR_REGNUM;
      }
  
    if (frame_pointer_needed)
      {
        live_regs_mask |= 0xD800;
!       insn = emit_insn (gen_movsi (gen_rtx_REG (SImode, IP_REGNUM),
! 				   stack_pointer_rtx));
        RTX_FRAME_RELATED_P (insn) = 1;
      }
  
--- 7351,7409 ----
  	live_regs_mask |= 1 << LR_REGNUM;
      }
  
+   ip_rtx = gen_rtx_REG (SImode, IP_REGNUM);
+   
    if (frame_pointer_needed)
      {
+       if (current_function_needs_context)
+ 	{
+ 	  /* The Static chain register is the same as the IP register
+ 	     used as a scratch register during stack frame creation.
+ 	     To get around this need to find somewhere to store IP
+ 	     whilst the frame is being created.  We try the following
+ 	     places in order:
+ 	     
+ 	       1. An unused 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).  */
+ 	  if (regs_ever_live[3] == 0)
+ 	    {
+ 	      insn = gen_rtx_REG (SImode, 3);
+ 	      insn = gen_rtx_SET (SImode, insn, ip_rtx);
+ 	      insn = emit_insn (insn);
+ 	      RTX_FRAME_RELATED_P (insn) = 1;	  
+ 	    }
+ 	  else if (current_function_pretend_args_size == 0)
+ 	    {
+ 	      insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx);
+ 	      insn = gen_rtx_MEM (SImode, insn);
+ 	      insn = gen_rtx_SET (VOIDmode, insn, ip_rtx);
+ 	      insn = emit_insn (insn);
+ 	      RTX_FRAME_RELATED_P (insn) = 1;
+ 	      fp_offset = 4;
+ 	    }
+ 	  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 os SP into IP until after the pretend args
+ 	       have been dumped, but this is not too hard.  */
+ 	    error ("Unable to find a temporary location for static chanin register");
+ 	}
+ 
        live_regs_mask |= 0xD800;
! 
!       if (fp_offset)
! 	{
! 	  insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset));
! 	  insn = gen_rtx_SET  (SImode, ip_rtx, insn);
! 	}
!       else
! 	insn = gen_movsi (ip_rtx, stack_pointer_rtx);
!       
!       insn = emit_insn (insn);
        RTX_FRAME_RELATED_P (insn) = 1;
      }
  
*************** arm_expand_prologue ()
*** 7426,7436 ****
  
    if (frame_pointer_needed)
      {
!       insn = GEN_INT (-(4 + current_function_pretend_args_size));
!       insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
! 				    gen_rtx_REG (SImode, IP_REGNUM),
! 				    insn));
        RTX_FRAME_RELATED_P (insn) = 1;
      }
  
    if (amount != const0_rtx)
--- 7480,7508 ----
  
    if (frame_pointer_needed)
      {
!       insn = GEN_INT (-(4 + current_function_pretend_args_size + fp_offset));
!       insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
        RTX_FRAME_RELATED_P (insn) = 1;
+       
+       if (current_function_needs_context)
+ 	{
+ 	  /* Recover the static chain register.  */
+ 	  if (regs_ever_live [3] == 0)
+ 	    {
+ 	      insn = gen_rtx_REG (SImode, 3);
+ 	      insn = gen_rtx_SET (SImode, ip_rtx, insn);
+ 	      insn = emit_insn (insn);
+ 	      RTX_FRAME_RELATED_P (insn) = 1;	  
+ 	    }
+ 	  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);
+ 	      RTX_FRAME_RELATED_P (insn) = 1;	  
+ 	    }
+ 	}
      }
  
    if (amount != const0_rtx)

Index: config/arm/arm.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.h,v
retrieving revision 1.85
diff -p -r1.85 arm.h
*** arm.h	2000/11/02 23:29:08	1.85
--- arm.h	2000/11/15 18:56:29
*************** extern const char * structure_size_strin
*** 891,897 ****
  /* The native (Norcroft) Pascal compiler for the ARM passes the static chain
     as an invisible last argument (possible since varargs don't exist in
     Pascal), so the following is not true.  */
! #define STATIC_CHAIN_REGNUM	(TARGET_ARM ? 8 : 9)
  
  /* Define this to be where the real frame pointer is if it is not possible to
     work out the offset between the frame pointer and the automatic variables
--- 891,897 ----
  /* The native (Norcroft) Pascal compiler for the ARM passes the static chain
     as an invisible last argument (possible since varargs don't exist in
     Pascal), so the following is not true.  */
! #define STATIC_CHAIN_REGNUM	(TARGET_ARM ? 12 : 9)
  
  /* Define this to be where the real frame pointer is if it is not possible to
     work out the offset between the frame pointer and the automatic variables
*************** typedef struct
*** 1600,1606 ****
  {									\
    int volatile_func = arm_volatile_func ();				\
    if ((FROM) == ARG_POINTER_REGNUM && (TO) == HARD_FRAME_POINTER_REGNUM)\
!     (OFFSET) = 0;							\
    else if ((FROM) == FRAME_POINTER_REGNUM				\
  	   && (TO) == STACK_POINTER_REGNUM)				\
      (OFFSET) = current_function_outgoing_args_size			\
--- 1600,1611 ----
  {									\
    int volatile_func = arm_volatile_func ();				\
    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			\

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]