/* Optimize by combining instructions for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GCC.
- 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
+ - there are extremely rare cases (see distribute_notes) 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
unsigned HOST_WIDE_INT last_set_nonzero_bits;
char last_set_sign_bit_copies;
- ENUM_BITFIELD(machine_mode) last_set_mode : 8;
+ ENUM_BITFIELD(machine_mode) last_set_mode : 8;
/* Set nonzero if references to register n in expressions should not be
used. last_set_invalid is set nonzero when this register is being
unsigned char sign_bit_copies;
unsigned HOST_WIDE_INT nonzero_bits;
+
+ /* Record the value of the label_tick when the last truncation
+ happened. The field truncated_to_mode is only valid if
+ truncation_label == label_tick. */
+
+ int truncation_label;
+
+ /* Record the last truncation seen for this register. If truncation
+ is not a nop to this mode we might be able to save an explicit
+ truncation if we know that value already contains a truncated
+ value. */
+
+ ENUM_BITFIELD(machine_mode) truncated_to_mode : 8;
};
static struct reg_stat *reg_stat;
\f
/* Record one modification to rtl structure
- to be undone by storing old_contents into *where.
- is_int is 1 if the contents are an int. */
+ to be undone by storing old_contents into *where. */
struct undo
{
struct undo *next;
- int is_int;
- union {rtx r; int i;} old_contents;
- union {rtx *r; int *i;} where;
+ enum { UNDO_RTX, UNDO_INT, UNDO_MODE } kind;
+ union { rtx r; int i; enum machine_mode m; } old_contents;
+ union { rtx *r; int *i; } where;
};
/* Record a bunch of changes to be undone, up to MAX_UNDO of them.
static rtx make_field_assignment (rtx);
static rtx apply_distributive_law (rtx);
static rtx distribute_and_simplify_rtx (rtx, int);
+static rtx simplify_and_const_int_1 (enum machine_mode, rtx,
+ unsigned HOST_WIDE_INT);
static rtx simplify_and_const_int (rtx, enum machine_mode, rtx,
unsigned HOST_WIDE_INT);
static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code,
HOST_WIDE_INT, enum machine_mode, int *);
-static rtx simplify_shift_const (rtx, enum rtx_code, enum machine_mode, rtx,
+static rtx simplify_shift_const_1 (enum rtx_code, enum machine_mode, rtx, int);
+static rtx simplify_shift_const (rtx, enum rtx_code, enum machine_mode, rtx,
int);
static int recog_for_combine (rtx *, rtx, rtx *);
static rtx gen_lowpart_for_combine (enum machine_mode, rtx);
static enum rtx_code simplify_comparison (enum rtx_code, rtx *, rtx *);
static void update_table_tick (rtx);
static void record_value_for_reg (rtx, rtx, rtx);
-static void check_promoted_subreg (rtx, rtx);
+static void check_conversions (rtx, rtx);
static void record_dead_and_set_regs_1 (rtx, rtx, void *);
static void record_dead_and_set_regs (rtx);
static int get_last_value_validate (rtx *, rtx, int, int);
static void record_promoted_value (rtx, rtx);
static int unmentioned_reg_p_1 (rtx *, void *);
static bool unmentioned_reg_p (rtx, rtx);
+static void record_truncated_value (rtx);
+static bool reg_truncated_to_mode (enum machine_mode, rtx);
+static rtx gen_lowpart_or_truncate (enum machine_mode, rtx);
\f
/* It is not safe to use ordinary gen_lowpart in combine.
else
buf = xmalloc (sizeof (struct undo));
- buf->is_int = 0;
+ buf->kind = UNDO_RTX;
buf->where.r = into;
buf->old_contents.r = oldval;
*into = newval;
else
buf = xmalloc (sizeof (struct undo));
- buf->is_int = 1;
+ buf->kind = UNDO_INT;
buf->where.i = into;
buf->old_contents.i = oldval;
*into = newval;
}
#define SUBST_INT(INTO, NEWVAL) do_SUBST_INT(&(INTO), (NEWVAL))
+
+/* Similar to SUBST, but just substitute the mode. This is used when
+ changing the mode of a pseudo-register, so that any other
+ references to the entry in the regno_reg_rtx array will change as
+ well. */
+
+static void
+do_SUBST_MODE (rtx *into, enum machine_mode newval)
+{
+ struct undo *buf;
+ enum machine_mode oldval = GET_MODE (*into);
+
+ if (oldval == newval)
+ return;
+
+ if (undobuf.frees)
+ buf = undobuf.frees, undobuf.frees = buf->next;
+ else
+ buf = xmalloc (sizeof (struct undo));
+
+ buf->kind = UNDO_MODE;
+ buf->where.r = into;
+ buf->old_contents.m = oldval;
+ PUT_MODE (*into, newval);
+
+ buf->next = undobuf.undos, undobuf.undos = buf;
+}
+
+#define SUBST_MODE(INTO, NEWVAL) do_SUBST_MODE(&(INTO), (NEWVAL))
\f
/* Subroutine of try_combine. Determine whether the combine replacement
patterns NEWPAT and NEWI2PAT are cheaper according to insn_rtx_cost
Return nonzero if the combiner has turned an indirect jump
instruction into a direct jump. */
-int
+static int
combine_instructions (rtx f, unsigned int nregs)
{
rtx insn, next;
{
/* See if we know about function return values before this
insn based upon SUBREG flags. */
- check_promoted_subreg (insn, PATTERN (insn));
+ check_conversions (insn, PATTERN (insn));
/* Try this insn with each insn it links back to. */
&& !REG_USERVAR_P (x));
}
+
+/* Check whether X, the destination of a set, refers to part of
+ the register specified by REG. */
+
+static bool
+reg_subword_p (rtx x, rtx reg)
+{
+ /* Check that reg is an integer mode register. */
+ if (!REG_P (reg) || GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT)
+ return false;
+
+ if (GET_CODE (x) == STRICT_LOW_PART
+ || GET_CODE (x) == ZERO_EXTRACT)
+ x = XEXP (x, 0);
+
+ return GET_CODE (x) == SUBREG
+ && SUBREG_REG (x) == reg
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT;
+}
+
+
/* 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.
}
}
- /* If I2 is setting a double-word pseudo to a constant and I3 is setting
- one of those words to another constant, merge them by making a new
+ /* If I2 is setting a pseudo to a constant and I3 is setting some
+ sub-part of it to another constant, merge them by making a new
constant. */
if (i1 == 0
&& (temp = single_set (i2)) != 0
&& (GET_CODE (SET_SRC (temp)) == CONST_INT
|| GET_CODE (SET_SRC (temp)) == CONST_DOUBLE)
- && REG_P (SET_DEST (temp))
- && GET_MODE_CLASS (GET_MODE (SET_DEST (temp))) == MODE_INT
- && GET_MODE_SIZE (GET_MODE (SET_DEST (temp))) == 2 * UNITS_PER_WORD
&& GET_CODE (PATTERN (i3)) == SET
- && GET_CODE (SET_DEST (PATTERN (i3))) == SUBREG
- && SUBREG_REG (SET_DEST (PATTERN (i3))) == SET_DEST (temp)
- && GET_MODE_CLASS (GET_MODE (SET_DEST (PATTERN (i3)))) == MODE_INT
- && GET_MODE_SIZE (GET_MODE (SET_DEST (PATTERN (i3)))) == UNITS_PER_WORD
- && GET_CODE (SET_SRC (PATTERN (i3))) == CONST_INT)
+ && (GET_CODE (SET_SRC (PATTERN (i3))) == CONST_INT
+ || GET_CODE (SET_SRC (PATTERN (i3))) == CONST_DOUBLE)
+ && reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp)))
{
- HOST_WIDE_INT lo, hi;
+ rtx dest = SET_DEST (PATTERN (i3));
+ int offset = -1;
+ int width = 0;
- if (GET_CODE (SET_SRC (temp)) == CONST_INT)
- lo = INTVAL (SET_SRC (temp)), hi = lo < 0 ? -1 : 0;
- else
+ if (GET_CODE (dest) == STRICT_LOW_PART)
{
- lo = CONST_DOUBLE_LOW (SET_SRC (temp));
- hi = CONST_DOUBLE_HIGH (SET_SRC (temp));
+ width = GET_MODE_BITSIZE (GET_MODE (XEXP (dest, 0)));
+ offset = 0;
}
-
- if (subreg_lowpart_p (SET_DEST (PATTERN (i3))))
+ else if (GET_CODE (dest) == ZERO_EXTRACT)
{
- /* We don't handle the case of the target word being wider
- than a host wide int. */
- gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD);
+ if (GET_CODE (XEXP (dest, 1)) == CONST_INT
+ && GET_CODE (XEXP (dest, 2)) == CONST_INT)
+ {
+ width = INTVAL (XEXP (dest, 1));
+ offset = INTVAL (XEXP (dest, 2));
- lo &= ~(UWIDE_SHIFT_LEFT_BY_BITS_PER_WORD (1) - 1);
- lo |= (INTVAL (SET_SRC (PATTERN (i3)))
- & (UWIDE_SHIFT_LEFT_BY_BITS_PER_WORD (1) - 1));
+ if (BITS_BIG_ENDIAN)
+ offset = GET_MODE_BITSIZE (GET_MODE (XEXP (dest, 0)))
+ - width - offset;
+ }
}
- else if (HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
- hi = INTVAL (SET_SRC (PATTERN (i3)));
- else if (HOST_BITS_PER_WIDE_INT >= 2 * BITS_PER_WORD)
+ else if (subreg_lowpart_p (dest))
{
- int sign = -(int) ((unsigned HOST_WIDE_INT) lo
- >> (HOST_BITS_PER_WIDE_INT - 1));
-
- lo &= ~ (UWIDE_SHIFT_LEFT_BY_BITS_PER_WORD
- (UWIDE_SHIFT_LEFT_BY_BITS_PER_WORD (1) - 1));
- lo |= (UWIDE_SHIFT_LEFT_BY_BITS_PER_WORD
- (INTVAL (SET_SRC (PATTERN (i3)))));
- if (hi == sign)
- hi = lo < 0 ? -1 : 0;
+ width = GET_MODE_BITSIZE (GET_MODE (dest));
+ offset = 0;
+ }
+ /* ??? Preserve the original logic to handle setting the high word
+ of double-word pseudos, where inner is half the size of outer
+ but not the lowpart. This could be generalized by handling
+ SUBREG_BYTE, WORDS_BIG_ENDIAN and BYTES_BIG_ENDIAN ourselves.
+ Unfortunately this logic is tricky to get right and probably
+ not worth the effort. */
+ else if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (temp)))
+ == 2 * GET_MODE_BITSIZE (GET_MODE (dest)))
+ {
+ width = GET_MODE_BITSIZE (GET_MODE (dest));
+ offset = width;
}
- else
- /* We don't handle the case of the higher word not fitting
- entirely in either hi or lo. */
- gcc_unreachable ();
- combine_merges++;
- subst_insn = i3;
- 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);
+ if (offset >= 0)
+ {
+ HOST_WIDE_INT mhi, ohi, ihi;
+ HOST_WIDE_INT mlo, olo, ilo;
+ rtx inner = SET_SRC (PATTERN (i3));
+ rtx outer = SET_SRC (temp);
- SUBST (SET_SRC (temp),
- immed_double_const (lo, hi, GET_MODE (SET_DEST (temp))));
+ if (GET_CODE (outer) == CONST_INT)
+ {
+ olo = INTVAL (outer);
+ ohi = olo < 0 ? -1 : 0;
+ }
+ else
+ {
+ olo = CONST_DOUBLE_LOW (outer);
+ ohi = CONST_DOUBLE_HIGH (outer);
+ }
- newpat = PATTERN (i2);
- goto validate_replacement;
+ if (GET_CODE (inner) == CONST_INT)
+ {
+ ilo = INTVAL (inner);
+ ihi = ilo < 0 ? -1 : 0;
+ }
+ else
+ {
+ ilo = CONST_DOUBLE_LOW (inner);
+ ihi = CONST_DOUBLE_HIGH (inner);
+ }
+
+ if (width < HOST_BITS_PER_WIDE_INT)
+ {
+ mlo = ((unsigned HOST_WIDE_INT) 1 << width) - 1;
+ mhi = 0;
+ }
+ else if (width < HOST_BITS_PER_WIDE_INT * 2)
+ {
+ mhi = ((unsigned HOST_WIDE_INT) 1
+ << (width - HOST_BITS_PER_WIDE_INT)) - 1;
+ mlo = -1;
+ }
+ else
+ {
+ mlo = -1;
+ mhi = -1;
+ }
+
+ ilo &= mlo;
+ ihi &= mhi;
+
+ if (offset >= HOST_BITS_PER_WIDE_INT)
+ {
+ mhi = mlo << (offset - HOST_BITS_PER_WIDE_INT);
+ mlo = 0;
+ ihi = ilo << (offset - HOST_BITS_PER_WIDE_INT);
+ ilo = 0;
+ }
+ else if (offset > 0)
+ {
+ mhi = (mhi << offset) | ((unsigned HOST_WIDE_INT) mlo
+ >> (HOST_BITS_PER_WIDE_INT - offset));
+ mlo = mlo << offset;
+ ihi = (ihi << offset) | ((unsigned HOST_WIDE_INT) ilo
+ >> (HOST_BITS_PER_WIDE_INT - offset));
+ ilo = ilo << offset;
+ }
+
+ olo = (olo & ~mlo) | ilo;
+ ohi = (ohi & ~mhi) | ihi;
+
+ combine_merges++;
+ subst_insn = i3;
+ 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 (olo, ohi, GET_MODE (SET_DEST (temp))));
+
+ newpat = PATTERN (i2);
+ goto validate_replacement;
+ }
}
#ifndef HAVE_cc0
compare_mode))
{
unsigned int regno = REGNO (SET_DEST (newpat));
- rtx new_dest = gen_rtx_REG (compare_mode, regno);
+ rtx new_dest;
- if (regno >= FIRST_PSEUDO_REGISTER)
- SUBST (regno_reg_rtx[regno], new_dest);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ new_dest = gen_rtx_REG (compare_mode, regno);
+ else
+ {
+ SUBST_MODE (regno_reg_rtx[regno], compare_mode);
+ new_dest = regno_reg_rtx[regno];
+ }
SUBST (SET_DEST (newpat), new_dest);
SUBST (XEXP (*cc_use, 0), new_dest);
&& asm_noperands (newpat) < 0)
{
rtx m_split, *split;
- rtx ni2dest = i2dest;
/* See if the MD file can split NEWPAT. If it can't, see if letting it
use I2DEST as a scratch register will help. In the latter case,
possible to try that as a scratch reg. This would require adding
more code to make it work though. */
- if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
+ if (m_split == 0 && ! reg_overlap_mentioned_p (i2dest, 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 (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));
+ enum machine_mode new_mode = GET_MODE (SET_DEST (newpat));
+ /* First try to split using the original register as a
+ scratch register. */
m_split = split_insns (gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2, newpat,
gen_rtx_CLOBBER (VOIDmode,
- ni2dest))),
+ i2dest))),
i3);
- /* If the split with the mode-changed register didn't work, try
- the original register. */
- if (! m_split && ni2dest != i2dest)
+
+ /* If that didn't work, try changing the mode of I2DEST if
+ we can. */
+ if (m_split == 0
+ && new_mode != GET_MODE (i2dest)
+ && new_mode != VOIDmode
+ && can_change_dest_mode (i2dest, added_sets_2, new_mode))
{
- ni2dest = i2dest;
+ enum machine_mode old_mode = GET_MODE (i2dest);
+ rtx ni2dest;
+
+ if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER)
+ ni2dest = gen_rtx_REG (new_mode, REGNO (i2dest));
+ else
+ {
+ SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], new_mode);
+ ni2dest = regno_reg_rtx[REGNO (i2dest)];
+ }
+
m_split = split_insns (gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2, newpat,
gen_rtx_CLOBBER (VOIDmode,
- i2dest))),
+ ni2dest))),
i3);
+
+ if (m_split == 0
+ && REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
+ {
+ struct undo *buf;
+
+ PUT_MODE (regno_reg_rtx[REGNO (i2dest)], old_mode);
+ buf = undobuf.undos;
+ undobuf.undos = buf->next;
+ buf->next = undobuf.frees;
+ undobuf.frees = buf;
+ }
}
}
i3set = single_set (NEXT_INSN (m_split));
i2set = single_set (m_split);
- /* In case we changed the mode of I2DEST, replace it in the
- pseudo-register table here. We can't do it above in case this
- code doesn't get executed and we do a split the other way. */
-
- if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
- SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
-
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* If I2 or I3 has multiple SETs, we won't know how to track
rtx newdest = i2dest;
enum rtx_code split_code = GET_CODE (*split);
enum machine_mode split_mode = GET_MODE (*split);
+ bool subst_done = false;
+ newi2pat = NULL_RTX;
/* Get NEWDEST as a register in the proper mode. We have already
validated that we can do this. */
if (GET_MODE (i2dest) != split_mode && split_mode != VOIDmode)
{
- newdest = gen_rtx_REG (split_mode, REGNO (i2dest));
-
- if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
- SUBST (regno_reg_rtx[REGNO (i2dest)], newdest);
+ if (REGNO (i2dest) < FIRST_PSEUDO_REGISTER)
+ newdest = gen_rtx_REG (split_mode, REGNO (i2dest));
+ else
+ {
+ SUBST_MODE (regno_reg_rtx[REGNO (i2dest)], split_mode);
+ newdest = regno_reg_rtx[REGNO (i2dest)];
+ }
}
/* If *SPLIT is a (mult FOO (const_int pow2)), convert it to
}
#endif
- newi2pat = gen_rtx_SET (VOIDmode, newdest, *split);
- SUBST (*split, newdest);
+ /* Attempt to split binary operators using arithmetic identities. */
+ if (BINARY_P (SET_SRC (newpat))
+ && split_mode == GET_MODE (SET_SRC (newpat))
+ && ! side_effects_p (SET_SRC (newpat)))
+ {
+ rtx setsrc = SET_SRC (newpat);
+ enum machine_mode mode = GET_MODE (setsrc);
+ enum rtx_code code = GET_CODE (setsrc);
+ rtx src_op0 = XEXP (setsrc, 0);
+ rtx src_op1 = XEXP (setsrc, 1);
+
+ /* Split "X = Y op Y" as "Z = Y; X = Z op Z". */
+ if (rtx_equal_p (src_op0, src_op1))
+ {
+ newi2pat = gen_rtx_SET (VOIDmode, newdest, src_op0);
+ SUBST (XEXP (setsrc, 0), newdest);
+ SUBST (XEXP (setsrc, 1), newdest);
+ subst_done = true;
+ }
+ /* Split "((P op Q) op R) op S" where op is PLUS or MULT. */
+ else if ((code == PLUS || code == MULT)
+ && GET_CODE (src_op0) == code
+ && GET_CODE (XEXP (src_op0, 0)) == code
+ && (INTEGRAL_MODE_P (mode)
+ || (FLOAT_MODE_P (mode)
+ && flag_unsafe_math_optimizations)))
+ {
+ rtx p = XEXP (XEXP (src_op0, 0), 0);
+ rtx q = XEXP (XEXP (src_op0, 0), 1);
+ rtx r = XEXP (src_op0, 1);
+ rtx s = src_op1;
+
+ /* Split both "((X op Y) op X) op Y" and
+ "((X op Y) op Y) op X" as "T op T" where T is
+ "X op Y". */
+ if ((rtx_equal_p (p,r) && rtx_equal_p (q,s))
+ || (rtx_equal_p (p,s) && rtx_equal_p (q,r)))
+ {
+ newi2pat = gen_rtx_SET (VOIDmode, newdest,
+ XEXP (src_op0, 0));
+ SUBST (XEXP (setsrc, 0), newdest);
+ SUBST (XEXP (setsrc, 1), newdest);
+ subst_done = true;
+ }
+ /* Split "((X op X) op Y) op Y)" as "T op T" where
+ T is "X op Y". */
+ else if (rtx_equal_p (p,q) && rtx_equal_p (r,s))
+ {
+ rtx tmp = simplify_gen_binary (code, mode, p, r);
+ newi2pat = gen_rtx_SET (VOIDmode, newdest, tmp);
+ SUBST (XEXP (setsrc, 0), newdest);
+ SUBST (XEXP (setsrc, 1), newdest);
+ subst_done = true;
+ }
+ }
+ }
+
+ if (!subst_done)
+ {
+ newi2pat = gen_rtx_SET (VOIDmode, newdest, *split);
+ SUBST (*split, newdest);
+ }
+
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* recog_for_combine might have added CLOBBERs to newi2pat.
for (undo = undobuf.undos; undo; undo = next)
{
next = undo->next;
- if (undo->is_int)
- *undo->where.i = undo->old_contents.i;
- else
- *undo->where.r = undo->old_contents.r;
+ switch (undo->kind)
+ {
+ case UNDO_RTX:
+ *undo->where.r = undo->old_contents.r;
+ break;
+ case UNDO_INT:
+ *undo->where.i = undo->old_contents.i;
+ break;
+ case UNDO_MODE:
+ PUT_MODE (*undo->where.r, undo->old_contents.m);
+ break;
+ default:
+ gcc_unreachable ();
+ }
undo->next = undobuf.frees;
undobuf.frees = undo;
if (GET_CODE (temp) == ASHIFTRT
&& GET_CODE (XEXP (temp, 1)) == CONST_INT
&& INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1)
- return simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0),
+ return simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (temp, 0),
INTVAL (XEXP (temp, 1)));
/* If X has only a single bit that might be nonzero, say, bit I, convert
if (can_change_dest_mode (dest, 0, compare_mode))
{
unsigned int regno = REGNO (dest);
- rtx new_dest = gen_rtx_REG (compare_mode, regno);
+ rtx new_dest;
- if (regno >= FIRST_PSEUDO_REGISTER)
- SUBST (regno_reg_rtx[regno], new_dest);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ new_dest = gen_rtx_REG (compare_mode, regno);
+ else
+ {
+ SUBST_MODE (regno_reg_rtx[regno], compare_mode);
+ new_dest = regno_reg_rtx[regno];
+ }
SUBST (SET_DEST (x), new_dest);
SUBST (XEXP (*cc_use, 0), new_dest);
&& ! (spans_byte && inner_mode != tmode)
&& ((pos_rtx == 0 && (pos % BITS_PER_WORD) == 0
&& !MEM_P (inner)
+ && (inner_mode == tmode
+ || !REG_P (inner)
+ || TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (tmode),
+ GET_MODE_BITSIZE (inner_mode))
+ || reg_truncated_to_mode (tmode, inner))
&& (! in_dest
|| (REG_P (inner)
&& have_insn_for (STRICT_LOW_PART, tmode))))
return x;
}
+/* Return X converted to MODE. If the value is already truncated to
+ MODE we can just return a subreg even though in the general case we
+ would need an explicit truncation. */
+
+static rtx
+gen_lowpart_or_truncate (enum machine_mode mode, rtx x)
+{
+ if (GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (mode)
+ || TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (GET_MODE (x)))
+ || (REG_P (x) && reg_truncated_to_mode (mode, x)))
+ return gen_lowpart (mode, x);
+ else
+ return gen_rtx_TRUNCATE (mode, x);
+}
+
/* See if X can be simplified knowing that we will only refer to it in
MODE and will only refer to those bits that are nonzero in MASK.
If other bits are being computed or if masking operations are done
/* For most binary operations, just propagate into the operation and
change the mode if we have an operation of that mode. */
- op0 = gen_lowpart (op_mode,
- force_to_mode (XEXP (x, 0), mode, mask,
- next_select));
- op1 = gen_lowpart (op_mode,
- force_to_mode (XEXP (x, 1), mode, mask,
+ op0 = gen_lowpart_or_truncate (op_mode,
+ force_to_mode (XEXP (x, 0), mode, mask,
+ next_select));
+ op1 = gen_lowpart_or_truncate (op_mode,
+ force_to_mode (XEXP (x, 1), mode, mask,
next_select));
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
else
mask = fuller_mask;
- op0 = gen_lowpart (op_mode,
- force_to_mode (XEXP (x, 0), op_mode,
- mask, next_select));
+ op0 = gen_lowpart_or_truncate (op_mode,
+ force_to_mode (XEXP (x, 0), op_mode,
+ mask, next_select));
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
x = simplify_gen_binary (code, op_mode, op0, XEXP (x, 1));
if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
{
- int i = -1;
+ int i;
/* If the considered data is wider than HOST_WIDE_INT, we can't
represent a mask for all its bits in a single scalar.
nonzero >>= INTVAL (XEXP (x, 1));
}
- if ((mask & ~nonzero) == 0
- || (i = exact_log2 (mask)) >= 0)
+ if ((mask & ~nonzero) == 0)
+ {
+ x = simplify_shift_const (NULL_RTX, LSHIFTRT, GET_MODE (x),
+ XEXP (x, 0), INTVAL (XEXP (x, 1)));
+ if (GET_CODE (x) != ASHIFTRT)
+ return force_to_mode (x, mode, mask, next_select);
+ }
+
+ else if ((i = exact_log2 (mask)) >= 0)
{
x = simplify_shift_const
- (x, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
- i < 0 ? INTVAL (XEXP (x, 1))
- : GET_MODE_BITSIZE (GET_MODE (x)) - 1 - i);
+ (NULL_RTX, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
+ GET_MODE_BITSIZE (GET_MODE (x)) - 1 - i);
if (GET_CODE (x) != ASHIFTRT)
return force_to_mode (x, mode, mask, next_select);
mask = fuller_mask;
unop:
- op0 = gen_lowpart (op_mode,
- force_to_mode (XEXP (x, 0), mode, mask,
- next_select));
+ op0 = gen_lowpart_or_truncate (op_mode,
+ force_to_mode (XEXP (x, 0), mode, mask,
+ next_select));
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
x = simplify_gen_unary (code, op_mode, op0, op_mode);
break;
written in a narrower mode. We play it safe and do not do so. */
SUBST (XEXP (x, 1),
- gen_lowpart (GET_MODE (x), force_to_mode (XEXP (x, 1), mode,
- mask, next_select)));
+ gen_lowpart_or_truncate (GET_MODE (x),
+ force_to_mode (XEXP (x, 1), mode,
+ mask, next_select)));
SUBST (XEXP (x, 2),
- gen_lowpart (GET_MODE (x), force_to_mode (XEXP (x, 2), mode,
- mask, next_select)));
+ gen_lowpart_or_truncate (GET_MODE (x),
+ force_to_mode (XEXP (x, 2), mode,
+ mask, next_select)));
break;
default:
}
/* Ensure we return a value of the proper mode. */
- return gen_lowpart (mode, x);
+ return gen_lowpart_or_truncate (mode, x);
}
\f
/* Return nonzero if X is an expression that has one of two values depending on
|| (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)
+ || GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD
+ /* Result might need to be truncated. Don't change mode if
+ explicit truncation is needed. */
+ || !TRULY_NOOP_TRUNCATION
+ (GET_MODE_BITSIZE (GET_MODE (x)),
+ GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (lhs)))))
return x;
tem = simplify_gen_binary (code, GET_MODE (SUBREG_REG (lhs)),
return NULL_RTX;
}
\f
-/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done
- in MODE.
-
- Return an equivalent form, if different from X. Otherwise, return X. If
- X is zero, we are to always construct the equivalent form. */
+/* Simplify a logical `and' of VAROP with the constant CONSTOP, to be done
+ in MODE. Return an equivalent form, if different from (and VAROP
+ (const_int CONSTOP)). Otherwise, return NULL_RTX. */
static rtx
-simplify_and_const_int (rtx x, enum machine_mode mode, rtx varop,
- unsigned HOST_WIDE_INT constop)
+simplify_and_const_int_1 (enum machine_mode mode, rtx varop,
+ unsigned HOST_WIDE_INT constop)
{
unsigned HOST_WIDE_INT nonzero;
+ unsigned HOST_WIDE_INT orig_constop;
+ rtx orig_varop;
int i;
+ orig_varop = varop;
+ orig_constop = constop;
+ if (GET_CODE (varop) == CLOBBER)
+ return NULL_RTX;
+
/* Simplify VAROP knowing that we will be only looking at some of the
bits in it.
return o0;
}
- /* Get VAROP in MODE. Try to get a SUBREG if not. Don't make a new SUBREG
- if we already had one (just check for the simplest cases). */
- if (x && GET_CODE (XEXP (x, 0)) == SUBREG
- && GET_MODE (XEXP (x, 0)) == mode
- && SUBREG_REG (XEXP (x, 0)) == varop)
- varop = XEXP (x, 0);
- else
- varop = gen_lowpart (mode, varop);
-
- /* If we can't make the SUBREG, try to return what we were given. */
- if (GET_CODE (varop) == CLOBBER)
- return x ? x : varop;
+ /* Make a SUBREG if necessary. If we can't make it, fail. */
+ varop = gen_lowpart (mode, varop);
+ if (varop == NULL_RTX || GET_CODE (varop) == CLOBBER)
+ return NULL_RTX;
/* If we are only masking insignificant bits, return VAROP. */
if (constop == nonzero)
- x = varop;
- else
- {
- /* Otherwise, return an AND. */
- constop = trunc_int_for_mode (constop, mode);
- /* See how much, if any, of X we can use. */
- if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode)
- x = simplify_gen_binary (AND, mode, varop, GEN_INT (constop));
+ return varop;
- else
- {
- if (GET_CODE (XEXP (x, 1)) != CONST_INT
- || (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) != constop)
- SUBST (XEXP (x, 1), GEN_INT (constop));
+ if (varop == orig_varop && constop == orig_constop)
+ return NULL_RTX;
- SUBST (XEXP (x, 0), varop);
- }
- }
+ /* Otherwise, return an AND. */
+ constop = trunc_int_for_mode (constop, mode);
+ return simplify_gen_binary (AND, mode, varop, GEN_INT (constop));
+}
+
+
+/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done
+ in MODE.
+
+ Return an equivalent form, if different from X. Otherwise, return X. If
+ X is zero, we are to always construct the equivalent form. */
+
+static rtx
+simplify_and_const_int (rtx x, enum machine_mode mode, rtx varop,
+ unsigned HOST_WIDE_INT constop)
+{
+ rtx tem = simplify_and_const_int_1 (mode, varop, constop);
+ if (tem)
+ return tem;
+ if (!x)
+ x = simplify_gen_binary (AND, GET_MODE (varop), varop, GEN_INT (constop));
+ if (GET_MODE (x) != mode)
+ x = gen_lowpart (mode, x);
return x;
}
\f
}
\f
/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift.
- The result of the shift is RESULT_MODE. X, if nonzero, is an expression
- that we started with.
+ The result of the shift is RESULT_MODE. Return NULL_RTX if we cannot
+ simplify it. Otherwise, return a simplified value.
The shift is normally computed in the widest mode we find in VAROP, as
long as it isn't a different number of words than RESULT_MODE. Exceptions
- are ASHIFTRT and ROTATE, which are always done in their original mode, */
+ are ASHIFTRT and ROTATE, which are always done in their original mode. */
static rtx
-simplify_shift_const (rtx x, enum rtx_code code,
- enum machine_mode result_mode, rtx varop,
- int orig_count)
+simplify_shift_const_1 (enum rtx_code code, enum machine_mode result_mode,
+ rtx varop, int orig_count)
{
enum rtx_code orig_code = code;
- unsigned int count;
- int signed_count;
+ rtx orig_varop = varop;
+ int count;
enum machine_mode mode = result_mode;
enum machine_mode shift_mode, tmode;
unsigned int mode_words
/* We form (outer_op (code varop count) (outer_const)). */
enum rtx_code outer_op = UNKNOWN;
HOST_WIDE_INT outer_const = 0;
- rtx const_rtx;
int complement_p = 0;
- rtx new;
+ rtx new, x;
/* Make sure and truncate the "natural" shift on the way in. We don't
want to do this inside the loop as it makes it more difficult to
what was requested. */
if (orig_count < 0 || orig_count >= (int) GET_MODE_BITSIZE (mode))
- {
- if (x)
- return x;
-
- return gen_rtx_fmt_ee (code, mode, varop, GEN_INT (orig_count));
- }
+ return NULL_RTX;
count = orig_count;
while (count != 0)
{
- /* If we have an operand of (clobber (const_int 0)), just return that
- value. */
+ /* If we have an operand of (clobber (const_int 0)), fail. */
if (GET_CODE (varop) == CLOBBER)
- return varop;
+ return NULL_RTX;
/* If we discovered we had to complement VAROP, leave. Making a NOT
here would cause an infinite loop. */
multiple operations, each of which are defined, we know what the
result is supposed to be. */
- if (count > (unsigned int) (GET_MODE_BITSIZE (shift_mode) - 1))
+ if (count > (GET_MODE_BITSIZE (shift_mode) - 1))
{
if (code == ASHIFTRT)
count = GET_MODE_BITSIZE (shift_mode) - 1;
interpreted as the sign bit in a narrower mode, so, if
the result is narrower, don't discard the shift. */
if (code == LSHIFTRT
- && count == (unsigned int) (GET_MODE_BITSIZE (result_mode) - 1)
+ && count == (GET_MODE_BITSIZE (result_mode) - 1)
&& (GET_MODE_BITSIZE (result_mode)
>= GET_MODE_BITSIZE (GET_MODE (varop))))
{
(ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0 C2) C3) C1).
This simplifies certain SIGN_EXTEND operations. */
if (code == ASHIFT && first_code == ASHIFTRT
- && count == (unsigned int)
- (GET_MODE_BITSIZE (result_mode)
+ && count == (GET_MODE_BITSIZE (result_mode)
- GET_MODE_BITSIZE (GET_MODE (varop))))
{
/* C3 has the low-order C1 bits zero. */
> first_count))
{
varop = XEXP (varop, 0);
-
- signed_count = count - first_count;
- if (signed_count < 0)
- count = -signed_count, code = ASHIFT;
- else
- count = signed_count;
+ count -= first_count;
+ if (count < 0)
+ {
+ count = -count;
+ code = ASHIFT;
+ }
continue;
}
mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop)));
mask_rtx
- = simplify_binary_operation (code, result_mode, mask_rtx,
- GEN_INT (count));
+ = simplify_const_binary_operation (code, result_mode, mask_rtx,
+ GEN_INT (count));
/* Give up if we can't compute an outer operation to use. */
if (mask_rtx == 0
/* If the shifts are in the same direction, we add the
counts. Otherwise, we subtract them. */
- signed_count = count;
if ((code == ASHIFTRT || code == LSHIFTRT)
== (first_code == ASHIFTRT || first_code == LSHIFTRT))
- signed_count += first_count;
+ count += first_count;
else
- signed_count -= first_count;
+ count -= first_count;
/* If COUNT is positive, the new shift is usually CODE,
except for the two exceptions below, in which case it is
FIRST_CODE. If the count is negative, FIRST_CODE should
always be used */
- if (signed_count > 0
+ if (count > 0
&& ((first_code == ROTATE && code == ASHIFT)
|| (first_code == ASHIFTRT && code == LSHIFTRT)))
- code = first_code, count = signed_count;
- else if (signed_count < 0)
- code = first_code, count = -signed_count;
- else
- count = signed_count;
+ code = first_code;
+ else if (count < 0)
+ code = first_code, count = -count;
varop = XEXP (varop, 0);
continue;
B is not a constant. */
else if (GET_CODE (varop) == code
- && GET_CODE (XEXP (varop, 1)) != CONST_INT
- && 0 != (new
- = simplify_binary_operation (code, mode,
- XEXP (varop, 0),
- GEN_INT (count))))
+ && GET_CODE (XEXP (varop, 0)) == CONST_INT
+ && GET_CODE (XEXP (varop, 1)) != CONST_INT)
{
+ rtx new = simplify_const_binary_operation (code, mode,
+ XEXP (varop, 0),
+ GEN_INT (count));
varop = gen_rtx_fmt_ee (code, mode, new, XEXP (varop, 1));
count = 0;
continue;
&& XEXP (XEXP (varop, 0), 1) == constm1_rtx
&& (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& (code == LSHIFTRT || code == ASHIFTRT)
- && count == (unsigned int)
- (GET_MODE_BITSIZE (GET_MODE (varop)) - 1)
+ && count == (GET_MODE_BITSIZE (GET_MODE (varop)) - 1)
&& rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
{
count = 0;
&& !(code == ASHIFTRT && GET_CODE (varop) == XOR
&& 0 > trunc_int_for_mode (INTVAL (XEXP (varop, 1)),
shift_mode))
- && (new = simplify_binary_operation (code, result_mode,
- XEXP (varop, 1),
- GEN_INT (count))) != 0
+ && (new = simplify_const_binary_operation (code, result_mode,
+ XEXP (varop, 1),
+ GEN_INT (count))) != 0
&& GET_CODE (new) == CONST_INT
&& merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop),
INTVAL (new), result_mode, &complement_p))
if (code == LSHIFTRT
&& XEXP (varop, 1) == const0_rtx
&& GET_MODE (XEXP (varop, 0)) == result_mode
- && count == (unsigned int) (GET_MODE_BITSIZE (result_mode) - 1)
+ && count == (GET_MODE_BITSIZE (result_mode) - 1)
&& GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
- && ((STORE_FLAG_VALUE
- & ((HOST_WIDE_INT) 1
- < (GET_MODE_BITSIZE (result_mode) - 1))))
+ && STORE_FLAG_VALUE == -1
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1
&& merge_outer_ops (&outer_op, &outer_const, XOR,
(HOST_WIDE_INT) 1, result_mode,
/* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less
than the number of bits in the mode is equivalent to A. */
if (code == LSHIFTRT
- && count == (unsigned int) (GET_MODE_BITSIZE (result_mode) - 1)
+ && count == (GET_MODE_BITSIZE (result_mode) - 1)
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1)
{
varop = XEXP (varop, 0);
is one less than the number of bits in the mode is
equivalent to (xor A 1). */
if (code == LSHIFTRT
- && count == (unsigned int) (GET_MODE_BITSIZE (result_mode) - 1)
+ && count == (GET_MODE_BITSIZE (result_mode) - 1)
&& XEXP (varop, 1) == constm1_rtx
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1
&& merge_outer_ops (&outer_op, &outer_const, XOR,
/* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */
if (code == ASHIFT
&& GET_CODE (XEXP (varop, 1)) == CONST_INT
- && (new = simplify_binary_operation (ASHIFT, result_mode,
- XEXP (varop, 1),
- GEN_INT (count))) != 0
+ && (new = simplify_const_binary_operation (ASHIFT, result_mode,
+ XEXP (varop, 1),
+ GEN_INT (count))) != 0
&& GET_CODE (new) == CONST_INT
&& merge_outer_ops (&outer_op, &outer_const, PLUS,
INTVAL (new), result_mode, &complement_p))
if (code == LSHIFTRT
&& GET_CODE (XEXP (varop, 1)) == CONST_INT
&& mode_signbit_p (result_mode, XEXP (varop, 1))
- && (new = simplify_binary_operation (code, result_mode,
- XEXP (varop, 1),
- GEN_INT (count))) != 0
+ && (new = simplify_const_binary_operation (code, result_mode,
+ XEXP (varop, 1),
+ GEN_INT (count))) != 0
&& GET_CODE (new) == CONST_INT
&& merge_outer_ops (&outer_op, &outer_const, XOR,
INTVAL (new), result_mode, &complement_p))
if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& GET_CODE (XEXP (varop, 0)) == ASHIFTRT
- && count == (unsigned int)
- (GET_MODE_BITSIZE (GET_MODE (varop)) - 1)
+ && count == (GET_MODE_BITSIZE (GET_MODE (varop)) - 1)
&& (code == LSHIFTRT || code == ASHIFTRT)
&& GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
- && (unsigned HOST_WIDE_INT) INTVAL (XEXP (XEXP (varop, 0), 1))
- == count
+ && INTVAL (XEXP (XEXP (varop, 0), 1)) == count
&& rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
{
count = 0;
a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If
OUTER_OP is non-UNKNOWN, it is an operation that needs to be applied
to the result of the shift. OUTER_CONST is the relevant constant,
- but we must turn off all bits turned off in the shift.
+ but we must turn off all bits turned off in the shift. */
- If we were passed a value for X, see if we can use any pieces of
- it. If not, make new rtx. */
-
- if (x && GET_RTX_CLASS (GET_CODE (x)) == RTX_BIN_ARITH
- && GET_CODE (XEXP (x, 1)) == CONST_INT
- && (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) == count)
- const_rtx = XEXP (x, 1);
- else
- const_rtx = GEN_INT (count);
-
- if (x && GET_CODE (XEXP (x, 0)) == SUBREG
- && GET_MODE (XEXP (x, 0)) == shift_mode
- && SUBREG_REG (XEXP (x, 0)) == varop)
- varop = XEXP (x, 0);
- else if (GET_MODE (varop) != shift_mode)
- varop = gen_lowpart (shift_mode, varop);
-
- /* If we can't make the SUBREG, try to return what we were given. */
- if (GET_CODE (varop) == CLOBBER)
- return x ? x : varop;
+ if (outer_op == UNKNOWN
+ && orig_code == code && orig_count == count
+ && varop == orig_varop
+ && shift_mode == GET_MODE (varop))
+ return NULL_RTX;
- new = simplify_binary_operation (code, shift_mode, varop, const_rtx);
- if (new != 0)
- x = new;
- else
- x = gen_rtx_fmt_ee (code, shift_mode, varop, const_rtx);
+ /* Make a SUBREG if necessary. If we can't make it, fail. */
+ varop = gen_lowpart (shift_mode, varop);
+ if (varop == NULL_RTX || GET_CODE (varop) == CLOBBER)
+ return NULL_RTX;
/* If we have an outer operation and we just made a shift, it is
possible that we could have simplified the shift were it not
for the outer operation. So try to do the simplification
recursively. */
- if (outer_op != UNKNOWN && GET_CODE (x) == code
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
- x = simplify_shift_const (x, code, shift_mode, XEXP (x, 0),
- INTVAL (XEXP (x, 1)));
+ if (outer_op != UNKNOWN)
+ x = simplify_shift_const_1 (code, shift_mode, varop, count);
+ else
+ x = NULL_RTX;
+
+ if (x == NULL_RTX)
+ x = simplify_gen_binary (code, shift_mode, varop, GEN_INT (count));
/* If we were doing an LSHIFTRT in a wider mode than it was originally,
turn off all the bits that the shift would have turned off. */
return x;
}
+
+/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift.
+ The result of the shift is RESULT_MODE. If we cannot simplify it,
+ return X or, if it is NULL, synthesize the expression with
+ simplify_gen_binary. Otherwise, return a simplified value.
+
+ The shift is normally computed in the widest mode we find in VAROP, as
+ long as it isn't a different number of words than RESULT_MODE. Exceptions
+ are ASHIFTRT and ROTATE, which are always done in their original mode. */
+
+static rtx
+simplify_shift_const (rtx x, enum rtx_code code, enum machine_mode result_mode,
+ rtx varop, int count)
+{
+ rtx tem = simplify_shift_const_1 (code, result_mode, varop, count);
+ if (tem)
+ return tem;
+
+ if (!x)
+ x = simplify_gen_binary (code, GET_MODE (varop), varop, GEN_INT (count));
+ if (GET_MODE (x) != result_mode)
+ x = gen_lowpart (result_mode, x);
+ return x;
+}
+
\f
/* Like recog, but we receive the address of a pointer to a new pattern.
We try to match the rtx that the pointer points to.
&& XEXP (XEXP (op0, 0), 0) == const1_rtx)
{
op0 = simplify_and_const_int
- (op0, mode, gen_rtx_LSHIFTRT (mode,
- XEXP (op0, 1),
- XEXP (XEXP (op0, 0), 1)),
+ (NULL_RTX, mode, gen_rtx_LSHIFTRT (mode,
+ XEXP (op0, 1),
+ XEXP (XEXP (op0, 0), 1)),
(HOST_WIDE_INT) 1);
continue;
}
reg_stat[i].last_set_nonzero_bits = 0;
reg_stat[i].last_set_sign_bit_copies = 0;
reg_stat[i].last_death = 0;
+ reg_stat[i].truncated_to_mode = 0;
}
/* Mark registers that are being referenced in this value. */
for (i = regno; i < endregno; i++)
{
reg_stat[i].last_set_label = label_tick;
- if (value && reg_stat[i].last_set_table_tick == label_tick)
+ if (!insn || (value && reg_stat[i].last_set_table_tick == label_tick))
reg_stat[i].last_set_invalid = 1;
else
reg_stat[i].last_set_invalid = 0;
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
+ if (!record_dead_insn)
+ {
+ if (REG_P (dest))
+ record_value_for_reg (dest, NULL_RTX, NULL_RTX);
+ return;
+ }
+
if (REG_P (dest))
{
/* If we are setting the whole register, we know its value. Otherwise
reg_stat[i].last_set_nonzero_bits = 0;
reg_stat[i].last_set_sign_bit_copies = 0;
reg_stat[i].last_death = 0;
+ reg_stat[i].truncated_to_mode = 0;
}
last_call_cuid = mem_last_set = INSN_CUID (insn);
- /* Don't bother recording what this insn does. It might set the
- return value register, but we can't combine into a call
- pattern anyway, so there's no point trying (and it may cause
- a crash, if e.g. we wind up asking for last_set_value of a
- SUBREG of the return value register). */
- return;
+ /* We can't combine into a call pattern. Remember, though, that
+ the return value register is set at this CUID. We could
+ still replace a register with the return value from the
+ wrong subroutine call! */
+ note_stores (PATTERN (insn), record_dead_and_set_regs_1, NULL_RTX);
}
-
- note_stores (PATTERN (insn), record_dead_and_set_regs_1, insn);
+ else
+ note_stores (PATTERN (insn), record_dead_and_set_regs_1, insn);
}
/* If a SUBREG has the promoted bit set, it is in fact a property of the
}
}
-/* Scan X for promoted SUBREGs. For each one found,
- note what it implies to the registers used in it. */
+/* Check if X, a register, is known to contain a value already
+ truncated to MODE. In this case we can use a subreg to refer to
+ the truncated value even though in the generic case we would need
+ an explicit truncation. */
+
+static bool
+reg_truncated_to_mode (enum machine_mode mode, rtx x)
+{
+ enum machine_mode truncated = reg_stat[REGNO (x)].truncated_to_mode;
+
+ if (truncated == 0 || reg_stat[REGNO (x)].truncation_label != label_tick)
+ return false;
+ if (GET_MODE_SIZE (truncated) <= GET_MODE_SIZE (mode))
+ return true;
+ if (TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (truncated)))
+ return true;
+ return false;
+}
+
+/* X is a REG or a SUBREG. If X is some sort of a truncation record
+ it. For non-TRULY_NOOP_TRUNCATION targets we might be able to turn
+ a truncate into a subreg using this information. */
+
+static void
+record_truncated_value (rtx x)
+{
+ enum machine_mode truncated_mode;
+
+ if (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x)))
+ {
+ enum machine_mode original_mode = GET_MODE (SUBREG_REG (x));
+ truncated_mode = GET_MODE (x);
+
+ if (GET_MODE_SIZE (original_mode) <= GET_MODE_SIZE (truncated_mode))
+ return;
+
+ if (TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (truncated_mode),
+ GET_MODE_BITSIZE (original_mode)))
+ return;
+
+ x = SUBREG_REG (x);
+ }
+ /* ??? For hard-regs we now record everthing. We might be able to
+ optimize this using last_set_mode. */
+ else if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ truncated_mode = GET_MODE (x);
+ else
+ return;
+
+ if (reg_stat[REGNO (x)].truncated_to_mode == 0
+ || reg_stat[REGNO (x)].truncation_label < label_tick
+ || (GET_MODE_SIZE (truncated_mode)
+ < GET_MODE_SIZE (reg_stat[REGNO (x)].truncated_to_mode)))
+ {
+ reg_stat[REGNO (x)].truncated_to_mode = truncated_mode;
+ reg_stat[REGNO (x)].truncation_label = label_tick;
+ }
+}
+
+/* Scan X for promoted SUBREGs and truncated REGs. For each one
+ found, note what it implies to the registers used in it. */
static void
-check_promoted_subreg (rtx insn, rtx x)
+check_conversions (rtx insn, rtx x)
{
- if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x)
- && REG_P (SUBREG_REG (x)))
- record_promoted_value (insn, x);
+ if (GET_CODE (x) == SUBREG || REG_P (x))
+ {
+ if (GET_CODE (x) == SUBREG
+ && SUBREG_PROMOTED_VAR_P (x)
+ && REG_P (SUBREG_REG (x)))
+ record_promoted_value (insn, x);
+
+ record_truncated_value (x);
+ }
else
{
const char *format = GET_RTX_FORMAT (GET_CODE (x));
switch (format[i])
{
case 'e':
- check_promoted_subreg (insn, XEXP (x, i));
+ check_conversions (insn, XEXP (x, i));
break;
case 'V':
case 'E':
if (XVEC (x, i) != 0)
for (j = 0; j < XVECLEN (x, i); j++)
- check_promoted_subreg (insn, XVECEXP (x, i, j));
+ check_conversions (insn, XVECEXP (x, i, j));
break;
}
}
{
rtx place = 0, place2 = 0;
- /* If this NOTE references a pseudo register, ensure it references
- the latest copy of that register. */
- if (XEXP (note, 0) && REG_P (XEXP (note, 0))
- && REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER)
- XEXP (note, 0) = regno_reg_rtx[REGNO (XEXP (note, 0))];
-
next_note = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{