flow.c aren't completely updated:
- reg_live_length is not updated
+ - reg_n_refs is not adjusted in the rare case when a register is
+ no longer required in a computation
+ - there are extremely rare cases (see distribute_regnotes) when a
+ REG_DEAD note is lost
- a LOG_LINKS entry that refers to an insn with multiple SETs may be
removed because there is no way to know which register it was
linking
static int reg_dead_at_p (rtx, rtx);
static void move_deaths (rtx, rtx, int, rtx, rtx *);
static int reg_bitfield_target_p (rtx, rtx);
-static void distribute_notes (rtx, rtx, rtx, rtx);
+static void distribute_notes (rtx, rtx, rtx, rtx, rtx, rtx);
static void distribute_links (rtx);
static void mark_used_regs_combine (rtx);
static int insn_cuid (rtx);
rtx temp = XEXP (links, 0);
if ((set = single_set (temp)) != 0
&& (note = find_reg_equal_equiv_note (temp)) != 0
- && GET_CODE (XEXP (note, 0)) != EXPR_LIST
+ && (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST
/* Avoid using a register that may already been marked
dead by an earlier instruction. */
- && ! unmentioned_reg_p (XEXP (note, 0), SET_SRC (set)))
+ && ! unmentioned_reg_p (note, SET_SRC (set))
+ && (GET_MODE (note) == VOIDmode
+ ? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set)))
+ : GET_MODE (SET_DEST (set)) == GET_MODE (note)))
{
/* Temporarily replace the set's source with the
contents of the REG_EQUAL note. The insn will
be deleted or recognized by try_combine. */
rtx orig = SET_SRC (set);
- SET_SRC (set) = XEXP (note, 0);
+ SET_SRC (set) = note;
next = try_combine (insn, temp, NULL_RTX,
&new_direct_jump_p);
if (next)
distribute_links (gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX));
}
+/* Return TRUE if combine can reuse reg X in mode MODE.
+ ADDED_SETS is nonzero if the original set is still required. */
+static bool
+can_change_dest_mode (rtx x, int added_sets, enum machine_mode mode)
+{
+ unsigned int regno;
+
+ if (!REG_P(x))
+ return false;
+
+ regno = REGNO (x);
+ /* Allow hard registers if the new mode is legal, and occupies no more
+ registers than the old mode. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ return (HARD_REGNO_MODE_OK (regno, mode)
+ && (hard_regno_nregs[regno][GET_MODE (x)]
+ >= hard_regno_nregs[regno][mode]));
+
+ /* Or a pseudo that is only used once. */
+ return (REG_N_SETS (regno) == 1 && !added_sets
+ && !REG_USERVAR_P (x));
+}
+
/* Try to combine the insns I1 and I2 into I3.
Here I1 and I2 appear earlier than I3.
I1 can be zero; then we combine just I2 into I3.
rtx i2pat;
/* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */
int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
+ int i2dest_killed = 0, i1dest_killed = 0;
int i1_feeds_i3 = 0;
/* Notes that must be added to REG_NOTES in I3 and I2. */
rtx new_i3_notes, new_i2_notes;
added_sets_2 = added_sets_1 = 0;
i2dest = SET_SRC (PATTERN (i3));
+ i2dest_killed = dead_or_set_p (i2, i2dest);
/* Replace the dest in I2 with our dest and make the resulting
insn the new pattern for I3. Then skip to where we
subst_low_cuid = INSN_CUID (i2);
added_sets_2 = added_sets_1 = 0;
i2dest = SET_DEST (temp);
+ i2dest_killed = dead_or_set_p (i2, i2dest);
SUBST (SET_SRC (temp),
immed_double_const (lo, hi, GET_MODE (SET_DEST (temp))));
i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src);
i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src);
i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src);
+ i2dest_killed = dead_or_set_p (i2, i2dest);
+ i1dest_killed = i1 && dead_or_set_p (i1, i1dest);
/* See if I1 directly feeds into I3. It does if I1DEST is not used
in I2SRC. */
i2src, const0_rtx))
!= GET_MODE (SET_DEST (newpat))))
{
- unsigned int regno = REGNO (SET_DEST (newpat));
- rtx new_dest = gen_rtx_REG (compare_mode, regno);
-
- if (regno < FIRST_PSEUDO_REGISTER
- || (REG_N_SETS (regno) == 1 && ! added_sets_2
- && ! REG_USERVAR_P (SET_DEST (newpat))))
+ if (can_change_dest_mode(SET_DEST (newpat), added_sets_2,
+ compare_mode))
{
+ unsigned int regno = REGNO (SET_DEST (newpat));
+ rtx new_dest = gen_rtx_REG (compare_mode, regno);
+
if (regno >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[regno], new_dest);
if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
{
+ enum machine_mode new_mode = GET_MODE (SET_DEST (newpat));
/* If I2DEST is a hard register or the only use of a pseudo,
we can change its mode. */
- if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest)
- && GET_MODE (SET_DEST (newpat)) != VOIDmode
- && REG_P (i2dest)
- && (REGNO (i2dest) < FIRST_PSEUDO_REGISTER
- || (REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
- && ! REG_USERVAR_P (i2dest))))
+ if (new_mode != GET_MODE (i2dest)
+ && new_mode != VOIDmode
+ && can_change_dest_mode (i2dest, added_sets_2, new_mode))
ni2dest = gen_rtx_REG (GET_MODE (SET_DEST (newpat)),
REGNO (i2dest));
isn't valid for it, or change the number of registers. */
&& (GET_MODE (*split) == GET_MODE (i2dest)
|| GET_MODE (*split) == VOIDmode
- || (REGNO (i2dest) < FIRST_PSEUDO_REGISTER
- && HARD_REGNO_MODE_OK (REGNO (i2dest), GET_MODE (*split))
- && (hard_regno_nregs[REGNO (i2dest)][GET_MODE (i2dest)]
- == hard_regno_nregs[REGNO (i2dest)][GET_MODE (*split)]))
- || (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER
- && REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
- && ! REG_USERVAR_P (i2dest)))
+ || can_change_dest_mode (i2dest, added_sets_2,
+ GET_MODE (*split)))
&& (next_real_insn (i2) == i3
|| ! use_crosses_set_p (*split, INSN_CUID (i2)))
/* We can't overwrite I2DEST if its value is still used by
REG_N_DEATHS (REGNO (XEXP (note, 0)))++;
distribute_notes (new_other_notes, undobuf.other_insn,
- undobuf.other_insn, NULL_RTX);
+ undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX);
}
#ifdef HAVE_cc0
/* If I2 is the CC0 setter and I3 is the CC0 user then check whether
rtx i3links, i2links, i1links = 0;
rtx midnotes = 0;
unsigned int regno;
+ /* Compute which registers we expect to eliminate. newi2pat may be setting
+ either i3dest or i2dest, so we must check it. Also, i1dest may be the
+ same as i3dest, in which case newi2pat may be setting i1dest. */
+ rtx elim_i2 = ((newi2pat && reg_set_p (i2dest, newi2pat))
+ || i2dest_in_i2src || i2dest_in_i1src
+ || !i2dest_killed
+ ? 0 : i2dest);
+ rtx elim_i1 = (i1 == 0 || i1dest_in_i1src
+ || (newi2pat && reg_set_p (i1dest, newi2pat))
+ || !i1dest_killed
+ ? 0 : i1dest);
/* Get the old REG_NOTES and LOG_LINKS from all our insns and
clear them. */
/* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */
if (i3notes)
- distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX);
+ distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
if (i2notes)
- distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX);
+ distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
if (i1notes)
- distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX);
+ distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
if (midnotes)
- distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
+ distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
/* Distribute any notes added to I2 or I3 by recog_for_combine. We
know these are REG_UNUSED and want them to go to the desired insn,
if (REG_P (XEXP (temp, 0)))
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
- distribute_notes (new_i2_notes, i2, i2, NULL_RTX);
+ distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX);
}
if (new_i3_notes)
if (REG_P (XEXP (temp, 0)))
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
- distribute_notes (new_i3_notes, i3, i3, NULL_RTX);
+ distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX);
}
/* If I3DEST was used in I3SRC, it really died in I3. We may need to
if (newi2pat && reg_set_p (i3dest_killed, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
NULL_RTX),
- NULL_RTX, i2, NULL_RTX);
+ NULL_RTX, i2, NULL_RTX, elim_i2, elim_i1);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
NULL_RTX),
- NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
}
if (i2dest_in_i2src)
if (newi2pat && reg_set_p (i2dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
- NULL_RTX, i2, NULL_RTX);
+ NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
- NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ NULL_RTX, NULL_RTX);
}
if (i1dest_in_i1src)
if (newi2pat && reg_set_p (i1dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
- NULL_RTX, i2, NULL_RTX);
+ NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
- NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ NULL_RTX, NULL_RTX);
}
distribute_links (i3links);
which case we can safely change its mode. */
if (compare_mode != GET_MODE (dest))
{
- unsigned int regno = REGNO (dest);
- rtx new_dest = gen_rtx_REG (compare_mode, regno);
-
- if (regno < FIRST_PSEUDO_REGISTER
- || (REG_N_SETS (regno) == 1 && ! REG_USERVAR_P (dest)))
+ if (can_change_dest_mode (dest, 0, compare_mode))
{
+ unsigned int regno = REGNO (dest);
+ rtx new_dest = gen_rtx_REG (compare_mode, regno);
+
if (regno >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[regno], new_dest);
/* Avoid creating invalid subregs, for example when
simplifying (x>>32)&255. */
- if (final_word >= GET_MODE_SIZE (inner_mode))
+ if (!validate_subreg (tmode, inner_mode, inner, final_word))
return NULL_RTX;
new = gen_rtx_SUBREG (tmode, inner, final_word);
&& GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode))
offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode);
- /* If this is a constant position, we can move to the desired byte. */
+ /* If this is a constant position, we can move to the desired byte.
+ Be careful not to go beyond the original object and maintain the
+ natural alignment of the memory. */
if (pos_rtx == 0)
{
- offset += pos / BITS_PER_UNIT;
- pos %= GET_MODE_BITSIZE (wanted_inner_mode);
+ enum machine_mode bfmode = smallest_mode_for_size (len, MODE_INT);
+ offset += (pos / GET_MODE_BITSIZE (bfmode)) * GET_MODE_SIZE (bfmode);
+ pos %= GET_MODE_BITSIZE (bfmode);
}
if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
break;
case SUBREG:
- /* Non-paradoxical SUBREGs distributes over all operations, provided
- the inner modes and byte offsets are the same, this is an extraction
- of a low-order part, we don't convert an fp operation to int or
- vice versa, and we would not be converting a single-word
- operation into a multi-word operation. The latter test is not
- required, but it prevents generating unneeded multi-word operations.
- Some of the previous tests are redundant given the latter test, but
- are retained because they are required for correctness.
+ /* Non-paradoxical SUBREGs distributes over all operations,
+ provided the inner modes and byte offsets are the same, this
+ is an extraction of a low-order part, we don't convert an fp
+ operation to int or vice versa, this is not a vector mode,
+ and we would not be converting a single-word operation into a
+ multi-word operation. The latter test is not required, but
+ it prevents generating unneeded multi-word operations. Some
+ of the previous tests are redundant given the latter test,
+ but are retained because they are required for correctness.
We produce the result slightly differently in this case. */
!= GET_MODE_CLASS (GET_MODE (SUBREG_REG (lhs))))
|| (GET_MODE_SIZE (GET_MODE (lhs))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))))
+ || VECTOR_MODE_P (GET_MODE (lhs))
|| GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD)
return x;
/* (A - C1) always sign-extends, like C2. */
&& num_sign_bit_copies (a, inner_mode)
> (unsigned int) (GET_MODE_BITSIZE (inner_mode)
- - mode_width - 1)))
+ - (mode_width - 1))))
{
op0 = SUBREG_REG (op0);
continue;
as appropriate. I3 and I2 are the insns resulting from the combination
insns including FROM (I2 may be zero).
+ ELIM_I2 and ELIM_I1 are either zero or registers that we know will
+ not need REG_DEAD notes because they are being substituted for. This
+ saves searching in the most common cases.
+
Each note in the list is either ignored or placed on some insns, depending
on the type of note. */
static void
-distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2)
+distribute_notes (rtx notes, rtx from_insn, rtx i3, rtx i2, rtx elim_i2,
+ rtx elim_i1)
{
rtx note, next_note;
rtx tem;
&& reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
place = i2;
+ if (place == 0
+ && (rtx_equal_p (XEXP (note, 0), elim_i2)
+ || rtx_equal_p (XEXP (note, 0), elim_i1)))
+ break;
+
if (place == 0)
{
basic_block bb = this_basic_block;
PATTERN (tem) = pc_rtx;
REG_NOTES (tem) = NULL;
- distribute_notes (old_notes, tem, tem, NULL_RTX);
+ distribute_notes (old_notes, tem, tem, NULL_RTX,
+ NULL_RTX, NULL_RTX);
distribute_links (LOG_LINKS (tem));
SET_INSN_DELETED (tem);
REG_NOTES (cc0_setter) = NULL;
distribute_notes (old_notes, cc0_setter,
- cc0_setter, NULL_RTX);
+ cc0_setter, NULL_RTX,
+ NULL_RTX, NULL_RTX);
distribute_links (LOG_LINKS (cc0_setter));
SET_INSN_DELETED (cc0_setter);
= gen_rtx_EXPR_LIST (REG_DEAD, piece, NULL_RTX);
distribute_notes (new_note, place, place,
- NULL_RTX);
+ NULL_RTX, NULL_RTX, NULL_RTX);
}
else if (! refers_to_regno_p (i, i + 1,
PATTERN (place), 0)