diff options
author | steven <steven@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-04-30 10:51:08 +0000 |
---|---|---|
committer | steven <steven@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-04-30 10:51:08 +0000 |
commit | 4b673aa1dde655e1d89d31e583168d5831f32819 (patch) | |
tree | 5267d15c4c34801fce266c5a6afb6cea477323ea /gcc | |
parent | aca3a78faf88f12a2de44b00e129c1f015548c90 (diff) | |
download | gcc-4b673aa1dde655e1d89d31e583168d5831f32819.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@147001 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/Makefile.in | 6 | ||||
-rw-r--r-- | gcc/gcse.c | 1178 | ||||
-rw-r--r-- | gcc/rtl.h | 1 | ||||
-rw-r--r-- | gcc/store-motion.c | 1405 |
5 files changed, 1443 insertions, 1166 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ec683ac4ce1..8cee45b87b2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +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> PR target/38571 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 1a253489f81..66d4590289d 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1199,6 +1199,7 @@ OBJS-common = \ statistics.o \ stmt.o \ stor-layout.o \ + store-motion.o \ stringpool.o \ targhooks.o \ timevar.o \ @@ -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) \ $(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) $(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) \ 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) \ diff --git a/gcc/gcse.c b/gcc/gcse.c index ab3137eae02..b3fa362aff3 100644 --- a/gcc/gcse.c +++ b/gcc/gcse.c @@ -437,7 +437,7 @@ static int global_const_prop_count; static int global_copy_prop_count; /* For available exprs */ -static sbitmap *ae_kill, *ae_gen; +static sbitmap *ae_kill; static void compute_can_copy (void); static void *gmalloc (size_t) ATTRIBUTE_MALLOC; @@ -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_call (rtx, rtx, struct hash_table *); static int want_to_gcse_p (rtx); -static bool can_assign_to_reg_p (rtx); static bool gcse_constant_p (const_rtx); static int oprs_unchanged_p (const_rtx, const_rtx, int); static int oprs_anticipatable_p (const_rtx, const_rtx); @@ -527,7 +526,6 @@ static void free_ldst_entry (struct ls_expr *); static void free_ldst_mems (void); static void print_ldst_list (FILE *); 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 * next_ls_expr (struct ls_expr *); static int simple_mem (const_rtx); @@ -535,26 +533,6 @@ static void invalidate_any_buried_refs (rtx); static void compute_ld_motion_mems (void); static void trim_ld_motion_mems (void); 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 clear_modify_mem_tables (void); static void free_modify_mem_tables (void); @@ -816,18 +794,23 @@ want_to_gcse_p (rtx x) return 0; 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; -/* 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 -can_assign_to_reg_p (rtx x) +bool +can_assign_to_reg_without_clobbers_p (rtx x) { int num_clobbers = 0; int icode; @@ -4642,20 +4625,6 @@ find_rtx_in_ldst (rtx x) 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. */ static inline struct ls_expr * @@ -4801,7 +4770,7 @@ compute_ld_motion_mems (void) && GET_CODE (src) != ASM_OPERANDS /* Check for REG manually since want_to_gcse_p 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); else ptr->invalid = 1; @@ -4920,1089 +4889,6 @@ update_ld_motion_stores (struct expr * expr) } } -/* 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; -} - -/* 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); - } - 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; -} - -/* 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_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; -} - -/* 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. */ @@ -6214,25 +5100,6 @@ execute_rtl_hoist (void) 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 = { { @@ -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" diff --git a/gcc/rtl.h b/gcc/rtl.h index 9941a9736f6..1282b909cf7 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -2222,6 +2222,7 @@ extern void expand_dec (rtx, rtx); /* In gcse.c */ extern bool can_copy_p (enum machine_mode); +extern bool can_assign_to_reg_without_clobbers_p (rtx); extern rtx fis_get_condition (rtx); /* In ira.c */ diff --git a/gcc/store-motion.c b/gcc/store-motion.c new file mode 100644 index 00000000000..da614cc944a --- /dev/null +++ b/gcc/store-motion.c @@ -0,0 +1,1405 @@ +/* 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 */ + } +}; + |