From c93320c457dc75596c5482cdbef7783ad6cdaf2e Mon Sep 17 00:00:00 2001 From: Steven Bosscher Date: Tue, 21 Sep 2004 07:48:29 +0000 Subject: [PATCH] re PR rtl-optimization/17482 (GCSE after reload replacing changing instructions) PR rtl-optimization/17482 * postreload-gcse.c (reg_avail_info, oprs_unchanged_p, load_killed_in_block_p): Clarify comments. (record_last_reg_set_info): Make static inline. (mark_call, mark_set, mark_clobber, mark_oprs_set): Remove. (record_opr_changes): New function to replace the above. (compute_hash_table): Clarify comments. Use record_opr_changes. (reg_set_between_after_reload_p): Clean up. (reg_used_between_after_reload_p): Likewise. (eliminate_partially_redundant_load): Clarify comments. From-SVN: r87794 --- gcc/ChangeLog | 13 +++ gcc/postreload-gcse.c | 264 +++++++++++++++++------------------------- 2 files changed, 119 insertions(+), 158 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4e4d19ad394..801759d5a89 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2004-09-21 Steven Bosscher + + PR rtl-optimization/17482 + * postreload-gcse.c (reg_avail_info, oprs_unchanged_p, + load_killed_in_block_p): Clarify comments. + (record_last_reg_set_info): Make static inline. + (mark_call, mark_set, mark_clobber, mark_oprs_set): Remove. + (record_opr_changes): New function to replace the above. + (compute_hash_table): Clarify comments. Use record_opr_changes. + (reg_set_between_after_reload_p): Clean up. + (reg_used_between_after_reload_p): Likewise. + (eliminate_partially_redundant_load): Clarify comments. + 2004-09-21 Eric Botcazou PR rtl-optimization/17266 diff --git a/gcc/postreload-gcse.c b/gcc/postreload-gcse.c index 93d81c48241..0238acefccf 100644 --- a/gcc/postreload-gcse.c +++ b/gcc/postreload-gcse.c @@ -135,7 +135,13 @@ static struct obstack unoccr_obstack; /* Array where each element is the CUID if the insn that last set the hard register with the number of the element, since the start of the current - basic block. */ + basic block. + + This array is used during the building of the hash table (step 1) to + determine if a reg is killed before the end of a basic block. + + It is also used when eliminating partial redundancies (step 2) to see + if a reg was modified since the start of a basic block. */ static int *reg_avail_info; /* A list of insns that may modify memory within the current basic block. */ @@ -169,10 +175,7 @@ static bool oprs_unchanged_p (rtx, rtx, bool); static void record_last_reg_set_info (rtx, int); static void record_last_mem_set_info (rtx); static void record_last_set_info (rtx, rtx, void *); -static void mark_call (rtx); -static void mark_set (rtx, rtx); -static void mark_clobber (rtx, rtx); -static void mark_oprs_set (rtx); +static void record_opr_changes (rtx); static void find_mem_conflicts (rtx, rtx, void *); static int load_killed_in_block_p (int, rtx, bool); @@ -466,10 +469,10 @@ dump_hash_table (FILE *file) } -/* Return nonzero if the operands of expression X are unchanged from the - start of INSN's basic block up to but not including INSN if AFTER_INSN - is false, or from INSN to the end of INSN's basic block if AFTER_INSN - is true. */ +/* Return nonzero if the operands of expression X are unchanged + 1) from the start of INSN's basic block up to but not including INSN + if AFTER_INSN is false, or + 2) from INSN to the end of INSN's basic block if AFTER_INSN is true. */ static bool oprs_unchanged_p (rtx x, rtx insn, bool after_insn) @@ -584,8 +587,12 @@ find_mem_conflicts (rtx dest, rtx setter ATTRIBUTE_UNUSED, /* Return nonzero if the expression in X (a memory reference) is killed - in block BB before if (AFTER_INSN is false) or after (if AFTER_INSN - is true) the insn with the CUID in UID_LIMIT. */ + in the current basic block before (if AFTER_INSN is false) or after + (if AFTER_INSN is true) the insn with the CUID in UID_LIMIT. + + This function assumes that the modifies_mem table is flushed when + the hash table construction or redundancy elimination phases start + processing a new basic block. */ static int load_killed_in_block_p (int uid_limit, rtx x, bool after_insn) @@ -629,7 +636,7 @@ load_killed_in_block_p (int uid_limit, rtx x, bool after_insn) /* Record register first/last/block set information for REGNO in INSN. */ -static void +static inline void record_last_reg_set_info (rtx insn, int regno) { reg_avail_info[regno] = INSN_CUID (insn); @@ -671,7 +678,7 @@ record_last_set_info (rtx dest, rtx setter ATTRIBUTE_UNUSED, void *data) && ! push_operand (dest, GET_MODE (dest))) record_last_mem_set_info (last_set_insn); } - + /* Reset tables used to keep track of what's still available since the start of the block. */ @@ -683,85 +690,44 @@ reset_opr_set_tables (void) obstack_free (&modifies_mem_obstack, modifies_mem_obstack_bottom); modifies_mem_list = NULL; } - -/* Mark things set by a CALL. */ - -static void -mark_call (rtx insn) -{ - if (! CONST_OR_PURE_CALL_P (insn)) - record_last_mem_set_info (insn); -} - -/* Mark things set by a SET. */ - -static void -mark_set (rtx pat, rtx insn) -{ - rtx dest = SET_DEST (pat); - - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - - if (REG_P (dest)) - record_last_reg_set_info (insn, REGNO (dest)); - else if (MEM_P (dest)) - record_last_mem_set_info (insn); - - if (GET_CODE (SET_SRC (pat)) == CALL) - mark_call (insn); -} - -/* Record things set by a CLOBBER. */ - -static void -mark_clobber (rtx pat, rtx insn) -{ - rtx clob = XEXP (pat, 0); - - while (GET_CODE (clob) == SUBREG - || GET_CODE (clob) == STRICT_LOW_PART) - clob = XEXP (clob, 0); - - if (REG_P (clob)) - record_last_reg_set_info (insn, REGNO (clob)); - else - record_last_mem_set_info (insn); -} + /* Record things set by INSN. This data is used by oprs_unchanged_p. */ static void -mark_oprs_set (rtx insn) +record_opr_changes (rtx insn) { - rtx pat = PATTERN (insn); - int i; + rtx note; - if (GET_CODE (pat) == SET) - mark_set (pat, insn); + /* Find all stores and record them. */ + note_stores (PATTERN (insn), record_last_set_info, insn); - else if (GET_CODE (pat) == PARALLEL) - for (i = 0; i < XVECLEN (pat, 0); i++) - { - rtx x = XVECEXP (pat, 0, i); - - if (GET_CODE (x) == SET) - mark_set (x, insn); - else if (GET_CODE (x) == CLOBBER) - mark_clobber (x, insn); - else if (GET_CODE (x) == CALL) - mark_call (insn); - } + /* Also record autoincremented REGs for this insn as changed. */ + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_INC) + record_last_reg_set_info (insn, REGNO (XEXP (note, 0))); - else if (GET_CODE (pat) == CLOBBER) - mark_clobber (pat, insn); + /* Finally, if this is a call, record all call clobbers. */ + if (CALL_P (insn)) + { + unsigned int regno; + bool clobbers_all = false; + +#ifdef NON_SAVING_SETJMP + if (NON_SAVING_SETJMP + && find_reg_note (insn, REG_SETJMP, NULL_RTX)) + clobbers_all = true; +#endif + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (clobbers_all + || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) + record_last_reg_set_info (insn, regno); - else if (GET_CODE (pat) == CALL) - mark_call (insn); + if (! CONST_OR_PURE_CALL_P (insn)) + record_last_mem_set_info (insn); + } } @@ -791,7 +757,7 @@ hash_scan_set (rtx insn) if (REG_P (dest)) { - if (/* Don't GCSE something if we can't do a reg/reg copy. */ + if (/* Don't CSE something if we can't do a reg/reg copy. */ can_copy_p (GET_MODE (dest)) /* Is SET_SRC something we want to gcse? */ && general_operand (src, GET_MODE (src)) @@ -805,7 +771,7 @@ hash_scan_set (rtx insn) else if (REG_P (src)) { /* Only record sets of pseudo-regs in the hash table. */ - if (/* Don't GCSE something if we can't do a reg/reg copy. */ + if (/* Don't CSE something if we can't do a reg/reg copy. */ can_copy_p (GET_MODE (src)) /* Is SET_DEST something we want to gcse? */ && general_operand (dest, GET_MODE (dest)) @@ -819,8 +785,12 @@ hash_scan_set (rtx insn) } } + /* Create hash table of memory expressions available at end of basic - blocks. */ + blocks. Basically you should think of this hash table as the + representation of AVAIL_OUT. This is the set of expressions that + is generated in a basic block and not killed before the end of the + same basic block. Notice that this is really a local computation. */ static void compute_hash_table (void) @@ -830,59 +800,20 @@ compute_hash_table (void) FOR_EACH_BB (bb) { rtx insn; - unsigned int regno; - - reset_opr_set_tables (); /* First pass over the instructions records information used to - determine when registers and memory are first and last set. */ + determine when registers and memory are last set. + Since we compute a "local" AVAIL_OUT, reset the tables that + help us keep track of what has been modified since the start + of the block. */ + reset_opr_set_tables (); FOR_BB_INSNS (bb, insn) { - if (! INSN_P (insn)) - continue; - - if (CALL_P (insn)) - { - bool clobbers_all = false; - -#ifdef NON_SAVING_SETJMP - if (NON_SAVING_SETJMP - && find_reg_note (insn, REG_SETJMP, NULL_RTX)) - clobbers_all = true; -#endif - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (clobbers_all - || TEST_HARD_REG_BIT (regs_invalidated_by_call, - regno)) - record_last_reg_set_info (insn, regno); - - if (! CONST_OR_PURE_CALL_P (insn)) - record_last_mem_set_info (insn); - } - - note_stores (PATTERN (insn), record_last_set_info, insn); - - if (GET_CODE (PATTERN (insn)) == SET) - { - rtx src, dest; - - src = SET_SRC (PATTERN (insn)); - dest = SET_DEST (PATTERN (insn)); - if (MEM_P (src) && auto_inc_p (XEXP (src, 0))) - { - regno = REGNO (XEXP (XEXP (src, 0), 0)); - record_last_reg_set_info (insn, regno); - } - if (MEM_P (dest) && auto_inc_p (XEXP (dest, 0))) - { - regno = REGNO (XEXP (XEXP (dest, 0), 0)); - record_last_reg_set_info (insn, regno); - } - } - } + if (INSN_P (insn)) + record_opr_changes (insn); + } - /* The next pass builds the hash table. */ + /* The next pass actually builds the hash table. */ FOR_BB_INSNS (bb, insn) if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == SET) hash_scan_set (insn); @@ -932,7 +863,6 @@ static rtx reg_set_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) { rtx insn; - int regno; #ifdef ENABLE_CHECKING /* We are called after register allocation. */ @@ -943,22 +873,21 @@ reg_set_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) if (from_insn == to_insn) return NULL_RTX; - regno = REGNO (reg); for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) - { - if (INSN_P (insn)) - { - if (FIND_REG_INC_NOTE (insn, reg) - || (CALL_P (insn) - && call_used_regs[regno]) - || find_reg_fusage (insn, CLOBBER, reg)) - return insn; - } - if (set_of (reg, insn) != NULL_RTX) - return insn; - } + if (INSN_P (insn)) + { + if (set_of (reg, insn) != NULL_RTX) + return insn; + if ((CALL_P (insn) + && call_used_regs[REGNO (reg)]) + || find_reg_fusage (insn, CLOBBER, reg)) + return insn; + + if (FIND_REG_INC_NOTE (insn, reg)) + return insn; + } return NULL_RTX; } @@ -971,7 +900,6 @@ static rtx reg_used_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) { rtx insn; - int regno; #ifdef ENABLE_CHECKING /* We are called after register allocation. */ @@ -982,17 +910,21 @@ reg_used_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn) if (from_insn == to_insn) return NULL_RTX; - regno = REGNO (reg); for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn)) - if (INSN_P (insn) - && (reg_overlap_mentioned_p (reg, PATTERN (insn)) + if (INSN_P (insn)) + { + if (reg_overlap_mentioned_p (reg, PATTERN (insn)) || (CALL_P (insn) - && call_used_regs[regno]) + && call_used_regs[REGNO (reg)]) || find_reg_fusage (insn, USE, reg) - || find_reg_fusage (insn, CLOBBER, reg))) - return insn; + || find_reg_fusage (insn, CLOBBER, reg)) + return insn; + + if (FIND_REG_INC_NOTE (insn, reg)) + return insn; + } return NULL_RTX; } @@ -1066,7 +998,16 @@ get_bb_avail_insn (basic_block bb, struct occr *occr) /* This handles the case where several stores feed a partially redundant load. It checks if the redundancy elimination is possible and if it's - worth it. */ + worth it. + + Redundancy elimination is possible if, + 1) None of the operands of an insn have been modified since the start + of the current basic block. + 2) In any predecessor of the current basic block, the same expression + is generated. + + See the function body for the heuristics that determine if eliminating + a redundancy is also worth doing, assuming it is possible. */ static void eliminate_partially_redundant_load (basic_block bb, rtx insn, @@ -1251,15 +1192,20 @@ eliminate_partially_redundant_loads (void) EXIT_BLOCK_PTR, next_bb) { + /* Don't try anything on basic blocks with strange predecessors. */ if (! bb_has_well_behaved_predecessors (bb)) continue; - /* Do not try this optimization on cold basic blocks. */ + /* Do not try anything on cold basic blocks. */ if (probably_cold_bb_p (bb)) continue; + /* Reset the table of things changed since the start of the current + basic block. */ reset_opr_set_tables (); + /* Look at all insns in the current basic block and see if there are + any loads in it that we can record. */ FOR_BB_INSNS (bb, insn) { /* Is it a load - of the form (set (reg) (mem))? */ @@ -1290,9 +1236,11 @@ eliminate_partially_redundant_loads (void) } } - /* Keep track of everything modified by this insn. */ + /* Keep track of everything modified by this insn, so that we + know what has been modified since the start of the current + basic block. */ if (INSN_P (insn)) - mark_oprs_set (insn); + record_opr_changes (insn); } } -- 2.30.2