/* If-conversion support.
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2016 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree.h"
#include "cfghooks.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "expmed.h"
#include "optabs.h"
#include "shrink-wrap.h"
#include "rtl-iter.h"
#include "ifcvt.h"
+#include "params.h"
#ifndef MAX_CONDITIONAL_EXECUTE
#define MAX_CONDITIONAL_EXECUTE \
/* If this branches to JUMP_LABEL when the condition is false,
reverse the condition. */
if (GET_CODE (XEXP (test_if, 2)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (test_if, 2)) == JUMP_LABEL (jump))
+ && label_ref_label (XEXP (test_if, 2)) == JUMP_LABEL (jump))
{
enum rtx_code rev = reversed_comparison_code (cond, jump);
if (rev == UNKNOWN)
rtx_insn *from = then_first_tail;
if (!INSN_P (from))
from = find_active_insn_after (then_bb, from);
- delete_insn_chain (from, BB_END (then_bb), false);
+ delete_insn_chain (from, get_last_bb_insn (then_bb), false);
}
if (else_last_head)
delete_insn_chain (first_active_insn (else_bb), else_last_head, false);
/* The SET_DEST of INSN_A. */
rtx x;
+ /* The original set destination that the THEN and ELSE basic blocks finally
+ write their result to. */
+ rtx orig_x;
/* True if this if block is not canonical. In the canonical form of
if blocks, the THEN_BB is the block reached via the fallthru edge
from TEST_BB. For the noce transformations, we allow the symmetric
bool then_simple;
bool else_simple;
- /* The total rtx cost of the instructions in then_bb and else_bb. */
- unsigned int then_cost;
- unsigned int else_cost;
+ /* True if we're optimisizing the control block for speed, false if
+ we're optimizing for size. */
+ bool speed_p;
- /* Estimated cost of the particular branch instruction. */
- unsigned int branch_cost;
+ /* The combined cost of COND, JUMP and the costs for THEN_BB and
+ ELSE_BB. */
+ unsigned int original_cost;
+
+ /* Maximum permissible cost for the unconditional sequence we should
+ generate to replace this branch. */
+ unsigned int max_seq_cost;
+
+ /* The name of the noce transform that succeeded in if-converting
+ this structure. Used for debugging. */
+ const char *transform_name;
};
static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
static int noce_try_move (struct noce_if_info *);
+static int noce_try_ifelse_collapse (struct noce_if_info *);
static int noce_try_store_flag (struct noce_if_info *);
static int noce_try_addcc (struct noce_if_info *);
static int noce_try_store_flag_constants (struct noce_if_info *);
static int noce_try_abs (struct noce_if_info *);
static int noce_try_sign_mask (struct noce_if_info *);
+/* Return TRUE if SEQ is a good candidate as a replacement for the
+ if-convertible sequence described in IF_INFO. */
+
+inline static bool
+noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info)
+{
+ bool speed_p = if_info->speed_p;
+
+ /* Cost up the new sequence. */
+ unsigned int cost = seq_cost (seq, speed_p);
+
+ /* When compiling for size, we can make a reasonably accurately guess
+ at the size growth. */
+ if (!speed_p)
+ return cost <= if_info->original_cost;
+ else
+ return cost <= if_info->max_seq_cost;
+}
+
/* Helper function for noce_try_store_flag*. */
static rtx
rtx set = pc_set (if_info->jump);
cond = XEXP (SET_SRC (set), 0);
if (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump))
+ && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump))
reversep = !reversep;
if (if_info->then_else_reversed)
reversep = !reversep;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
}
+ if_info->transform_name = "noce_try_move";
return TRUE;
}
return FALSE;
}
+/* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that
+ through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE.
+ If that is the case, emit the result into x. */
+
+static int
+noce_try_ifelse_collapse (struct noce_if_info * if_info)
+{
+ if (!noce_simple_bbs (if_info))
+ return FALSE;
+
+ machine_mode mode = GET_MODE (if_info->x);
+ rtx if_then_else = simplify_gen_ternary (IF_THEN_ELSE, mode, mode,
+ if_info->cond, if_info->b,
+ if_info->a);
+
+ if (GET_CODE (if_then_else) == IF_THEN_ELSE)
+ return FALSE;
+
+ rtx_insn *seq;
+ start_sequence ();
+ noce_emit_move_insn (if_info->x, if_then_else);
+ seq = end_ifcvt_sequence (if_info);
+ if (!seq)
+ return FALSE;
+
+ emit_insn_before_setloc (seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+
+ if_info->transform_name = "noce_try_ifelse_collapse";
+ return TRUE;
+}
+
+
/* Convert "if (test) x = 1; else x = 0".
Only try 0 and STORE_FLAG_VALUE here. Other combinations will be
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_store_flag";
return TRUE;
}
else
}
}
+
+/* Convert "if (test) x = -A; else x = A" into
+ x = A; if (test) x = -x if the machine can do the
+ conditional negate form of this cheaply.
+ Try this before noce_try_cmove that will just load the
+ immediates into two registers and do a conditional select
+ between them. If the target has a conditional negate or
+ conditional invert operation we can save a potentially
+ expensive constant synthesis. */
+
+static bool
+noce_try_inverse_constants (struct noce_if_info *if_info)
+{
+ if (!noce_simple_bbs (if_info))
+ return false;
+
+ if (!CONST_INT_P (if_info->a)
+ || !CONST_INT_P (if_info->b)
+ || !REG_P (if_info->x))
+ return false;
+
+ machine_mode mode = GET_MODE (if_info->x);
+
+ HOST_WIDE_INT val_a = INTVAL (if_info->a);
+ HOST_WIDE_INT val_b = INTVAL (if_info->b);
+
+ rtx cond = if_info->cond;
+
+ rtx x = if_info->x;
+ rtx target;
+
+ start_sequence ();
+
+ rtx_code code;
+ if (val_b != HOST_WIDE_INT_MIN && val_a == -val_b)
+ code = NEG;
+ else if (val_a == ~val_b)
+ code = NOT;
+ else
+ {
+ end_sequence ();
+ return false;
+ }
+
+ rtx tmp = gen_reg_rtx (mode);
+ noce_emit_move_insn (tmp, if_info->a);
+
+ target = emit_conditional_neg_or_complement (x, code, mode, cond, tmp, tmp);
+
+ if (target)
+ {
+ rtx_insn *seq = get_insns ();
+
+ if (!seq)
+ {
+ end_sequence ();
+ return false;
+ }
+
+ if (target != if_info->x)
+ noce_emit_move_insn (if_info->x, target);
+
+ seq = end_ifcvt_sequence (if_info);
+
+ if (!seq)
+ return false;
+
+ emit_insn_before_setloc (seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_inverse_constants";
+ return true;
+ }
+
+ end_sequence ();
+ return false;
+}
+
+
/* Convert "if (test) x = a; else x = b", for A and B constant.
Also allow A = y + c1, B = y + c2, with a common y between A
and B. */
&& CONST_INT_P (XEXP (a, 1))
&& CONST_INT_P (XEXP (b, 1))
&& rtx_equal_p (XEXP (a, 0), XEXP (b, 0))
- && noce_operand_ok (XEXP (a, 0))
- && if_info->branch_cost >= 2)
+ /* Allow expressions that are not using the result or plain
+ registers where we handle overlap below. */
+ && (REG_P (XEXP (a, 0))
+ || (noce_operand_ok (XEXP (a, 0))
+ && ! reg_overlap_mentioned_p (if_info->x, XEXP (a, 0)))))
{
common = XEXP (a, 0);
a = XEXP (a, 1);
else
gcc_unreachable ();
}
- else if (ifalse == 0 && exact_log2 (itrue) >= 0
- && (STORE_FLAG_VALUE == 1
- || if_info->branch_cost >= 2))
+ /* Is this (cond) ? 2^n : 0? */
+ else if (ifalse == 0 && pow2p_hwi (itrue)
+ && STORE_FLAG_VALUE == 1)
normalize = 1;
- else if (itrue == 0 && exact_log2 (ifalse) >= 0 && can_reverse
- && (STORE_FLAG_VALUE == 1 || if_info->branch_cost >= 2))
+ /* Is this (cond) ? 0 : 2^n? */
+ else if (itrue == 0 && pow2p_hwi (ifalse) && can_reverse
+ && STORE_FLAG_VALUE == 1)
{
normalize = 1;
reversep = true;
}
+ /* Is this (cond) ? -1 : x? */
else if (itrue == -1
- && (STORE_FLAG_VALUE == -1
- || if_info->branch_cost >= 2))
+ && STORE_FLAG_VALUE == -1)
normalize = -1;
+ /* Is this (cond) ? x : -1? */
else if (ifalse == -1 && can_reverse
- && (STORE_FLAG_VALUE == -1 || if_info->branch_cost >= 2))
+ && STORE_FLAG_VALUE == -1)
{
normalize = -1;
reversep = true;
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
- if (!seq)
+ if (!seq || !noce_conversion_profitable_p (seq, if_info))
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_store_flag_constants";
+
return TRUE;
}
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
- if (!seq)
+ if (!seq || !noce_conversion_profitable_p (seq, if_info))
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_addcc";
+
return TRUE;
}
end_sequence ();
}
/* If that fails, construct conditional increment or decrement using
- setcc. */
- if (if_info->branch_cost >= 2
- && (XEXP (if_info->a, 1) == const1_rtx
- || XEXP (if_info->a, 1) == constm1_rtx))
+ setcc. We're changing a branch and an increment to a comparison and
+ an ADD/SUB. */
+ if (XEXP (if_info->a, 1) == const1_rtx
+ || XEXP (if_info->a, 1) == constm1_rtx)
{
start_sequence ();
if (STORE_FLAG_VALUE == INTVAL (XEXP (if_info->a, 1)))
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
- if (!seq)
+ if (!seq || !noce_conversion_profitable_p (seq, if_info))
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_addcc";
return TRUE;
}
end_sequence ();
return FALSE;
reversep = 0;
- if ((if_info->branch_cost >= 2
- || STORE_FLAG_VALUE == -1)
- && ((if_info->a == const0_rtx
- && rtx_equal_p (if_info->b, if_info->x))
- || ((reversep = (reversed_comparison_code (if_info->cond,
- if_info->jump)
- != UNKNOWN))
- && if_info->b == const0_rtx
- && rtx_equal_p (if_info->a, if_info->x))))
+
+ if ((if_info->a == const0_rtx
+ && rtx_equal_p (if_info->b, if_info->x))
+ || ((reversep = (reversed_comparison_code (if_info->cond,
+ if_info->jump)
+ != UNKNOWN))
+ && if_info->b == const0_rtx
+ && rtx_equal_p (if_info->a, if_info->x)))
{
start_sequence ();
target = noce_emit_store_flag (if_info,
if (target)
{
- int old_cost, new_cost, insn_cost;
- int speed_p;
-
if (target != if_info->x)
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
- if (!seq)
- return FALSE;
-
- speed_p = optimize_bb_for_speed_p (BLOCK_FOR_INSN (if_info->insn_a));
- insn_cost = insn_rtx_cost (PATTERN (if_info->insn_a), speed_p);
- old_cost = COSTS_N_INSNS (if_info->branch_cost) + insn_cost;
- new_cost = seq_cost (seq, speed_p);
-
- if (new_cost > old_cost)
+ if (!seq || !noce_conversion_profitable_p (seq, if_info))
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_store_flag_mask";
+
return TRUE;
}
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_cmove";
+
return TRUE;
}
/* If both a and b are constants try a last-ditch transformation:
we don't know about, so give them a chance before trying this
approach. */
else if (!targetm.have_conditional_execution ()
- && CONST_INT_P (if_info->a) && CONST_INT_P (if_info->b)
- && ((if_info->branch_cost >= 2 && STORE_FLAG_VALUE == -1)
- || if_info->branch_cost >= 3))
+ && CONST_INT_P (if_info->a) && CONST_INT_P (if_info->b))
{
machine_mode mode = GET_MODE (if_info->x);
HOST_WIDE_INT ifalse = INTVAL (if_info->a);
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
- if (!seq)
+ if (!seq || !noce_conversion_profitable_p (seq, if_info))
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_cmove";
return TRUE;
}
else
}
-/* Return true iff the registers that the insns in BB_A set do not
- get used in BB_B. */
+/* Return true iff the registers that the insns in BB_A set do not get
+ used in BB_B. If TO_RENAME is non-NULL then it is a location that will be
+ renamed later by the caller and so conflicts on it should be ignored
+ in this function. */
static bool
-bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b)
+bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b, rtx to_rename)
{
rtx_insn *a_insn;
bitmap bba_sets = BITMAP_ALLOC (®_obstack);
BITMAP_FREE (bba_sets);
return false;
}
-
/* Record all registers that BB_A sets. */
FOR_EACH_INSN_DEF (def, a_insn)
- bitmap_set_bit (bba_sets, DF_REF_REGNO (def));
+ if (!(to_rename && DF_REF_REG (def) == to_rename))
+ bitmap_set_bit (bba_sets, DF_REF_REGNO (def));
}
rtx_insn *b_insn;
}
/* Make sure this is a REG and not some instance
- of ZERO_EXTRACT or SUBREG or other dangerous stuff. */
- if (!REG_P (SET_DEST (sset_b)))
+ of ZERO_EXTRACT or SUBREG or other dangerous stuff.
+ If we have a memory destination then we have a pair of simple
+ basic blocks performing an operation of the form [addr] = c ? a : b.
+ bb_valid_for_noce_process_p will have ensured that these are
+ the only stores present. In that case [addr] should be the location
+ to be renamed. Assert that the callers set this up properly. */
+ if (MEM_P (SET_DEST (sset_b)))
+ gcc_assert (rtx_equal_p (SET_DEST (sset_b), to_rename));
+ else if (!REG_P (SET_DEST (sset_b)))
{
BITMAP_FREE (bba_sets);
return false;
conditional on their addresses followed by a load. Don't do this
early because it'll screw alias analysis. Note that we've
already checked for no side effects. */
- /* ??? FIXME: Magic number 5. */
if (cse_not_expected
&& MEM_P (a) && MEM_P (b)
- && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b)
- && if_info->branch_cost >= 5)
+ && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b))
{
machine_mode address_mode = get_address_mode (a);
if (!can_conditionally_move_p (x_mode))
return FALSE;
- unsigned int then_cost;
- unsigned int else_cost;
- if (insn_a)
- then_cost = if_info->then_cost;
- else
- then_cost = 0;
-
- if (insn_b)
- else_cost = if_info->else_cost;
- else
- else_cost = 0;
-
- /* We're going to execute one of the basic blocks anyway, so
- bail out if the most expensive of the two blocks is unacceptable. */
- if (MAX (then_cost, else_cost) > COSTS_N_INSNS (if_info->branch_cost))
- return FALSE;
-
/* Possibly rearrange operands to make things come out more natural. */
if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN)
{
}
}
- if (then_bb && else_bb && !a_simple && !b_simple
- && (!bbs_ok_for_cmove_arith (then_bb, else_bb)
- || !bbs_ok_for_cmove_arith (else_bb, then_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 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. */
-
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)
- if (modified_in_p (orig_b, 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;
}
}
- if (emit_a && modified_in_a)
- {
- modified_in_b = emit_b != NULL_RTX && modified_in_p (orig_a, emit_b);
- if (tmp_b && else_bb)
- {
- FOR_BB_INSNS (else_bb, tmp_insn)
- if (modified_in_p (orig_a, tmp_insn))
- {
- modified_in_b = true;
- break;
- }
-
- }
- if (modified_in_b)
- goto end_seq_and_fail;
- if (!noce_emit_bb (emit_b, else_bb, b_simple))
- goto end_seq_and_fail;
+ 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;
+ }
+ }
- if (!noce_emit_bb (emit_a, then_bb, a_simple))
- goto end_seq_and_fail;
- }
- else
- {
- if (!noce_emit_bb (emit_a, then_bb, a_simple))
- goto end_seq_and_fail;
+ /* 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_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_a, then_bb, a_simple))
+ goto end_seq_and_fail;
- }
+ if (!noce_emit_bb (emit_b, else_bb, b_simple))
+ goto end_seq_and_fail;
+ }
+ else
+ goto end_seq_and_fail;
target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0),
XEXP (if_info->cond, 1), a, b);
noce_emit_move_insn (x, target);
ifcvt_seq = end_ifcvt_sequence (if_info);
- if (!ifcvt_seq)
+ if (!ifcvt_seq || !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_arith";
return TRUE;
end_seq_and_fail:
cond = XEXP (SET_SRC (set), 0);
reverse
= GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump);
+ && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (if_info->jump);
if (if_info->then_else_reversed)
reverse = !reverse;
switch (code)
{
case LT:
- if (actual_val == desired_val + 1)
+ if (desired_val != HOST_WIDE_INT_MAX
+ && actual_val == desired_val + 1)
{
code = LE;
op_b = GEN_INT (desired_val);
}
break;
case LE:
- if (actual_val == desired_val - 1)
+ if (desired_val != HOST_WIDE_INT_MIN
+ && actual_val == desired_val - 1)
{
code = LT;
op_b = GEN_INT (desired_val);
}
break;
case GT:
- if (actual_val == desired_val - 1)
+ if (desired_val != HOST_WIDE_INT_MIN
+ && actual_val == desired_val - 1)
{
code = GE;
op_b = GEN_INT (desired_val);
}
break;
case GE:
- if (actual_val == desired_val + 1)
+ if (desired_val != HOST_WIDE_INT_MAX
+ && actual_val == desired_val + 1)
{
code = GT;
op_b = GEN_INT (desired_val);
emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a));
if_info->cond = cond;
if_info->cond_earliest = earliest;
+ if_info->transform_name = "noce_try_minmax";
return TRUE;
}
/* Work around funny ideas get_condition has wrt canonicalization.
Note that these rtx constants are known to be CONST_INT, and
- therefore imply integer comparisons. */
+ therefore imply integer comparisons.
+ The one_cmpl case is more complicated, as we want to handle
+ only x < 0 ? ~x : x or x >= 0 ? x : ~x to one_cmpl_abs (x)
+ and x < 0 ? x : ~x or x >= 0 ? ~x : x to ~one_cmpl_abs (x),
+ but not other cases (x > -1 is equivalent of x >= 0). */
if (c == constm1_rtx && GET_CODE (cond) == GT)
;
else if (c == const1_rtx && GET_CODE (cond) == LT)
- ;
- else if (c != CONST0_RTX (GET_MODE (b)))
+ {
+ if (one_cmpl)
+ return FALSE;
+ }
+ else if (c == CONST0_RTX (GET_MODE (b)))
+ {
+ if (one_cmpl
+ && GET_CODE (cond) != GE
+ && GET_CODE (cond) != LT)
+ return FALSE;
+ }
+ else
return FALSE;
/* Determine what sort of operation this is. */
emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a));
if_info->cond = cond;
if_info->cond_earliest = earliest;
+ if_info->transform_name = "noce_try_abs";
return TRUE;
}
&& (if_info->insn_b == NULL_RTX
|| BLOCK_FOR_INSN (if_info->insn_b) == if_info->test_bb));
if (!(t_unconditional
- || (set_src_cost (t, mode, optimize_bb_for_speed_p (if_info->test_bb))
+ || (set_src_cost (t, mode, if_info->speed_p)
< COSTS_N_INSNS (2))))
return FALSE;
return FALSE;
emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_sign_mask";
+
return TRUE;
}
if (! rtx_equal_p (x, XEXP (a, 0))
|| !CONST_INT_P (XEXP (a, 1))
|| (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
- != (unsigned HOST_WIDE_INT) 1 << bitnum)
+ != HOST_WIDE_INT_1U << bitnum)
return FALSE;
/* if ((x & C) == 0) x |= C; is transformed to x |= C. */
else if (code == NE)
{
/* if ((x & C) == 0) x ^= C; is transformed to x |= C. */
- result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode);
+ result = gen_int_mode (HOST_WIDE_INT_1 << bitnum, mode);
result = simplify_gen_binary (IOR, mode, x, result);
}
else
{
/* if ((x & C) != 0) x ^= C; is transformed to x &= ~C. */
- result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode);
+ result = gen_int_mode (~(HOST_WIDE_INT_1 << bitnum), mode);
result = simplify_gen_binary (AND, mode, x, result);
}
}
if (! rtx_equal_p (x, XEXP (a, 0))
|| !CONST_INT_P (XEXP (a, 1))
|| (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
- != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode)))
+ != (~(HOST_WIDE_INT_1 << bitnum) & GET_MODE_MASK (mode)))
return FALSE;
/* if ((x & C) == 0) x &= ~C; is transformed to nothing. */
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATION (if_info->insn_a));
}
+ if_info->transform_name = "noce_try_bitop";
return TRUE;
}
/* If this branches to JUMP_LABEL when the condition is false,
reverse the condition. */
reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (jump));
+ && label_ref_label (XEXP (SET_SRC (set), 2)) == JUMP_LABEL (jump));
/* We may have to reverse because the caller's if block is not canonical,
i.e. the THEN block isn't the fallthrough block for the TEST block
return ! may_trap_p (op);
}
-/* Return true if a write into MEM may trap or fault. */
-
-static bool
-noce_mem_write_may_trap_or_fault_p (const_rtx mem)
-{
- rtx addr;
-
- if (MEM_READONLY_P (mem))
- return true;
-
- if (may_trap_or_fault_p (mem))
- return true;
-
- addr = XEXP (mem, 0);
-
- /* Call target hook to avoid the effects of -fpic etc.... */
- addr = targetm.delegitimize_address (addr);
-
- while (addr)
- switch (GET_CODE (addr))
- {
- case CONST:
- case PRE_DEC:
- case PRE_INC:
- case POST_DEC:
- case POST_INC:
- case POST_MODIFY:
- addr = XEXP (addr, 0);
- break;
- case LO_SUM:
- case PRE_MODIFY:
- addr = XEXP (addr, 1);
- break;
- case PLUS:
- if (CONST_INT_P (XEXP (addr, 1)))
- addr = XEXP (addr, 0);
- else
- return false;
- break;
- case LABEL_REF:
- return true;
- case SYMBOL_REF:
- if (SYMBOL_REF_DECL (addr)
- && decl_readonly_section (SYMBOL_REF_DECL (addr), 0))
- return true;
- return false;
- default:
- return false;
- }
-
- return false;
-}
-
-/* Return whether we can use store speculation for MEM. TOP_BB is the
- basic block above the conditional block where we are considering
- doing the speculative store. We look for whether MEM is set
- unconditionally later in the function. */
-
-static bool
-noce_can_store_speculate_p (basic_block top_bb, const_rtx mem)
-{
- basic_block dominator;
-
- for (dominator = get_immediate_dominator (CDI_POST_DOMINATORS, top_bb);
- dominator != NULL;
- dominator = get_immediate_dominator (CDI_POST_DOMINATORS, dominator))
- {
- rtx_insn *insn;
-
- FOR_BB_INSNS (dominator, insn)
- {
- /* If we see something that might be a memory barrier, we
- have to stop looking. Even if the MEM is set later in
- the function, we still don't want to set it
- unconditionally before the barrier. */
- if (INSN_P (insn)
- && (volatile_insn_p (PATTERN (insn))
- || (CALL_P (insn) && (!RTL_CONST_CALL_P (insn)))))
- return false;
-
- if (memory_must_be_modified_in_insn_p (mem, insn))
- return true;
- if (modified_in_p (XEXP (mem, 0), insn))
- return false;
-
- }
- }
-
- return false;
-}
-
/* Return true if X contains a MEM subrtx. */
static bool
x := a and all previous computations
in TEST_BB don't produce any values that are live after TEST_BB.
In other words, all the insns in TEST_BB are there only
- to compute a value for x. Put the rtx cost of the insns
- in TEST_BB into COST. Record whether TEST_BB is a single simple
+ to compute a value for x. Add the rtx cost of the insns
+ in TEST_BB to COST. Record whether TEST_BB is a single simple
set instruction in SIMPLE_P. */
static bool
if (first_insn == last_insn)
{
*simple_p = noce_operand_ok (SET_DEST (first_set));
- *cost = insn_rtx_cost (first_set, speed_p);
+ *cost += insn_rtx_cost (first_set, speed_p);
return *simple_p;
}
goto free_bitmap_and_fail;
BITMAP_FREE (test_bb_temps);
- *cost = potential_cost;
+ *cost += potential_cost;
*simple_p = false;
return true;
if (if_info->then_else_reversed)
std::swap (old_val, new_val);
+
+ /* We allow simple lowpart register subreg SET sources in
+ bb_ok_for_noce_convert_multiple_sets. Be careful when processing
+ sequences like:
+ (set (reg:SI r1) (reg:SI r2))
+ (set (reg:HI r3) (subreg:HI (r1)))
+ For the second insn new_val or old_val (r1 in this example) will be
+ taken from the temporaries and have the wider mode which will not
+ match with the mode of the other source of the conditional move, so
+ we'll end up trying to emit r4:HI = cond ? (r1:SI) : (r3:HI).
+ Wrap the two cmove operands into subregs if appropriate to prevent
+ that. */
+ if (GET_MODE (new_val) != GET_MODE (temp))
+ {
+ machine_mode src_mode = GET_MODE (new_val);
+ machine_mode dst_mode = GET_MODE (temp);
+ if (GET_MODE_SIZE (src_mode) <= GET_MODE_SIZE (dst_mode))
+ {
+ end_sequence ();
+ return FALSE;
+ }
+ new_val = lowpart_subreg (dst_mode, new_val, src_mode);
+ }
+ if (GET_MODE (old_val) != GET_MODE (temp))
+ {
+ machine_mode src_mode = GET_MODE (old_val);
+ machine_mode dst_mode = GET_MODE (temp);
+ if (GET_MODE_SIZE (src_mode) <= GET_MODE_SIZE (dst_mode))
+ {
+ end_sequence ();
+ return FALSE;
+ }
+ old_val = lowpart_subreg (dst_mode, old_val, src_mode);
+ }
+
/* Actually emit the conditional move. */
rtx temp_dest = noce_emit_cmove (if_info, temp, cond_code,
x, y, new_val, old_val);
for (int i = 0; i < count; i++)
noce_emit_move_insn (targets[i], temporaries[i]);
- /* Actually emit the sequence. */
+ /* Actually emit the sequence if it isn't too expensive. */
rtx_insn *seq = get_insns ();
+ if (!noce_conversion_profitable_p (seq, if_info))
+ {
+ end_sequence ();
+ return FALSE;
+ }
+
for (insn = seq; insn; insn = NEXT_INSN (insn))
set_used_flags (insn);
}
num_updated_if_blocks++;
+ if_info->transform_name = "noce_convert_multiple_sets";
return TRUE;
}
/* Return true iff basic block TEST_BB is comprised of only
(SET (REG) (REG)) insns suitable for conversion to a series
- of conditional moves. FORNOW: Use II to find the expected cost of
- the branch into/over TEST_BB.
-
- TODO: This creates an implicit "magic number" for branch_cost.
- II->branch_cost now guides the maximum number of set instructions in
- a basic block which is considered profitable to completely
- if-convert. */
+ of conditional moves. Also check that we have more than one set
+ (other routines can handle a single set better than we would), and
+ fewer than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. */
static bool
-bb_ok_for_noce_convert_multiple_sets (basic_block test_bb,
- struct noce_if_info *ii)
+bb_ok_for_noce_convert_multiple_sets (basic_block test_bb)
{
rtx_insn *insn;
unsigned count = 0;
+ unsigned param = PARAM_VALUE (PARAM_MAX_RTL_IF_CONVERSION_INSNS);
FOR_BB_INSNS (test_bb, insn)
{
rtx src = SET_SRC (set);
/* We can possibly relax this, but for now only handle REG to REG
- moves. This avoids any issues that might come from introducing
- loads/stores that might violate data-race-freedom guarantees. */
- if (!(REG_P (src) && REG_P (dest)))
+ (including subreg) moves. This avoids any issues that might come
+ from introducing loads/stores that might violate data-race-freedom
+ guarantees. */
+ if (!REG_P (dest))
+ return false;
+
+ if (!(REG_P (src)
+ || (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src))
+ && subreg_lowpart_p (src))))
return false;
/* Destination must be appropriate for a conditional write. */
if (!can_conditionally_move_p (GET_MODE (dest)))
return false;
- ++count;
+ count++;
}
- /* FORNOW: Our cost model is a count of the number of instructions we
- would if-convert. This is suboptimal, and should be improved as part
- of a wider rework of branch_cost. */
- if (count > ii->branch_cost)
- return FALSE;
-
- return count > 0;
+ /* If we would only put out one conditional move, the other strategies
+ this pass tries are better optimized and will be more appropriate.
+ Some targets want to strictly limit the number of conditional moves
+ that are emitted, they set this through PARAM, we need to respect
+ that. */
+ return count > 1 && count <= param;
}
/* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
if (!else_bb
&& HAVE_conditional_move
&& !HAVE_cc0
- && bb_ok_for_noce_convert_multiple_sets (then_bb, if_info))
+ && bb_ok_for_noce_convert_multiple_sets (then_bb))
{
if (noce_convert_multiple_sets (if_info))
- return TRUE;
+ {
+ if (dump_file && if_info->transform_name)
+ fprintf (dump_file, "if-conversion succeeded through %s\n",
+ if_info->transform_name);
+ return TRUE;
+ }
}
- if (! bb_valid_for_noce_process_p (then_bb, cond, &if_info->then_cost,
+ if (! bb_valid_for_noce_process_p (then_bb, cond, &if_info->original_cost,
&if_info->then_simple))
return false;
if (else_bb
- && ! bb_valid_for_noce_process_p (else_bb, cond, &if_info->else_cost,
+ && ! bb_valid_for_noce_process_p (else_bb, cond, &if_info->original_cost,
&if_info->else_simple))
return false;
/* Only operate on register destinations, and even then avoid extending
the lifetime of hard registers on small register class machines. */
orig_x = x;
+ if_info->orig_x = orig_x;
if (!REG_P (x)
|| (HARD_REGISTER_P (x)
&& targetm.small_register_classes_for_mode_p (GET_MODE (x))))
}
if (!set_b && MEM_P (orig_x))
- {
- /* Disallow the "if (...) x = a;" form (implicit "else x = x;")
- for optimizations if writing to x may trap or fault,
- i.e. it's a memory other than a static var or a stack slot,
- is misaligned on strict aligned machines or is read-only. If
- x is a read-only memory, then the program is valid only if we
- avoid the store into it. If there are stores on both the
- THEN and ELSE arms, then we can go ahead with the conversion;
- either the program is broken, or the condition is always
- false such that the other memory is selected. */
- if (noce_mem_write_may_trap_or_fault_p (orig_x))
- return FALSE;
-
- /* Avoid store speculation: given "if (...) x = a" where x is a
- MEM, we only want to do the store if x is always set
- somewhere in the function. This avoids cases like
- if (pthread_mutex_trylock(mutex))
- ++global_variable;
- where we only want global_variable to be changed if the mutex
- is held. FIXME: This should ideally be expressed directly in
- RTL somehow. */
- if (!noce_can_store_speculate_p (test_bb, orig_x))
- return FALSE;
- }
+ /* 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;
if (noce_try_move (if_info))
goto success;
+ if (noce_try_ifelse_collapse (if_info))
+ goto success;
if (noce_try_store_flag (if_info))
goto success;
if (noce_try_bitop (if_info))
goto success;
if (noce_try_abs (if_info))
goto success;
+ if (noce_try_inverse_constants (if_info))
+ goto success;
if (!targetm.have_conditional_execution ()
&& noce_try_store_flag_constants (if_info))
goto success;
return FALSE;
success:
+ if (dump_file && if_info->transform_name)
+ fprintf (dump_file, "if-conversion succeeded through %s\n",
+ if_info->transform_name);
/* If we used a temporary, fix it up now. */
if (orig_x != x)
vec<rtx> else_regs = vNULL;
unsigned int i;
int success_p = FALSE;
+ int limit = PARAM_VALUE (PARAM_MAX_RTL_IF_CONVERSION_INSNS);
/* Build a mapping for each block to the value used for each
register. */
is the number of assignments currently made in only one of the
branches, since if we convert we are going to always execute
them. */
- if (c > MAX_CONDITIONAL_EXECUTE)
+ if (c > MAX_CONDITIONAL_EXECUTE
+ || c > limit)
goto done;
/* Try to emit the conditional moves. First do the then block,
}
num_updated_if_blocks++;
-
success_p = TRUE;
done:
rtx cond;
rtx_insn *cond_earliest;
struct noce_if_info if_info;
+ bool speed_p = optimize_bb_for_speed_p (test_bb);
/* We only ever should get here before reload. */
gcc_assert (!reload_completed);
if_info.cond_earliest = cond_earliest;
if_info.jump = jump;
if_info.then_else_reversed = then_else_reversed;
- if_info.branch_cost = BRANCH_COST (optimize_bb_for_speed_p (test_bb),
- predictable_edge_p (then_edge));
+ if_info.speed_p = speed_p;
+ if_info.max_seq_cost
+ = targetm.max_noce_ifcvt_seq_cost (then_edge);
+ /* We'll add in the cost of THEN_BB and ELSE_BB later, when we check
+ that they are valid to transform. We can't easily get back to the insn
+ for COND (and it may not exist if we had to canonicalize to get COND),
+ and jump_insns are always given a cost of 1 by seq_cost, so treat
+ both instructions as having cost COSTS_N_INSNS (1). */
+ if_info.original_cost = COSTS_N_INSNS (2);
+
/* Do the real work. */
return FALSE;
/* If the conditional jump is more than just a conditional jump, then
- we can not do if-conversion on this block. */
- if (! onlyjump_p (jump))
+ we can not do if-conversion on this block. Give up for returnjump_p,
+ changing a conditional return followed by unconditional trap for
+ conditional trap followed by unconditional return is likely not
+ beneficial and harder to handle. */
+ if (! onlyjump_p (jump) || returnjump_p (jump))
return FALSE;
/* We must be comparing objects whose modes imply the size. */
num_true_changes++;
num_updated_if_blocks++;
-
return TRUE;
}