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;
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.
{
/* 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. */
&& ! (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));
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
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. */
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);
}
}
-/* 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
-check_promoted_subreg (rtx insn, rtx x)
+record_truncated_value (rtx x)
{
- if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x)
- && REG_P (SUBREG_REG (x)))
- record_promoted_value (insn, 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_conversions (rtx insn, rtx 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;
}
}