diff options
Diffstat (limited to 'gcc/cselib.c')
-rw-r--r-- | gcc/cselib.c | 407 |
1 files changed, 336 insertions, 71 deletions
diff --git a/gcc/cselib.c b/gcc/cselib.c index 456f1359b6f..8d52c519ff3 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see #include "output.h" #include "ggc.h" #include "hashtab.h" +#include "tree-pass.h" #include "cselib.h" #include "params.h" #include "alloc-pool.h" @@ -54,9 +55,8 @@ static void unchain_one_elt_loc_list (struct elt_loc_list **); static int discard_useless_locs (void **, void *); static int discard_useless_values (void **, void *); static void remove_useless_values (void); -static rtx wrap_constant (enum machine_mode, rtx); static unsigned int cselib_hash_rtx (rtx, int); -static cselib_val *new_cselib_val (unsigned int, enum machine_mode); +static cselib_val *new_cselib_val (unsigned int, enum machine_mode, rtx); static void add_mem_for_addr (cselib_val *, cselib_val *, rtx); static cselib_val *cselib_lookup_mem (rtx, int); static void cselib_invalidate_regno (unsigned int, enum machine_mode); @@ -64,6 +64,15 @@ static void cselib_invalidate_mem (rtx); static void cselib_record_set (rtx, cselib_val *, cselib_val *); static void cselib_record_sets (rtx); +struct expand_value_data +{ + bitmap regs_active; + cselib_expand_callback callback; + void *callback_arg; +}; + +static rtx cselib_expand_value_rtx_1 (rtx, struct expand_value_data *, int); + /* There are three ways in which cselib can look up an rtx: - for a REG, the reg_values table (which is indexed by regno) is used - for a MEM, we recursively look up its address and then follow the @@ -134,6 +143,20 @@ static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool; /* If nonnull, cselib will call this function before freeing useless VALUEs. A VALUE is deemed useless if its "locs" field is null. */ void (*cselib_discard_hook) (cselib_val *); + +/* If nonnull, cselib will call this function before recording sets or + even clobbering outputs of INSN. All the recorded sets will be + represented in the array sets[n_sets]. new_val_min can be used to + tell whether values present in sets are introduced by this + instruction. */ +void (*cselib_record_sets_hook) (rtx insn, struct cselib_set *sets, + int n_sets); + +#define PRESERVED_VALUE_P(RTX) \ + (RTL_FLAG_CHECK1("PRESERVED_VALUE_P", (RTX), VALUE)->unchanging) +#define LONG_TERM_PRESERVED_VALUE_P(RTX) \ + (RTL_FLAG_CHECK1("LONG_TERM_PRESERVED_VALUE_P", (RTX), VALUE)->in_struct) + /* Allocate a struct elt_list and fill in its two elements with the @@ -199,12 +222,20 @@ unchain_one_value (cselib_val *v) } /* Remove all entries from the hash table. Also used during - initialization. If CLEAR_ALL isn't set, then only clear the entries - which are known to have been used. */ + initialization. */ void cselib_clear_table (void) { + cselib_reset_table_with_next_value (0); +} + +/* Remove all entries from the hash table, arranging for the next + value to be numbered NUM. */ + +void +cselib_reset_table_with_next_value (unsigned int num) +{ unsigned int i; for (i = 0; i < n_used_regs; i++) @@ -214,15 +245,24 @@ cselib_clear_table (void) n_used_regs = 0; + /* ??? Preserve constants? */ htab_empty (cselib_hash_table); n_useless_values = 0; - next_unknown_value = 0; + next_unknown_value = num; first_containing_mem = &dummy_val; } +/* Return the number of the next value that will be generated. */ + +unsigned int +cselib_get_next_unknown_value (void) +{ + return next_unknown_value; +} + /* The equality test for our hash table. The first argument ENTRY is a table element (i.e. a cselib_val), while the second arg X is an rtx. We know that all callers of htab_find_slot_with_hash will wrap CONST_INTs into a @@ -317,7 +357,7 @@ discard_useless_locs (void **x, void *info ATTRIBUTE_UNUSED) p = &(*p)->next; } - if (had_locs && v->locs == 0) + if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx)) { n_useless_values++; values_became_useless = 1; @@ -332,7 +372,7 @@ discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED) { cselib_val *v = (cselib_val *)*x; - if (v->locs == 0) + if (v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx)) { if (cselib_discard_hook) cselib_discard_hook (v); @@ -378,6 +418,78 @@ remove_useless_values (void) gcc_assert (!n_useless_values); } +/* Arrange for a value to not be removed from the hash table even if + it becomes useless. */ + +void +cselib_preserve_value (cselib_val *v) +{ + PRESERVED_VALUE_P (v->val_rtx) = 1; +} + +/* Test whether a value is preserved. */ + +bool +cselib_preserved_value_p (cselib_val *v) +{ + return PRESERVED_VALUE_P (v->val_rtx); +} + +/* Mark preserved values as preserved for the long term. */ + +static int +cselib_preserve_definitely (void **slot, void *info ATTRIBUTE_UNUSED) +{ + cselib_val *v = (cselib_val *)*slot; + + if (PRESERVED_VALUE_P (v->val_rtx) + && !LONG_TERM_PRESERVED_VALUE_P (v->val_rtx)) + LONG_TERM_PRESERVED_VALUE_P (v->val_rtx) = true; + + return 1; +} + +/* Clear the preserve marks for values not preserved for the long + term. */ + +static int +cselib_clear_preserve (void **slot, void *info ATTRIBUTE_UNUSED) +{ + cselib_val *v = (cselib_val *)*slot; + + if (PRESERVED_VALUE_P (v->val_rtx) + && !LONG_TERM_PRESERVED_VALUE_P (v->val_rtx)) + { + PRESERVED_VALUE_P (v->val_rtx) = false; + if (!v->locs) + n_useless_values++; + } + + return 1; +} + +/* Clean all non-constant expressions in the hash table, but retain + their values. */ + +void +cselib_preserve_only_values (bool retain) +{ + int i; + + htab_traverse (cselib_hash_table, + retain ? cselib_preserve_definitely : cselib_clear_preserve, + NULL); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + cselib_invalidate_regno (i, reg_raw_mode[i]); + + cselib_invalidate_mem (callmem); + + remove_useless_values (); + + gcc_assert (first_containing_mem == &dummy_val); +} + /* Return the mode in which a register was last set. If X is not a register, return its mode. If the mode in which the register was set is not known, or the value was already clobbered, return @@ -549,19 +661,6 @@ rtx_equal_for_cselib_p (rtx x, rtx y) return 1; } -/* We need to pass down the mode of constants through the hash table - functions. For that purpose, wrap them in a CONST of the appropriate - mode. */ -static rtx -wrap_constant (enum machine_mode mode, rtx x) -{ - if (!CONST_INT_P (x) && GET_CODE (x) != CONST_FIXED - && (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode)) - return x; - gcc_assert (mode != VOIDmode); - return gen_rtx_CONST (mode, x); -} - /* Hash an rtx. Return 0 if we couldn't hash the rtx. For registers and memory locations, we look up their cselib_val structure and return its VALUE element. @@ -748,7 +847,7 @@ cselib_hash_rtx (rtx x, int create) value is MODE. */ static inline cselib_val * -new_cselib_val (unsigned int value, enum machine_mode mode) +new_cselib_val (unsigned int value, enum machine_mode mode, rtx x) { cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool); @@ -768,6 +867,18 @@ new_cselib_val (unsigned int value, enum machine_mode mode) e->addr_list = 0; e->locs = 0; e->next_containing_mem = 0; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "cselib value %u ", value); + if (flag_dump_noaddr || flag_dump_unnumbered) + fputs ("# ", dump_file); + else + fprintf (dump_file, "%p ", (void*)e); + print_rtl_single (dump_file, x); + fputc ('\n', dump_file); + } + return e; } @@ -827,7 +938,7 @@ cselib_lookup_mem (rtx x, int create) if (! create) return 0; - mem_elt = new_cselib_val (++next_unknown_value, mode); + mem_elt = new_cselib_val (++next_unknown_value, mode, x); add_mem_for_addr (addr, mem_elt, x); slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x), mem_elt->value, INSERT); @@ -842,7 +953,8 @@ cselib_lookup_mem (rtx x, int create) expand to the same place. */ static rtx -expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) +expand_loc (struct elt_loc_list *p, struct expand_value_data *evd, + int max_depth) { rtx reg_result = NULL; unsigned int regno = UINT_MAX; @@ -854,7 +966,7 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) the same reg. */ if ((REG_P (p->loc)) && (REGNO (p->loc) < regno) - && !bitmap_bit_p (regs_active, REGNO (p->loc))) + && !bitmap_bit_p (evd->regs_active, REGNO (p->loc))) { reg_result = p->loc; regno = REGNO (p->loc); @@ -867,7 +979,7 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) else if (!REG_P (p->loc)) { rtx result, note; - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) { print_inline_rtx (dump_file, p->loc, 0); fprintf (dump_file, "\n"); @@ -878,7 +990,7 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) && (note = find_reg_note (p->setting_insn, REG_EQUAL, NULL_RTX)) && XEXP (note, 0) == XEXP (p->loc, 1)) return XEXP (p->loc, 1); - result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1); + result = cselib_expand_value_rtx_1 (p->loc, evd, max_depth - 1); if (result) return result; } @@ -888,15 +1000,15 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) if (regno != UINT_MAX) { rtx result; - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "r%d\n", regno); - result = cselib_expand_value_rtx (reg_result, regs_active, max_depth - 1); + result = cselib_expand_value_rtx_1 (reg_result, evd, max_depth - 1); if (result) return result; } - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) { if (reg_result) { @@ -931,6 +1043,35 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) rtx cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) { + struct expand_value_data evd; + + evd.regs_active = regs_active; + evd.callback = NULL; + evd.callback_arg = NULL; + + return cselib_expand_value_rtx_1 (orig, &evd, max_depth); +} + +/* Same as cselib_expand_value_rtx, but using a callback to try to + resolve VALUEs that expand to nothing. */ + +rtx +cselib_expand_value_rtx_cb (rtx orig, bitmap regs_active, int max_depth, + cselib_expand_callback cb, void *data) +{ + struct expand_value_data evd; + + evd.regs_active = regs_active; + evd.callback = cb; + evd.callback_arg = data; + + return cselib_expand_value_rtx_1 (orig, &evd, max_depth); +} + +static rtx +cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd, + int max_depth) +{ rtx copy, scopy; int i, j; RTX_CODE code; @@ -980,13 +1121,13 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) || regno == HARD_FRAME_POINTER_REGNUM) return orig; - bitmap_set_bit (regs_active, regno); + bitmap_set_bit (evd->regs_active, regno); - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "expanding: r%d into: ", regno); - result = expand_loc (l->elt->locs, regs_active, max_depth); - bitmap_clear_bit (regs_active, regno); + result = expand_loc (l->elt->locs, evd, max_depth); + bitmap_clear_bit (evd->regs_active, regno); if (result) return result; @@ -1017,8 +1158,8 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) case SUBREG: { - rtx subreg = cselib_expand_value_rtx (SUBREG_REG (orig), regs_active, - max_depth - 1); + rtx subreg = cselib_expand_value_rtx_1 (SUBREG_REG (orig), evd, + max_depth - 1); if (!subreg) return NULL; scopy = simplify_gen_subreg (GET_MODE (orig), subreg, @@ -1027,18 +1168,39 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) if (scopy == NULL || (GET_CODE (scopy) == SUBREG && !REG_P (SUBREG_REG (scopy)) - && !MEM_P (SUBREG_REG (scopy)))) + && !MEM_P (SUBREG_REG (scopy)) + && (REG_P (SUBREG_REG (orig)) + || MEM_P (SUBREG_REG (orig))))) return shallow_copy_rtx (orig); return scopy; } case VALUE: - if (dump_file) - fprintf (dump_file, "expanding value %s into: ", - GET_MODE_NAME (GET_MODE (orig))); + { + rtx result; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fputs ("\nexpanding ", dump_file); + print_rtl_single (dump_file, orig); + fputs (" into...", dump_file); + } - return expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth); + if (!evd->callback) + result = NULL; + else + { + result = evd->callback (orig, evd->regs_active, max_depth, + evd->callback_arg); + if (result == orig) + result = NULL; + else if (result) + result = cselib_expand_value_rtx_1 (result, evd, max_depth); + } + if (!result) + result = expand_loc (CSELIB_VAL_PTR (orig)->locs, evd, max_depth); + return result; + } default: break; } @@ -1057,7 +1219,8 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) case 'e': if (XEXP (orig, i) != NULL) { - rtx result = cselib_expand_value_rtx (XEXP (orig, i), regs_active, max_depth - 1); + rtx result = cselib_expand_value_rtx_1 (XEXP (orig, i), evd, + max_depth - 1); if (!result) return NULL; XEXP (copy, i) = result; @@ -1071,7 +1234,8 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); for (j = 0; j < XVECLEN (copy, i); j++) { - rtx result = cselib_expand_value_rtx (XVECEXP (orig, i, j), regs_active, max_depth - 1); + rtx result = cselib_expand_value_rtx_1 (XVECEXP (orig, i, j), + evd, max_depth - 1); if (!result) return NULL; XVECEXP (copy, i, j) = result; @@ -1155,13 +1319,17 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) { XEXP (copy, 0) = gen_rtx_CONST (GET_MODE (XEXP (orig, 0)), XEXP (copy, 0)); - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n", GET_MODE_NAME (GET_MODE (XEXP (copy, 0)))); } scopy = simplify_rtx (copy); if (scopy) - return scopy; + { + if (GET_MODE (copy) != GET_MODE (scopy)) + scopy = wrap_constant (GET_MODE (copy), scopy); + return scopy; + } return copy; } @@ -1199,7 +1367,7 @@ cselib_subst_to_values (rtx x) { /* This happens for autoincrements. Assign a value that doesn't match any other. */ - e = new_cselib_val (++next_unknown_value, GET_MODE (x)); + e = new_cselib_val (++next_unknown_value, GET_MODE (x), x); } return e->val_rtx; @@ -1215,7 +1383,7 @@ cselib_subst_to_values (rtx x) case PRE_DEC: case POST_MODIFY: case PRE_MODIFY: - e = new_cselib_val (++next_unknown_value, GET_MODE (x)); + e = new_cselib_val (++next_unknown_value, GET_MODE (x), x); return e->val_rtx; default: @@ -1259,6 +1427,21 @@ cselib_subst_to_values (rtx x) return copy; } +/* Log a lookup of X to the cselib table along with the result RET. */ + +static cselib_val * +cselib_log_lookup (rtx x, cselib_val *ret) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fputs ("cselib lookup ", dump_file); + print_inline_rtx (dump_file, x, 2); + fprintf (dump_file, " => %u\n", ret ? ret->value : 0); + } + + return ret; +} + /* Look up the rtl expression X in our tables and return the value it has. If CREATE is zero, we return NULL if we don't know the value. Otherwise, we create a new one if possible, using mode MODE if X doesn't have a mode @@ -1287,10 +1470,10 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) l = l->next; for (; l; l = l->next) if (mode == GET_MODE (l->elt->val_rtx)) - return l->elt; + return cselib_log_lookup (x, l->elt); if (! create) - return 0; + return cselib_log_lookup (x, 0); if (i < FIRST_PSEUDO_REGISTER) { @@ -1300,7 +1483,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) max_value_regs = n; } - e = new_cselib_val (++next_unknown_value, GET_MODE (x)); + e = new_cselib_val (++next_unknown_value, GET_MODE (x), x); e->locs = new_elt_loc_list (e->locs, x); if (REG_VALUES (i) == 0) { @@ -1313,34 +1496,34 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e); slot = htab_find_slot_with_hash (cselib_hash_table, x, e->value, INSERT); *slot = e; - return e; + return cselib_log_lookup (x, e); } if (MEM_P (x)) - return cselib_lookup_mem (x, create); + return cselib_log_lookup (x, cselib_lookup_mem (x, create)); hashval = cselib_hash_rtx (x, create); /* Can't even create if hashing is not possible. */ if (! hashval) - return 0; + return cselib_log_lookup (x, 0); slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x), hashval, create ? INSERT : NO_INSERT); if (slot == 0) - return 0; + return cselib_log_lookup (x, 0); e = (cselib_val *) *slot; if (e) - return e; + return cselib_log_lookup (x, e); - e = new_cselib_val (hashval, mode); + e = new_cselib_val (hashval, mode, x); /* We have to fill the slot before calling cselib_subst_to_values: the hash table is inconsistent until we do so, and cselib_subst_to_values will need to do lookups. */ *slot = (void *) e; e->locs = new_elt_loc_list (e->locs, cselib_subst_to_values (x)); - return e; + return cselib_log_lookup (x, e); } /* Invalidate any entries in reg_values that overlap REGNO. This is called @@ -1427,7 +1610,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode) break; } } - if (v->locs == 0) + if (v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx)) n_useless_values++; } } @@ -1510,7 +1693,7 @@ cselib_invalidate_mem (rtx mem_rtx) unchain_one_elt_loc_list (p); } - if (had_locs && v->locs == 0) + if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx)) n_useless_values++; next = v->next_containing_mem; @@ -1591,28 +1774,19 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt) REG_VALUES (dreg)->elt = src_elt; } - if (src_elt->locs == 0) + if (src_elt->locs == 0 && !PRESERVED_VALUE_P (src_elt->val_rtx)) n_useless_values--; src_elt->locs = new_elt_loc_list (src_elt->locs, dest); } else if (MEM_P (dest) && dest_addr_elt != 0 && cselib_record_memory) { - if (src_elt->locs == 0) + if (src_elt->locs == 0 && !PRESERVED_VALUE_P (src_elt->val_rtx)) n_useless_values--; add_mem_for_addr (dest_addr_elt, src_elt, dest); } } -/* Describe a single set that is part of an insn. */ -struct set -{ - rtx src; - rtx dest; - cselib_val *src_elt; - cselib_val *dest_addr_elt; -}; - /* There is no good way to determine how many elements there can be in a PARALLEL. Since it's fairly cheap, use a really large number. */ #define MAX_SETS (FIRST_PSEUDO_REGISTER * 2) @@ -1623,7 +1797,7 @@ cselib_record_sets (rtx insn) { int n_sets = 0; int i; - struct set sets[MAX_SETS]; + struct cselib_set sets[MAX_SETS]; rtx body = PATTERN (insn); rtx cond = 0; @@ -1695,6 +1869,9 @@ cselib_record_sets (rtx insn) } } + if (cselib_record_sets_hook) + cselib_record_sets_hook (insn, sets, n_sets); + /* Invalidate all locations written by this insn. Note that the elts we looked up in the previous loop aren't affected, just some of their locations may go away. */ @@ -1751,7 +1928,7 @@ cselib_process_insn (rtx insn) && GET_CODE (PATTERN (insn)) == ASM_OPERANDS && MEM_VOLATILE_P (PATTERN (insn)))) { - cselib_clear_table (); + cselib_reset_table_with_next_value (next_unknown_value); return; } @@ -1868,4 +2045,92 @@ cselib_finish (void) next_unknown_value = 0; } +/* Dump the cselib_val *X to FILE *info. */ + +static int +dump_cselib_val (void **x, void *info) +{ + cselib_val *v = (cselib_val *)*x; + FILE *out = (FILE *)info; + bool need_lf = true; + + print_inline_rtx (out, v->val_rtx, 0); + + if (v->locs) + { + struct elt_loc_list *l = v->locs; + if (need_lf) + { + fputc ('\n', out); + need_lf = false; + } + fputs (" locs:", out); + do + { + fprintf (out, "\n from insn %i ", + INSN_UID (l->setting_insn)); + print_inline_rtx (out, l->loc, 4); + } + while ((l = l->next)); + fputc ('\n', out); + } + else + { + fputs (" no locs", out); + need_lf = true; + } + + if (v->addr_list) + { + struct elt_list *e = v->addr_list; + if (need_lf) + { + fputc ('\n', out); + need_lf = false; + } + fputs (" addr list:", out); + do + { + fputs ("\n ", out); + print_inline_rtx (out, e->elt->val_rtx, 2); + } + while ((e = e->next)); + fputc ('\n', out); + } + else + { + fputs (" no addrs", out); + need_lf = true; + } + + if (v->next_containing_mem == &dummy_val) + fputs (" last mem\n", out); + else if (v->next_containing_mem) + { + fputs (" next mem ", out); + print_inline_rtx (out, v->next_containing_mem->val_rtx, 2); + fputc ('\n', out); + } + else if (need_lf) + fputc ('\n', out); + + return 1; +} + +/* Dump to OUT everything in the CSELIB table. */ + +void +dump_cselib_table (FILE *out) +{ + fprintf (out, "cselib hash table:\n"); + htab_traverse (cselib_hash_table, dump_cselib_val, out); + if (first_containing_mem != &dummy_val) + { + fputs ("first mem ", out); + print_inline_rtx (out, first_containing_mem->val_rtx, 2); + fputc ('\n', out); + } + fprintf (out, "last unknown value %i\n", next_unknown_value); +} + #include "gt-cselib.h" |