@@ -783,6 +783,8 @@ static rtx noce_emit_cmove (struct noce_if_info *, rtx, enum rtx_code, rtx,
rtx, rtx, rtx, rtx = NULL, rtx = NULL);
static bool noce_try_cmove (struct noce_if_info *);
static bool noce_try_cmove_arith (struct noce_if_info *);
+static bool noce_try_cmove_load_mem_notrap (struct noce_if_info *);
+static bool noce_try_cmove_store_mem_notrap (struct noce_if_info *, rtx *, rtx);
static rtx noce_get_alt_condition (struct noce_if_info *, rtx, rtx_insn **);
static bool noce_try_minmax (struct noce_if_info *);
static bool noce_try_abs (struct noce_if_info *);
@@ -2401,6 +2403,237 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
return false;
}
+/* When target support suppress memory fault, try more complex cases involving
+ conditional_move's source or dest may trap or fault. */
+
+static bool
+noce_try_cmove_load_mem_notrap (struct noce_if_info *if_info)
+{
+ rtx a = if_info->a;
+ rtx b = if_info->b;
+ rtx x = if_info->x;
+
+ if (MEM_P (x))
+ return false;
+ /* Just handle a conditional move from one trap MEM + other non_trap,
+ non mem cases. */
+ if (!(MEM_P (a) ^ MEM_P (b)))
+ return false;
+ bool a_trap = may_trap_or_fault_p (a);
+ bool b_trap = may_trap_or_fault_p (b);
+
+ if (!(a_trap ^ b_trap))
+ return false;
+ if (a_trap && (!MEM_P (a) || !targetm.have_conditional_move_mem_notrap (a)))
+ return false;
+ if (b_trap && (!MEM_P (b) || !targetm.have_conditional_move_mem_notrap (b)))
+ return false;
+
+ rtx orig_b;
+ rtx_insn *insn_a, *insn_b;
+ bool a_simple = if_info->then_simple;
+ bool b_simple = if_info->else_simple;
+ basic_block then_bb = if_info->then_bb;
+ basic_block else_bb = if_info->else_bb;
+ rtx target;
+ enum rtx_code code;
+ rtx cond = if_info->cond;
+ rtx_insn *ifcvt_seq;
+
+ /* if (test) x = *a; else x = c - d;
+ => x = c - d;
+ if (test)
+ x = *a;
+ */
+
+ code = GET_CODE (cond);
+ insn_a = if_info->insn_a;
+ insn_b = if_info->insn_b;
+
+ machine_mode x_mode = GET_MODE (x);
+
+ if (!can_conditionally_move_p (x_mode))
+ return false;
+
+ /* Because we only handle one trap MEM + other non_trap, non mem cases,
+ just move one trap MEM always in then_bb. */
+ if (noce_reversed_cond_code (if_info) != UNKNOWN)
+ {
+ bool reversep = false;
+ if (b_trap)
+ reversep = true;
+
+ if (reversep)
+ {
+ if (if_info->rev_cond)
+ {
+ cond = if_info->rev_cond;
+ code = GET_CODE (cond);
+ }
+ else
+ code = reversed_comparison_code (cond, if_info->jump);
+ std::swap (a, b);
+ std::swap (insn_a, insn_b);
+ std::swap (a_simple, b_simple);
+ std::swap (then_bb, else_bb);
+ }
+ }
+
+ if (then_bb && else_bb
+ && (!bbs_ok_for_cmove_arith (then_bb, else_bb, if_info->orig_x)
+ || !bbs_ok_for_cmove_arith (else_bb, then_bb, if_info->orig_x)))
+ return false;
+
+ 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_b = NULL_RTX;
+
+ /* Don't move trap mem to a pseudo. */
+ if (!may_trap_or_fault_p (b) && (b_simple || !else_bb))
+ tmp_b = gen_reg_rtx (x_mode);
+
+ 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 (b, GET_MODE (b)) || tmp_b)
+ {
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ }
+
+ modified_in_b = emit_b != NULL_RTX && modified_in_p (a, emit_b);
+ /* 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
+ that doesn't help, punt. */
+ if (modified_in_a && !modified_in_b)
+ {
+ if (!noce_emit_bb (emit_b, else_bb, b_simple))
+ goto end_seq_and_fail;
+
+ if (!noce_emit_bb (emit_a, then_bb, a_simple))
+ goto end_seq_and_fail;
+ }
+ else if (!modified_in_a)
+ {
+ if (!noce_emit_bb (emit_b, else_bb, b_simple))
+ goto end_seq_and_fail;
+
+ if (!noce_emit_bb (emit_a, then_bb, a_simple))
+ goto end_seq_and_fail;
+ }
+ else
+ goto end_seq_and_fail;
+
+ target = noce_emit_cmove (if_info, x, code, XEXP (cond, 0), XEXP (cond, 1),
+ a, b);
+
+ if (! target)
+ goto end_seq_and_fail;
+
+ if (target != x)
+ noce_emit_move_insn (x, target);
+
+ ifcvt_seq = end_ifcvt_sequence (if_info);
+ if (!ifcvt_seq || !targetm.noce_conversion_profitable_p (ifcvt_seq, if_info))
+ return false;
+
+ emit_insn_before_setloc (ifcvt_seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_cmove_load_mem_notrap";
+ return true;
+
+ end_seq_and_fail:
+ end_sequence ();
+ return false;
+}
+
+static bool
+noce_try_cmove_store_mem_notrap (struct noce_if_info *if_info, rtx *x_ptr, rtx orig_x)
+{
+ rtx a = if_info->a;
+ rtx b = if_info->b;
+ rtx x = orig_x;
+ machine_mode x_mode = GET_MODE (x);
+
+ if (!MEM_P (x) || !rtx_equal_p (x, b))
+ return false;
+ if (!may_trap_or_fault_p (x) || !targetm.have_conditional_move_mem_notrap (x))
+ return false;
+ if (!if_info->then_simple || !register_operand (a, x_mode))
+ return false;
+
+ rtx cond = if_info->cond;
+ enum rtx_code code = GET_CODE (cond);
+ rtx_insn *ifcvt_seq;
+
+ start_sequence ();
+
+ rtx target = noce_emit_cmove (if_info, x, code, XEXP (cond, 0), XEXP (cond, 1),
+ a, b);
+
+ if (! target)
+ goto end_seq_and_fail;
+
+ if (target != x)
+ noce_emit_move_insn (x, target);
+
+ ifcvt_seq = end_ifcvt_sequence (if_info);
+ if (!ifcvt_seq || !targetm.noce_conversion_profitable_p (ifcvt_seq, if_info))
+ return false;
+
+ emit_insn_before_setloc (ifcvt_seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_cmove_load_mem_notrap";
+ if_info->x = orig_x;
+ *x_ptr = orig_x;
+ return true;
+
+ end_seq_and_fail:
+ end_sequence ();
+ return false;
+}
+
/* For most cases, the simplified condition we found is the best
choice, but this is not the case for the min/max/abs transforms.
For these we wish to know that it is A or B in the condition. */
@@ -4121,12 +4354,21 @@ noce_process_if_block (struct noce_if_info *if_info)
}
if (!set_b && MEM_P (orig_x))
+ {
+ /* Conditional_move_suppress_fault for condition mem store would not
+ move any arithmetic calculations. */
+ if (targetm.have_conditional_move_mem_notrap (orig_x)
+ && HAVE_conditional_move
+ && noce_try_cmove_store_mem_notrap (if_info, &x, orig_x))
+ goto success;
+ else
/* We want to avoid store speculation to avoid cases like
if (pthread_mutex_trylock(mutex))
++global_variable;
Rather than go to much effort here, we rely on the SSA optimizers,
which do a good enough job these days. */
- return false;
+ return false;
+ }
if (noce_try_move (if_info))
goto success;
@@ -4160,6 +4402,9 @@ noce_process_if_block (struct noce_if_info *if_info)
if (HAVE_conditional_move
&& noce_try_cmove_arith (if_info))
goto success;
+ if (HAVE_conditional_move
+ && noce_try_cmove_load_mem_notrap (if_info))
+ goto success;
if (noce_try_sign_mask (if_info))
goto success;
}
From: Lingling Kong <lingling.kong@intel.com> After added target HOOK TARGET_HAVE_CONDITIONAL_MOVE_MEM_NOTRAP, we could support a conditional move that load or store mem may trap or fault in if convert pass. Conditional move suppress fault for conditional mem store would not move any arithmetic calculations. For conditional mem load now just support a conditional move one trap mem and one no trap and no mem cases. gcc/ChangeLog: * ifcvt.cc (noce_try_cmove_load_mem_notrap): Use target hook to allow convert to cfcmov for conditional load. (noce_try_cmove_store_mem_notrap): Convert to conditional store. (noce_process_if_block): Ditto. --- gcc/ifcvt.cc | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-)