expr.c (store_constructor): When initializing a field that is smaller than a word...

* 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.

From-SVN: r22567
This commit is contained in:
J"orn Rennecke 1998-09-24 09:59:41 +00:00 committed by Joern Rennecke
parent c5a951a851
commit 34c7390981
3 changed files with 178 additions and 4 deletions

View File

@ -1,3 +1,19 @@
Thu Sep 24 17:45:55 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* 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 <davem@pierdol.cobaltmicro.com>
* config/sparc/sol2-sld-64.h (TRANSFER_FROM_TRAMPOLINE): Define.

134
gcc/cse.c
View File

@ -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. */

View File

@ -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)