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]

patch to improve i386 epilogue (version 5)


[ This version uses life analysis to determine if the stack pointer
  is stable.  If it is stable then it's mark as being alive at the
  end of the function. ]

This change allows gcc to skip the loading of the stack pointer
when it is already known to contain the proper value.

ChangeLog:

Fri Oct  9 01:10:08 EDT 1998  John Wehle  (john@feith.com)

	* flow.c: Update comment.
	(life_analysis_1): Set current_function_stack_pointer_is_stable.
	* function.c: Define it.
	(init_function_start): Initialize it.
	* output.h: Declare it.
	* i386.c (ix86_epilogue): Optimize the restoring
	of the stack pointer.

Enjoy!

-- John Wehle
------------------8<------------------------8<------------------------
*** gcc/flow.c.ORIGINAL	Thu Oct  8 22:08:06 1998
--- gcc/flow.c	Fri Oct  9 00:59:32 1998
*************** Boston, MA 02111-1307, USA.  */
*** 106,112 ****
  
     life_analysis fills in certain vectors containing information about
     register usage: reg_n_refs, reg_n_deaths, reg_n_sets, reg_live_length,
!    reg_n_calls_crosses and reg_basic_block.  */
  
  #include "config.h"
  #include "system.h"
--- 106,115 ----
  
     life_analysis fills in certain vectors containing information about
     register usage: reg_n_refs, reg_n_deaths, reg_n_sets, reg_live_length,
!    reg_n_calls_crosses and reg_basic_block.
! 
!    life_analysis sets current_function_stack_pointer_is_stable if the
!    function doesn't modify the stack pointer.  */
  
  #include "config.h"
  #include "system.h"
*************** life_analysis_1 (f, nregs)
*** 1198,1206 ****
      = (regset *) alloca (n_basic_blocks * sizeof (regset));
    init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack);
  
    /* Record which insns refer to any volatile memory
       or for any reason can't be deleted just because they are dead stores.
!      Also, delete any insns that copy a register to itself.  */
  
    for (insn = f; insn; insn = NEXT_INSN (insn))
      {
--- 1201,1216 ----
      = (regset *) alloca (n_basic_blocks * sizeof (regset));
    init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack);
  
+   /* Assume that the stack pointer is stable if alloca hasn't been used.
+      This will be cleared if we encounter an INSN which modifies the stack
+      pointer.  */
+ 
+   current_function_stack_pointer_is_stable = !current_function_calls_alloca;
+ 
    /* Record which insns refer to any volatile memory
       or for any reason can't be deleted just because they are dead stores.
!      Also, delete any insns that copy a register to itself.
!      And see if the stack pointer is modified.  */
  
    for (insn = f; insn; insn = NEXT_INSN (insn))
      {
*************** life_analysis_1 (f, nregs)
*** 1287,1292 ****
--- 1297,1346 ----
  		   && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx)
  	    INSN_VOLATILE (insn) = 1;
  	}
+ 
+       if ( current_function_stack_pointer_is_stable )
+ 	{
+ 	  /* If the INSN hasn't been deleted then see if it modifies the
+ 	     stack pointer.  */
+ 	  if (GET_CODE (insn) == PARALLEL)
+ 	    for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ 	      {
+ 		rtx tem = XVECEXP (PATTERN (insn), 0, i);
+ 
+ 		if ((GET_CODE (tem) == CLOBBER
+ 		     && XEXP (tem, 0) == stack_pointer_rtx)
+ 		    || (GET_CODE (tem) == SET
+ 			&& SET_DEST (tem) == stack_pointer_rtx)
+ 		    || (GET_CODE (tem) == SET
+ 			&& GET_CODE (SET_DEST (tem)) == MEM
+ 			&& (GET_CODE (XEXP (SET_DEST (tem), 0)) == PRE_DEC
+ 			    || GET_CODE (XEXP (SET_DEST (tem), 0)) == PRE_INC
+ 			    || GET_CODE (XEXP (SET_DEST (tem), 0)) == POST_DEC
+ 			    || GET_CODE (XEXP (SET_DEST (tem), 0)) == POST_INC)
+ 			&& XEXP (XEXP (SET_DEST (tem), 0), 0) == stack_pointer_rtx))
+ 		  {
+ 		    current_function_stack_pointer_is_stable = 0;
+ 		    break;
+ 		  }
+ 	      }
+ 	  else if (GET_CODE (insn) == INSN)
+ 	    {
+ 	      rtx tem = PATTERN (insn);
+ 
+ 	      if ((GET_CODE (tem) == CLOBBER
+ 		   && XEXP (tem, 0) == stack_pointer_rtx)
+ 		  || (GET_CODE (tem) == SET
+ 		      && SET_DEST (tem) == stack_pointer_rtx)
+ 		  || (GET_CODE (tem) == SET
+ 		      && GET_CODE (SET_DEST (tem)) == MEM
+ 		      && (GET_CODE (XEXP (SET_DEST (tem), 0)) == PRE_DEC
+ 			  || GET_CODE (XEXP (SET_DEST (tem), 0)) == PRE_INC
+ 			  || GET_CODE (XEXP (SET_DEST (tem), 0)) == POST_DEC
+ 			  || GET_CODE (XEXP (SET_DEST (tem), 0)) == POST_INC)
+ 		      && XEXP (XEXP (SET_DEST (tem), 0), 0) == stack_pointer_rtx))
+ 		current_function_stack_pointer_is_stable = 0;
+ 	    }
+ 	}
      }
  
    if (n_basic_blocks > 0)
*************** life_analysis_1 (f, nregs)
*** 1294,1300 ****
      if (! EXIT_IGNORE_STACK
  	|| (! FRAME_POINTER_REQUIRED
  	    && ! current_function_calls_alloca
! 	    && flag_omit_frame_pointer))
  #endif
        {
  	/* If exiting needs the right stack value,
--- 1348,1355 ----
      if (! EXIT_IGNORE_STACK
  	|| (! FRAME_POINTER_REQUIRED
  	    && ! current_function_calls_alloca
! 	    && flag_omit_frame_pointer)
! 	|| current_function_stack_pointer_is_stable)
  #endif
        {
  	/* If exiting needs the right stack value,
*** gcc/function.c.ORIGINAL	Thu Oct  8 02:10:12 1998
--- gcc/function.c	Thu Oct  8 22:56:39 1998
*************** int current_function_has_computed_jump;
*** 138,143 ****
--- 138,149 ----
  
  int current_function_contains_functions;
  
+ /* Nonzero if function being compiled doesn't modify the stack pointer
+    (ignoring the prologue and epilogue).  This is only valid after
+    life_analysis has run. */
+ 
+ int current_function_stack_pointer_is_stable;
+ 
  /* Nonzero if the current function is a thunk (a lightweight function that
     just adjusts one of its arguments and forwards to another function), so
     we should try to cut corners where we can.  */
*************** init_function_start (subr, filename, lin
*** 5431,5436 ****
--- 5437,5443 ----
    current_function_has_nonlocal_label = 0;
    current_function_has_nonlocal_goto = 0;
    current_function_contains_functions = 0;
+   current_function_stack_pointer_is_stable = 0;
    current_function_is_thunk = 0;
  
    current_function_returns_pcc_struct = 0;
*** gcc/output.h.ORIGINAL	Thu Oct  8 02:10:13 1998
--- gcc/output.h	Thu Oct  8 22:58:37 1998
*************** extern int current_function_has_nonlocal
*** 380,385 ****
--- 380,391 ----
  
  extern int current_function_contains_functions;
  
+ /* Nonzero if function being compiled doesn't modify the stack pointer
+    (ignoring the prologue and epilogue).  This is only valid after
+    life_analysis has run. */
+ 
+ extern int current_function_stack_pointer_is_stable;
+ 
  /* Nonzero if the current function returns a pointer type */
  
  extern int current_function_returns_pointer;
*** gcc/config/i386/i386.c.ORIGINAL	Thu Oct  8 02:10:27 1998
--- gcc/config/i386/i386.c	Fri Oct  9 00:02:16 1998
*************** ix86_epilogue (do_rtl)
*** 2294,2299 ****
--- 2294,2301 ----
    rtx xops[3];
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
+   int sp_valid = !frame_pointer_needed
+ 		 || current_function_stack_pointer_is_stable;
    long tsize = get_frame_size ();
  
    /* Compute the number of registers to pop */
*************** ix86_epilogue (do_rtl)
*** 2307,2318 ****
  	|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
        nregs++;
  
!   /* sp is often  unreliable so we must go off the frame pointer.
! 
!      In reality, we may not care if sp is unreliable, because we can restore
!      the register relative to the frame pointer.  In theory, since each move
!      is the same speed as a pop, and we don't need the leal, this is faster.
!      For now restore multiple registers the old way. */
  
    offset = - tsize - (nregs * UNITS_PER_WORD);
  
--- 2309,2315 ----
  	|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
        nregs++;
  
!   /* sp is often unreliable so we may have to go off the frame pointer. */
  
    offset = - tsize - (nregs * UNITS_PER_WORD);
  
*************** ix86_epilogue (do_rtl)
*** 2329,2337 ****
    if (flag_pic || profile_flag || profile_block_flag)
      emit_insn (gen_blockage ());
  
!   if (nregs > 1 || ! frame_pointer_needed)
      {
!       if (frame_pointer_needed)
  	{
  	  xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
  	  if (do_rtl)
--- 2326,2339 ----
    if (flag_pic || profile_flag || profile_block_flag)
      emit_insn (gen_blockage ());
  
!   /* If we're only restoring one register and sp is not valid then
!      using a move instruction to restore the register since it's
!      less work than reloading sp and popping the register.  Otherwise,
!      restore sp (if necessary) and pop the registers. */
! 
!   if (nregs > 1 || sp_valid)
      {
!       if ( !sp_valid )
  	{
  	  xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
  	  if (do_rtl)
-------------------------------------------------------------------------
|   Feith Systems  |   Voice: 1-215-646-8000  |  Email: john@feith.com  |
|    John Wehle    |     Fax: 1-215-540-5495  |                         |
-------------------------------------------------------------------------



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