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]

Fix for ARM prologue construction failures


Hi Guys,

  I am checking in the patch below to fix a bug in the reorganisation
  of ARM function prologue generation that I recently made.  This
  patch fixes several gcc testsuite failures.

  The problem was that if the prologue decided not to save the link
  register onto the stack, the second conditional execution pass would
  find it and use it to hold scratch values, totally destroying the
  return address.  The patch adds a USE of the link register to
  prevent this, and makes sure that the epilogue code is not fooled by
  the fact that the link register has now become set in
  regs_ever_live[].

Cheers
	Nick


2000-12-22  Nick Clifton  <nickc@redhat.com>

	* config/arm/arm.h (struct machine_function): Add new field
	'lr_save_eliminated'.
	* config/arm/arm.c (arm_compute_save_reg_mask): Do not include the
	link register if its save has been eliminated.
	(output_arm_prologue): Mention if the save of the link register
	has been eliminated.
	(output_arm_epilogue): Rename 'live_regs_mask' to
	'saved_regs_mask'.
	Test 'saved_regs_mask' for the link register, not regs_ever_live.
	(arm_expand_prologue): If the link register has not been saved set
	lr_save_eliminated and emit a USE to prevent later passes from
	scavenging it.
	
Index: config/arm/arm.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.h,v
retrieving revision 1.92
diff -p -r1.92 arm.h
*** arm.h	2000/12/08 19:25:33	1.92
--- arm.h	2000/12/22 18:10:31
*************** typedef struct machine_function
*** 1444,1449 ****
--- 1444,1451 ----
    int far_jump_used;
    /* Records if ARG_POINTER was ever live.  */
    int arg_pointer_live;
+   /* Records if the save of LR has been eliminated.  */
+   int lr_save_eliminated;
    /* Records the type of the current function.  */
    unsigned long func_type;
  }

Index: config/arm/arm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.c,v
retrieving revision 1.127
diff -p -r1.127 arm.c
*** arm.c	2000/12/15 15:10:30	1.127
--- arm.c	2000/12/22 18:10:31
*************** arm_compute_save_reg_mask ()
*** 6906,6911 ****
--- 6906,6914 ----
  	      && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)))
      save_reg_mask |= 1 << LR_REGNUM;
  
+   if (cfun->machine->lr_save_eliminated)
+     save_reg_mask &= ~ (1 << LR_REGNUM);
+ 
    return save_reg_mask;
  }
  
*************** output_arm_prologue (f, frame_size)
*** 7204,7209 ****
--- 7207,7215 ----
  	       frame_pointer_needed,
  	       current_function_anonymous_args);
  
+   if (cfun->machine->lr_save_eliminated)
+     asm_fprintf (f, "\t%@ link register save eliminated.\n");
+ 
  #ifdef AOF_ASSEMBLER
    if (flag_pic)
      asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM);
*************** arm_output_epilogue (really_return)
*** 7217,7223 ****
       int really_return;
  {
    int reg;
!   unsigned long live_regs_mask;
    unsigned long func_type;
    /* If we need this, then it will always be at least this much.  */
    int floats_offset = 12;
--- 7223,7229 ----
       int really_return;
  {
    int reg;
!   unsigned long saved_regs_mask;
    unsigned long func_type;
    /* If we need this, then it will always be at least this much.  */
    int floats_offset = 12;
*************** arm_output_epilogue (really_return)
*** 7255,7265 ****
         be doing a return,  so we can't tail-call.  */
      abort ();
    
!   live_regs_mask = arm_compute_save_reg_mask ();
    
    /* Compute how far away the floats will be.  */
    for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
!     if (live_regs_mask & (1 << reg))
        floats_offset += 4;
    
    if (frame_pointer_needed)
--- 7261,7271 ----
         be doing a return,  so we can't tail-call.  */
      abort ();
    
!   saved_regs_mask = arm_compute_save_reg_mask ();
    
    /* Compute how far away the floats will be.  */
    for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
!     if (saved_regs_mask & (1 << reg))
        floats_offset += 4;
    
    if (frame_pointer_needed)
*************** arm_output_epilogue (really_return)
*** 7309,7335 ****
  			 FP_REGNUM, floats_offset);
  	}
  
!       /* live_regs_mask should contain the IP, which at the time of stack
  	 frame generation actually contains the old stack pointer.  So a
  	 quick way to unwind the stack is just pop the IP register directly
  	 into the stack pointer.  */
!       if ((live_regs_mask & (1 << IP_REGNUM)) == 0)
  	abort ();
!       live_regs_mask &= ~ (1 << IP_REGNUM);
!       live_regs_mask |=   (1 << SP_REGNUM);
  
!       /* There are two registers left in live_regs_mask - LR and PC.  We
  	 only need to restore the LR register (the return address), but to
  	 save time we can load it directly into the PC, unless we need a
  	 special function exit sequence, or we are not really returning.  */
        if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
  	/* Delete the LR from the register mask, so that the LR on
  	   the stack is loaded into the PC in the register mask.  */
! 	live_regs_mask &= ~ (1 << LR_REGNUM);
        else
! 	live_regs_mask &= ~ (1 << PC_REGNUM);
        
!       print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask);
      }
    else
      {
--- 7315,7341 ----
  			 FP_REGNUM, floats_offset);
  	}
  
!       /* saved_regs_mask should contain the IP, which at the time of stack
  	 frame generation actually contains the old stack pointer.  So a
  	 quick way to unwind the stack is just pop the IP register directly
  	 into the stack pointer.  */
!       if ((saved_regs_mask & (1 << IP_REGNUM)) == 0)
  	abort ();
!       saved_regs_mask &= ~ (1 << IP_REGNUM);
!       saved_regs_mask |=   (1 << SP_REGNUM);
  
!       /* There are two registers left in saved_regs_mask - LR and PC.  We
  	 only need to restore the LR register (the return address), but to
  	 save time we can load it directly into the PC, unless we need a
  	 special function exit sequence, or we are not really returning.  */
        if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
  	/* Delete the LR from the register mask, so that the LR on
  	   the stack is loaded into the PC in the register mask.  */
! 	saved_regs_mask &= ~ (1 << LR_REGNUM);
        else
! 	saved_regs_mask &= ~ (1 << PC_REGNUM);
        
!       print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask);
      }
    else
      {
*************** arm_output_epilogue (really_return)
*** 7385,7399 ****
        if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
  	  && really_return
  	  && current_function_pretend_args_size == 0
! 	  && regs_ever_live [LR_REGNUM])
  	{
! 	  live_regs_mask &= ~ (1 << LR_REGNUM);
! 	  live_regs_mask |=   (1 << PC_REGNUM);
  	}
  
        /* Load the registers off the stack.  If we only have one register
  	 to load use the LDR instruction - it is faster.  */
!       if (live_regs_mask == (1 << LR_REGNUM))
  	{
  	  /* The excpetion handler ignores the LR, so we do
  	     not really need to load it off the stack.  */
--- 7391,7405 ----
        if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
  	  && really_return
  	  && current_function_pretend_args_size == 0
! 	  && saved_regs_mask & (1 << LR_REGNUM))
  	{
! 	  saved_regs_mask &= ~ (1 << LR_REGNUM);
! 	  saved_regs_mask |=   (1 << PC_REGNUM);
  	}
  
        /* Load the registers off the stack.  If we only have one register
  	 to load use the LDR instruction - it is faster.  */
!       if (saved_regs_mask == (1 << LR_REGNUM))
  	{
  	  /* The excpetion handler ignores the LR, so we do
  	     not really need to load it off the stack.  */
*************** arm_output_epilogue (really_return)
*** 7402,7409 ****
  	  else
  	    asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
  	}
!       else if (live_regs_mask)
! 	print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask);
  
        if (current_function_pretend_args_size)
  	{
--- 7408,7415 ----
  	  else
  	    asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
  	}
!       else if (saved_regs_mask)
! 	print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
  
        if (current_function_pretend_args_size)
  	{
*************** arm_output_epilogue (really_return)
*** 7452,7458 ****
  	   here.  */
  	;
        else if (current_function_pretend_args_size == 0
! 	       && regs_ever_live [LR_REGNUM])
  	/* Similarly we may have been able to load LR into the PC
  	   even if we did not create a stack frame.  */
  	;
--- 7458,7464 ----
  	   here.  */
  	;
        else if (current_function_pretend_args_size == 0
! 	       && (saved_regs_mask & (1 << LR_REGNUM)))
  	/* Similarly we may have been able to load LR into the PC
  	   even if we did not create a stack frame.  */
  	;
*************** arm_expand_prologue ()
*** 7879,7884 ****
--- 7885,7898 ----
       scheduling in the prolog.  */
    if (profile_flag || profile_block_flag || TARGET_NO_SCHED_PRO)
      emit_insn (gen_blockage ());
+ 
+   /* If the link register is being kept alive, with the return address in it,
+      then make sure that it does not get reused by the ce2 pass.  */
+   if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
+     {
+       emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, LR_REGNUM)));
+       cfun->machine->lr_save_eliminated = 1;
+     }
  }
  
  /* If CODE is 'd', then the X is a condition operand and the instruction

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