@@ -2068,23 +2068,127 @@ noce_emit_bb (rtx last_insn, basic_block bb, bool simple)
return true;
}
-/* Try more complex cases involving conditional_move. */
+/* Helper for noce_try_cmove_arith. This gets called twice, once for the
+ then branch, once for the else branch X_BB gives the basic block for the
+ branch we are currently interested in. X is the destination for this
+ branch. If X is complex, we need to move it in to a register first, by
+ possibly copying from INSN_X such that we preserve clobbers etc from the
+ original instruction. EMIT_X is the target register for this branch
+ result. ORIG_OTHER_DEST gives the original destination from the
+ opposite branch. OTHER_BB_EXISTS_P is true if there was an opposite
+ branch for us to consider. */
+
+bool
+noce_arith_helper (rtx *x, rtx *emit_x, rtx_insn *insn_x,
+ basic_block x_bb, rtx orig_other_dest,
+ bool other_bb_exists_p)
+{
+ rtx set_tmp = NULL_RTX;
+
+ machine_mode x_mode = GET_MODE (*x);
+
+ /* Two cases to catch here. Either X is not yet a general operand, in
+ which case we need to move it to an appropriate register. Or, the other
+ block is empty, in which case ORIG_OTHER_DEST came from the test block.
+ The non-empty complex block that we will emit might clobber the register
+ used by ORIG_OTHER_DEST, so move it to a pseudo first. */
+ if (! general_operand (*x, x_mode)
+ || !other_bb_exists_p)
+ {
+ rtx reg = gen_reg_rtx (x_mode);
+ if (insn_x)
+ {
+ rtx_insn *copy_of_x = as_a <rtx_insn *> (copy_rtx (insn_x));
+ rtx set = single_set (copy_of_x);
+ SET_DEST (set) = reg;
+ set_tmp = PATTERN (copy_of_x);
+ }
+ else
+ {
+ set_tmp = gen_rtx_SET (reg, *x);
+ }
+ *x = reg;
+ }
+
+ /* Check that our new insn isn't going to clobber ORIG_OTHER_DEST. */
+ bool modified_in_x = (set_tmp != NULL_RTX)
+ && modified_in_p (orig_other_dest, set_tmp);
+
+ /* If we have a X_BB to check, go through it and make sure the insns we'd
+ duplicate don't write ORIG_OTHER_DEST. */
+ if (x_bb)
+ {
+ rtx_insn *tmp_insn = NULL;
+ FOR_BB_INSNS (x_bb, tmp_insn)
+ /* Don't check inside the destination insn, we will have changed
+ it to use a register that doesn't conflict. */
+ if (!(insn_x && tmp_insn == insn_x)
+ && modified_in_p (orig_other_dest, tmp_insn))
+ {
+ modified_in_x = true;
+ break;
+ }
+ }
+
+ /* Store the SET back in EMIT_X. */
+ *emit_x = set_tmp;
+ return modified_in_x;
+}
+
+/* Try more complex cases involving conditional_move.
+
+ We have:
+
+ if (test)
+ x = a + b;
+ else
+ x = c - d;
+
+ Make it:
+
+ t1 = a + b;
+ t2 = c - d;
+ x = (test) ? t1 : t2;
+
+ Alternatively, we have:
+
+ if (test)
+ x = *y;
+ else
+ x = *z;
+
+ Make it:
+
+ p1 = (test) ? y : z;
+ x = *p1;
+*/
static int
noce_try_cmove_arith (struct noce_if_info *if_info)
{
+ /* SET_SRC from the two branches. */
rtx a = if_info->a;
rtx b = if_info->b;
+ /* SET_DEST of both branches. */
rtx x = if_info->x;
- rtx orig_a, orig_b;
- rtx_insn *insn_a, *insn_b;
+ /* Full insns from the two branches. */
+ rtx_insn *insn_a = if_info->insn_a;
+ rtx_insn *insn_b = if_info->insn_b;
+ /* Whether the branches are single set. */
bool a_simple = if_info->then_simple;
bool b_simple = if_info->else_simple;
+ /* Our two basic blocks. */
basic_block then_bb = if_info->then_bb;
basic_block else_bb = if_info->else_bb;
+ /* Whether we're handling the transformation of a load. */
+ bool is_mem = false;
+ /* Copies of A and B before we modified them. */
+ rtx orig_a = a, orig_b = b;
+ /* A new target to be used by the conditional select. */
rtx target;
- int is_mem = 0;
- enum rtx_code code;
+ /* The RTX code for the condition in the test block. */
+ enum rtx_code code = GET_CODE (if_info->cond);
+ /* Our generated sequence. */
rtx_insn *ifcvt_seq;
/* A conditional move from two memory sources is equivalent to a
@@ -2094,33 +2198,19 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
if (cse_not_expected
&& MEM_P (a) && MEM_P (b)
&& MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b))
- {
- machine_mode address_mode = get_address_mode (a);
-
- a = XEXP (a, 0);
- b = XEXP (b, 0);
- x = gen_reg_rtx (address_mode);
- is_mem = 1;
- }
-
+ is_mem = true;
/* ??? We could handle this if we knew that a load from A or B could
not trap or fault. This is also true if we've already loaded
from the address along the path from ENTRY. */
else if (may_trap_or_fault_p (a) || may_trap_or_fault_p (b))
return FALSE;
- /* if (test) x = a + b; else x = c - d;
- => y = a + b;
- x = c - d;
- if (test)
- x = y;
- */
code = GET_CODE (if_info->cond);
insn_a = if_info->insn_a;
insn_b = if_info->insn_b;
- machine_mode x_mode = GET_MODE (x);
+ machine_mode x_mode = is_mem ? get_address_mode (a) : GET_MODE (x);
if (!can_conditionally_move_p (x_mode))
return FALSE;
@@ -2151,117 +2241,27 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
start_sequence ();
- /* If one of the blocks is empty then the corresponding B or A value
- came from the test block. The non-empty complex block that we will
- emit might clobber the register used by B or A, so move it to a pseudo
- first. */
-
- rtx tmp_a = NULL_RTX;
- rtx tmp_b = NULL_RTX;
-
- if (b_simple || !else_bb)
- tmp_b = gen_reg_rtx (x_mode);
-
- if (a_simple || !then_bb)
- tmp_a = gen_reg_rtx (x_mode);
-
orig_a = a;
orig_b = b;
- rtx emit_a = NULL_RTX;
- rtx emit_b = NULL_RTX;
- rtx_insn *tmp_insn = NULL;
- bool modified_in_a = false;
- bool modified_in_b = false;
- /* If either operand is complex, load it into a register first.
- The best way to do this is to copy the original insn. In this
- way we preserve any clobbers etc that the insn may have had.
- This is of course not possible in the IS_MEM case. */
-
- if (! general_operand (a, GET_MODE (a)) || tmp_a)
- {
-
- if (is_mem)
- {
- rtx reg = gen_reg_rtx (GET_MODE (a));
- emit_a = gen_rtx_SET (reg, a);
- }
- else
- {
- if (insn_a)
- {
- a = tmp_a ? tmp_a : gen_reg_rtx (GET_MODE (a));
-
- rtx_insn *copy_of_a = as_a <rtx_insn *> (copy_rtx (insn_a));
- rtx set = single_set (copy_of_a);
- SET_DEST (set) = a;
-
- emit_a = PATTERN (copy_of_a);
- }
- else
- {
- rtx tmp_reg = tmp_a ? tmp_a : gen_reg_rtx (GET_MODE (a));
- emit_a = gen_rtx_SET (tmp_reg, a);
- a = tmp_reg;
- }
- }
- }
-
- if (! general_operand (b, GET_MODE (b)) || tmp_b)
+ /* Get the addresses if this is a MEM. */
+ if (is_mem)
{
- if (is_mem)
- {
- rtx reg = gen_reg_rtx (GET_MODE (b));
- emit_b = gen_rtx_SET (reg, b);
- }
- else
- {
- if (insn_b)
- {
- b = tmp_b ? tmp_b : gen_reg_rtx (GET_MODE (b));
- rtx_insn *copy_of_b = as_a <rtx_insn *> (copy_rtx (insn_b));
- rtx set = single_set (copy_of_b);
-
- SET_DEST (set) = b;
- emit_b = PATTERN (copy_of_b);
- }
- else
- {
- rtx tmp_reg = tmp_b ? tmp_b : gen_reg_rtx (GET_MODE (b));
- emit_b = gen_rtx_SET (tmp_reg, b);
- b = tmp_reg;
- }
- }
+ a = XEXP (a, 0);
+ b = XEXP (b, 0);
}
- modified_in_a = emit_a != NULL_RTX && modified_in_p (orig_b, emit_a);
- if (tmp_b && then_bb)
- {
- FOR_BB_INSNS (then_bb, tmp_insn)
- /* Don't check inside insn_a. We will have changed it to emit_a
- with a destination that doesn't conflict. */
- if (!(insn_a && tmp_insn == insn_a)
- && modified_in_p (orig_b, tmp_insn))
- {
- modified_in_a = true;
- break;
- }
-
- }
+ rtx emit_a = NULL_RTX;
+ rtx emit_b = NULL_RTX;
- modified_in_b = emit_b != NULL_RTX && modified_in_p (orig_a, emit_b);
- if (tmp_a && else_bb)
- {
- FOR_BB_INSNS (else_bb, tmp_insn)
- /* Don't check inside insn_b. We will have changed it to emit_b
- with a destination that doesn't conflict. */
- if (!(insn_b && tmp_insn == insn_b)
- && modified_in_p (orig_a, tmp_insn))
- {
- modified_in_b = true;
- break;
- }
- }
+ /* Sort out temporary registers and figure out whether either branch
+ would clobber the other. */
+ bool modified_in_a
+ = noce_arith_helper (&a, &emit_a, insn_a, then_bb,
+ orig_b, (else_bb != NULL));
+ bool modified_in_b
+ = noce_arith_helper (&b, &emit_b, insn_b, else_bb,
+ orig_a, (then_bb != NULL));
/* If insn to set up A clobbers any registers B depends on, try to
swap insn that sets up A with the one that sets up B. If even
@@ -2285,6 +2285,12 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
else
goto end_seq_and_fail;
+ /* Emit the conditional move. If our operands were MEMs, we
+ need to generate a temporary to hold the result of conditionally
+ selecting between the two possible addresses. We'll fix this up
+ immediately afterwards. */
+ if (is_mem)
+ x = gen_reg_rtx (x_mode);
target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0),
XEXP (if_info->cond, 1), a, b);