Nested functions on ARM

Nick Clifton nickc@redhat.com
Mon Nov 13 18:02:00 GMT 2000


Hi Richard,

  I believe that I have a fix for the nested function problem on the
  ARM.  The idea is to make IP the static chain register, and to save
  it onto the stack during stack frame creation, in order to prevent
  its corruption.

  The patch below implements this idea, and seems to work.  (Certainly
  the test case that Uli posted now compiles and runs.  I am currently
  running the gcc testsuite to make sure that it does break anything
  else).

  There are two problems with the patch.  Firstly it does not handle
  the corruption of the IP register by the PIC code when AOF_ASSEMBLER
  is true.  I was unable to decipher what this code was doing (and why
  it is in output_arm_prologue instead of arm_expand_prologue).  If it
  is important I could try to add code for this case as well.

  The second problem is that the code uses an unallocated stack slot
  (ie a slot below the stack pointer).  It is only used for a short
  period of time, but it is vulnerable to corruption by interrupt
  handlers and the like.  I do not know if this will be a problem in
  real life.

  What do you think - shall I go ahead and apply it ?

Cheers
	Nick

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

	* config/arm/arm.h (STATIC_CHAIN_REGNUM): Change to 12, a
	call-clobbered register.
	* config/arm/arm.c (output_arm_prologue): Note when a function
	is a nested function.
	(arm_expand_prologue): When creating a stack frame for a
	nested function, preserve the IP register by pushing it into
	an allocated stack slot.

Index: 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/14 01:51:23
*************** 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

Index: 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/14 01:51:23
*************** 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 ()
*** 7313,7323 ****
--- 7316,7329 ----
    rtx amount = GEN_INT (-(get_frame_size ()
  			  + current_function_outgoing_args_size));
    int live_regs_mask = 0;
+   int fp_live_regs_mask = 0;
    int store_arg_regs = 0;
    /* If this function doesn't return, then there is no need to push
       the call-saved regs.  */
    int volatile_func = arm_volatile_func ();
    rtx insn;
+   rtx ip_rtx;
+   int ip_offset = 0;	  
  
    /* Naked functions don't have prologues.  */
    if (arm_naked_function_p (current_function_decl))
*************** arm_expand_prologue ()
*** 7343,7355 ****
  
        if (regs_ever_live[LR_REGNUM])
  	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;
      }
  
--- 7349,7393 ----
  
        if (regs_ever_live[LR_REGNUM])
  	live_regs_mask |= 1 << LR_REGNUM;
+ 
+       for (reg = 23; reg > 15; reg--)
+ 	if (regs_ever_live[reg] && ! call_used_regs[reg])
+ 	  fp_live_regs_mask |= 1 << reg;
      }
  
+   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 we push the static chian onto the stack
+ 	     (in an unallocated slot!), create the frame and then
+ 	     retrieve it.
+ 	     The stack offset to use is compted by taking into account
+ 	     the number of pretend arguments we are going to have to push,
+ 	     plus the number of callee-saved registers we are going to
+ 	     store in the frame, plus the 4 registers that always go
+ 	     into a stack frame.  */
+ 	  ip_offset = bit_count (live_regs_mask & 0xFF0);
+ 	  ip_offset += bit_count (fp_live_regs_mask);
+ 	  ip_offset += 4 + 1; /* fp, sp, lr, pc */
+ 	  ip_offset *= 4;
+ 	  ip_offset += current_function_pretend_args_size;
+ 	  
+ 	  insn = GEN_INT (- ip_offset);
+ 	  insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, insn);
+ 	  insn = gen_rtx_MEM  (SImode, insn);
+ 	  insn = gen_rtx_SET  (VOIDmode, insn, ip_rtx);
+ 	  insn = emit_insn (insn);
+ 			    
+ 	  RTX_FRAME_RELATED_P (insn) = 1;	  
+ 	}
+ 
        live_regs_mask |= 0xD800;
!       insn = emit_insn (gen_movsi (ip_rtx, stack_pointer_rtx));
        RTX_FRAME_RELATED_P (insn) = 1;
      }
  
*************** arm_expand_prologue ()
*** 7375,7387 ****
      }
        
    /* For now the integer regs are still pushed in output_arm_epilogue ().  */
- 
    if (!volatile_func)
      {
        if (arm_fpu_arch == FP_SOFT2)
  	{
  	  for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
! 	    if (regs_ever_live[reg] && !call_used_regs[reg])
  	      {
  		insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
  		insn = gen_rtx_MEM (XFmode, insn);
--- 7413,7424 ----
      }
        
    /* For now the integer regs are still pushed in output_arm_epilogue ().  */
    if (!volatile_func)
      {
        if (arm_fpu_arch == FP_SOFT2)
  	{
  	  for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
! 	    if (fp_live_regs_mask & (1 << reg))
  	      {
  		insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
  		insn = gen_rtx_MEM (XFmode, insn);
*************** arm_expand_prologue ()
*** 7396,7402 ****
  
  	  for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
  	    {
! 	      if (regs_ever_live[reg] && !call_used_regs[reg])
  		{
  		  if (start_reg - reg == 3)
  		    {
--- 7433,7439 ----
  
  	  for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
  	    {
! 	      if (fp_live_regs_mask & (1 << reg))
  		{
  		  if (start_reg - reg == 3)
  		    {
*************** arm_expand_prologue ()
*** 7427,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)
--- 7464,7482 ----
    if (frame_pointer_needed)
      {
        insn = GEN_INT (-(4 + current_function_pretend_args_size));
!       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.  */
+ 	  insn = GEN_INT (4 - ip_offset);
+ 	  insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, insn);
+ 	  insn = gen_rtx_MEM  (SImode, insn);
+ 	  insn = gen_rtx_SET  (VOIDmode, ip_rtx, insn);
+ 	  insn = emit_insn (insn);
+ 	  RTX_FRAME_RELATED_P (insn) = 1;
+ 	}
      }
  
    if (amount != const0_rtx)



More information about the Gcc-bugs mailing list