X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcselib.c;h=66d92a01ad0d304bf2d6986c76470b6d0a2ed4e6;hb=f018d957a72d418d69c6d2d8bc80c9415666a9f6;hp=54af8e1bfa81f09f89cd7c404370d555f0ec367b;hpb=dea3189b45f05f83dc99494d66b5a4311c811ce6;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cselib.c b/gcc/cselib.c index 54af8e1bfa8..66d92a01ad0 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -1,12 +1,13 @@ /* Common subexpression elimination library for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + 1999, 2000, 2001, 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 2, or (at your option) any later +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 @@ -15,9 +16,8 @@ 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 COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -38,6 +38,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #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 @@ -79,7 +88,6 @@ static htab_t cselib_hash_table; /* This is a global so we don't have to pass this through every function. It is used in new_elt_loc_list to set SETTING_INSN. */ static rtx cselib_current_insn; -static bool cselib_current_insn_in_libcall; /* Every new unknown value gets a unique number. */ static unsigned int next_unknown_value; @@ -135,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 @@ -144,7 +166,7 @@ static inline struct elt_list * new_elt_list (struct elt_list *next, cselib_val *elt) { struct elt_list *el; - el = pool_alloc (elt_list_pool); + el = (struct elt_list *) pool_alloc (elt_list_pool); el->next = next; el->elt = elt; return el; @@ -157,11 +179,10 @@ static inline struct elt_loc_list * new_elt_loc_list (struct elt_loc_list *next, rtx loc) { struct elt_loc_list *el; - el = pool_alloc (elt_loc_list_pool); + el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool); el->next = next; el->loc = loc; el->setting_insn = cselib_current_insn; - el->in_libcall = cselib_current_insn_in_libcall; return el; } @@ -201,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++) @@ -216,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 @@ -235,18 +273,19 @@ entry_and_rtx_equal_p (const void *entry, const void *x_arg) { struct elt_loc_list *l; const cselib_val *const v = (const cselib_val *) entry; - rtx x = (rtx) x_arg; + rtx x = CONST_CAST_RTX ((const_rtx)x_arg); enum machine_mode mode = GET_MODE (x); - gcc_assert (GET_CODE (x) != CONST_INT + gcc_assert (!CONST_INT_P (x) && GET_CODE (x) != CONST_FIXED && (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE)); - + if (mode != GET_MODE (v->val_rtx)) return 0; /* Unwrap X if necessary. */ if (GET_CODE (x) == CONST - && (GET_CODE (XEXP (x, 0)) == CONST_INT + && (CONST_INT_P (XEXP (x, 0)) + || GET_CODE (XEXP (x, 0)) == CONST_FIXED || GET_CODE (XEXP (x, 0)) == CONST_DOUBLE)) x = XEXP (x, 0); @@ -266,7 +305,7 @@ entry_and_rtx_equal_p (const void *entry, const void *x_arg) static hashval_t get_value_hash (const void *entry) { - const cselib_val *v = (const cselib_val *) entry; + const cselib_val *const v = (const cselib_val *) entry; return v->value; } @@ -276,9 +315,9 @@ get_value_hash (const void *entry) removed. */ int -references_value_p (rtx x, int only_useless) +references_value_p (const_rtx x, int only_useless) { - enum rtx_code code = GET_CODE (x); + const enum rtx_code code = GET_CODE (x); const char *fmt = GET_RTX_FORMAT (code); int i, j; @@ -318,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; @@ -333,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); @@ -379,13 +418,85 @@ 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 VOIDmode. */ enum machine_mode -cselib_reg_set_mode (rtx x) +cselib_reg_set_mode (const_rtx x) { if (!REG_P (x)) return GET_MODE (x); @@ -473,6 +584,8 @@ rtx_equal_for_cselib_p (rtx x, rtx y) switch (GET_CODE (x)) { case CONST_DOUBLE: + case CONST_FIXED: + case DEBUG_EXPR: return 0; case LABEL_REF: @@ -555,7 +668,7 @@ rtx_equal_for_cselib_p (rtx x, rtx y) static rtx wrap_constant (enum machine_mode mode, rtx x) { - if (GET_CODE (x) != CONST_INT + 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); @@ -604,6 +717,11 @@ cselib_hash_rtx (rtx x, int create) return e->value; + case DEBUG_EXPR: + hash += ((unsigned) DEBUG_EXPR << 7) + + DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x)); + return hash ? hash : (unsigned int) DEBUG_EXPR; + case CONST_INT: hash += ((unsigned) CONST_INT << 7) + INTVAL (x); return hash ? hash : (unsigned int) CONST_INT; @@ -619,6 +737,11 @@ cselib_hash_rtx (rtx x, int create) + (unsigned) CONST_DOUBLE_HIGH (x)); return hash ? hash : (unsigned int) CONST_DOUBLE; + case CONST_FIXED: + hash += (unsigned int) code + (unsigned int) GET_MODE (x); + hash += fixed_hash (CONST_FIXED_VALUE (x)); + return hash ? hash : (unsigned int) CONST_FIXED; + case CONST_VECTOR: { int units; @@ -692,10 +815,10 @@ cselib_hash_rtx (rtx x, int create) { rtx tem = XEXP (x, i); unsigned int tem_hash = cselib_hash_rtx (tem, create); - + if (tem_hash == 0) return 0; - + hash += tem_hash; } break; @@ -704,10 +827,10 @@ cselib_hash_rtx (rtx x, int create) { unsigned int tem_hash = cselib_hash_rtx (XVECEXP (x, i, j), create); - + if (tem_hash == 0) return 0; - + hash += tem_hash; } break; @@ -715,13 +838,13 @@ cselib_hash_rtx (rtx x, int create) case 's': { const unsigned char *p = (const unsigned char *) XSTR (x, i); - + if (p) while (*p) hash += *p++; break; } - + case 'i': hash += XINT (x, i); break; @@ -730,7 +853,7 @@ cselib_hash_rtx (rtx x, int create) case 't': /* unused */ break; - + default: gcc_unreachable (); } @@ -743,9 +866,9 @@ 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 = pool_alloc (cselib_val_pool); + cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool); gcc_assert (value); @@ -755,7 +878,7 @@ new_cselib_val (unsigned int value, enum machine_mode mode) precisely when we can have VALUE RTXen (when cselib is active) so we don't need to put them in garbage collected memory. ??? Why should a VALUE be an RTX in the first place? */ - e->val_rtx = pool_alloc (value_pool); + e->val_rtx = (rtx) pool_alloc (value_pool); memset (e->val_rtx, 0, RTX_HDR_SIZE); PUT_CODE (e->val_rtx, VALUE); PUT_MODE (e->val_rtx, mode); @@ -763,6 +886,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; } @@ -822,7 +957,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); @@ -836,8 +971,9 @@ cselib_lookup_mem (rtx x, int create) non-reg results, we just take the first one because they will all expand to the same place. */ -static rtx -expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) +static rtx +expand_loc (struct elt_loc_list *p, struct expand_value_data *evd, + int max_depth) { rtx reg_result = NULL; unsigned int regno = UINT_MAX; @@ -847,52 +983,58 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) { /* Avoid infinite recursion trying to expand a reg into a the same reg. */ - if ((REG_P (p->loc)) - && (REGNO (p->loc) < regno) - && !bitmap_bit_p (regs_active, REGNO (p->loc))) + if ((REG_P (p->loc)) + && (REGNO (p->loc) < regno) + && !bitmap_bit_p (evd->regs_active, REGNO (p->loc))) { reg_result = p->loc; regno = REGNO (p->loc); } /* Avoid infinite recursion and do not try to expand the value. */ - else if (GET_CODE (p->loc) == VALUE + else if (GET_CODE (p->loc) == VALUE && CSELIB_VAL_PTR (p->loc)->locs == p_in) continue; else if (!REG_P (p->loc)) { - rtx result; - if (dump_file) + rtx result, note; + if (dump_file && (dump_flags & TDF_DETAILS)) { print_inline_rtx (dump_file, p->loc, 0); fprintf (dump_file, "\n"); } - result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1); + if (GET_CODE (p->loc) == LO_SUM + && GET_CODE (XEXP (p->loc, 1)) == SYMBOL_REF + && p->setting_insn + && (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_1 (p->loc, evd, max_depth - 1); if (result) return result; } - + } - + 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) { print_inline_rtx (dump_file, reg_result, 0); fprintf (dump_file, "\n"); } - else + else fprintf (dump_file, "NULL\n"); } return reg_result; @@ -903,7 +1045,7 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth) This is the opposite of common subexpression. Because local value numbering is such a weak optimization, the expanded expression is pretty much unique (not from a pointer equals point of view but - from a tree shape point of view. + from a tree shape point of view. This function returns NULL if the expansion fails. The expansion will fail if there is no value number for one of the operands or if @@ -920,10 +1062,46 @@ 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 some expressions. The CB function should return ORIG if it + can't or does not want to deal with a certain RTX. Any other + return value, including NULL, will be used as the expansion for + VALUE, without any further changes. */ + +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); +} + +/* Internal implementation of cselib_expand_value_rtx and + cselib_expand_value_rtx_cb. */ + +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; const char *format_ptr; + enum machine_mode mode; code = GET_CODE (orig); @@ -946,7 +1124,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) { rtx result; int regno = REGNO (orig); - + /* The only thing that we are not willing to do (this is requirement of dse and if others potential uses need this function we should add a parm to control @@ -954,7 +1132,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) STACK_POINTER_REGNUM, FRAME_POINTER or the HARD_FRAME_POINTER. - Thses expansions confuses the code that notices that + These expansions confuses the code that notices that stores into the frame go dead at the end of the function and that the frame is not effected by calls to subroutines. If you allow the @@ -968,21 +1146,21 @@ 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; - else + else return orig; } } - + case CONST_INT: case CONST_DOUBLE: case CONST_VECTOR: @@ -1003,25 +1181,64 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) return orig; break; + case SUBREG: + { + rtx subreg; + + if (evd->callback) + { + subreg = evd->callback (orig, evd->regs_active, max_depth, + evd->callback_arg); + if (subreg != orig) + return subreg; + } + + 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, + GET_MODE (SUBREG_REG (orig)), + SUBREG_BYTE (orig)); + if (scopy == NULL + || (GET_CODE (scopy) == SUBREG + && !REG_P (SUBREG_REG (scopy)) + && !MEM_P (SUBREG_REG (scopy)))) + return NULL; + + return scopy; + } case VALUE: { rtx result; - if (dump_file) - fprintf (dump_file, "expanding value %s into: ", GET_MODE_NAME (GET_MODE (orig))); - - result = expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth); - if (result - && GET_CODE (result) == CONST_INT - && GET_MODE (orig) != VOIDmode) + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fputs ("\nexpanding ", dump_file); + print_rtl_single (dump_file, orig); + fputs (" into...", dump_file); + } + + if (evd->callback) { - result = gen_rtx_CONST (GET_MODE (orig), result); - if (dump_file) - fprintf (dump_file, " wrapping const_int result in const to preserve mode %s\n", - GET_MODE_NAME (GET_MODE (orig))); + result = evd->callback (orig, evd->regs_active, max_depth, + evd->callback_arg); + + if (result != orig) + return result; } + + result = expand_loc (CSELIB_VAL_PTR (orig)->locs, evd, max_depth); return result; } + + case DEBUG_EXPR: + if (evd->callback) + return evd->callback (orig, evd->regs_active, max_depth, + evd->callback_arg); + return orig; + default: break; } @@ -1032,15 +1249,16 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) us to explicitly document why we are *not* copying a flag. */ copy = shallow_copy_rtx (orig); - format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); + format_ptr = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) + for (i = 0; i < GET_RTX_LENGTH (code); i++) switch (*format_ptr++) { 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; @@ -1054,7 +1272,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; @@ -1078,6 +1297,62 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) gcc_unreachable (); } + mode = GET_MODE (copy); + /* If an operand has been simplified into CONST_INT, which doesn't + have a mode and the mode isn't derivable from whole rtx's mode, + try simplify_*_operation first with mode from original's operand + and as a fallback wrap CONST_INT into gen_rtx_CONST. */ + scopy = copy; + switch (GET_RTX_CLASS (code)) + { + case RTX_UNARY: + if (CONST_INT_P (XEXP (copy, 0)) + && GET_MODE (XEXP (orig, 0)) != VOIDmode) + { + scopy = simplify_unary_operation (code, mode, XEXP (copy, 0), + GET_MODE (XEXP (orig, 0))); + if (scopy) + return scopy; + } + break; + case RTX_COMM_ARITH: + case RTX_BIN_ARITH: + /* These expressions can derive operand modes from the whole rtx's mode. */ + break; + case RTX_TERNARY: + case RTX_BITFIELD_OPS: + if (CONST_INT_P (XEXP (copy, 0)) + && GET_MODE (XEXP (orig, 0)) != VOIDmode) + { + scopy = simplify_ternary_operation (code, mode, + GET_MODE (XEXP (orig, 0)), + XEXP (copy, 0), XEXP (copy, 1), + XEXP (copy, 2)); + if (scopy) + return scopy; + } + break; + case RTX_COMPARE: + case RTX_COMM_COMPARE: + if (CONST_INT_P (XEXP (copy, 0)) + && GET_MODE (XEXP (copy, 1)) == VOIDmode + && (GET_MODE (XEXP (orig, 0)) != VOIDmode + || GET_MODE (XEXP (orig, 1)) != VOIDmode)) + { + scopy = simplify_relational_operation (code, mode, + (GET_MODE (XEXP (orig, 0)) + != VOIDmode) + ? GET_MODE (XEXP (orig, 0)) + : GET_MODE (XEXP (orig, 1)), + XEXP (copy, 0), + XEXP (copy, 1)); + if (scopy) + return scopy; + } + break; + default: + break; + } scopy = simplify_rtx (copy); if (scopy) return scopy; @@ -1118,13 +1393,14 @@ 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; case CONST_DOUBLE: case CONST_VECTOR: case CONST_INT: + case CONST_FIXED: return x; case POST_INC: @@ -1133,7 +1409,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: @@ -1146,30 +1422,31 @@ cselib_subst_to_values (rtx x) { rtx t = cselib_subst_to_values (XEXP (x, i)); - if (t != XEXP (x, i) && x == copy) - copy = shallow_copy_rtx (x); - - XEXP (copy, i) = t; + if (t != XEXP (x, i)) + { + if (x == copy) + copy = shallow_copy_rtx (x); + XEXP (copy, i) = t; + } } else if (fmt[i] == 'E') { - int j, k; + int j; for (j = 0; j < XVECLEN (x, i); j++) { rtx t = cselib_subst_to_values (XVECEXP (x, i, j)); - if (t != XVECEXP (x, i, j) && XVEC (x, i) == XVEC (copy, i)) + if (t != XVECEXP (x, i, j)) { - if (x == copy) - copy = shallow_copy_rtx (x); - - XVEC (copy, i) = rtvec_alloc (XVECLEN (x, i)); - for (k = 0; k < j; k++) - XVECEXP (copy, i, k) = XVECEXP (x, i, k); + if (XVEC (x, i) == XVEC (copy, i)) + { + if (x == copy) + copy = shallow_copy_rtx (x); + XVEC (copy, i) = shallow_copy_rtvec (XVEC (x, i)); + } + XVECEXP (copy, i, j) = t; } - - XVECEXP (copy, i, j) = t; } } } @@ -1177,6 +1454,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 @@ -1205,10 +1497,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) { @@ -1218,7 +1510,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) { @@ -1231,34 +1523,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 @@ -1345,7 +1637,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++; } } @@ -1355,8 +1647,8 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode) executions of the program. 0 means X can be compared reliably against certain constants or near-constants. */ -static int -cselib_rtx_varies_p (rtx x ATTRIBUTE_UNUSED, int from_alias ATTRIBUTE_UNUSED) +static bool +cselib_rtx_varies_p (const_rtx x ATTRIBUTE_UNUSED, bool from_alias ATTRIBUTE_UNUSED) { /* We actually don't need to verify very hard. This is because if X has actually changed, we invalidate the memory anyway, @@ -1401,7 +1693,7 @@ cselib_invalidate_mem (rtx mem_rtx) } if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS) && ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx), mem_addr, - x, cselib_rtx_varies_p)) + x, NULL_RTX, cselib_rtx_varies_p)) { has_mem = true; num_mems++; @@ -1428,7 +1720,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; @@ -1469,7 +1761,7 @@ cselib_invalidate_rtx (rtx dest) /* A wrapper for cselib_invalidate_rtx to be called via note_stores. */ static void -cselib_invalidate_rtx_note_stores (rtx dest, rtx ignore ATTRIBUTE_UNUSED, +cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) { cselib_invalidate_rtx (dest); @@ -1509,28 +1801,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) @@ -1541,7 +1824,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; @@ -1576,6 +1859,17 @@ cselib_record_sets (rtx insn) } } + if (n_sets == 1 + && MEM_P (sets[0].src) + && !cselib_record_memory + && MEM_READONLY_P (sets[0].src)) + { + rtx note = find_reg_equal_equiv_note (insn); + + if (note && CONSTANT_P (XEXP (note, 0))) + sets[0].src = XEXP (note, 0); + } + /* Look up the values that are read. Do this before invalidating the locations that are written. */ for (i = 0; i < n_sets; i++) @@ -1593,15 +1887,24 @@ cselib_record_sets (rtx insn) { rtx src = sets[i].src; if (cond) - src = gen_rtx_IF_THEN_ELSE (GET_MODE (src), cond, src, dest); + src = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), cond, src, dest); sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1); if (MEM_P (dest)) - sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1); + { + enum machine_mode address_mode + = targetm.addr_space.address_mode (MEM_ADDR_SPACE (dest)); + + sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), + address_mode, 1); + } else sets[i].dest_addr_elt = 0; } } + 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. */ @@ -1648,8 +1951,6 @@ cselib_process_insn (rtx insn) int i; rtx x; - if (find_reg_note (insn, REG_LIBCALL, NULL)) - cselib_current_insn_in_libcall = true; cselib_current_insn = insn; /* Forget everything at a CODE_LABEL, a volatile asm, or a setjmp. */ @@ -1660,16 +1961,12 @@ cselib_process_insn (rtx insn) && GET_CODE (PATTERN (insn)) == ASM_OPERANDS && MEM_VOLATILE_P (PATTERN (insn)))) { - if (find_reg_note (insn, REG_RETVAL, NULL)) - cselib_current_insn_in_libcall = false; - cselib_clear_table (); + cselib_reset_table_with_next_value (next_unknown_value); return; } if (! INSN_P (insn)) { - if (find_reg_note (insn, REG_RETVAL, NULL)) - cselib_current_insn_in_libcall = false; cselib_current_insn = 0; return; } @@ -1682,11 +1979,15 @@ cselib_process_insn (rtx insn) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (call_used_regs[i] || (REG_VALUES (i) && REG_VALUES (i)->elt - && HARD_REGNO_CALL_PART_CLOBBERED (i, + && HARD_REGNO_CALL_PART_CLOBBERED (i, GET_MODE (REG_VALUES (i)->elt->val_rtx)))) cselib_invalidate_regno (i, reg_raw_mode[i]); - if (! CONST_OR_PURE_CALL_P (insn)) + /* Since it is not clear how cselib is going to be used, be + conservative here and treat looping pure or const functions + as if they were regular functions. */ + if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn) + || !(RTL_CONST_OR_PURE_CALL_P (insn))) cselib_invalidate_mem (callmem); } @@ -1708,8 +2009,6 @@ cselib_process_insn (rtx insn) if (GET_CODE (XEXP (x, 0)) == CLOBBER) cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0)); - if (find_reg_note (insn, REG_RETVAL, NULL)) - cselib_current_insn_in_libcall = false; cselib_current_insn = 0; if (n_useless_values > MAX_USELESS_VALUES @@ -1726,11 +2025,11 @@ cselib_process_insn (rtx insn) void cselib_init (bool record_memory) { - elt_list_pool = create_alloc_pool ("elt_list", + elt_list_pool = create_alloc_pool ("elt_list", sizeof (struct elt_list), 10); - elt_loc_list_pool = create_alloc_pool ("elt_loc_list", + elt_loc_list_pool = create_alloc_pool ("elt_loc_list", sizeof (struct elt_loc_list), 10); - cselib_val_pool = create_alloc_pool ("cselib_val_list", + cselib_val_pool = create_alloc_pool ("cselib_val_list", sizeof (cselib_val), 10); value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100); cselib_record_memory = record_memory; @@ -1758,7 +2057,6 @@ cselib_init (bool record_memory) n_used_regs = 0; cselib_hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL); - cselib_current_insn_in_libcall = false; } /* Called when the current user is done with cselib. */ @@ -1780,4 +2078,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"