This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
patch to improve i386 epilogue (version 5)
- To: egcs-patches at cygnus dot com
- Subject: patch to improve i386 epilogue (version 5)
- From: John Wehle <john at feith dot com>
- Date: Fri, 9 Oct 1998 01:52:25 -0400 (EDT)
- Cc: law at cygnus dot com
[ 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 | |
-------------------------------------------------------------------------