static struct table_elt *insert (rtx, struct table_elt *, unsigned,
machine_mode);
static void merge_equiv_classes (struct table_elt *, struct table_elt *);
+static void invalidate_reg (rtx, bool);
static void invalidate (rtx, machine_mode);
static void remove_invalid_refs (unsigned int);
static void remove_invalid_subreg_refs (unsigned int, poly_uint64,
}
return false;
}
-\f
+
+/* Remove from the hash table, or mark as invalid, all expressions whose
+ values could be altered by storing in register X.
+
+ CLOBBER_HIGH is set if X was part of a CLOBBER_HIGH expression. */
+
+static void
+invalidate_reg (rtx x, bool clobber_high)
+{
+ gcc_assert (GET_CODE (x) == REG);
+
+ /* If X is a register, dependencies on its contents are recorded
+ through the qty number mechanism. Just change the qty number of
+ the register, mark it as invalid for expressions that refer to it,
+ and remove it itself. */
+ unsigned int regno = REGNO (x);
+ unsigned int hash = HASH (x, GET_MODE (x));
+
+ /* Remove REGNO from any quantity list it might be on and indicate
+ that its value might have changed. If it is a pseudo, remove its
+ entry from the hash table.
+
+ For a hard register, we do the first two actions above for any
+ additional hard registers corresponding to X. Then, if any of these
+ registers are in the table, we must remove any REG entries that
+ overlap these registers. */
+
+ delete_reg_equiv (regno);
+ REG_TICK (regno)++;
+ SUBREG_TICKED (regno) = -1;
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ gcc_assert (!clobber_high);
+ remove_pseudo_from_table (x, hash);
+ }
+ else
+ {
+ HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+ unsigned int endregno = END_REGNO (x);
+ unsigned int rn;
+ struct table_elt *p, *next;
+
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+ for (rn = regno + 1; rn < endregno; rn++)
+ {
+ in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
+ delete_reg_equiv (rn);
+ REG_TICK (rn)++;
+ SUBREG_TICKED (rn) = -1;
+ }
+
+ if (in_table)
+ for (hash = 0; hash < HASH_SIZE; hash++)
+ for (p = table[hash]; p; p = next)
+ {
+ next = p->next_same_hash;
+
+ if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ if (clobber_high)
+ {
+ if (reg_is_clobbered_by_clobber_high (p->exp, x))
+ remove_from_table (p, hash);
+ }
+ else
+ {
+ unsigned int tregno = REGNO (p->exp);
+ unsigned int tendregno = END_REGNO (p->exp);
+ if (tendregno > regno && tregno < endregno)
+ remove_from_table (p, hash);
+ }
+ }
+ }
+}
+
/* Remove from the hash table, or mark as invalid, all expressions whose
values could be altered by storing in X. X is a register, a subreg, or
a memory reference with nonvarying address (because, when a memory
switch (GET_CODE (x))
{
case REG:
- {
- /* If X is a register, dependencies on its contents are recorded
- through the qty number mechanism. Just change the qty number of
- the register, mark it as invalid for expressions that refer to it,
- and remove it itself. */
- unsigned int regno = REGNO (x);
- unsigned int hash = HASH (x, GET_MODE (x));
-
- /* Remove REGNO from any quantity list it might be on and indicate
- that its value might have changed. If it is a pseudo, remove its
- entry from the hash table.
-
- For a hard register, we do the first two actions above for any
- additional hard registers corresponding to X. Then, if any of these
- registers are in the table, we must remove any REG entries that
- overlap these registers. */
-
- delete_reg_equiv (regno);
- REG_TICK (regno)++;
- SUBREG_TICKED (regno) = -1;
-
- if (regno >= FIRST_PSEUDO_REGISTER)
- remove_pseudo_from_table (x, hash);
- else
- {
- HOST_WIDE_INT in_table
- = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
- unsigned int endregno = END_REGNO (x);
- unsigned int tregno, tendregno, rn;
- struct table_elt *p, *next;
-
- CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
-
- for (rn = regno + 1; rn < endregno; rn++)
- {
- in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
- CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
- delete_reg_equiv (rn);
- REG_TICK (rn)++;
- SUBREG_TICKED (rn) = -1;
- }
-
- if (in_table)
- for (hash = 0; hash < HASH_SIZE; hash++)
- for (p = table[hash]; p; p = next)
- {
- next = p->next_same_hash;
-
- if (!REG_P (p->exp)
- || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
- continue;
-
- tregno = REGNO (p->exp);
- tendregno = END_REGNO (p->exp);
- if (tendregno > regno && tregno < endregno)
- remove_from_table (p, hash);
- }
- }
- }
+ invalidate_reg (x, false);
return;
case SUBREG:
if (MEM_P (XEXP (x, 0)))
canon_reg (XEXP (x, 0), insn);
}
+ else if (GET_CODE (x) == CLOBBER_HIGH)
+ gcc_assert (REG_P (XEXP (x, 0)));
else if (GET_CODE (x) == USE
&& ! (REG_P (XEXP (x, 0))
&& REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
if (MEM_P (XEXP (y, 0)))
canon_reg (XEXP (y, 0), insn);
}
+ else if (GET_CODE (y) == CLOBBER_HIGH)
+ gcc_assert (REG_P (XEXP (y, 0)));
else if (GET_CODE (y) == USE
&& ! (REG_P (XEXP (y, 0))
&& REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
+ if (GET_CODE (x) == CLOBBER_HIGH)
+ {
+ rtx ref = XEXP (x, 0);
+ gcc_assert (REG_P (ref));
+ invalidate_reg (ref, true);
+ }
else if (GET_CODE (x) == PARALLEL)
{
int i;
|| GET_CODE (ref) == ZERO_EXTRACT)
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
+ else if (GET_CODE (y) == CLOBBER_HIGH)
+ {
+ rtx ref = XEXP (y, 0);
+ gcc_assert (REG_P (ref));
+ invalidate_reg (ref, true);
+ }
}
}
}
if (CALL_P (insn))
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
- if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
- invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+ {
+ rtx temx = XEXP (tem, 0);
+ if (GET_CODE (temx) == CLOBBER)
+ invalidate (SET_DEST (temx), VOIDmode);
+ else if (GET_CODE (temx) == CLOBBER_HIGH)
+ {
+ rtx temref = XEXP (temx, 0);
+ gcc_assert (REG_P (temref));
+ invalidate_reg (temref, true);
+ }
+ }
}
/* Ensure we invalidate the destination register of a CALL insn.
|| GET_CODE (clobbered) == ZERO_EXTRACT)
invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
}
+ else if (GET_CODE (y) == CLOBBER_HIGH)
+ {
+ rtx ref = XEXP (y, 0);
+ gcc_assert (REG_P (ref));
+ invalidate_reg (ref, true);
+ }
else if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL)
invalidate (SET_DEST (y), VOIDmode);
}
count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
return;
+ case CLOBBER_HIGH:
+ gcc_assert (REG_P ((XEXP (x, 0))));
+ return;
+
case SET:
/* Unless we are setting a REG, count everything in SET_DEST. */
if (!REG_P (SET_DEST (x)))
|| (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE)
/* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)),
involving registers in the address. */
- || GET_CODE (XEXP (x, 0)) == CLOBBER)
+ || GET_CODE (XEXP (x, 0)) == CLOBBER
+ || GET_CODE (XEXP (x, 0)) == CLOBBER_HIGH)
count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
if (set_live_p (elt, insn, counts))
return true;
}
- else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+ else if (GET_CODE (elt) != CLOBBER
+ && GET_CODE (elt) != CLOBBER_HIGH
+ && GET_CODE (elt) != USE)
return true;
}
return false;
static cselib_val *new_cselib_val (unsigned int, 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, machine_mode);
+static void cselib_invalidate_regno (unsigned int, machine_mode,
+ const_rtx = NULL);
static void cselib_invalidate_mem (rtx);
static void cselib_record_set (rtx, cselib_val *, cselib_val *);
static void cselib_record_sets (rtx_insn *);
/* SCRATCH must be shared because they represent distinct values. */
return orig;
case CLOBBER:
+ case CLOBBER_HIGH:
if (REG_P (XEXP (orig, 0)) && HARD_REGISTER_NUM_P (REGNO (XEXP (orig, 0))))
return orig;
break;
invalidating call clobbered registers across a call. */
static void
-cselib_invalidate_regno (unsigned int regno, machine_mode mode)
+cselib_invalidate_regno (unsigned int regno, machine_mode mode,
+ const_rtx setter)
{
unsigned int endregno;
unsigned int i;
i = regno - max_value_regs;
endregno = end_hard_regno (mode, regno);
+
+ if (setter && GET_CODE (setter) == CLOBBER_HIGH)
+ gcc_assert (endregno == regno + 1);
}
else
{
continue;
}
+ /* Ignore if clobber high and the register isn't clobbered. */
+ if (setter && GET_CODE (setter) == CLOBBER_HIGH)
+ {
+ gcc_assert (endregno == regno + 1);
+ const_rtx x = XEXP (setter, 0);
+ if (!reg_is_clobbered_by_clobber_high (i, GET_MODE (v->val_rtx),
+ x))
+ {
+ l = &(*l)->next;
+ continue;
+ }
+ }
+
/* We have an overlap. */
if (*l == REG_VALUES (i))
{
*vp = &dummy_val;
}
-/* Invalidate DEST, which is being assigned to or clobbered. */
+/* Invalidate DEST, which is being assigned to or clobbered by SETTER. */
void
-cselib_invalidate_rtx (rtx dest)
+cselib_invalidate_rtx (rtx dest, const_rtx setter)
{
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
dest = XEXP (dest, 0);
if (REG_P (dest))
- cselib_invalidate_regno (REGNO (dest), GET_MODE (dest));
+ cselib_invalidate_regno (REGNO (dest), GET_MODE (dest), setter);
else if (MEM_P (dest))
cselib_invalidate_mem (dest);
}
/* A wrapper for cselib_invalidate_rtx to be called via note_stores. */
static void
-cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx setter,
void *data ATTRIBUTE_UNUSED)
{
- cselib_invalidate_rtx (dest);
+ cselib_invalidate_rtx (dest, setter);
}
/* Record the result of a SET instruction. DEST is being set; the source
if (CALL_P (insn))
{
for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
- if (GET_CODE (XEXP (x, 0)) == CLOBBER)
- cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
- /* Flush evertything on setjmp. */
+ {
+ gcc_assert (GET_CODE (XEXP (x, 0)) != CLOBBER_HIGH);
+ if (GET_CODE (XEXP (x, 0)) == CLOBBER)
+ cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
+ }
+ /* Flush everything on setjmp. */
if (cselib_preserve_constants
&& find_reg_note (insn, REG_SETJMP, NULL))
{