Commit df35c271 by Steven Bosscher

gcse.c (ae_gen): Remove.

	* gcse.c (ae_gen): Remove.
	(can_assign_to_reg_p): Rename to can_assign_to_reg_without_clobbers_p
	and make non-static function to make it available in store-motion.c.
	Update call sites with search-and-replace.
	(enumerate_ldsts, reg_set_info, reg_clear_last_set, store_ops_ok,
	extract_mentioned_regs, extract_mentioned_regs_helper,
	find_moveable_store, compute_store_table, load_kills_store, find_loads,
	store_killed_in_insn, store_killed_after, store_killed_before,
	build_store_vectors, insert_insn_start_basic_block, insert-store,
	remove_reachable_equiv_notes, replace_store_insn, delete_store,
	free_store_memory, one_store_motion_pass, gate_rtl_store_motion,
	execute_rtl_store_motion, pass_rtl_store_motion): Move to...
	* store-motion.c: ...new file.  Also copy data structures from gcse.c
	and clean up to remove parts not used by store motion.
	* rtl.h (can_assign_to_reg_without_clobbers_p): Add prototype.
	* Makefile.in (store-motion.o): New rule. Add to OBJS-common.

From-SVN: r147001
parent f711a87a
2009-04-30 Steven Bosscher <steven@gcc.gnu.org>
* gcse.c (ae_gen): Remove.
(can_assign_to_reg_p): Rename to can_assign_to_reg_without_clobbers_p
and make non-static function to make it available in store-motion.c.
Update call sites with search-and-replace.
(enumerate_ldsts, reg_set_info, reg_clear_last_set, store_ops_ok,
extract_mentioned_regs, extract_mentioned_regs_helper,
find_moveable_store, compute_store_table, load_kills_store, find_loads,
store_killed_in_insn, store_killed_after, store_killed_before,
build_store_vectors, insert_insn_start_basic_block, insert-store,
remove_reachable_equiv_notes, replace_store_insn, delete_store,
free_store_memory, one_store_motion_pass, gate_rtl_store_motion,
execute_rtl_store_motion, pass_rtl_store_motion): Move to...
* store-motion.c: ...new file. Also copy data structures from gcse.c
and clean up to remove parts not used by store motion.
* rtl.h (can_assign_to_reg_without_clobbers_p): Add prototype.
* Makefile.in (store-motion.o): New rule. Add to OBJS-common.
2009-04-30 Ramana Radhakrishnan <ramana.radhakrishnan@arm.com> 2009-04-30 Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
PR target/38571 PR target/38571
......
...@@ -1199,6 +1199,7 @@ OBJS-common = \ ...@@ -1199,6 +1199,7 @@ OBJS-common = \
statistics.o \ statistics.o \
stmt.o \ stmt.o \
stor-layout.o \ stor-layout.o \
store-motion.o \
stringpool.o \ stringpool.o \
targhooks.o \ targhooks.o \
timevar.o \ timevar.o \
...@@ -2700,6 +2701,11 @@ see.o : see.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ ...@@ -2700,6 +2701,11 @@ see.o : see.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(REGS_H) hard-reg-set.h $(FLAGS_H) $(REAL_H) insn-config.h $(GGC_H) \ $(REGS_H) hard-reg-set.h $(FLAGS_H) $(REAL_H) insn-config.h $(GGC_H) \
$(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \ $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \
$(TM_P_H) $(PARAMS_H) $(EXCEPT_H) $(TREE_H) $(TIMEVAR_H) \
intl.h $(OBSTACK_H) $(TREE_PASS_H) $(DF_H) $(DBGCNT_H)
store-motion.o : store-motion.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(REGS_H) hard-reg-set.h $(FLAGS_H) $(REAL_H) insn-config.h $(GGC_H) \
$(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h $(TOPLEV_H) \
$(TM_P_H) $(PARAMS_H) $(EXCEPT_H) gt-gcse.h $(TREE_H) cselib.h $(TIMEVAR_H) \ $(TM_P_H) $(PARAMS_H) $(EXCEPT_H) gt-gcse.h $(TREE_H) cselib.h $(TIMEVAR_H) \
intl.h $(OBSTACK_H) $(TREE_PASS_H) $(DF_H) $(DBGCNT_H) intl.h $(OBSTACK_H) $(TREE_PASS_H) $(DF_H) $(DBGCNT_H)
resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h $(SYSTEM_H) \ resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h $(SYSTEM_H) \
......
...@@ -437,7 +437,7 @@ static int global_const_prop_count; ...@@ -437,7 +437,7 @@ static int global_const_prop_count;
static int global_copy_prop_count; static int global_copy_prop_count;
/* For available exprs */ /* For available exprs */
static sbitmap *ae_kill, *ae_gen; static sbitmap *ae_kill;
static void compute_can_copy (void); static void compute_can_copy (void);
static void *gmalloc (size_t) ATTRIBUTE_MALLOC; static void *gmalloc (size_t) ATTRIBUTE_MALLOC;
...@@ -450,7 +450,6 @@ static void hash_scan_set (rtx, rtx, struct hash_table *); ...@@ -450,7 +450,6 @@ static void hash_scan_set (rtx, rtx, struct hash_table *);
static void hash_scan_clobber (rtx, rtx, struct hash_table *); static void hash_scan_clobber (rtx, rtx, struct hash_table *);
static void hash_scan_call (rtx, rtx, struct hash_table *); static void hash_scan_call (rtx, rtx, struct hash_table *);
static int want_to_gcse_p (rtx); static int want_to_gcse_p (rtx);
static bool can_assign_to_reg_p (rtx);
static bool gcse_constant_p (const_rtx); static bool gcse_constant_p (const_rtx);
static int oprs_unchanged_p (const_rtx, const_rtx, int); static int oprs_unchanged_p (const_rtx, const_rtx, int);
static int oprs_anticipatable_p (const_rtx, const_rtx); static int oprs_anticipatable_p (const_rtx, const_rtx);
...@@ -527,7 +526,6 @@ static void free_ldst_entry (struct ls_expr *); ...@@ -527,7 +526,6 @@ static void free_ldst_entry (struct ls_expr *);
static void free_ldst_mems (void); static void free_ldst_mems (void);
static void print_ldst_list (FILE *); static void print_ldst_list (FILE *);
static struct ls_expr * find_rtx_in_ldst (rtx); static struct ls_expr * find_rtx_in_ldst (rtx);
static int enumerate_ldsts (void);
static inline struct ls_expr * first_ls_expr (void); static inline struct ls_expr * first_ls_expr (void);
static inline struct ls_expr * next_ls_expr (struct ls_expr *); static inline struct ls_expr * next_ls_expr (struct ls_expr *);
static int simple_mem (const_rtx); static int simple_mem (const_rtx);
...@@ -535,26 +533,6 @@ static void invalidate_any_buried_refs (rtx); ...@@ -535,26 +533,6 @@ static void invalidate_any_buried_refs (rtx);
static void compute_ld_motion_mems (void); static void compute_ld_motion_mems (void);
static void trim_ld_motion_mems (void); static void trim_ld_motion_mems (void);
static void update_ld_motion_stores (struct expr *); static void update_ld_motion_stores (struct expr *);
static void reg_set_info (rtx, const_rtx, void *);
static void reg_clear_last_set (rtx, const_rtx, void *);
static bool store_ops_ok (const_rtx, int *);
static rtx extract_mentioned_regs (rtx);
static rtx extract_mentioned_regs_helper (rtx, rtx);
static void find_moveable_store (rtx, int *, int *);
static int compute_store_table (void);
static bool load_kills_store (const_rtx, const_rtx, int);
static bool find_loads (const_rtx, const_rtx, int);
static bool store_killed_in_insn (const_rtx, const_rtx, const_rtx, int);
static bool store_killed_after (const_rtx, const_rtx, const_rtx, const_basic_block, int *, rtx *);
static bool store_killed_before (const_rtx, const_rtx, const_rtx, const_basic_block, int *);
static void build_store_vectors (void);
static void insert_insn_start_basic_block (rtx, basic_block);
static int insert_store (struct ls_expr *, edge);
static void remove_reachable_equiv_notes (basic_block, struct ls_expr *);
static void replace_store_insn (rtx, rtx, basic_block, struct ls_expr *);
static void delete_store (struct ls_expr *, basic_block);
static void free_store_memory (void);
static int one_store_motion_pass (void);
static void free_insn_expr_list_list (rtx *); static void free_insn_expr_list_list (rtx *);
static void clear_modify_mem_tables (void); static void clear_modify_mem_tables (void);
static void free_modify_mem_tables (void); static void free_modify_mem_tables (void);
...@@ -816,18 +794,23 @@ want_to_gcse_p (rtx x) ...@@ -816,18 +794,23 @@ want_to_gcse_p (rtx x)
return 0; return 0;
default: default:
return can_assign_to_reg_p (x); return can_assign_to_reg_without_clobbers_p (x);
} }
} }
/* Used internally by can_assign_to_reg_p. */ /* Used internally by can_assign_to_reg_without_clobbers_p. */
static GTY(()) rtx test_insn; static GTY(()) rtx test_insn;
/* Return true if we can assign X to a pseudo register. */ /* Return true if we can assign X to a pseudo register such that the
resulting insn does not result in clobbering a hard register as a
side-effect.
This function is typically used by code motion passes, to verify
that it is safe to insert an insn without worrying about clobbering
maybe live hard regs. */
static bool bool
can_assign_to_reg_p (rtx x) can_assign_to_reg_without_clobbers_p (rtx x)
{ {
int num_clobbers = 0; int num_clobbers = 0;
int icode; int icode;
...@@ -4642,20 +4625,6 @@ find_rtx_in_ldst (rtx x) ...@@ -4642,20 +4625,6 @@ find_rtx_in_ldst (rtx x)
return (struct ls_expr *) *slot; return (struct ls_expr *) *slot;
} }
/* Assign each element of the list of mems a monotonically increasing value. */
static int
enumerate_ldsts (void)
{
struct ls_expr * ptr;
int n = 0;
for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
ptr->index = n++;
return n;
}
/* Return first item in the list. */ /* Return first item in the list. */
static inline struct ls_expr * static inline struct ls_expr *
...@@ -4801,7 +4770,7 @@ compute_ld_motion_mems (void) ...@@ -4801,7 +4770,7 @@ compute_ld_motion_mems (void)
&& GET_CODE (src) != ASM_OPERANDS && GET_CODE (src) != ASM_OPERANDS
/* Check for REG manually since want_to_gcse_p /* Check for REG manually since want_to_gcse_p
returns 0 for all REGs. */ returns 0 for all REGs. */
&& can_assign_to_reg_p (src)) && can_assign_to_reg_without_clobbers_p (src))
ptr->stores = alloc_INSN_LIST (insn, ptr->stores); ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
else else
ptr->invalid = 1; ptr->invalid = 1;
...@@ -4920,1229 +4889,146 @@ update_ld_motion_stores (struct expr * expr) ...@@ -4920,1229 +4889,146 @@ update_ld_motion_stores (struct expr * expr)
} }
} }
/* Store motion code. */ /* Return true if the graph is too expensive to optimize. PASS is the
optimization about to be performed. */
#define ANTIC_STORE_LIST(x) ((x)->loads)
#define AVAIL_STORE_LIST(x) ((x)->stores)
#define LAST_AVAIL_CHECK_FAILURE(x) ((x)->reaching_reg)
/* This is used to communicate the target bitvector we want to use in the
reg_set_info routine when called via the note_stores mechanism. */
static int * regvec;
/* And current insn, for the same routine. */
static rtx compute_store_table_current_insn;
/* Used in computing the reverse edge graph bit vectors. */
static sbitmap * st_antloc;
/* Global holding the number of store expressions we are dealing with. */
static int num_stores;
/* Checks to set if we need to mark a register set. Called from
note_stores. */
static void
reg_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (REG_P (dest))
regvec[REGNO (dest)] = INSN_UID (compute_store_table_current_insn);
}
/* Clear any mark that says that this insn sets dest. Called from
note_stores. */
static void
reg_clear_last_set (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
void *data)
{
int *dead_vec = (int *) data;
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (REG_P (dest) &&
dead_vec[REGNO (dest)] == INSN_UID (compute_store_table_current_insn))
dead_vec[REGNO (dest)] = 0;
}
/* Return zero if some of the registers in list X are killed
due to set of registers in bitmap REGS_SET. */
static bool static bool
store_ops_ok (const_rtx x, int *regs_set) is_too_expensive (const char *pass)
{ {
const_rtx reg; /* Trying to perform global optimizations on flow graphs which have
a high connectivity will take a long time and is unlikely to be
particularly useful.
for (; x; x = XEXP (x, 1)) In normal circumstances a cfg should have about twice as many
edges as blocks. But we do not want to punish small functions
which have a couple switch statements. Rather than simply
threshold the number of blocks, uses something with a more
graceful degradation. */
if (n_edges > 20000 + n_basic_blocks * 4)
{ {
reg = XEXP (x, 0); warning (OPT_Wdisabled_optimization,
if (regs_set[REGNO(reg)]) "%s: %d basic blocks and %d edges/basic block",
return false; pass, n_basic_blocks, n_edges / n_basic_blocks);
}
return true; return true;
}
/* Returns a list of registers mentioned in X. */
static rtx
extract_mentioned_regs (rtx x)
{
return extract_mentioned_regs_helper (x, NULL_RTX);
}
/* Helper for extract_mentioned_regs; ACCUM is used to accumulate used
registers. */
static rtx
extract_mentioned_regs_helper (rtx x, rtx accum)
{
int i;
enum rtx_code code;
const char * fmt;
/* Repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return accum;
code = GET_CODE (x);
switch (code)
{
case REG:
return alloc_EXPR_LIST (0, x, accum);
case MEM:
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case PRE_MODIFY:
case POST_DEC:
case POST_INC:
case POST_MODIFY:
/* We do not run this function with arguments having side effects. */
gcc_unreachable ();
case PC:
case CC0: /*FIXME*/
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return accum;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration. */
if (i == 0)
{
x = tem;
goto repeat;
} }
accum = extract_mentioned_regs_helper (tem, accum); /* If allocating memory for the cprop bitmap would take up too much
} storage it's better just to disable the optimization. */
else if (fmt[i] == 'E') if ((n_basic_blocks
* SBITMAP_SET_SIZE (max_reg_num ())
* sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
{ {
int j; warning (OPT_Wdisabled_optimization,
"%s: %d basic blocks and %d registers",
pass, n_basic_blocks, max_reg_num ());
for (j = 0; j < XVECLEN (x, i); j++) return true;
accum = extract_mentioned_regs_helper (XVECEXP (x, i, j), accum);
}
} }
return accum; return false;
} }
/* Determine whether INSN is MEM store pattern that we will consider moving.
REGS_SET_BEFORE is bitmap of registers set before (and including) the
current insn, REGS_SET_AFTER is bitmap of registers set after (and
including) the insn in this basic block. We must be passing through BB from
head to end, as we are using this fact to speed things up.
The results are stored this way:
-- the first anticipatable expression is added into ANTIC_STORE_LIST
-- if the processed expression is not anticipatable, NULL_RTX is added
there instead, so that we can use it as indicator that no further
expression of this type may be anticipatable
-- if the expression is available, it is added as head of AVAIL_STORE_LIST;
consequently, all of them but this head are dead and may be deleted.
-- if the expression is not available, the insn due to that it fails to be
available is stored in reaching_reg.
The things are complicated a bit by fact that there already may be stores
to the same MEM from other blocks; also caller must take care of the
necessary cleanup of the temporary markers after end of the basic block.
*/
static void
find_moveable_store (rtx insn, int *regs_set_before, int *regs_set_after)
{
struct ls_expr * ptr;
rtx dest, set, tmp;
int check_anticipatable, check_available;
basic_block bb = BLOCK_FOR_INSN (insn);
set = single_set (insn); /* Main function for the CPROP pass. */
if (!set)
return;
dest = SET_DEST (set); static int
one_cprop_pass (void)
{
int changed = 0;
if (! MEM_P (dest) || MEM_VOLATILE_P (dest) /* Return if there's nothing to do, or it is too expensive. */
|| GET_MODE (dest) == BLKmode) if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
return; || is_too_expensive (_ ("const/copy propagation disabled")))
return 0;
if (side_effects_p (dest)) global_const_prop_count = local_const_prop_count = 0;
return; global_copy_prop_count = local_copy_prop_count = 0;
/* If we are handling exceptions, we must be careful with memory references bytes_used = 0;
that may trap. If we are not, the behavior is undefined, so we may just gcc_obstack_init (&gcse_obstack);
continue. */ alloc_gcse_mem ();
if (flag_non_call_exceptions && may_trap_p (dest))
return;
/* Even if the destination cannot trap, the source may. In this case we'd /* Do a local const/copy propagation pass first. The global pass
need to handle updating the REG_EH_REGION note. */ only handles global opportunities.
if (find_reg_note (insn, REG_EH_REGION, NULL_RTX)) If the local pass changes something, remove any unreachable blocks
return; because the CPROP global dataflow analysis may get into infinite
loops for CFGs with unreachable blocks.
/* Make sure that the SET_SRC of this store insns can be assigned to FIXME: This local pass should not be necessary after CSE (but for
a register, or we will fail later on in replace_store_insn, which some reason it still is). It is also (proven) not necessary
assumes that we can do this. But sometimes the target machine has to run the local pass right after FWPWOP.
oddities like MEM read-modify-write instruction. See for example
PR24257. */
if (!can_assign_to_reg_p (SET_SRC (set)))
return;
ptr = ldst_entry (dest); FIXME: The global analysis would not get into infinite loops if it
if (!ptr->pattern_regs) would use the DF solver (via df_simple_dataflow) instead of
ptr->pattern_regs = extract_mentioned_regs (dest); the solver implemented in this file. */
if (local_cprop_pass ())
/* Do not check for anticipatability if we either found one anticipatable
store already, or tested for one and found out that it was killed. */
check_anticipatable = 0;
if (!ANTIC_STORE_LIST (ptr))
check_anticipatable = 1;
else
{
tmp = XEXP (ANTIC_STORE_LIST (ptr), 0);
if (tmp != NULL_RTX
&& BLOCK_FOR_INSN (tmp) != bb)
check_anticipatable = 1;
}
if (check_anticipatable)
{ {
if (store_killed_before (dest, ptr->pattern_regs, insn, bb, regs_set_before)) delete_unreachable_blocks ();
tmp = NULL_RTX; df_analyze ();
else
tmp = insn;
ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (tmp,
ANTIC_STORE_LIST (ptr));
} }
/* It is not necessary to check whether store is available if we did /* Determine implicit sets. */
it successfully before; if we failed before, do not bother to check implicit_sets = XCNEWVEC (rtx, last_basic_block);
until we reach the insn that caused us to fail. */ find_implicit_sets ();
check_available = 0;
if (!AVAIL_STORE_LIST (ptr))
check_available = 1;
else
{
tmp = XEXP (AVAIL_STORE_LIST (ptr), 0);
if (BLOCK_FOR_INSN (tmp) != bb)
check_available = 1;
}
if (check_available)
{
/* Check that we have already reached the insn at that the check
failed last time. */
if (LAST_AVAIL_CHECK_FAILURE (ptr))
{
for (tmp = BB_END (bb);
tmp != insn && tmp != LAST_AVAIL_CHECK_FAILURE (ptr);
tmp = PREV_INSN (tmp))
continue;
if (tmp == insn)
check_available = 0;
}
else
check_available = store_killed_after (dest, ptr->pattern_regs, insn,
bb, regs_set_after,
&LAST_AVAIL_CHECK_FAILURE (ptr));
}
if (!check_available)
AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn, AVAIL_STORE_LIST (ptr));
}
/* Find available and anticipatable stores. */ alloc_hash_table (get_max_uid (), &set_hash_table, 1);
compute_hash_table (&set_hash_table);
static int /* Free implicit_sets before peak usage. */
compute_store_table (void) free (implicit_sets);
{ implicit_sets = NULL;
int ret;
if (dump_file)
dump_hash_table (dump_file, "SET", &set_hash_table);
if (set_hash_table.n_elems > 0)
{
basic_block bb; basic_block bb;
unsigned regno; rtx insn;
rtx insn, pat, tmp;
int *last_set_in, *already_set;
struct ls_expr * ptr, **prev_next_ptr_ptr;
unsigned int max_gcse_regno = max_reg_num ();
pre_ldst_mems = 0; alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
pre_ldst_table = htab_create (13, pre_ldst_expr_hash, compute_cprop_data ();
pre_ldst_expr_eq, NULL);
last_set_in = XCNEWVEC (int, max_gcse_regno);
already_set = XNEWVEC (int, max_gcse_regno);
/* Find all the stores we care about. */ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
FOR_EACH_BB (bb)
{ {
/* First compute the registers set in this block. */ /* Reset tables used to keep track of what's still valid [since
regvec = last_set_in; the start of the block]. */
reset_opr_set_tables ();
FOR_BB_INSNS (bb, insn) FOR_BB_INSNS (bb, insn)
if (INSN_P (insn))
{ {
if (! INSN_P (insn)) changed |= cprop_insn (insn);
continue;
if (CALL_P (insn)) /* Keep track of everything modified by this insn. */
{ /* ??? Need to be careful w.r.t. mods done to INSN.
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) Don't call mark_oprs_set if we turned the
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) insn into a NOTE. */
last_set_in[regno] = INSN_UID (insn); if (! NOTE_P (insn))
mark_oprs_set (insn);
} }
pat = PATTERN (insn);
compute_store_table_current_insn = insn;
note_stores (pat, reg_set_info, NULL);
} }
/* Now find the stores. */ changed |= bypass_conditional_jumps ();
memset (already_set, 0, sizeof (int) * max_gcse_regno); free_cprop_mem ();
regvec = already_set;
FOR_BB_INSNS (bb, insn)
{
if (! INSN_P (insn))
continue;
if (CALL_P (insn))
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
already_set[regno] = 1;
} }
pat = PATTERN (insn); free_hash_table (&set_hash_table);
note_stores (pat, reg_set_info, NULL); free_gcse_mem ();
obstack_free (&gcse_obstack, NULL);
/* Now that we've marked regs, look for stores. */
find_moveable_store (insn, already_set, last_set_in);
/* Unmark regs that are no longer set. */ if (dump_file)
compute_store_table_current_insn = insn;
note_stores (pat, reg_clear_last_set, last_set_in);
if (CALL_P (insn))
{ {
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) fprintf (dump_file, "CPROP of %s, %d basic blocks, %d bytes needed, ",
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno) current_function_name (), n_basic_blocks, bytes_used);
&& last_set_in[regno] == INSN_UID (insn)) fprintf (dump_file, "%d local const props, %d local copy props, ",
last_set_in[regno] = 0; local_const_prop_count, local_copy_prop_count);
} fprintf (dump_file, "%d global const props, %d global copy props\n\n",
global_const_prop_count, global_copy_prop_count);
} }
#ifdef ENABLE_CHECKING return changed;
/* last_set_in should now be all-zero. */ }
for (regno = 0; regno < max_gcse_regno; regno++)
gcc_assert (!last_set_in[regno]);
#endif
/* Clear temporary marks. */
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
LAST_AVAIL_CHECK_FAILURE(ptr) = NULL_RTX;
if (ANTIC_STORE_LIST (ptr)
&& (tmp = XEXP (ANTIC_STORE_LIST (ptr), 0)) == NULL_RTX)
ANTIC_STORE_LIST (ptr) = XEXP (ANTIC_STORE_LIST (ptr), 1);
}
}
/* Remove the stores that are not available anywhere, as there will
be no opportunity to optimize them. */
for (ptr = pre_ldst_mems, prev_next_ptr_ptr = &pre_ldst_mems;
ptr != NULL;
ptr = *prev_next_ptr_ptr)
{
if (!AVAIL_STORE_LIST (ptr))
{
*prev_next_ptr_ptr = ptr->next;
htab_remove_elt_with_hash (pre_ldst_table, ptr, ptr->hash_index);
free_ldst_entry (ptr);
}
else
prev_next_ptr_ptr = &ptr->next;
}
ret = enumerate_ldsts ();
if (dump_file)
{
fprintf (dump_file, "ST_avail and ST_antic (shown under loads..)\n");
print_ldst_list (dump_file);
}
free (last_set_in);
free (already_set);
return ret;
}
/* Check to see if the load X is aliased with STORE_PATTERN.
AFTER is true if we are checking the case when STORE_PATTERN occurs
after the X. */
static bool
load_kills_store (const_rtx x, const_rtx store_pattern, int after)
{
if (after)
return anti_dependence (x, store_pattern);
else
return true_dependence (store_pattern, GET_MODE (store_pattern), x,
rtx_addr_varies_p);
}
/* Go through the entire insn X, looking for any loads which might alias
STORE_PATTERN. Return true if found.
AFTER is true if we are checking the case when STORE_PATTERN occurs
after the insn X. */
static bool
find_loads (const_rtx x, const_rtx store_pattern, int after)
{
const char * fmt;
int i, j;
int ret = false;
if (!x)
return false;
if (GET_CODE (x) == SET)
x = SET_SRC (x);
if (MEM_P (x))
{
if (load_kills_store (x, store_pattern, after))
return true;
}
/* Recursively process the insn. */
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
{
if (fmt[i] == 'e')
ret |= find_loads (XEXP (x, i), store_pattern, after);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
ret |= find_loads (XVECEXP (x, i, j), store_pattern, after);
}
return ret;
}
static inline bool
store_killed_in_pat (const_rtx x, const_rtx pat, int after)
{
if (GET_CODE (pat) == SET)
{
rtx dest = SET_DEST (pat);
if (GET_CODE (dest) == ZERO_EXTRACT)
dest = XEXP (dest, 0);
/* Check for memory stores to aliased objects. */
if (MEM_P (dest)
&& !expr_equiv_p (dest, x))
{
if (after)
{
if (output_dependence (dest, x))
return true;
}
else
{
if (output_dependence (x, dest))
return true;
}
}
}
if (find_loads (pat, x, after))
return true;
return false;
}
/* Check if INSN kills the store pattern X (is aliased with it).
AFTER is true if we are checking the case when store X occurs
after the insn. Return true if it does. */
static bool
store_killed_in_insn (const_rtx x, const_rtx x_regs, const_rtx insn, int after)
{
const_rtx reg, base, note, pat;
if (!INSN_P (insn))
return false;
if (CALL_P (insn))
{
/* A normal or pure call might read from pattern,
but a const call will not. */
if (!RTL_CONST_CALL_P (insn))
return true;
/* But even a const call reads its parameters. Check whether the
base of some of registers used in mem is stack pointer. */
for (reg = x_regs; reg; reg = XEXP (reg, 1))
{
base = find_base_term (XEXP (reg, 0));
if (!base
|| (GET_CODE (base) == ADDRESS
&& GET_MODE (base) == Pmode
&& XEXP (base, 0) == stack_pointer_rtx))
return true;
}
return false;
}
pat = PATTERN (insn);
if (GET_CODE (pat) == SET)
{
if (store_killed_in_pat (x, pat, after))
return true;
}
else if (GET_CODE (pat) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
if (store_killed_in_pat (x, XVECEXP (pat, 0, i), after))
return true;
}
else if (find_loads (PATTERN (insn), x, after))
return true;
/* If this insn has a REG_EQUAL or REG_EQUIV note referencing a memory
location aliased with X, then this insn kills X. */
note = find_reg_equal_equiv_note (insn);
if (! note)
return false;
note = XEXP (note, 0);
/* However, if the note represents a must alias rather than a may
alias relationship, then it does not kill X. */
if (expr_equiv_p (note, x))
return false;
/* See if there are any aliased loads in the note. */
return find_loads (note, x, after);
}
/* Returns true if the expression X is loaded or clobbered on or after INSN
within basic block BB. REGS_SET_AFTER is bitmap of registers set in
or after the insn. X_REGS is list of registers mentioned in X. If the store
is killed, return the last insn in that it occurs in FAIL_INSN. */
static bool
store_killed_after (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
int *regs_set_after, rtx *fail_insn)
{
rtx last = BB_END (bb), act;
if (!store_ops_ok (x_regs, regs_set_after))
{
/* We do not know where it will happen. */
if (fail_insn)
*fail_insn = NULL_RTX;
return true;
}
/* Scan from the end, so that fail_insn is determined correctly. */
for (act = last; act != PREV_INSN (insn); act = PREV_INSN (act))
if (store_killed_in_insn (x, x_regs, act, false))
{
if (fail_insn)
*fail_insn = act;
return true;
}
return false;
}
/* Returns true if the expression X is loaded or clobbered on or before INSN
within basic block BB. X_REGS is list of registers mentioned in X.
REGS_SET_BEFORE is bitmap of registers set before or in this insn. */
static bool
store_killed_before (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
int *regs_set_before)
{
rtx first = BB_HEAD (bb);
if (!store_ops_ok (x_regs, regs_set_before))
return true;
for ( ; insn != PREV_INSN (first); insn = PREV_INSN (insn))
if (store_killed_in_insn (x, x_regs, insn, true))
return true;
return false;
}
/* Fill in available, anticipatable, transparent and kill vectors in
STORE_DATA, based on lists of available and anticipatable stores. */
static void
build_store_vectors (void)
{
basic_block bb;
int *regs_set_in_block;
rtx insn, st;
struct ls_expr * ptr;
unsigned int max_gcse_regno = max_reg_num ();
/* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */
ae_gen = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (ae_gen, last_basic_block);
st_antloc = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (st_antloc, last_basic_block);
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
for (st = AVAIL_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
/* If we've already seen an available expression in this block,
we can delete this one (It occurs earlier in the block). We'll
copy the SRC expression to an unused register in case there
are any side effects. */
if (TEST_BIT (ae_gen[bb->index], ptr->index))
{
rtx r = gen_reg_rtx_and_attrs (ptr->pattern);
if (dump_file)
fprintf (dump_file, "Removing redundant store:\n");
replace_store_insn (r, XEXP (st, 0), bb, ptr);
continue;
}
SET_BIT (ae_gen[bb->index], ptr->index);
}
for (st = ANTIC_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
SET_BIT (st_antloc[bb->index], ptr->index);
}
}
ae_kill = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (ae_kill, last_basic_block);
transp = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (transp, last_basic_block);
regs_set_in_block = XNEWVEC (int, max_gcse_regno);
FOR_EACH_BB (bb)
{
FOR_BB_INSNS (bb, insn)
if (INSN_P (insn))
{
df_ref *def_rec;
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
{
unsigned int ref_regno = DF_REF_REGNO (*def_rec);
if (ref_regno < max_gcse_regno)
regs_set_in_block[DF_REF_REGNO (*def_rec)] = 1;
}
}
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
if (store_killed_after (ptr->pattern, ptr->pattern_regs, BB_HEAD (bb),
bb, regs_set_in_block, NULL))
{
/* It should not be necessary to consider the expression
killed if it is both anticipatable and available. */
if (!TEST_BIT (st_antloc[bb->index], ptr->index)
|| !TEST_BIT (ae_gen[bb->index], ptr->index))
SET_BIT (ae_kill[bb->index], ptr->index);
}
else
SET_BIT (transp[bb->index], ptr->index);
}
}
free (regs_set_in_block);
if (dump_file)
{
dump_sbitmap_vector (dump_file, "st_antloc", "", st_antloc, last_basic_block);
dump_sbitmap_vector (dump_file, "st_kill", "", ae_kill, last_basic_block);
dump_sbitmap_vector (dump_file, "Transpt", "", transp, last_basic_block);
dump_sbitmap_vector (dump_file, "st_avloc", "", ae_gen, last_basic_block);
}
}
/* Insert an instruction at the beginning of a basic block, and update
the BB_HEAD if needed. */
static void
insert_insn_start_basic_block (rtx insn, basic_block bb)
{
/* Insert at start of successor block. */
rtx prev = PREV_INSN (BB_HEAD (bb));
rtx before = BB_HEAD (bb);
while (before != 0)
{
if (! LABEL_P (before)
&& !NOTE_INSN_BASIC_BLOCK_P (before))
break;
prev = before;
if (prev == BB_END (bb))
break;
before = NEXT_INSN (before);
}
insn = emit_insn_after_noloc (insn, prev, bb);
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION insert store at start of BB %d:\n",
bb->index);
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
}
/* This routine will insert a store on an edge. EXPR is the ldst entry for
the memory reference, and E is the edge to insert it on. Returns nonzero
if an edge insertion was performed. */
static int
insert_store (struct ls_expr * expr, edge e)
{
rtx reg, insn;
basic_block bb;
edge tmp;
edge_iterator ei;
/* We did all the deleted before this insert, so if we didn't delete a
store, then we haven't set the reaching reg yet either. */
if (expr->reaching_reg == NULL_RTX)
return 0;
if (e->flags & EDGE_FAKE)
return 0;
reg = expr->reaching_reg;
insn = gen_move_insn (copy_rtx (expr->pattern), reg);
/* If we are inserting this expression on ALL predecessor edges of a BB,
insert it at the start of the BB, and reset the insert bits on the other
edges so we don't try to insert it on the other edges. */
bb = e->dest;
FOR_EACH_EDGE (tmp, ei, e->dest->preds)
if (!(tmp->flags & EDGE_FAKE))
{
int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
gcc_assert (index != EDGE_INDEX_NO_EDGE);
if (! TEST_BIT (pre_insert_map[index], expr->index))
break;
}
/* If tmp is NULL, we found an insertion on every edge, blank the
insertion vector for these edges, and insert at the start of the BB. */
if (!tmp && bb != EXIT_BLOCK_PTR)
{
FOR_EACH_EDGE (tmp, ei, e->dest->preds)
{
int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
RESET_BIT (pre_insert_map[index], expr->index);
}
insert_insn_start_basic_block (insn, bb);
return 0;
}
/* We can't put stores in the front of blocks pointed to by abnormal
edges since that may put a store where one didn't used to be. */
gcc_assert (!(e->flags & EDGE_ABNORMAL));
insert_insn_on_edge (insn, e);
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION insert insn on edge (%d, %d):\n",
e->src->index, e->dest->index);
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
return 1;
}
/* Remove any REG_EQUAL or REG_EQUIV notes containing a reference to the
memory location in SMEXPR set in basic block BB.
This could be rather expensive. */
static void
remove_reachable_equiv_notes (basic_block bb, struct ls_expr *smexpr)
{
edge_iterator *stack, ei;
int sp;
edge act;
sbitmap visited = sbitmap_alloc (last_basic_block);
rtx last, insn, note;
rtx mem = smexpr->pattern;
stack = XNEWVEC (edge_iterator, n_basic_blocks);
sp = 0;
ei = ei_start (bb->succs);
sbitmap_zero (visited);
act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
while (1)
{
if (!act)
{
if (!sp)
{
free (stack);
sbitmap_free (visited);
return;
}
act = ei_edge (stack[--sp]);
}
bb = act->dest;
if (bb == EXIT_BLOCK_PTR
|| TEST_BIT (visited, bb->index))
{
if (!ei_end_p (ei))
ei_next (&ei);
act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
continue;
}
SET_BIT (visited, bb->index);
if (TEST_BIT (st_antloc[bb->index], smexpr->index))
{
for (last = ANTIC_STORE_LIST (smexpr);
BLOCK_FOR_INSN (XEXP (last, 0)) != bb;
last = XEXP (last, 1))
continue;
last = XEXP (last, 0);
}
else
last = NEXT_INSN (BB_END (bb));
for (insn = BB_HEAD (bb); insn != last; insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
note = find_reg_equal_equiv_note (insn);
if (!note || !expr_equiv_p (XEXP (note, 0), mem))
continue;
if (dump_file)
fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
INSN_UID (insn));
remove_note (insn, note);
}
if (!ei_end_p (ei))
ei_next (&ei);
act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
if (EDGE_COUNT (bb->succs) > 0)
{
if (act)
stack[sp++] = ei;
ei = ei_start (bb->succs);
act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
}
}
}
/* This routine will replace a store with a SET to a specified register. */
static void
replace_store_insn (rtx reg, rtx del, basic_block bb, struct ls_expr *smexpr)
{
rtx insn, mem, note, set, ptr;
mem = smexpr->pattern;
insn = gen_move_insn (reg, SET_SRC (single_set (del)));
for (ptr = ANTIC_STORE_LIST (smexpr); ptr; ptr = XEXP (ptr, 1))
if (XEXP (ptr, 0) == del)
{
XEXP (ptr, 0) = insn;
break;
}
/* Move the notes from the deleted insn to its replacement. */
REG_NOTES (insn) = REG_NOTES (del);
/* Emit the insn AFTER all the notes are transferred.
This is cheaper since we avoid df rescanning for the note change. */
insn = emit_insn_after (insn, del);
if (dump_file)
{
fprintf (dump_file,
"STORE_MOTION delete insn in BB %d:\n ", bb->index);
print_inline_rtx (dump_file, del, 6);
fprintf (dump_file, "\nSTORE_MOTION replaced with insn:\n ");
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
delete_insn (del);
/* Now we must handle REG_EQUAL notes whose contents is equal to the mem;
they are no longer accurate provided that they are reached by this
definition, so drop them. */
for (; insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
set = single_set (insn);
if (!set)
continue;
if (expr_equiv_p (SET_DEST (set), mem))
return;
note = find_reg_equal_equiv_note (insn);
if (!note || !expr_equiv_p (XEXP (note, 0), mem))
continue;
if (dump_file)
fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
INSN_UID (insn));
remove_note (insn, note);
}
remove_reachable_equiv_notes (bb, smexpr);
}
/* Delete a store, but copy the value that would have been stored into
the reaching_reg for later storing. */
static void
delete_store (struct ls_expr * expr, basic_block bb)
{
rtx reg, i, del;
if (expr->reaching_reg == NULL_RTX)
expr->reaching_reg = gen_reg_rtx_and_attrs (expr->pattern);
reg = expr->reaching_reg;
for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
{
del = XEXP (i, 0);
if (BLOCK_FOR_INSN (del) == bb)
{
/* We know there is only one since we deleted redundant
ones during the available computation. */
replace_store_insn (reg, del, bb, expr);
break;
}
}
}
/* Free memory used by store motion. */
static void
free_store_memory (void)
{
free_ldst_mems ();
if (ae_gen)
sbitmap_vector_free (ae_gen);
if (ae_kill)
sbitmap_vector_free (ae_kill);
if (transp)
sbitmap_vector_free (transp);
if (st_antloc)
sbitmap_vector_free (st_antloc);
if (pre_insert_map)
sbitmap_vector_free (pre_insert_map);
if (pre_delete_map)
sbitmap_vector_free (pre_delete_map);
ae_gen = ae_kill = transp = st_antloc = NULL;
pre_insert_map = pre_delete_map = NULL;
}
/* Perform store motion. Much like gcse, except we move expressions the
other way by looking at the flowgraph in reverse.
Return non-zero if transformations are performed by the pass. */
static int
one_store_motion_pass (void)
{
basic_block bb;
int x;
struct ls_expr * ptr;
int update_flow = 0;
gcse_subst_count = 0;
gcse_create_count = 0;
init_alias_analysis ();
/* Find all the available and anticipatable stores. */
num_stores = compute_store_table ();
if (num_stores == 0)
{
htab_delete (pre_ldst_table);
pre_ldst_table = NULL;
end_alias_analysis ();
return 0;
}
/* Now compute kill & transp vectors. */
build_store_vectors ();
add_noreturn_fake_exit_edges ();
connect_infinite_loops_to_exit ();
edge_list = pre_edge_rev_lcm (num_stores, transp, ae_gen,
st_antloc, ae_kill, &pre_insert_map,
&pre_delete_map);
/* Now we want to insert the new stores which are going to be needed. */
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
/* If any of the edges we have above are abnormal, we can't move this
store. */
for (x = NUM_EDGES (edge_list) - 1; x >= 0; x--)
if (TEST_BIT (pre_insert_map[x], ptr->index)
&& (INDEX_EDGE (edge_list, x)->flags & EDGE_ABNORMAL))
break;
if (x >= 0)
{
if (dump_file != NULL)
fprintf (dump_file,
"Can't replace store %d: abnormal edge from %d to %d\n",
ptr->index, INDEX_EDGE (edge_list, x)->src->index,
INDEX_EDGE (edge_list, x)->dest->index);
continue;
}
/* Now we want to insert the new stores which are going to be needed. */
FOR_EACH_BB (bb)
if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
{
delete_store (ptr, bb);
gcse_subst_count++;
}
for (x = 0; x < NUM_EDGES (edge_list); x++)
if (TEST_BIT (pre_insert_map[x], ptr->index))
{
update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
gcse_create_count++;
}
}
if (update_flow)
commit_edge_insertions ();
free_store_memory ();
free_edge_list (edge_list);
remove_fake_exit_edges ();
end_alias_analysis ();
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION of %s, %d basic blocks, ",
current_function_name (), n_basic_blocks);
fprintf (dump_file, "%d substs, %d insns created\n",
gcse_subst_count, gcse_create_count);
}
return (gcse_subst_count > 0 || gcse_create_count > 0);
}
/* Return true if the graph is too expensive to optimize. PASS is the
optimization about to be performed. */
static bool
is_too_expensive (const char *pass)
{
/* Trying to perform global optimizations on flow graphs which have
a high connectivity will take a long time and is unlikely to be
particularly useful.
In normal circumstances a cfg should have about twice as many
edges as blocks. But we do not want to punish small functions
which have a couple switch statements. Rather than simply
threshold the number of blocks, uses something with a more
graceful degradation. */
if (n_edges > 20000 + n_basic_blocks * 4)
{
warning (OPT_Wdisabled_optimization,
"%s: %d basic blocks and %d edges/basic block",
pass, n_basic_blocks, n_edges / n_basic_blocks);
return true;
}
/* If allocating memory for the cprop bitmap would take up too much
storage it's better just to disable the optimization. */
if ((n_basic_blocks
* SBITMAP_SET_SIZE (max_reg_num ())
* sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
{
warning (OPT_Wdisabled_optimization,
"%s: %d basic blocks and %d registers",
pass, n_basic_blocks, max_reg_num ());
return true;
}
return false;
}
/* Main function for the CPROP pass. */
static int
one_cprop_pass (void)
{
int changed = 0;
/* Return if there's nothing to do, or it is too expensive. */
if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|| is_too_expensive (_ ("const/copy propagation disabled")))
return 0;
global_const_prop_count = local_const_prop_count = 0;
global_copy_prop_count = local_copy_prop_count = 0;
bytes_used = 0;
gcc_obstack_init (&gcse_obstack);
alloc_gcse_mem ();
/* Do a local const/copy propagation pass first. The global pass
only handles global opportunities.
If the local pass changes something, remove any unreachable blocks
because the CPROP global dataflow analysis may get into infinite
loops for CFGs with unreachable blocks.
FIXME: This local pass should not be necessary after CSE (but for
some reason it still is). It is also (proven) not necessary
to run the local pass right after FWPWOP.
FIXME: The global analysis would not get into infinite loops if it
would use the DF solver (via df_simple_dataflow) instead of
the solver implemented in this file. */
if (local_cprop_pass ())
{
delete_unreachable_blocks ();
df_analyze ();
}
/* Determine implicit sets. */
implicit_sets = XCNEWVEC (rtx, last_basic_block);
find_implicit_sets ();
alloc_hash_table (get_max_uid (), &set_hash_table, 1);
compute_hash_table (&set_hash_table);
/* Free implicit_sets before peak usage. */
free (implicit_sets);
implicit_sets = NULL;
if (dump_file)
dump_hash_table (dump_file, "SET", &set_hash_table);
if (set_hash_table.n_elems > 0)
{
basic_block bb;
rtx insn;
alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
compute_cprop_data ();
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
{
/* Reset tables used to keep track of what's still valid [since
the start of the block]. */
reset_opr_set_tables ();
FOR_BB_INSNS (bb, insn)
if (INSN_P (insn))
{
changed |= cprop_insn (insn);
/* Keep track of everything modified by this insn. */
/* ??? Need to be careful w.r.t. mods done to INSN.
Don't call mark_oprs_set if we turned the
insn into a NOTE. */
if (! NOTE_P (insn))
mark_oprs_set (insn);
}
}
changed |= bypass_conditional_jumps ();
free_cprop_mem ();
}
free_hash_table (&set_hash_table);
free_gcse_mem ();
obstack_free (&gcse_obstack, NULL);
if (dump_file)
{
fprintf (dump_file, "CPROP of %s, %d basic blocks, %d bytes needed, ",
current_function_name (), n_basic_blocks, bytes_used);
fprintf (dump_file, "%d local const props, %d local copy props, ",
local_const_prop_count, local_copy_prop_count);
fprintf (dump_file, "%d global const props, %d global copy props\n\n",
global_const_prop_count, global_copy_prop_count);
}
return changed;
}
/* All the passes implemented in this file. Each pass has its /* All the passes implemented in this file. Each pass has its
...@@ -6214,25 +5100,6 @@ execute_rtl_hoist (void) ...@@ -6214,25 +5100,6 @@ execute_rtl_hoist (void)
return 0; return 0;
} }
static bool
gate_rtl_store_motion (void)
{
return optimize > 0 && flag_gcse_sm
&& !cfun->calls_setjmp
&& optimize_function_for_speed_p (cfun)
&& dbg_cnt (store_motion);
}
static unsigned int
execute_rtl_store_motion (void)
{
delete_unreachable_blocks ();
df_note_add_problem ();
df_analyze ();
flag_rerun_cse_after_global_opts |= one_store_motion_pass ();
return 0;
}
struct rtl_opt_pass pass_rtl_cprop = struct rtl_opt_pass pass_rtl_cprop =
{ {
{ {
...@@ -6296,25 +5163,4 @@ struct rtl_opt_pass pass_rtl_hoist = ...@@ -6296,25 +5163,4 @@ struct rtl_opt_pass pass_rtl_hoist =
} }
}; };
struct rtl_opt_pass pass_rtl_store_motion =
{
{
RTL_PASS,
"store_motion", /* name */
gate_rtl_store_motion, /* gate */
execute_rtl_store_motion, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
TV_LSM, /* tv_id */
PROP_cfglayout, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_df_finish | TODO_verify_rtl_sharing |
TODO_dump_func |
TODO_verify_flow | TODO_ggc_collect /* todo_flags_finish */
}
};
#include "gt-gcse.h" #include "gt-gcse.h"
...@@ -2222,6 +2222,7 @@ extern void expand_dec (rtx, rtx); ...@@ -2222,6 +2222,7 @@ extern void expand_dec (rtx, rtx);
/* In gcse.c */ /* In gcse.c */
extern bool can_copy_p (enum machine_mode); extern bool can_copy_p (enum machine_mode);
extern bool can_assign_to_reg_without_clobbers_p (rtx);
extern rtx fis_get_condition (rtx); extern rtx fis_get_condition (rtx);
/* In ira.c */ /* In ira.c */
......
/* Store motion via Lazy Code Motion on the reverse CFG.
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "basic-block.h"
#include "output.h"
#include "function.h"
#include "expr.h"
#include "except.h"
#include "ggc.h"
#include "params.h"
#include "intl.h"
#include "timevar.h"
#include "tree-pass.h"
#include "hashtab.h"
#include "df.h"
#include "dbgcnt.h"
/* This is a list of expressions which are MEMs and will be used by load
or store motion.
Load motion tracks MEMs which aren't killed by
anything except itself. (i.e., loads and stores to a single location).
We can then allow movement of these MEM refs with a little special
allowance. (all stores copy the same value to the reaching reg used
for the loads). This means all values used to store into memory must have
no side effects so we can re-issue the setter value.
Store Motion uses this structure as an expression table to track stores
which look interesting, and might be moveable towards the exit block. */
struct ls_expr
{
rtx pattern; /* Pattern of this mem. */
rtx pattern_regs; /* List of registers mentioned by the mem. */
rtx loads; /* INSN list of loads seen. */
rtx stores; /* INSN list of stores seen. */
struct ls_expr * next; /* Next in the list. */
int invalid; /* Invalid for some reason. */
int index; /* If it maps to a bitmap index. */
unsigned int hash_index; /* Index when in a hash table. */
rtx reaching_reg; /* Register to use when re-writing. */
};
/* Head of the list of load/store memory refs. */
static struct ls_expr * pre_ldst_mems = NULL;
/* Hashtable for the load/store memory refs. */
static htab_t pre_ldst_table = NULL;
/* Various variables for statistics gathering. */
/* GCSE substitutions made. */
static int gcse_subst_count;
/* Number of copy instructions created. */
static int gcse_create_count;
/* For available exprs */
static sbitmap *ae_kill, *ae_gen;
/* Nonzero for expressions that are transparent in the block. */
static sbitmap *transp;
/* Nonzero for expressions which should be inserted on a specific edge. */
static sbitmap *pre_insert_map;
/* Nonzero for expressions which should be deleted in a specific block. */
static sbitmap *pre_delete_map;
/* Contains the edge_list returned by pre_edge_lcm. */
static struct edge_list *edge_list;
/* Here we provide the things required to do store motion towards
the exit. In order for this to be effective, PRE load motion also needed
to be taught how to move a load when it is kill only by a store to itself.
int i;
float a[10];
void foo(float scale)
{
for (i=0; i<10; i++)
a[i] *= scale;
}
'i' is both loaded and stored to in the loop. Normally, gcse cannot move
the load out since its live around the loop, and stored at the bottom
of the loop.
The 'Load Motion' referred to and implemented in this file is
an enhancement to gcse which when using edge based lcm, recognizes
this situation and allows gcse to move the load out of the loop.
Once gcse has hoisted the load, store motion can then push this
load towards the exit, and we end up with no loads or stores of 'i'
in the loop. */
static hashval_t
pre_ldst_expr_hash (const void *p)
{
int do_not_record_p = 0;
const struct ls_expr *const x = (const struct ls_expr *) p;
return hash_rtx (x->pattern, GET_MODE (x->pattern), &do_not_record_p, NULL, false);
}
static int
pre_ldst_expr_eq (const void *p1, const void *p2)
{
const struct ls_expr *const ptr1 = (const struct ls_expr *) p1,
*const ptr2 = (const struct ls_expr *) p2;
return exp_equiv_p (ptr1->pattern, ptr2->pattern, 0, true);
}
/* This will search the ldst list for a matching expression. If it
doesn't find one, we create one and initialize it. */
static struct ls_expr *
ldst_entry (rtx x)
{
int do_not_record_p = 0;
struct ls_expr * ptr;
unsigned int hash;
void **slot;
struct ls_expr e;
hash = hash_rtx (x, GET_MODE (x), &do_not_record_p,
NULL, /*have_reg_qty=*/false);
e.pattern = x;
slot = htab_find_slot_with_hash (pre_ldst_table, &e, hash, INSERT);
if (*slot)
return (struct ls_expr *)*slot;
ptr = XNEW (struct ls_expr);
ptr->next = pre_ldst_mems;
ptr->pattern = x;
ptr->pattern_regs = NULL_RTX;
ptr->loads = NULL_RTX;
ptr->stores = NULL_RTX;
ptr->reaching_reg = NULL_RTX;
ptr->invalid = 0;
ptr->index = 0;
ptr->hash_index = hash;
pre_ldst_mems = ptr;
*slot = ptr;
return ptr;
}
/* Free up an individual ldst entry. */
static void
free_ldst_entry (struct ls_expr * ptr)
{
free_INSN_LIST_list (& ptr->loads);
free_INSN_LIST_list (& ptr->stores);
free (ptr);
}
/* Free up all memory associated with the ldst list. */
static void
free_ldst_mems (void)
{
if (pre_ldst_table)
htab_delete (pre_ldst_table);
pre_ldst_table = NULL;
while (pre_ldst_mems)
{
struct ls_expr * tmp = pre_ldst_mems;
pre_ldst_mems = pre_ldst_mems->next;
free_ldst_entry (tmp);
}
pre_ldst_mems = NULL;
}
/* Assign each element of the list of mems a monotonically increasing value. */
static int
enumerate_ldsts (void)
{
struct ls_expr * ptr;
int n = 0;
for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
ptr->index = n++;
return n;
}
/* Return first item in the list. */
static inline struct ls_expr *
first_ls_expr (void)
{
return pre_ldst_mems;
}
/* Return the next item in the list after the specified one. */
static inline struct ls_expr *
next_ls_expr (struct ls_expr * ptr)
{
return ptr->next;
}
/* Dump debugging info about the ldst list. */
static void
print_ldst_list (FILE * file)
{
struct ls_expr * ptr;
fprintf (file, "LDST list: \n");
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
fprintf (file, " Pattern (%3d): ", ptr->index);
print_rtl (file, ptr->pattern);
fprintf (file, "\n Loads : ");
if (ptr->loads)
print_rtl (file, ptr->loads);
else
fprintf (file, "(nil)");
fprintf (file, "\n Stores : ");
if (ptr->stores)
print_rtl (file, ptr->stores);
else
fprintf (file, "(nil)");
fprintf (file, "\n\n");
}
fprintf (file, "\n");
}
/* Store motion code. */
#define ANTIC_STORE_LIST(x) ((x)->loads)
#define AVAIL_STORE_LIST(x) ((x)->stores)
#define LAST_AVAIL_CHECK_FAILURE(x) ((x)->reaching_reg)
/* This is used to communicate the target bitvector we want to use in the
reg_set_info routine when called via the note_stores mechanism. */
static int * regvec;
/* And current insn, for the same routine. */
static rtx compute_store_table_current_insn;
/* Used in computing the reverse edge graph bit vectors. */
static sbitmap * st_antloc;
/* Global holding the number of store expressions we are dealing with. */
static int num_stores;
/* Checks to set if we need to mark a register set. Called from
note_stores. */
static void
reg_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (REG_P (dest))
regvec[REGNO (dest)] = INSN_UID (compute_store_table_current_insn);
}
/* Clear any mark that says that this insn sets dest. Called from
note_stores. */
static void
reg_clear_last_set (rtx dest, const_rtx setter ATTRIBUTE_UNUSED,
void *data)
{
int *dead_vec = (int *) data;
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (REG_P (dest) &&
dead_vec[REGNO (dest)] == INSN_UID (compute_store_table_current_insn))
dead_vec[REGNO (dest)] = 0;
}
/* Return zero if some of the registers in list X are killed
due to set of registers in bitmap REGS_SET. */
static bool
store_ops_ok (const_rtx x, int *regs_set)
{
const_rtx reg;
for (; x; x = XEXP (x, 1))
{
reg = XEXP (x, 0);
if (regs_set[REGNO(reg)])
return false;
}
return true;
}
/* Helper for extract_mentioned_regs; ACCUM is used to accumulate used
registers. */
static rtx
extract_mentioned_regs_helper (rtx x, rtx accum)
{
int i;
enum rtx_code code;
const char * fmt;
/* Repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return accum;
code = GET_CODE (x);
switch (code)
{
case REG:
return alloc_EXPR_LIST (0, x, accum);
case MEM:
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case PRE_MODIFY:
case POST_DEC:
case POST_INC:
case POST_MODIFY:
/* We do not run this function with arguments having side effects. */
gcc_unreachable ();
case PC:
case CC0: /*FIXME*/
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return accum;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration. */
if (i == 0)
{
x = tem;
goto repeat;
}
accum = extract_mentioned_regs_helper (tem, accum);
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
accum = extract_mentioned_regs_helper (XVECEXP (x, i, j), accum);
}
}
return accum;
}
/* Returns a list of registers mentioned in X. */
/* ??? Reimplement with for_each_rtx? */
static rtx
extract_mentioned_regs (rtx x)
{
return extract_mentioned_regs_helper (x, NULL_RTX);
}
/* Check to see if the load X is aliased with STORE_PATTERN.
AFTER is true if we are checking the case when STORE_PATTERN occurs
after the X. */
static bool
load_kills_store (const_rtx x, const_rtx store_pattern, int after)
{
if (after)
return anti_dependence (x, store_pattern);
else
return true_dependence (store_pattern, GET_MODE (store_pattern), x,
rtx_addr_varies_p);
}
/* Go through the entire insn X, looking for any loads which might alias
STORE_PATTERN. Return true if found.
AFTER is true if we are checking the case when STORE_PATTERN occurs
after the insn X. */
static bool
find_loads (const_rtx x, const_rtx store_pattern, int after)
{
const char * fmt;
int i, j;
int ret = false;
if (!x)
return false;
if (GET_CODE (x) == SET)
x = SET_SRC (x);
if (MEM_P (x))
{
if (load_kills_store (x, store_pattern, after))
return true;
}
/* Recursively process the insn. */
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
{
if (fmt[i] == 'e')
ret |= find_loads (XEXP (x, i), store_pattern, after);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
ret |= find_loads (XVECEXP (x, i, j), store_pattern, after);
}
return ret;
}
/* Go through pattern PAT looking for any loads which might kill the
store in X. Return true if found.
AFTER is true if we are checking the case when loads kill X occurs
after the insn for PAT. */
static inline bool
store_killed_in_pat (const_rtx x, const_rtx pat, int after)
{
if (GET_CODE (pat) == SET)
{
rtx dest = SET_DEST (pat);
if (GET_CODE (dest) == ZERO_EXTRACT)
dest = XEXP (dest, 0);
/* Check for memory stores to aliased objects. */
if (MEM_P (dest)
&& !exp_equiv_p (dest, x, 0, true))
{
if (after)
{
if (output_dependence (dest, x))
return true;
}
else
{
if (output_dependence (x, dest))
return true;
}
}
}
if (find_loads (pat, x, after))
return true;
return false;
}
/* Check if INSN kills the store pattern X (is aliased with it).
AFTER is true if we are checking the case when store X occurs
after the insn. Return true if it does. */
static bool
store_killed_in_insn (const_rtx x, const_rtx x_regs, const_rtx insn, int after)
{
const_rtx reg, base, note, pat;
if (!INSN_P (insn))
return false;
if (CALL_P (insn))
{
/* A normal or pure call might read from pattern,
but a const call will not. */
if (!RTL_CONST_CALL_P (insn))
return true;
/* But even a const call reads its parameters. Check whether the
base of some of registers used in mem is stack pointer. */
for (reg = x_regs; reg; reg = XEXP (reg, 1))
{
base = find_base_term (XEXP (reg, 0));
if (!base
|| (GET_CODE (base) == ADDRESS
&& GET_MODE (base) == Pmode
&& XEXP (base, 0) == stack_pointer_rtx))
return true;
}
return false;
}
pat = PATTERN (insn);
if (GET_CODE (pat) == SET)
{
if (store_killed_in_pat (x, pat, after))
return true;
}
else if (GET_CODE (pat) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
if (store_killed_in_pat (x, XVECEXP (pat, 0, i), after))
return true;
}
else if (find_loads (PATTERN (insn), x, after))
return true;
/* If this insn has a REG_EQUAL or REG_EQUIV note referencing a memory
location aliased with X, then this insn kills X. */
note = find_reg_equal_equiv_note (insn);
if (! note)
return false;
note = XEXP (note, 0);
/* However, if the note represents a must alias rather than a may
alias relationship, then it does not kill X. */
if (exp_equiv_p (note, x, 0, true))
return false;
/* See if there are any aliased loads in the note. */
return find_loads (note, x, after);
}
/* Returns true if the expression X is loaded or clobbered on or after INSN
within basic block BB. REGS_SET_AFTER is bitmap of registers set in
or after the insn. X_REGS is list of registers mentioned in X. If the store
is killed, return the last insn in that it occurs in FAIL_INSN. */
static bool
store_killed_after (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
int *regs_set_after, rtx *fail_insn)
{
rtx last = BB_END (bb), act;
if (!store_ops_ok (x_regs, regs_set_after))
{
/* We do not know where it will happen. */
if (fail_insn)
*fail_insn = NULL_RTX;
return true;
}
/* Scan from the end, so that fail_insn is determined correctly. */
for (act = last; act != PREV_INSN (insn); act = PREV_INSN (act))
if (store_killed_in_insn (x, x_regs, act, false))
{
if (fail_insn)
*fail_insn = act;
return true;
}
return false;
}
/* Returns true if the expression X is loaded or clobbered on or before INSN
within basic block BB. X_REGS is list of registers mentioned in X.
REGS_SET_BEFORE is bitmap of registers set before or in this insn. */
static bool
store_killed_before (const_rtx x, const_rtx x_regs, const_rtx insn, const_basic_block bb,
int *regs_set_before)
{
rtx first = BB_HEAD (bb);
if (!store_ops_ok (x_regs, regs_set_before))
return true;
for ( ; insn != PREV_INSN (first); insn = PREV_INSN (insn))
if (store_killed_in_insn (x, x_regs, insn, true))
return true;
return false;
}
/* Determine whether INSN is MEM store pattern that we will consider moving.
REGS_SET_BEFORE is bitmap of registers set before (and including) the
current insn, REGS_SET_AFTER is bitmap of registers set after (and
including) the insn in this basic block. We must be passing through BB from
head to end, as we are using this fact to speed things up.
The results are stored this way:
-- the first anticipatable expression is added into ANTIC_STORE_LIST
-- if the processed expression is not anticipatable, NULL_RTX is added
there instead, so that we can use it as indicator that no further
expression of this type may be anticipatable
-- if the expression is available, it is added as head of AVAIL_STORE_LIST;
consequently, all of them but this head are dead and may be deleted.
-- if the expression is not available, the insn due to that it fails to be
available is stored in reaching_reg.
The things are complicated a bit by fact that there already may be stores
to the same MEM from other blocks; also caller must take care of the
necessary cleanup of the temporary markers after end of the basic block.
*/
static void
find_moveable_store (rtx insn, int *regs_set_before, int *regs_set_after)
{
struct ls_expr * ptr;
rtx dest, set, tmp;
int check_anticipatable, check_available;
basic_block bb = BLOCK_FOR_INSN (insn);
set = single_set (insn);
if (!set)
return;
dest = SET_DEST (set);
if (! MEM_P (dest) || MEM_VOLATILE_P (dest)
|| GET_MODE (dest) == BLKmode)
return;
if (side_effects_p (dest))
return;
/* If we are handling exceptions, we must be careful with memory references
that may trap. If we are not, the behavior is undefined, so we may just
continue. */
if (flag_non_call_exceptions && may_trap_p (dest))
return;
/* Even if the destination cannot trap, the source may. In this case we'd
need to handle updating the REG_EH_REGION note. */
if (find_reg_note (insn, REG_EH_REGION, NULL_RTX))
return;
/* Make sure that the SET_SRC of this store insns can be assigned to
a register, or we will fail later on in replace_store_insn, which
assumes that we can do this. But sometimes the target machine has
oddities like MEM read-modify-write instruction. See for example
PR24257. */
if (!can_assign_to_reg_without_clobbers_p (SET_SRC (set)))
return;
ptr = ldst_entry (dest);
if (!ptr->pattern_regs)
ptr->pattern_regs = extract_mentioned_regs (dest);
/* Do not check for anticipatability if we either found one anticipatable
store already, or tested for one and found out that it was killed. */
check_anticipatable = 0;
if (!ANTIC_STORE_LIST (ptr))
check_anticipatable = 1;
else
{
tmp = XEXP (ANTIC_STORE_LIST (ptr), 0);
if (tmp != NULL_RTX
&& BLOCK_FOR_INSN (tmp) != bb)
check_anticipatable = 1;
}
if (check_anticipatable)
{
if (store_killed_before (dest, ptr->pattern_regs, insn, bb, regs_set_before))
tmp = NULL_RTX;
else
tmp = insn;
ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (tmp,
ANTIC_STORE_LIST (ptr));
}
/* It is not necessary to check whether store is available if we did
it successfully before; if we failed before, do not bother to check
until we reach the insn that caused us to fail. */
check_available = 0;
if (!AVAIL_STORE_LIST (ptr))
check_available = 1;
else
{
tmp = XEXP (AVAIL_STORE_LIST (ptr), 0);
if (BLOCK_FOR_INSN (tmp) != bb)
check_available = 1;
}
if (check_available)
{
/* Check that we have already reached the insn at that the check
failed last time. */
if (LAST_AVAIL_CHECK_FAILURE (ptr))
{
for (tmp = BB_END (bb);
tmp != insn && tmp != LAST_AVAIL_CHECK_FAILURE (ptr);
tmp = PREV_INSN (tmp))
continue;
if (tmp == insn)
check_available = 0;
}
else
check_available = store_killed_after (dest, ptr->pattern_regs, insn,
bb, regs_set_after,
&LAST_AVAIL_CHECK_FAILURE (ptr));
}
if (!check_available)
AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn, AVAIL_STORE_LIST (ptr));
}
/* Find available and anticipatable stores. */
static int
compute_store_table (void)
{
int ret;
basic_block bb;
unsigned regno;
rtx insn, pat, tmp;
int *last_set_in, *already_set;
struct ls_expr * ptr, **prev_next_ptr_ptr;
unsigned int max_gcse_regno = max_reg_num ();
pre_ldst_mems = 0;
pre_ldst_table = htab_create (13, pre_ldst_expr_hash,
pre_ldst_expr_eq, NULL);
last_set_in = XCNEWVEC (int, max_gcse_regno);
already_set = XNEWVEC (int, max_gcse_regno);
/* Find all the stores we care about. */
FOR_EACH_BB (bb)
{
/* First compute the registers set in this block. */
regvec = last_set_in;
FOR_BB_INSNS (bb, insn)
{
if (! INSN_P (insn))
continue;
if (CALL_P (insn))
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
last_set_in[regno] = INSN_UID (insn);
}
pat = PATTERN (insn);
compute_store_table_current_insn = insn;
note_stores (pat, reg_set_info, NULL);
}
/* Now find the stores. */
memset (already_set, 0, sizeof (int) * max_gcse_regno);
regvec = already_set;
FOR_BB_INSNS (bb, insn)
{
if (! INSN_P (insn))
continue;
if (CALL_P (insn))
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
already_set[regno] = 1;
}
pat = PATTERN (insn);
note_stores (pat, reg_set_info, NULL);
/* Now that we've marked regs, look for stores. */
find_moveable_store (insn, already_set, last_set_in);
/* Unmark regs that are no longer set. */
compute_store_table_current_insn = insn;
note_stores (pat, reg_clear_last_set, last_set_in);
if (CALL_P (insn))
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)
&& last_set_in[regno] == INSN_UID (insn))
last_set_in[regno] = 0;
}
}
#ifdef ENABLE_CHECKING
/* last_set_in should now be all-zero. */
for (regno = 0; regno < max_gcse_regno; regno++)
gcc_assert (!last_set_in[regno]);
#endif
/* Clear temporary marks. */
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
LAST_AVAIL_CHECK_FAILURE(ptr) = NULL_RTX;
if (ANTIC_STORE_LIST (ptr)
&& (tmp = XEXP (ANTIC_STORE_LIST (ptr), 0)) == NULL_RTX)
ANTIC_STORE_LIST (ptr) = XEXP (ANTIC_STORE_LIST (ptr), 1);
}
}
/* Remove the stores that are not available anywhere, as there will
be no opportunity to optimize them. */
for (ptr = pre_ldst_mems, prev_next_ptr_ptr = &pre_ldst_mems;
ptr != NULL;
ptr = *prev_next_ptr_ptr)
{
if (!AVAIL_STORE_LIST (ptr))
{
*prev_next_ptr_ptr = ptr->next;
htab_remove_elt_with_hash (pre_ldst_table, ptr, ptr->hash_index);
free_ldst_entry (ptr);
}
else
prev_next_ptr_ptr = &ptr->next;
}
ret = enumerate_ldsts ();
if (dump_file)
{
fprintf (dump_file, "ST_avail and ST_antic (shown under loads..)\n");
print_ldst_list (dump_file);
}
free (last_set_in);
free (already_set);
return ret;
}
/* Insert an instruction at the beginning of a basic block, and update
the BB_HEAD if needed. */
static void
insert_insn_start_basic_block (rtx insn, basic_block bb)
{
/* Insert at start of successor block. */
rtx prev = PREV_INSN (BB_HEAD (bb));
rtx before = BB_HEAD (bb);
while (before != 0)
{
if (! LABEL_P (before)
&& !NOTE_INSN_BASIC_BLOCK_P (before))
break;
prev = before;
if (prev == BB_END (bb))
break;
before = NEXT_INSN (before);
}
insn = emit_insn_after_noloc (insn, prev, bb);
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION insert store at start of BB %d:\n",
bb->index);
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
}
/* This routine will insert a store on an edge. EXPR is the ldst entry for
the memory reference, and E is the edge to insert it on. Returns nonzero
if an edge insertion was performed. */
static int
insert_store (struct ls_expr * expr, edge e)
{
rtx reg, insn;
basic_block bb;
edge tmp;
edge_iterator ei;
/* We did all the deleted before this insert, so if we didn't delete a
store, then we haven't set the reaching reg yet either. */
if (expr->reaching_reg == NULL_RTX)
return 0;
if (e->flags & EDGE_FAKE)
return 0;
reg = expr->reaching_reg;
insn = gen_move_insn (copy_rtx (expr->pattern), reg);
/* If we are inserting this expression on ALL predecessor edges of a BB,
insert it at the start of the BB, and reset the insert bits on the other
edges so we don't try to insert it on the other edges. */
bb = e->dest;
FOR_EACH_EDGE (tmp, ei, e->dest->preds)
if (!(tmp->flags & EDGE_FAKE))
{
int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
gcc_assert (index != EDGE_INDEX_NO_EDGE);
if (! TEST_BIT (pre_insert_map[index], expr->index))
break;
}
/* If tmp is NULL, we found an insertion on every edge, blank the
insertion vector for these edges, and insert at the start of the BB. */
if (!tmp && bb != EXIT_BLOCK_PTR)
{
FOR_EACH_EDGE (tmp, ei, e->dest->preds)
{
int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
RESET_BIT (pre_insert_map[index], expr->index);
}
insert_insn_start_basic_block (insn, bb);
return 0;
}
/* We can't put stores in the front of blocks pointed to by abnormal
edges since that may put a store where one didn't used to be. */
gcc_assert (!(e->flags & EDGE_ABNORMAL));
insert_insn_on_edge (insn, e);
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION insert insn on edge (%d, %d):\n",
e->src->index, e->dest->index);
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
return 1;
}
/* Remove any REG_EQUAL or REG_EQUIV notes containing a reference to the
memory location in SMEXPR set in basic block BB.
This could be rather expensive. */
static void
remove_reachable_equiv_notes (basic_block bb, struct ls_expr *smexpr)
{
edge_iterator *stack, ei;
int sp;
edge act;
sbitmap visited = sbitmap_alloc (last_basic_block);
rtx last, insn, note;
rtx mem = smexpr->pattern;
stack = XNEWVEC (edge_iterator, n_basic_blocks);
sp = 0;
ei = ei_start (bb->succs);
sbitmap_zero (visited);
act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
while (1)
{
if (!act)
{
if (!sp)
{
free (stack);
sbitmap_free (visited);
return;
}
act = ei_edge (stack[--sp]);
}
bb = act->dest;
if (bb == EXIT_BLOCK_PTR
|| TEST_BIT (visited, bb->index))
{
if (!ei_end_p (ei))
ei_next (&ei);
act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
continue;
}
SET_BIT (visited, bb->index);
if (TEST_BIT (st_antloc[bb->index], smexpr->index))
{
for (last = ANTIC_STORE_LIST (smexpr);
BLOCK_FOR_INSN (XEXP (last, 0)) != bb;
last = XEXP (last, 1))
continue;
last = XEXP (last, 0);
}
else
last = NEXT_INSN (BB_END (bb));
for (insn = BB_HEAD (bb); insn != last; insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
note = find_reg_equal_equiv_note (insn);
if (!note || !exp_equiv_p (XEXP (note, 0), mem, 0, true))
continue;
if (dump_file)
fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
INSN_UID (insn));
remove_note (insn, note);
}
if (!ei_end_p (ei))
ei_next (&ei);
act = (! ei_end_p (ei)) ? ei_edge (ei) : NULL;
if (EDGE_COUNT (bb->succs) > 0)
{
if (act)
stack[sp++] = ei;
ei = ei_start (bb->succs);
act = (EDGE_COUNT (ei_container (ei)) > 0 ? EDGE_I (ei_container (ei), 0) : NULL);
}
}
}
/* This routine will replace a store with a SET to a specified register. */
static void
replace_store_insn (rtx reg, rtx del, basic_block bb, struct ls_expr *smexpr)
{
rtx insn, mem, note, set, ptr;
mem = smexpr->pattern;
insn = gen_move_insn (reg, SET_SRC (single_set (del)));
for (ptr = ANTIC_STORE_LIST (smexpr); ptr; ptr = XEXP (ptr, 1))
if (XEXP (ptr, 0) == del)
{
XEXP (ptr, 0) = insn;
break;
}
/* Move the notes from the deleted insn to its replacement. */
REG_NOTES (insn) = REG_NOTES (del);
/* Emit the insn AFTER all the notes are transferred.
This is cheaper since we avoid df rescanning for the note change. */
insn = emit_insn_after (insn, del);
if (dump_file)
{
fprintf (dump_file,
"STORE_MOTION delete insn in BB %d:\n ", bb->index);
print_inline_rtx (dump_file, del, 6);
fprintf (dump_file, "\nSTORE_MOTION replaced with insn:\n ");
print_inline_rtx (dump_file, insn, 6);
fprintf (dump_file, "\n");
}
delete_insn (del);
/* Now we must handle REG_EQUAL notes whose contents is equal to the mem;
they are no longer accurate provided that they are reached by this
definition, so drop them. */
for (; insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn))
if (INSN_P (insn))
{
set = single_set (insn);
if (!set)
continue;
if (exp_equiv_p (SET_DEST (set), mem, 0, true))
return;
note = find_reg_equal_equiv_note (insn);
if (!note || !exp_equiv_p (XEXP (note, 0), mem, 0, true))
continue;
if (dump_file)
fprintf (dump_file, "STORE_MOTION drop REG_EQUAL note at insn %d:\n",
INSN_UID (insn));
remove_note (insn, note);
}
remove_reachable_equiv_notes (bb, smexpr);
}
/* Delete a store, but copy the value that would have been stored into
the reaching_reg for later storing. */
static void
delete_store (struct ls_expr * expr, basic_block bb)
{
rtx reg, i, del;
if (expr->reaching_reg == NULL_RTX)
expr->reaching_reg = gen_reg_rtx_and_attrs (expr->pattern);
reg = expr->reaching_reg;
for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
{
del = XEXP (i, 0);
if (BLOCK_FOR_INSN (del) == bb)
{
/* We know there is only one since we deleted redundant
ones during the available computation. */
replace_store_insn (reg, del, bb, expr);
break;
}
}
}
/* Fill in available, anticipatable, transparent and kill vectors in
STORE_DATA, based on lists of available and anticipatable stores. */
static void
build_store_vectors (void)
{
basic_block bb;
int *regs_set_in_block;
rtx insn, st;
struct ls_expr * ptr;
unsigned int max_gcse_regno = max_reg_num ();
/* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */
ae_gen = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (ae_gen, last_basic_block);
st_antloc = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (st_antloc, last_basic_block);
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
for (st = AVAIL_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
/* If we've already seen an available expression in this block,
we can delete this one (It occurs earlier in the block). We'll
copy the SRC expression to an unused register in case there
are any side effects. */
if (TEST_BIT (ae_gen[bb->index], ptr->index))
{
rtx r = gen_reg_rtx_and_attrs (ptr->pattern);
if (dump_file)
fprintf (dump_file, "Removing redundant store:\n");
replace_store_insn (r, XEXP (st, 0), bb, ptr);
continue;
}
SET_BIT (ae_gen[bb->index], ptr->index);
}
for (st = ANTIC_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
SET_BIT (st_antloc[bb->index], ptr->index);
}
}
ae_kill = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (ae_kill, last_basic_block);
transp = sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (transp, last_basic_block);
regs_set_in_block = XNEWVEC (int, max_gcse_regno);
FOR_EACH_BB (bb)
{
FOR_BB_INSNS (bb, insn)
if (INSN_P (insn))
{
df_ref *def_rec;
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
{
unsigned int ref_regno = DF_REF_REGNO (*def_rec);
if (ref_regno < max_gcse_regno)
regs_set_in_block[DF_REF_REGNO (*def_rec)] = 1;
}
}
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
if (store_killed_after (ptr->pattern, ptr->pattern_regs, BB_HEAD (bb),
bb, regs_set_in_block, NULL))
{
/* It should not be necessary to consider the expression
killed if it is both anticipatable and available. */
if (!TEST_BIT (st_antloc[bb->index], ptr->index)
|| !TEST_BIT (ae_gen[bb->index], ptr->index))
SET_BIT (ae_kill[bb->index], ptr->index);
}
else
SET_BIT (transp[bb->index], ptr->index);
}
}
free (regs_set_in_block);
if (dump_file)
{
dump_sbitmap_vector (dump_file, "st_antloc", "", st_antloc, last_basic_block);
dump_sbitmap_vector (dump_file, "st_kill", "", ae_kill, last_basic_block);
dump_sbitmap_vector (dump_file, "Transpt", "", transp, last_basic_block);
dump_sbitmap_vector (dump_file, "st_avloc", "", ae_gen, last_basic_block);
}
}
/* Free memory used by store motion. */
static void
free_store_memory (void)
{
free_ldst_mems ();
if (ae_gen)
sbitmap_vector_free (ae_gen);
if (ae_kill)
sbitmap_vector_free (ae_kill);
if (transp)
sbitmap_vector_free (transp);
if (st_antloc)
sbitmap_vector_free (st_antloc);
if (pre_insert_map)
sbitmap_vector_free (pre_insert_map);
if (pre_delete_map)
sbitmap_vector_free (pre_delete_map);
ae_gen = ae_kill = transp = st_antloc = NULL;
pre_insert_map = pre_delete_map = NULL;
}
/* Perform store motion. Much like gcse, except we move expressions the
other way by looking at the flowgraph in reverse.
Return non-zero if transformations are performed by the pass. */
static int
one_store_motion_pass (void)
{
basic_block bb;
int x;
struct ls_expr * ptr;
int update_flow = 0;
gcse_subst_count = 0;
gcse_create_count = 0;
init_alias_analysis ();
/* Find all the available and anticipatable stores. */
num_stores = compute_store_table ();
if (num_stores == 0)
{
htab_delete (pre_ldst_table);
pre_ldst_table = NULL;
end_alias_analysis ();
return 0;
}
/* Now compute kill & transp vectors. */
build_store_vectors ();
add_noreturn_fake_exit_edges ();
connect_infinite_loops_to_exit ();
edge_list = pre_edge_rev_lcm (num_stores, transp, ae_gen,
st_antloc, ae_kill, &pre_insert_map,
&pre_delete_map);
/* Now we want to insert the new stores which are going to be needed. */
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
/* If any of the edges we have above are abnormal, we can't move this
store. */
for (x = NUM_EDGES (edge_list) - 1; x >= 0; x--)
if (TEST_BIT (pre_insert_map[x], ptr->index)
&& (INDEX_EDGE (edge_list, x)->flags & EDGE_ABNORMAL))
break;
if (x >= 0)
{
if (dump_file != NULL)
fprintf (dump_file,
"Can't replace store %d: abnormal edge from %d to %d\n",
ptr->index, INDEX_EDGE (edge_list, x)->src->index,
INDEX_EDGE (edge_list, x)->dest->index);
continue;
}
/* Now we want to insert the new stores which are going to be needed. */
FOR_EACH_BB (bb)
if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
{
delete_store (ptr, bb);
gcse_subst_count++;
}
for (x = 0; x < NUM_EDGES (edge_list); x++)
if (TEST_BIT (pre_insert_map[x], ptr->index))
{
update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
gcse_create_count++;
}
}
if (update_flow)
commit_edge_insertions ();
free_store_memory ();
free_edge_list (edge_list);
remove_fake_exit_edges ();
end_alias_analysis ();
if (dump_file)
{
fprintf (dump_file, "STORE_MOTION of %s, %d basic blocks, ",
current_function_name (), n_basic_blocks);
fprintf (dump_file, "%d substs, %d insns created\n",
gcse_subst_count, gcse_create_count);
}
return (gcse_subst_count > 0 || gcse_create_count > 0);
}
static bool
gate_rtl_store_motion (void)
{
return optimize > 0 && flag_gcse_sm
&& !cfun->calls_setjmp
&& optimize_function_for_speed_p (cfun)
&& dbg_cnt (store_motion);
}
static unsigned int
execute_rtl_store_motion (void)
{
delete_unreachable_blocks ();
df_note_add_problem ();
df_analyze ();
flag_rerun_cse_after_global_opts |= one_store_motion_pass ();
return 0;
}
struct rtl_opt_pass pass_rtl_store_motion =
{
{
RTL_PASS,
"store_motion", /* name */
gate_rtl_store_motion, /* gate */
execute_rtl_store_motion, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
TV_LSM, /* tv_id */
PROP_cfglayout, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_df_finish | TODO_verify_rtl_sharing |
TODO_dump_func |
TODO_verify_flow | TODO_ggc_collect /* todo_flags_finish */
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment