/* 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. */
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);
}
\f
-/* 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)
\f
/* 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)
/* 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);
&& ! push_operand (dest, GET_MODE (dest)))
record_last_mem_set_info (last_set_insn);
}
-\f
+
/* Reset tables used to keep track of what's still available since the
start of the block. */
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);
-}
+\f
/* 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);
+ }
}
\f
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))
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))
}
}
\f
+
/* 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)
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);
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. */
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;
}
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. */
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;
}
/* 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,
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))? */
}
}
- /* 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);
}
}