X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcselib.c;h=945a4a118de7830a0914ab32ef51ed898f1674f3;hb=f04755301385186ff70270c700a7f68cc1e01968;hp=b57125bf9fa890b8aaac741392f77edb30f2755b;hpb=10689255b2456e896b39f50c4b77ffb13210e326;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cselib.c b/gcc/cselib.c index b57125bf9fa..945a4a118de 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 Free Software Foundation, Inc. + 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008 + 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, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -41,6 +41,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "cselib.h" #include "params.h" #include "alloc-pool.h" +#include "target.h" static bool cselib_record_memory; static int entry_and_rtx_equal_p (const void *, const void *); @@ -50,12 +51,11 @@ static struct elt_loc_list *new_elt_loc_list (struct elt_loc_list *, rtx); static void unchain_one_value (cselib_val *); static void unchain_one_elt_list (struct elt_list **); static void unchain_one_elt_loc_list (struct elt_loc_list **); -static void clear_table (void); 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, enum machine_mode, int); +static unsigned int cselib_hash_rtx (rtx, int); static cselib_val *new_cselib_val (unsigned int, enum machine_mode); static void add_mem_for_addr (cselib_val *, cselib_val *, rtx); static cselib_val *cselib_lookup_mem (rtx, int); @@ -74,12 +74,11 @@ static void cselib_record_sets (rtx); the locations of the entries with the rtx we are looking up. */ /* A table that enables us to look up elts by their value. */ -static htab_t hash_table; +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; @@ -101,16 +100,16 @@ static int n_useless_values; which the register was set; if the mode is unknown or the value is no longer valid in that mode, ELT will be NULL for the first element. */ -struct elt_list **reg_values; -unsigned int reg_values_size; +static struct elt_list **reg_values; +static unsigned int reg_values_size; #define REG_VALUES(i) reg_values[i] /* The largest number of hard regs used by any entry added to the - REG_VALUES table. Cleared on each clear_table() invocation. */ + REG_VALUES table. Cleared on each cselib_clear_table() invocation. */ static unsigned int max_value_regs; /* Here the set of indices I with REG_VALUES(I) != 0 is saved. This is used - in clear_table() for fast emptying. */ + in cselib_clear_table() for fast emptying. */ static unsigned int *used_regs; static unsigned int n_used_regs; @@ -131,6 +130,10 @@ static cselib_val dummy_val; each time memory is invalidated. */ static cselib_val *first_containing_mem = &dummy_val; 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 *); /* Allocate a struct elt_list and fill in its two elements with the @@ -140,7 +143,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; @@ -153,11 +156,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; } @@ -200,8 +202,8 @@ unchain_one_value (cselib_val *v) initialization. If CLEAR_ALL isn't set, then only clear the entries which are known to have been used. */ -static void -clear_table (void) +void +cselib_clear_table (void) { unsigned int i; @@ -212,7 +214,7 @@ clear_table (void) n_used_regs = 0; - htab_empty (hash_table); + htab_empty (cselib_hash_table); n_useless_values = 0; @@ -230,19 +232,20 @@ static int entry_and_rtx_equal_p (const void *entry, const void *x_arg) { struct elt_loc_list *l; - const cselib_val *v = (const cselib_val *) entry; - rtx x = (rtx) x_arg; + const cselib_val *const v = (const cselib_val *) entry; + 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 (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_FIXED && (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE)); - if (mode != GET_MODE (v->u.val_rtx)) + 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 + || GET_CODE (XEXP (x, 0)) == CONST_FIXED || GET_CODE (XEXP (x, 0)) == CONST_DOUBLE)) x = XEXP (x, 0); @@ -262,7 +265,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; } @@ -272,9 +275,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; @@ -331,8 +334,11 @@ discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED) if (v->locs == 0) { - CSELIB_VAL_PTR (v->u.val_rtx) = NULL; - htab_clear_slot (hash_table, x); + if (cselib_discard_hook) + cselib_discard_hook (v); + + CSELIB_VAL_PTR (v->val_rtx) = NULL; + htab_clear_slot (cselib_hash_table, x); unchain_one_value (v); n_useless_values--; } @@ -352,7 +358,7 @@ remove_useless_values (void) do { values_became_useless = 0; - htab_traverse (hash_table, discard_useless_locs, 0); + htab_traverse (cselib_hash_table, discard_useless_locs, 0); } while (values_became_useless); @@ -367,7 +373,7 @@ remove_useless_values (void) } *p = &dummy_val; - htab_traverse (hash_table, discard_useless_values, 0); + htab_traverse (cselib_hash_table, discard_useless_values, 0); gcc_assert (!n_useless_values); } @@ -378,7 +384,7 @@ remove_useless_values (void) 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); @@ -387,7 +393,7 @@ cselib_reg_set_mode (rtx x) || REG_VALUES (REGNO (x))->elt == NULL) return VOIDmode; - return GET_MODE (REG_VALUES (REGNO (x))->elt->u.val_rtx); + return GET_MODE (REG_VALUES (REGNO (x))->elt->val_rtx); } /* Return nonzero if we can prove that X and Y contain the same value, taking @@ -405,7 +411,7 @@ rtx_equal_for_cselib_p (rtx x, rtx y) cselib_val *e = cselib_lookup (x, GET_MODE (x), 0); if (e) - x = e->u.val_rtx; + x = e->val_rtx; } if (REG_P (y) || MEM_P (y)) @@ -413,7 +419,7 @@ rtx_equal_for_cselib_p (rtx x, rtx y) cselib_val *e = cselib_lookup (y, GET_MODE (y), 0); if (e) - y = e->u.val_rtx; + y = e->val_rtx; } if (x == y) @@ -462,9 +468,19 @@ rtx_equal_for_cselib_p (rtx x, rtx y) if (GET_CODE (x) != GET_CODE (y) || GET_MODE (x) != GET_MODE (y)) return 0; - /* This won't be handled correctly by the code below. */ - if (GET_CODE (x) == LABEL_REF) - return XEXP (x, 0) == XEXP (y, 0); + /* These won't be handled correctly by the code below. */ + switch (GET_CODE (x)) + { + case CONST_DOUBLE: + case CONST_FIXED: + return 0; + + case LABEL_REF: + return XEXP (x, 0) == XEXP (y, 0); + + default: + break; + } code = GET_CODE (x); fmt = GET_RTX_FORMAT (code); @@ -500,6 +516,11 @@ rtx_equal_for_cselib_p (rtx x, rtx y) break; case 'e': + if (i == 1 + && targetm.commutative_p (x, UNKNOWN) + && rtx_equal_for_cselib_p (XEXP (x, 1), XEXP (y, 0)) + && rtx_equal_for_cselib_p (XEXP (x, 0), XEXP (y, 1))) + return 1; if (! rtx_equal_for_cselib_p (XEXP (x, i), XEXP (y, i))) return 0; break; @@ -534,7 +555,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 (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_FIXED && (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode)) return x; gcc_assert (mode != VOIDmode); @@ -547,11 +568,22 @@ wrap_constant (enum machine_mode mode, rtx x) Possible reasons for return 0 are: the object is volatile, or we couldn't find a register or memory location in the table and CREATE is zero. If CREATE is nonzero, table elts are created for regs and mem. - MODE is used in hashing for CONST_INTs only; - otherwise the mode of X is used. */ + N.B. this hash function returns the same hash value for RTXes that + differ only in the order of operands, thus it is suitable for comparisons + that take commutativity into account. + If we wanted to also support associative rules, we'd have to use a different + strategy to avoid returning spurious 0, e.g. return ~(~0U >> 1) . + We used to have a MODE argument for hashing for CONST_INTs, but that + didn't make sense, since it caused spurious hash differences between + (set (reg:SI 1) (const_int)) + (plus:SI (reg:SI 2) (reg:SI 1)) + and + (plus:SI (reg:SI 2) (const_int)) + If the mode is important in any context, it must be checked specifically + in a comparison anyway, since relying on hash differences is unsafe. */ static unsigned int -cselib_hash_rtx (rtx x, enum machine_mode mode, int create) +cselib_hash_rtx (rtx x, int create) { cselib_val *e; int i, j; @@ -573,7 +605,7 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) return e->value; case CONST_INT: - hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + INTVAL (x); + hash += ((unsigned) CONST_INT << 7) + INTVAL (x); return hash ? hash : (unsigned int) CONST_INT; case CONST_DOUBLE: @@ -587,6 +619,11 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, 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; @@ -597,7 +634,7 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) for (i = 0; i < units; ++i) { elt = CONST_VECTOR_ELT (x, i); - hash += cselib_hash_rtx (elt, GET_MODE (elt), 0); + hash += cselib_hash_rtx (elt, 0); } return hash; @@ -605,14 +642,28 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) /* Assume there is only one rtx object for any given label. */ case LABEL_REF: - hash - += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0); + /* We don't hash on the address of the CODE_LABEL to avoid bootstrap + differences and differences between each stage's debugging dumps. */ + hash += (((unsigned int) LABEL_REF << 7) + + CODE_LABEL_NUMBER (XEXP (x, 0))); return hash ? hash : (unsigned int) LABEL_REF; case SYMBOL_REF: - hash - += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0); - return hash ? hash : (unsigned int) SYMBOL_REF; + { + /* Don't hash on the symbol's address to avoid bootstrap differences. + Different hash values may cause expressions to be recorded in + different orders and thus different registers to be used in the + final assembler. This also avoids differences in the dump files + between various stages. */ + unsigned int h = 0; + const unsigned char *p = (const unsigned char *) XSTR (x, 0); + + while (*p) + h += (h << 7) + *p++; /* ??? revisit */ + + hash += ((unsigned int) SYMBOL_REF << 7) + h; + return hash ? hash : (unsigned int) SYMBOL_REF; + } case PRE_DEC: case PRE_INC: @@ -645,7 +696,7 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) case 'e': { rtx tem = XEXP (x, i); - unsigned int tem_hash = cselib_hash_rtx (tem, 0, create); + unsigned int tem_hash = cselib_hash_rtx (tem, create); if (tem_hash == 0) return 0; @@ -657,7 +708,7 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) for (j = 0; j < XVECLEN (x, i); j++) { unsigned int tem_hash - = cselib_hash_rtx (XVECEXP (x, i, j), 0, create); + = cselib_hash_rtx (XVECEXP (x, i, j), create); if (tem_hash == 0) return 0; @@ -699,7 +750,7 @@ cselib_hash_rtx (rtx x, enum machine_mode mode, int create) static inline cselib_val * new_cselib_val (unsigned int value, enum machine_mode mode) { - cselib_val *e = pool_alloc (cselib_val_pool); + cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool); gcc_assert (value); @@ -709,11 +760,11 @@ 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->u.val_rtx = pool_alloc (value_pool); - memset (e->u.val_rtx, 0, RTX_HDR_SIZE); - PUT_CODE (e->u.val_rtx, VALUE); - PUT_MODE (e->u.val_rtx, mode); - CSELIB_VAL_PTR (e->u.val_rtx) = e; + 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); + CSELIB_VAL_PTR (e->val_rtx) = e; e->addr_list = 0; e->locs = 0; e->next_containing_mem = 0; @@ -738,7 +789,7 @@ add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x) addr_elt->addr_list = new_elt_list (addr_elt->addr_list, mem_elt); mem_elt->locs = new_elt_loc_list (mem_elt->locs, - replace_equiv_address_nv (x, addr_elt->u.val_rtx)); + replace_equiv_address_nv (x, addr_elt->val_rtx)); if (mem_elt->next_containing_mem == NULL) { mem_elt->next_containing_mem = first_containing_mem; @@ -770,7 +821,7 @@ cselib_lookup_mem (rtx x, int create) /* Find a value that describes a value of our mode at that address. */ for (l = addr->addr_list; l; l = l->next) - if (GET_MODE (l->elt->u.val_rtx) == mode) + if (GET_MODE (l->elt->val_rtx) == mode) return l->elt; if (! create) @@ -778,12 +829,342 @@ cselib_lookup_mem (rtx x, int create) mem_elt = new_cselib_val (++next_unknown_value, mode); add_mem_for_addr (addr, mem_elt, x); - slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x), + slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x), mem_elt->value, INSERT); *slot = mem_elt; return mem_elt; } +/* Search thru the possible substitutions in P. We prefer a non reg + substitution because this allows us to expand the tree further. If + we find, just a reg, take the lowest regno. There may be several + 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) +{ + rtx reg_result = NULL; + unsigned int regno = UINT_MAX; + struct elt_loc_list *p_in = p; + + for (; p; p = p -> next) + { + /* 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))) + { + 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 + && CSELIB_VAL_PTR (p->loc)->locs == p_in) + continue; + else if (!REG_P (p->loc)) + { + rtx result, note; + if (dump_file) + { + print_inline_rtx (dump_file, p->loc, 0); + fprintf (dump_file, "\n"); + } + 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 (p->loc, regs_active, max_depth - 1); + if (result) + return result; + } + + } + + if (regno != UINT_MAX) + { + rtx result; + if (dump_file) + fprintf (dump_file, "r%d\n", regno); + + result = cselib_expand_value_rtx (reg_result, regs_active, max_depth - 1); + if (result) + return result; + } + + if (dump_file) + { + if (reg_result) + { + print_inline_rtx (dump_file, reg_result, 0); + fprintf (dump_file, "\n"); + } + else + fprintf (dump_file, "NULL\n"); + } + return reg_result; +} + + +/* Forward substitute and expand an expression out to its roots. + 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. + + 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 + one of the operands has been overwritten between the current insn + and the beginning of the basic block. For instance x has no + expansion in: + + r1 <- r1 + 3 + x <- r1 + 8 + + REGS_ACTIVE is a scratch bitmap that should be clear when passing in. + It is clear on return. */ + +rtx +cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth) +{ + rtx copy, scopy; + int i, j; + RTX_CODE code; + const char *format_ptr; + enum machine_mode mode; + + code = GET_CODE (orig); + + /* For the context of dse, if we end up expand into a huge tree, we + will not have a useful address, so we might as well just give up + quickly. */ + if (max_depth <= 0) + return NULL; + + switch (code) + { + case REG: + { + struct elt_list *l = REG_VALUES (REGNO (orig)); + + if (l && l->elt == NULL) + l = l->next; + for (; l; l = l->next) + if (GET_MODE (l->elt->val_rtx) == GET_MODE (orig)) + { + 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 + it) is that we will not substitute the + STACK_POINTER_REGNUM, FRAME_POINTER or the + HARD_FRAME_POINTER. + + 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 + STACK_POINTER_REGNUM substitution, then dse will + think that parameter pushing also goes dead which is + wrong. If you allow the FRAME_POINTER or the + HARD_FRAME_POINTER then you lose the opportunity to + make the frame assumptions. */ + if (regno == STACK_POINTER_REGNUM + || regno == FRAME_POINTER_REGNUM + || regno == HARD_FRAME_POINTER_REGNUM) + return orig; + + bitmap_set_bit (regs_active, regno); + + if (dump_file) + fprintf (dump_file, "expanding: r%d into: ", regno); + + result = expand_loc (l->elt->locs, regs_active, max_depth); + bitmap_clear_bit (regs_active, regno); + + if (result) + return result; + else + return orig; + } + } + + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case SCRATCH: + /* SCRATCH must be shared because they represent distinct values. */ + return orig; + case CLOBBER: + if (REG_P (XEXP (orig, 0)) && HARD_REGISTER_NUM_P (REGNO (XEXP (orig, 0)))) + return orig; + break; + + case CONST: + if (shared_const_p (orig)) + return orig; + break; + + case SUBREG: + { + rtx subreg = cselib_expand_value_rtx (SUBREG_REG (orig), regs_active, + 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 shallow_copy_rtx (orig); + return scopy; + } + + case VALUE: + if (dump_file) + fprintf (dump_file, "expanding value %s into: ", + GET_MODE_NAME (GET_MODE (orig))); + + return expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth); + + default: + break; + } + + /* Copy the various flags, fields, and other information. We assume + that all fields need copying, and then clear the fields that should + not be copied. That is the sensible default behavior, and forces + us to explicitly document why we are *not* copying a flag. */ + copy = shallow_copy_rtx (orig); + + format_ptr = GET_RTX_FORMAT (code); + + 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); + if (!result) + return NULL; + XEXP (copy, i) = result; + } + break; + + case 'E': + case 'V': + if (XVEC (orig, i) != NULL) + { + 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); + if (!result) + return NULL; + XVECEXP (copy, i, j) = result; + } + } + break; + + case 't': + case 'w': + case 'i': + case 's': + case 'S': + case 'T': + case 'u': + case 'B': + case '0': + /* These are left unchanged. */ + break; + + default: + 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; + } + if (scopy == NULL_RTX) + { + XEXP (copy, 0) + = gen_rtx_CONST (GET_MODE (XEXP (orig, 0)), XEXP (copy, 0)); + if (dump_file) + 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; + return copy; +} + /* Walk rtx X and replace all occurrences of REG and MEM subexpressions with VALUE expressions. This way, it becomes independent of changes to registers and memory. @@ -807,8 +1188,8 @@ cselib_subst_to_values (rtx x) if (l && l->elt == NULL) l = l->next; for (; l; l = l->next) - if (GET_MODE (l->elt->u.val_rtx) == GET_MODE (x)) - return l->elt->u.val_rtx; + if (GET_MODE (l->elt->val_rtx) == GET_MODE (x)) + return l->elt->val_rtx; gcc_unreachable (); @@ -820,11 +1201,12 @@ cselib_subst_to_values (rtx x) match any other. */ e = new_cselib_val (++next_unknown_value, GET_MODE (x)); } - return e->u.val_rtx; + return e->val_rtx; case CONST_DOUBLE: case CONST_VECTOR: case CONST_INT: + case CONST_FIXED: return x; case POST_INC: @@ -834,7 +1216,7 @@ cselib_subst_to_values (rtx x) case POST_MODIFY: case PRE_MODIFY: e = new_cselib_val (++next_unknown_value, GET_MODE (x)); - return e->u.val_rtx; + return e->val_rtx; default: break; @@ -904,7 +1286,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) if (l && l->elt == NULL) l = l->next; for (; l; l = l->next) - if (mode == GET_MODE (l->elt->u.val_rtx)) + if (mode == GET_MODE (l->elt->val_rtx)) return l->elt; if (! create) @@ -929,7 +1311,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL); } REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e); - slot = htab_find_slot_with_hash (hash_table, x, e->value, INSERT); + slot = htab_find_slot_with_hash (cselib_hash_table, x, e->value, INSERT); *slot = e; return e; } @@ -937,12 +1319,12 @@ cselib_lookup (rtx x, enum machine_mode mode, int create) if (MEM_P (x)) return cselib_lookup_mem (x, create); - hashval = cselib_hash_rtx (x, mode, create); + hashval = cselib_hash_rtx (x, create); /* Can't even create if hashing is not possible. */ if (! hashval) return 0; - slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x), + slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x), hashval, create ? INSERT : NO_INSERT); if (slot == 0) return 0; @@ -990,7 +1372,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode) else i = regno - max_value_regs; - endregno = regno + hard_regno_nregs[regno][mode]; + endregno = end_hard_regno (mode, regno); } else { @@ -1011,7 +1393,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode) unsigned int this_last = i; if (i < FIRST_PSEUDO_REGISTER && v != NULL) - this_last += hard_regno_nregs[i][GET_MODE (v->u.val_rtx)] - 1; + this_last = end_hard_regno (GET_MODE (v->val_rtx), i) - 1; if (this_last < regno || v == NULL) { @@ -1055,8 +1437,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, @@ -1148,8 +1530,9 @@ cselib_invalidate_mem (rtx mem_rtx) void cselib_invalidate_rtx (rtx dest) { - while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG) + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) dest = XEXP (dest, 0); if (REG_P (dest)) @@ -1168,7 +1551,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); @@ -1275,6 +1658,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++) @@ -1292,7 +1686,7 @@ 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); @@ -1347,8 +1741,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. */ @@ -1359,16 +1751,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; - clear_table (); + cselib_clear_table (); return; } if (! INSN_P (insn)) { - if (find_reg_note (insn, REG_RETVAL, NULL)) - cselib_current_insn_in_libcall = false; cselib_current_insn = 0; return; } @@ -1379,10 +1767,17 @@ cselib_process_insn (rtx insn) if (CALL_P (insn)) { for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i]) + if (call_used_regs[i] + || (REG_VALUES (i) && REG_VALUES (i)->elt + && 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); } @@ -1404,11 +1799,13 @@ 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) + if (n_useless_values > MAX_USELESS_VALUES + /* remove_useless_values is linear in the hash table size. Avoid + quadratic behavior for very large hashtables with very few + useless elements. */ + && (unsigned int)n_useless_values > cselib_hash_table->n_elements / 4) remove_useless_values (); } @@ -1424,12 +1821,13 @@ cselib_init (bool record_memory) sizeof (struct elt_loc_list), 10); cselib_val_pool = create_alloc_pool ("cselib_val_list", sizeof (cselib_val), 10); - value_pool = create_alloc_pool ("value", - RTX_SIZE (VALUE), 100); + value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100); cselib_record_memory = record_memory; - /* This is only created once. */ + + /* (mem:BLK (scratch)) is a special mechanism to conflict with everything, + see canon_true_dependence. This is only created once. */ if (! callmem) - callmem = gen_rtx_MEM (BLKmode, const0_rtx); + callmem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); cselib_nregs = max_reg_num (); @@ -1443,12 +1841,12 @@ cselib_init (bool record_memory) /* Some space for newly emit instructions so we don't end up reallocating in between passes. */ reg_values_size = cselib_nregs + (63 + cselib_nregs) / 16; - reg_values = xcalloc (reg_values_size, sizeof (reg_values)); + reg_values = XCNEWVEC (struct elt_list *, reg_values_size); } - used_regs = xmalloc (sizeof (*used_regs) * cselib_nregs); + used_regs = XNEWVEC (unsigned int, cselib_nregs); n_used_regs = 0; - hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL); - cselib_current_insn_in_libcall = false; + cselib_hash_table = htab_create (31, get_value_hash, + entry_and_rtx_equal_p, NULL); } /* Called when the current user is done with cselib. */ @@ -1456,15 +1854,16 @@ cselib_init (bool record_memory) void cselib_finish (void) { + cselib_discard_hook = NULL; free_alloc_pool (elt_list_pool); free_alloc_pool (elt_loc_list_pool); free_alloc_pool (cselib_val_pool); free_alloc_pool (value_pool); - clear_table (); - htab_delete (hash_table); + cselib_clear_table (); + htab_delete (cselib_hash_table); free (used_regs); used_regs = 0; - hash_table = 0; + cselib_hash_table = 0; n_useless_values = 0; next_unknown_value = 0; }