/* 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, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GCC.
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_conversions (rtx, rtx);
+static void check_promoted_subreg (rtx, rtx);
static void record_dead_and_set_regs_1 (rtx, const_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 int record_truncated_value (rtx *, void *);
+static void record_truncated_values (rtx *, void *);
static bool reg_truncated_to_mode (enum machine_mode, const_rtx);
static rtx gen_lowpart_or_truncate (enum machine_mode, rtx);
\f
assignments later. */
if (regno >= FIRST_PSEUDO_REGISTER
|| asm_noperands (PATTERN (use_insn)) < 0)
- LOG_LINKS (use_insn) =
- alloc_INSN_LIST (insn, LOG_LINKS (use_insn));
+ {
+ /* Don't add duplicate links between instructions. */
+ rtx links;
+ for (links = LOG_LINKS (use_insn); links;
+ links = XEXP (links, 1))
+ if (insn == XEXP (links, 0))
+ break;
+
+ if (!links)
+ LOG_LINKS (use_insn) =
+ alloc_INSN_LIST (insn, LOG_LINKS (use_insn));
+ }
}
next_use[regno] = NULL_RTX;
}
{
/* See if we know about function return values before this
insn based upon SUBREG flags. */
- check_conversions (insn, PATTERN (insn));
+ check_promoted_subreg (insn, PATTERN (insn));
+
+ /* See if we can find hardregs and subreg of pseudos in
+ narrower modes. This could help turning TRUNCATEs
+ into SUBREGs. */
+ note_uses (&PATTERN (insn), record_truncated_values, NULL);
/* Try this insn with each insn it links back to. */
/* Don't eliminate a store in the stack pointer. */
if (dest == stack_pointer_rtx
/* Don't combine with an insn that sets a register to itself if it has
- a REG_EQUAL note. This may be part of a REG_NO_CONFLICT sequence. */
+ a REG_EQUAL note. This may be part of a LIBCALL sequence. */
|| (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX))
/* Can't merge an ASM_OPERANDS. */
|| GET_CODE (src) == ASM_OPERANDS
&& use_crosses_set_p (src, DF_INSN_LUID (insn)))
|| (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))
|| GET_CODE (src) == UNSPEC_VOLATILE))
- /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get
- better register allocation by not doing the combine. */
- || find_reg_note (i3, REG_NO_CONFLICT, dest)
- || (succ && find_reg_note (succ, REG_NO_CONFLICT, dest))
/* Don't combine across a CALL_INSN, because that would possibly
change whether the life span of some REGs crosses calls or not,
and it is a pain to update that information.
if (i1 && GET_CODE (newpat) != CLOBBER)
{
- /* Before we can do this substitution, we must redo the test done
- above (see detailed comments there) that ensures that I1DEST
- isn't mentioned in any SETs in NEWPAT that are field assignments. */
-
- if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX,
- 0, (rtx*) 0))
+ /* Check that an autoincrement side-effect on I1 has not been lost.
+ This happens if I1DEST is mentioned in I2 and dies there, and
+ has disappeared from the new pattern. */
+ if ((FIND_REG_INC_NOTE (i1, NULL_RTX) != 0
+ && !i1_feeds_i3
+ && dead_or_set_p (i2, i1dest)
+ && !reg_overlap_mentioned_p (i1dest, newpat))
+ /* Before we can do this substitution, we must redo the test done
+ above (see detailed comments there) that ensures that I1DEST
+ isn't mentioned in any SETs in NEWPAT that are field assignments. */
+ || !combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX, 0, 0))
{
undo_all ();
return 0;
/* Look for cases where we have (abs x) or (neg (abs X)). */
if (GET_MODE_CLASS (mode) == MODE_INT
+ && comparison_p
+ && XEXP (cond, 1) == const0_rtx
&& GET_CODE (false_rtx) == NEG
&& rtx_equal_p (true_rtx, XEXP (false_rtx, 0))
- && comparison_p
&& rtx_equal_p (true_rtx, XEXP (cond, 0))
&& ! side_effects_p (true_rtx))
switch (true_code)
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. */
+/* Callback for for_each_rtx. If *P is a hard reg or a subreg record the mode
+ that the register is accessed in. For non-TRULY_NOOP_TRUNCATION targets we
+ might be able to turn a truncate into a subreg using this information.
+ Return -1 if traversing *P is complete or 0 otherwise. */
-static void
-record_truncated_value (rtx x)
+static int
+record_truncated_value (rtx *p, void *data ATTRIBUTE_UNUSED)
{
+ rtx x = *p;
enum machine_mode truncated_mode;
reg_stat_type *rsp;
truncated_mode = GET_MODE (x);
if (GET_MODE_SIZE (original_mode) <= GET_MODE_SIZE (truncated_mode))
- return;
+ return -1;
if (TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (truncated_mode),
GET_MODE_BITSIZE (original_mode)))
- return;
+ return -1;
x = SUBREG_REG (x);
}
else if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
truncated_mode = GET_MODE (x);
else
- return;
+ return 0;
rsp = VEC_index (reg_stat_type, reg_stat, REGNO (x));
if (rsp->truncated_to_mode == 0
rsp->truncated_to_mode = truncated_mode;
rsp->truncation_label = label_tick;
}
+
+ return -1;
}
-/* Scan X for promoted SUBREGs and truncated REGs. For each one
- found, note what it implies to the registers used in it. */
+/* Callback for note_uses. Find hardregs and subregs of pseudos and
+ the modes they are used in. This can help truning TRUNCATEs into
+ SUBREGs. */
static void
-check_conversions (rtx insn, rtx x)
+record_truncated_values (rtx *x, void *data ATTRIBUTE_UNUSED)
{
- 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);
+ for_each_rtx (x, record_truncated_value, NULL);
+}
- record_truncated_value (x);
- }
+/* Scan X for promoted SUBREGs. For each one found,
+ note what it implies to the registers used in it. */
+
+static void
+check_promoted_subreg (rtx insn, rtx x)
+{
+ if (GET_CODE (x) == SUBREG
+ && SUBREG_PROMOTED_VAR_P (x)
+ && REG_P (SUBREG_REG (x)))
+ record_promoted_value (insn, x);
else
{
const char *format = GET_RTX_FORMAT (GET_CODE (x));
switch (format[i])
{
case 'e':
- check_conversions (insn, XEXP (x, i));
+ check_promoted_subreg (insn, XEXP (x, i));
break;
case 'V':
case 'E':
if (XVEC (x, i) != 0)
for (j = 0; j < XVECLEN (x, i); j++)
- check_conversions (insn, XVECEXP (x, i, j));
+ check_promoted_subreg (insn, XVECEXP (x, i, j));
break;
}
}
break;
case REG_INC:
- case REG_NO_CONFLICT:
/* These notes say something about how a register is used. They must
be present on any use of the register in I2 or I3. */
if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3)))
return 0;
}
-struct tree_opt_pass pass_combine =
+struct rtl_opt_pass pass_combine =
{
+ {
+ RTL_PASS,
"combine", /* name */
gate_handle_combine, /* gate */
rest_of_handle_combine, /* execute */
TODO_dump_func |
TODO_df_finish | TODO_verify_rtl_sharing |
TODO_ggc_collect, /* todo_flags_finish */
- 'c' /* letter */
+ }
};