diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0c2c7fba96c..e178f528dbc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +Thu Sep 24 17:45:55 1998 J"orn Rennecke + + * expr.c (store_constructor): When initializing a field that is smaller + than a word, at the start of a word, try to widen it to a full word. + + * cse.c (cse_insn): When we are about to change a register, + remove any invalid references to it. + + (remove_invalid_subreg_refs): New function. + (mention_regs): Special treatment for SUBREGs. + (insert_regs): Don't strip SUBREG for call to mention_regs. + Check if reg_tick needs to be bumped up before that call. + (lookup_as_function): Try to match known word_mode constants when + looking for a norrower constant. + (canon_hash): Special treatment for SUBREGs. + Thu Sep 24 01:35:34 1998 David S. Miller * config/sparc/sol2-sld-64.h (TRANSFER_FROM_TRAMPOLINE): Define. diff --git a/gcc/cse.c b/gcc/cse.c index 94e80c3ee6d..ddb164d9629 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -626,6 +626,7 @@ static void merge_equiv_classes PROTO((struct table_elt *, static void invalidate PROTO((rtx, enum machine_mode)); static int cse_rtx_varies_p PROTO((rtx)); static void remove_invalid_refs PROTO((int)); +static void remove_invalid_subreg_refs PROTO((int, int, enum machine_mode)); static void rehash_using_reg PROTO((rtx)); static void invalidate_memory PROTO((void)); static void invalidate_for_call PROTO((void)); @@ -980,6 +981,30 @@ mention_regs (x) return 0; } + /* If this is a SUBREG, we don't want to discard other SUBREGs of the same + pseudo if they don't use overlapping words. We handle only pseudos + here for simplicity. */ + if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG + && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER) + { + int i = REGNO (SUBREG_REG (x)); + + if (reg_in_table[i] >= 0 && reg_in_table[i] != reg_tick[i]) + { + /* If reg_tick has been incremented more than once since + reg_in_table was last set, that means that the entire + register has been set before, so discard anything memorized + for the entrire register, including all SUBREG expressions. */ + if (reg_in_table[i] != reg_tick[i] - 1) + remove_invalid_refs (i); + else + remove_invalid_subreg_refs (i, SUBREG_WORD (x), GET_MODE (x)); + } + + reg_in_table[i] = reg_tick[i]; + return 0; + } + /* If X is a comparison or a COMPARE and either operand is a register that does not have a quantity, give it one. This is so that a later call to record_jump_equiv won't cause X to be assigned a different @@ -1077,8 +1102,19 @@ insert_regs (x, classp, modified) else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x)))) { + int regno = REGNO (SUBREG_REG (x)); + insert_regs (SUBREG_REG (x), NULL_PTR, 0); - mention_regs (SUBREG_REG (x)); + /* Mention_regs checks if REG_TICK is exactly one larger than + REG_IN_TABLE to find out if there was only a single preceding + invalidation - for the SUBREG - or another one, which would be + for the full register. Since we don't invalidate the SUBREG + here first, we might have to bump up REG_TICK so that mention_regs + will do the right thing. */ + if (reg_in_table[regno] >= 0 + && reg_tick[regno] == reg_in_table[regno] + 1) + reg_tick++; + mention_regs (x); return 1; } else @@ -1254,6 +1290,17 @@ lookup_as_function (x, code) { register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, GET_MODE (x)); + /* If we are looking for a CONST_INT, the mode doesn't really matter, as + long as we are narrowing. So if we looked in vain for a mode narrower + than word_mode before, look for word_mode now. */ + if (p == 0 && code == CONST_INT + && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode)) + { + x = copy_rtx (x); + PUT_MODE (x, word_mode); + p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, word_mode); + } + if (p == 0) return 0; @@ -1684,6 +1731,37 @@ remove_invalid_refs (regno) remove_from_table (p, i); } } + +/* Likewise for a subreg with subreg_reg WORD and mode MODE. */ +static void +remove_invalid_subreg_refs (regno, word, mode) + int regno; + int word; + enum machine_mode mode; +{ + register int i; + register struct table_elt *p, *next; + int end = word + (GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD; + + for (i = 0; i < NBUCKETS; i++) + for (p = table[i]; p; p = next) + { + rtx exp; + next = p->next_same_hash; + + exp = p->exp; + if (GET_CODE (p->exp) != REG + && (GET_CODE (exp) != SUBREG + || GET_CODE (SUBREG_REG (exp)) != REG + || REGNO (SUBREG_REG (exp)) != regno + || (((SUBREG_WORD (exp) + + (GET_MODE_SIZE (GET_MODE (exp)) - 1) / UNITS_PER_WORD) + >= word) + && SUBREG_WORD (exp) <= end)) + && refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR)) + remove_from_table (p, i); + } +} /* Recompute the hash codes of any valid entries in the hash table that reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG. @@ -1930,6 +2008,20 @@ canon_hash (x, mode) return hash; } + /* We handle SUBREG of a REG specially because the underlying + reg changes its hash value with every value change; we don't + want to have to forget unrelated subregs when one subreg changes. */ + case SUBREG: + { + if (GET_CODE (SUBREG_REG (x)) == REG) + { + hash += (((unsigned) SUBREG << 7) + + REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)); + return hash; + } + break; + } + case CONST_INT: { unsigned HOST_WIDE_INT tem = INTVAL (x); @@ -7409,8 +7501,44 @@ cse_insn (insn, libcall_insn) we are going to hash the SET_DEST values unconditionally. */ for (i = 0; i < n_sets; i++) - if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG) - mention_regs (SET_DEST (sets[i].rtl)); + { + if (sets[i].rtl) + { + rtx x = SET_DEST (sets[i].rtl); + + if (GET_CODE (x) != REG) + mention_regs (x); + else + { + /* We used to rely on all references to a register becoming + inaccessible when a register changes to a new quantity, + since that changes the hash code. However, that is not + safe, since after NBUCKETS new quantities we get a + hash 'collision' of a register with its own invalid + entries. And since SUBREGs have been changed not to + change their hash code with the hash code of the register, + it wouldn't work any longer at all. So we have to check + for any invalid references lying around now. + This code is similar to the REG case in mention_regs, + but it knows that reg_tick has been incremented, and + it leaves reg_in_table as -1 . */ + register int regno = REGNO (x); + register int endregno + = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 + : HARD_REGNO_NREGS (regno, GET_MODE (x))); + int i; + + for (i = regno; i < endregno; i++) + { + if (reg_in_table[i] >= 0) + { + remove_invalid_refs (i); + reg_in_table[i] = -1; + } + } + } + } + } /* We may have just removed some of the src_elt's from the hash table. So replace each one with the current head of the same class. */ diff --git a/gcc/expr.c b/gcc/expr.c index 028f9b21cfd..2c82924d220 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -3819,6 +3819,7 @@ store_constructor (exp, target, cleared) int cleared; { tree type = TREE_TYPE (exp); + rtx exp_size = expr_size (exp); /* We know our target cannot conflict, since safe_from_p has been called. */ #if 0 @@ -3880,6 +3881,7 @@ store_constructor (exp, target, cleared) for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt)) { register tree field = TREE_PURPOSE (elt); + tree value = TREE_VALUE (elt); register enum machine_mode mode; int bitsize; int bitpos = 0; @@ -3951,8 +3953,36 @@ store_constructor (exp, target, cleared) RTX_UNCHANGING_P (to_rtx) = 1; } +#ifdef WORD_REGISTER_OPERATIONS + /* If this initializes a field that is smaller than a word, at the + start of a word, try to widen it to a full word. + This special case allows us to output C++ member function + initializations in a form that the optimizers can understand. */ + if (constant + && GET_CODE (target) == REG + && bitsize < BITS_PER_WORD + && bitpos % BITS_PER_WORD == 0 + && GET_MODE_CLASS (mode) == MODE_INT + && TREE_CODE (value) == INTEGER_CST + && GET_CODE (exp_size) == CONST_INT + && bitpos + BITS_PER_WORD <= INTVAL (exp_size) * BITS_PER_UNIT) + { + tree type = TREE_TYPE (value); + if (TYPE_PRECISION (type) < BITS_PER_WORD) + { + type = type_for_size (BITS_PER_WORD, TREE_UNSIGNED (type)); + value = convert (type, value); + } + if (BYTES_BIG_ENDIAN) + value + = fold (build (LSHIFT_EXPR, type, value, + build_int_2 (BITS_PER_WORD - bitsize, 0))); + bitsize = BITS_PER_WORD; + mode = word_mode; + } +#endif store_constructor_field (to_rtx, bitsize, bitpos, - mode, TREE_VALUE (elt), type, cleared); + mode, value, type, cleared); } } else if (TREE_CODE (type) == ARRAY_TYPE)