return (gain > 0);
}
+
+/* Flags for the last parameter of propagate_rtx_1. */
+
+enum {
+ /* If PR_CAN_APPEAR is true, propagate_rtx_1 always returns true;
+ if it is false, propagate_rtx_1 returns false if, for at least
+ one occurrence OLD, it failed to collapse the result to a constant.
+ For example, (mult:M (reg:M A) (minus:M (reg:M B) (reg:M A))) may
+ collapse to zero if replacing (reg:M B) with (reg:M A).
+
+ PR_CAN_APPEAR is disregarded inside MEMs: in that case,
+ propagate_rtx_1 just tries to make cheaper and valid memory
+ addresses. */
+ PR_CAN_APPEAR = 1,
+
+ /* If PR_HANDLE_MEM is not set, propagate_rtx_1 won't attempt any replacement
+ outside memory addresses. This is needed because propagate_rtx_1 does
+ not do any analysis on memory; thus it is very conservative and in general
+ it will fail if non-read-only MEMs are found in the source expression.
+
+ PR_HANDLE_MEM is set when the source of the propagation was not
+ another MEM. Then, it is safe not to treat non-read-only MEMs as
+ ``opaque'' objects. */
+ PR_HANDLE_MEM = 2
+};
+
+
/* Replace all occurrences of OLD in *PX with NEW and try to simplify the
resulting expression. Replace *PX with a new RTL expression if an
occurrence of OLD was found.
- If CAN_APPEAR is true, we always return true; if it is false, we
- can return false if, for at least one occurrence OLD, we failed to
- collapse the result to a constant. For example, (mult:M (reg:M A)
- (minus:M (reg:M B) (reg:M A))) may collapse to zero if replacing
- (reg:M B) with (reg:M A).
-
- CAN_APPEAR is disregarded inside MEMs: in that case, we always return
- true if the simplification is a cheaper and valid memory address.
-
This is only a wrapper around simplify-rtx.c: do not add any pattern
matching code here. (The sole exception is the handling of LO_SUM, but
that is because there is no simplify_gen_* function for LO_SUM). */
static bool
-propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
+propagate_rtx_1 (rtx *px, rtx old, rtx new, int flags)
{
rtx x = *px, tem = NULL_RTX, op0, op1, op2;
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
enum machine_mode op_mode;
+ bool can_appear = (flags & PR_CAN_APPEAR) != 0;
bool valid_ops = true;
- /* If X is OLD_RTX, return NEW_RTX. Otherwise, if this is an expression,
- try to build a new expression from recursive substitution. */
+ if (!(flags & PR_HANDLE_MEM) && MEM_P (x) && !MEM_READONLY_P (x))
+ {
+ /* If unsafe, change MEMs to CLOBBERs or SCRATCHes (to preserve whether
+ they have side effects or not). */
+ *px = (side_effects_p (x)
+ ? gen_rtx_CLOBBER (GET_MODE (x), const0_rtx)
+ : gen_rtx_SCRATCH (GET_MODE (x)));
+ return false;
+ }
+ /* If X is OLD_RTX, return NEW_RTX. But not if replacing only within an
+ address, and we are *not* inside one. */
if (x == old)
{
*px = new;
return can_appear;
}
+ /* If this is an expression, try recursive substitution. */
switch (GET_RTX_CLASS (code))
{
case RTX_UNARY:
op0 = XEXP (x, 0);
op_mode = GET_MODE (op0);
- valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
+ valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
if (op0 == XEXP (x, 0))
return true;
tem = simplify_gen_unary (code, mode, op0, op_mode);
case RTX_COMM_ARITH:
op0 = XEXP (x, 0);
op1 = XEXP (x, 1);
- valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
- valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+ valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+ valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return true;
tem = simplify_gen_binary (code, mode, op0, op1);
op0 = XEXP (x, 0);
op1 = XEXP (x, 1);
op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1);
- valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
- valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+ valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+ valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return true;
tem = simplify_gen_relational (code, mode, op_mode, op0, op1);
op1 = XEXP (x, 1);
op2 = XEXP (x, 2);
op_mode = GET_MODE (op0);
- valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
- valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
- valid_ops &= propagate_rtx_1 (&op2, old, new, can_appear);
+ valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+ valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
+ valid_ops &= propagate_rtx_1 (&op2, old, new, flags);
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2))
return true;
if (op_mode == VOIDmode)
if (code == SUBREG)
{
op0 = XEXP (x, 0);
- valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
+ valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
if (op0 == XEXP (x, 0))
return true;
tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)),
return true;
op0 = new_op0 = targetm.delegitimize_address (op0);
- valid_ops &= propagate_rtx_1 (&new_op0, old, new, true);
+ valid_ops &= propagate_rtx_1 (&new_op0, old, new,
+ flags | PR_CAN_APPEAR);
/* Dismiss transformation that we do not want to carry on. */
if (!valid_ops
/* The only simplification we do attempts to remove references to op0
or make it constant -- in both cases, op0's invalidity will not
make the result invalid. */
- propagate_rtx_1 (&op0, old, new, true);
- valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+ propagate_rtx_1 (&op0, old, new, flags | PR_CAN_APPEAR);
+ valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
return true;
return valid_ops || can_appear || CONSTANT_P (tem);
}
+
+/* for_each_rtx traversal function that returns 1 if BODY points to
+ a non-constant mem. */
+
+static int
+varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED)
+{
+ rtx x = *body;
+ return MEM_P (x) && !MEM_READONLY_P (x);
+}
+
+
/* Replace all occurrences of OLD in X with NEW and try to simplify the
resulting expression (in mode MODE). Return a new expression if it is
a constant, otherwise X.
{
rtx tem;
bool collapsed;
+ int flags;
if (REG_P (new) && REGNO (new) < FIRST_PSEUDO_REGISTER)
return NULL_RTX;
- new = copy_rtx (new);
+ flags = 0;
+ if (REG_P (new) || CONSTANT_P (new))
+ flags |= PR_CAN_APPEAR;
+ if (!for_each_rtx (&new, varying_mem_p, NULL))
+ flags |= PR_HANDLE_MEM;
tem = x;
- collapsed = propagate_rtx_1 (&tem, old, new, REG_P (new) || CONSTANT_P (new));
+ collapsed = propagate_rtx_1 (&tem, old, copy_rtx (new), flags);
if (tem == x || !collapsed)
return NULL_RTX;
/* See if USE is killed between DEF_INSN and the last insn in the
basic block containing DEF_INSN. */
x = df_bb_regno_last_def_find (def_bb, regno);
- if (x && DF_INSN_LUID (x->insn) >= DF_INSN_LUID (def_insn))
+ if (x && DF_INSN_LUID (DF_REF_INSN (x)) >= DF_INSN_LUID (def_insn))
return true;
/* See if USE is killed between TARGET_INSN and the first insn in the
basic block containing TARGET_INSN. */
x = df_bb_regno_first_def_find (target_bb, regno);
- if (x && DF_INSN_LUID (x->insn) < DF_INSN_LUID (target_insn))
+ if (x && DF_INSN_LUID (DF_REF_INSN (x)) < DF_INSN_LUID (target_insn))
return true;
return false;
}
-/* for_each_rtx traversal function that returns 1 if BODY points to
- a non-constant mem. */
-
-static int
-varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED)
-{
- rtx x = *body;
- return MEM_P (x) && !MEM_READONLY_P (x);
-}
-
/* Check if all uses in DEF_INSN can be used in TARGET_INSN. This
would require full computation of available expressions;
we check only restricted conditions, see use_killed_between. */
all_uses_available_at (rtx def_insn, rtx target_insn)
{
struct df_ref **use_rec;
+ struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn);
rtx def_set = single_set (def_insn);
gcc_assert (def_set);
/* If the insn uses the reg that it defines, the substitution is
invalid. */
- for (use_rec = DF_INSN_USES (def_insn); *use_rec; use_rec++)
+ for (use_rec = DF_INSN_INFO_USES (insn_info); *use_rec; use_rec++)
{
struct df_ref *use = *use_rec;
if (rtx_equal_p (DF_REF_REG (use), def_reg))
return false;
}
- for (use_rec = DF_INSN_EQ_USES (def_insn); *use_rec; use_rec++)
+ for (use_rec = DF_INSN_INFO_EQ_USES (insn_info); *use_rec; use_rec++)
{
struct df_ref *use = *use_rec;
if (rtx_equal_p (use->reg, def_reg))
{
/* Look at all the uses of DEF_INSN, and see if they are not
killed between DEF_INSN and TARGET_INSN. */
- for (use_rec = DF_INSN_USES (def_insn); *use_rec; use_rec++)
+ for (use_rec = DF_INSN_INFO_USES (insn_info); *use_rec; use_rec++)
{
struct df_ref *use = *use_rec;
if (use_killed_between (use, def_insn, target_insn))
return false;
}
- for (use_rec = DF_INSN_EQ_USES (def_insn); *use_rec; use_rec++)
+ for (use_rec = DF_INSN_INFO_EQ_USES (insn_info); *use_rec; use_rec++)
{
struct df_ref *use = *use_rec;
if (use_killed_between (use, def_insn, target_insn))
}
}
- /* We don't do any analysis of memories or aliasing. Reject any
- instruction that involves references to non-constant memory. */
- return !for_each_rtx (&SET_SRC (def_set), varying_mem_p, NULL);
+ return true;
}
\f
{
struct df_ref *use = *use_rec;
struct df_ref *orig_use = use, *new_use;
+ int width = -1;
+ int offset = -1;
+ enum machine_mode mode = 0;
rtx *new_loc = find_occurrence (loc, DF_REF_REG (orig_use));
use_rec++;
if (!new_loc)
continue;
+ if (DF_REF_FLAGS_IS_SET (orig_use, DF_REF_SIGN_EXTRACT | DF_REF_ZERO_EXTRACT))
+ {
+ width = DF_REF_EXTRACT_WIDTH (orig_use);
+ offset = DF_REF_EXTRACT_OFFSET (orig_use);
+ mode = DF_REF_EXTRACT_MODE (orig_use);
+ }
+
/* Add a new insn use. Use the original type, because it says if the
use was within a MEM. */
new_use = df_ref_create (DF_REF_REG (orig_use), new_loc,
insn, BLOCK_FOR_INSN (insn),
- type, DF_REF_FLAGS (orig_use) | new_flags);
+ type, DF_REF_FLAGS (orig_use) | new_flags,
+ width, offset, mode);
/* Set up the use-def chain. */
df_chain_copy (new_use, DF_REF_CHAIN (orig_use));
rtx insn = DF_REF_INSN (use);
enum df_ref_type type = DF_REF_TYPE (use);
int flags = DF_REF_FLAGS (use);
+ rtx set = single_set (insn);
+ int old_cost = rtx_cost (SET_SRC (set), SET);
+ bool ok;
if (dump_file)
{
fprintf (dump_file, "\n");
}
- if (validate_unshare_change (insn, loc, new, false))
+ validate_unshare_change (insn, loc, new, true);
+ if (!verify_changes (0))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Changes to insn %d not recognized\n",
+ INSN_UID (insn));
+ ok = false;
+ }
+
+ else if (DF_REF_TYPE (use) == DF_REF_REG_USE
+ && rtx_cost (SET_SRC (set), SET) > old_cost)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Changes to insn %d not profitable\n",
+ INSN_UID (insn));
+ ok = false;
+ }
+
+ else
{
- num_changes++;
if (dump_file)
fprintf (dump_file, "Changed insn %d\n", INSN_UID (insn));
+ ok = true;
+ }
+
+ if (ok)
+ {
+ confirm_change_group ();
+ num_changes++;
df_ref_remove (use);
if (!CONSTANT_P (new))
{
- update_df (insn, loc, DF_INSN_USES (def_insn), type, flags);
- update_df (insn, loc, DF_INSN_EQ_USES (def_insn), type, flags);
+ struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn);
+ update_df (insn, loc, DF_INSN_INFO_USES (insn_info), type, flags);
+ update_df (insn, loc, DF_INSN_INFO_EQ_USES (insn_info), type, flags);
}
- return true;
}
else
{
- if (dump_file)
- fprintf (dump_file, "Changes to insn %d not recognized\n",
- INSN_UID (insn));
+ cancel_changes (0);
- /* Can also record a simplified value in a REG_EQUAL note, making a
- new one if one does not already exist. */
+ /* Can also record a simplified value in a REG_EQUAL note,
+ making a new one if one does not already exist. */
if (set_reg_equal)
{
if (dump_file)
set_unique_reg_note? */
if (!CONSTANT_P (new))
{
- update_df (insn, loc, DF_INSN_USES (def_insn),
+ struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn);
+ update_df (insn, loc, DF_INSN_INFO_USES (insn_info),
type, DF_REF_IN_NOTE);
- update_df (insn, loc, DF_INSN_EQ_USES (def_insn),
+ update_df (insn, loc, DF_INSN_INFO_EQ_USES (insn_info),
type, DF_REF_IN_NOTE);
}
}
-
- return false;
}
+
+ return ok;
}
else
parent = PATTERN (use_insn);
- if (!loc_mentioned_in_p (DF_REF_LOC (use), parent))
+ if (!reg_mentioned_p (DF_REF_REG (use), parent))
return;
def_insn = DF_REF_INSN (def);
return 0;
}
-struct tree_opt_pass pass_rtl_fwprop =
+struct rtl_opt_pass pass_rtl_fwprop =
{
+ {
+ RTL_PASS,
"fwprop1", /* name */
gate_fwprop, /* gate */
fwprop, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_df_finish |
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_dump_func /* todo_flags_finish */
+ }
};
static unsigned int
return 0;
}
-struct tree_opt_pass pass_rtl_fwprop_addr =
+struct rtl_opt_pass pass_rtl_fwprop_addr =
{
+ {
+ RTL_PASS,
"fwprop2", /* name */
gate_fwprop, /* gate */
fwprop_addr, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_df_finish |
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_dump_func /* todo_flags_finish */
+ }
};