#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "function.h"
#include "flags.h"
#include "insn-config.h"
#include "recog.h"
#include "except.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "expr.h"
#include "real.h"
#include "output.h"
#include "optabs.h"
#include "toplev.h"
#include "tm_p.h"
#include "cfgloop.h"
#include "target.h"
#include "timevar.h"
#include "tree-pass.h"
#ifndef HAVE_conditional_execution
#define HAVE_conditional_execution 0
#endif
#ifndef HAVE_conditional_move
#define HAVE_conditional_move 0
#endif
#ifndef HAVE_incscc
#define HAVE_incscc 0
#endif
#ifndef HAVE_decscc
#define HAVE_decscc 0
#endif
#ifndef HAVE_trap
#define HAVE_trap 0
#endif
#ifndef HAVE_conditional_trap
#define HAVE_conditional_trap 0
#endif
#ifndef MAX_CONDITIONAL_EXECUTE
#define MAX_CONDITIONAL_EXECUTE (BRANCH_COST + 1)
#endif
#define NULL_BLOCK ((basic_block) NULL)
static int num_possible_if_blocks;
static int num_updated_if_blocks;
static int num_true_changes;
static int cond_exec_changed_p;
static bool life_data_ok;
static int count_bb_insns (basic_block);
static bool cheap_bb_rtx_cost_p (basic_block, int);
static rtx first_active_insn (basic_block);
static rtx last_active_insn (basic_block, int);
static basic_block block_fallthru (basic_block);
static int cond_exec_process_insns (ce_if_block_t *, rtx, rtx, rtx, rtx, int);
static rtx cond_exec_get_condition (rtx);
static int cond_exec_process_if_block (ce_if_block_t *, int);
static rtx noce_get_condition (rtx, rtx *);
static int noce_operand_ok (rtx);
static int noce_process_if_block (ce_if_block_t *);
static int process_if_block (ce_if_block_t *);
static void merge_if_block (ce_if_block_t *);
static int find_cond_trap (basic_block, edge, edge);
static basic_block find_if_header (basic_block, int);
static int block_jumps_and_fallthru_p (basic_block, basic_block);
static int find_if_block (ce_if_block_t *);
static int find_if_case_1 (basic_block, edge, edge);
static int find_if_case_2 (basic_block, edge, edge);
static int find_memory (rtx *, void *);
static int dead_or_predicable (basic_block, basic_block, basic_block,
basic_block, int);
static void noce_emit_move_insn (rtx, rtx);
static rtx block_has_only_trap (basic_block);
static int
count_bb_insns (basic_block bb)
{
int count = 0;
rtx insn = BB_HEAD (bb);
while (1)
{
if (CALL_P (insn) || NONJUMP_INSN_P (insn))
count++;
if (insn == BB_END (bb))
break;
insn = NEXT_INSN (insn);
}
return count;
}
static bool
cheap_bb_rtx_cost_p (basic_block bb, int max_cost)
{
int count = 0;
rtx insn = BB_HEAD (bb);
while (1)
{
if (NONJUMP_INSN_P (insn))
{
int cost = insn_rtx_cost (PATTERN (insn));
if (cost == 0)
return false;
#ifdef STACK_REGS
{
rtx set = single_set (insn);
if (set && STACK_REG_P (SET_DEST (set)))
return false;
}
#endif
count += cost;
if (count >= max_cost)
return false;
}
else if (CALL_P (insn))
return false;
if (insn == BB_END (bb))
break;
insn = NEXT_INSN (insn);
}
return true;
}
static rtx
first_active_insn (basic_block bb)
{
rtx insn = BB_HEAD (bb);
if (LABEL_P (insn))
{
if (insn == BB_END (bb))
return NULL_RTX;
insn = NEXT_INSN (insn);
}
while (NOTE_P (insn))
{
if (insn == BB_END (bb))
return NULL_RTX;
insn = NEXT_INSN (insn);
}
if (JUMP_P (insn))
return NULL_RTX;
return insn;
}
static rtx
last_active_insn (basic_block bb, int skip_use_p)
{
rtx insn = BB_END (bb);
rtx head = BB_HEAD (bb);
while (NOTE_P (insn)
|| JUMP_P (insn)
|| (skip_use_p
&& NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == USE))
{
if (insn == head)
return NULL_RTX;
insn = PREV_INSN (insn);
}
if (LABEL_P (insn))
return NULL_RTX;
return insn;
}
static basic_block
block_fallthru (basic_block bb)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
break;
return (e) ? e->dest : NULL_BLOCK;
}
static int
cond_exec_process_insns (ce_if_block_t *ce_info ATTRIBUTE_UNUSED,
rtx start,
rtx end,
rtx test,
rtx prob_val,
int mod_ok)
{
int must_be_last = FALSE;
rtx insn;
rtx xtest;
rtx pattern;
if (!start || !end)
return FALSE;
for (insn = start; ; insn = NEXT_INSN (insn))
{
if (NOTE_P (insn))
goto insn_done;
gcc_assert(NONJUMP_INSN_P (insn) || CALL_P (insn));
if (reload_completed && GET_CODE (PATTERN (insn)) == USE)
{
SET_INSN_DELETED (insn);
goto insn_done;
}
#ifdef TARGET_ARM
if (must_be_last
&& GET_CODE (PATTERN (insn)) == COND_EXEC
&& rtx_equal_p (COND_EXEC_TEST (PATTERN (insn)), test))
goto insn_done;
#endif
if (must_be_last)
return FALSE;
if (modified_in_p (test, insn))
{
if (!mod_ok)
return FALSE;
must_be_last = TRUE;
}
if (CALL_P (insn) && find_reg_note (insn, REG_NORETURN, NULL_RTX))
return FALSE;
pattern = PATTERN (insn);
xtest = copy_rtx (test);
if (GET_CODE (pattern) == COND_EXEC)
{
if (GET_MODE (xtest) != GET_MODE (COND_EXEC_TEST (pattern)))
return FALSE;
xtest = gen_rtx_AND (GET_MODE (xtest), xtest,
COND_EXEC_TEST (pattern));
pattern = COND_EXEC_CODE (pattern);
}
pattern = gen_rtx_COND_EXEC (VOIDmode, xtest, pattern);
#ifdef IFCVT_MODIFY_INSN
IFCVT_MODIFY_INSN (ce_info, pattern, insn);
if (! pattern)
return FALSE;
#endif
validate_change (insn, &PATTERN (insn), pattern, 1);
if (CALL_P (insn) && prob_val)
validate_change (insn, ®_NOTES (insn),
alloc_EXPR_LIST (REG_BR_PROB, prob_val,
REG_NOTES (insn)), 1);
insn_done:
if (insn == end)
break;
}
return TRUE;
}
static rtx
cond_exec_get_condition (rtx jump)
{
rtx test_if, cond;
if (any_condjump_p (jump))
test_if = SET_SRC (pc_set (jump));
else
return NULL_RTX;
cond = XEXP (test_if, 0);
if (GET_CODE (XEXP (test_if, 2)) == LABEL_REF
&& XEXP (XEXP (test_if, 2), 0) == JUMP_LABEL (jump))
{
enum rtx_code rev = reversed_comparison_code (cond, jump);
if (rev == UNKNOWN)
return NULL_RTX;
cond = gen_rtx_fmt_ee (rev, GET_MODE (cond), XEXP (cond, 0),
XEXP (cond, 1));
}
return cond;
}
static bool
cond_exec_branch_targets_equiv (rtx insn1, rtx insn2)
{
rtx cond1, cond2;
if (!any_condjump_p (insn1) || !any_condjump_p (insn2))
return false;
cond1 = SET_SRC (pc_set (insn1));
cond2 = SET_SRC (pc_set (insn2));
if (rtx_equal_p (XEXP (cond1, 1), XEXP (cond2, 1))
&& rtx_equal_p (XEXP (cond1, 2), XEXP (cond2, 2)))
return true;
return false;
}
static int
cond_exec_process_if_block (ce_if_block_t * ce_info,
int do_multiple_p)
{
basic_block test_bb = ce_info->test_bb;
basic_block then_bb = ce_info->then_bb;
basic_block else_bb = ce_info->else_bb;
rtx test_expr;
rtx then_start = NULL_RTX;
rtx then_end = NULL_RTX;
rtx else_start = NULL_RTX;
rtx else_end = NULL_RTX;
int max;
int then_mod_ok;
rtx true_expr;
rtx false_expr;
rtx true_prob_val;
rtx false_prob_val;
int n_insns = 0;
enum rtx_code false_code;
if (!do_multiple_p && ce_info->num_multiple_test_blocks)
{
if (else_bb || ! ce_info->and_and_p)
return FALSE;
ce_info->test_bb = test_bb = ce_info->last_test_bb;
ce_info->num_multiple_test_blocks = 0;
ce_info->num_and_and_blocks = 0;
ce_info->num_or_or_blocks = 0;
}
test_expr = cond_exec_get_condition (BB_END (test_bb));
if (! test_expr)
return FALSE;
if (! onlyjump_p (BB_END (test_bb)))
return FALSE;
if (then_bb)
{
then_start = first_active_insn (then_bb);
then_end = last_active_insn (then_bb, TRUE);
n_insns = ce_info->num_then_insns = count_bb_insns (then_bb);
max = MAX_CONDITIONAL_EXECUTE;
if (else_bb)
{
max *= 2;
else_start = first_active_insn (else_bb);
else_end = last_active_insn (else_bb, TRUE);
n_insns += ce_info->num_else_insns = count_bb_insns (else_bb);
}
if (n_insns > max)
return FALSE;
}
true_expr = test_expr;
false_code = reversed_comparison_code (true_expr, BB_END (test_bb));
if (false_code != UNKNOWN)
false_expr = gen_rtx_fmt_ee (false_code, GET_MODE (true_expr),
XEXP (true_expr, 0), XEXP (true_expr, 1));
else
false_expr = NULL_RTX;
#ifdef IFCVT_MODIFY_TESTS
IFCVT_MODIFY_TESTS (ce_info, true_expr, false_expr);
if (!true_expr || !false_expr)
goto fail;
#endif
true_prob_val = find_reg_note (BB_END (test_bb), REG_BR_PROB, NULL_RTX);
if (true_prob_val)
{
true_prob_val = XEXP (true_prob_val, 0);
false_prob_val = GEN_INT (REG_BR_PROB_BASE - INTVAL (true_prob_val));
}
else
false_prob_val = NULL_RTX;
if (ce_info->num_multiple_test_blocks > 0)
{
basic_block bb = test_bb;
basic_block last_test_bb = ce_info->last_test_bb;
if (! false_expr)
goto fail;
do
{
int mod_ok = 0;
rtx start, end;
rtx t, f;
enum rtx_code f_code;
bb = block_fallthru (bb);
start = first_active_insn (bb);
end = last_active_insn (bb, TRUE);
t = cond_exec_get_condition (BB_END (bb));
if (t && rtx_equal_p (t, true_expr)
&& cond_exec_branch_targets_equiv (BB_END (bb), BB_END (test_bb)))
mod_ok = 1;
if (start
&& ! cond_exec_process_insns (ce_info, start, end, false_expr,
false_prob_val, mod_ok))
goto fail;
if (! onlyjump_p (BB_END (bb)))
goto fail;
t = cond_exec_get_condition (BB_END (bb));
if (! t)
goto fail;
f_code = reversed_comparison_code (t, BB_END (bb));
if (f_code == UNKNOWN)
goto fail;
f = gen_rtx_fmt_ee (f_code, GET_MODE (t), XEXP (t, 0), XEXP (t, 1));
if (ce_info->and_and_p)
{
t = gen_rtx_IOR (GET_MODE (t), true_expr, t);
f = gen_rtx_AND (GET_MODE (t), false_expr, f);
}
else
{
f = gen_rtx_IOR (GET_MODE (t), true_expr, f);
t = gen_rtx_AND (GET_MODE (t), false_expr, t);
}
#ifdef IFCVT_MODIFY_MULTIPLE_TESTS
IFCVT_MODIFY_MULTIPLE_TESTS (ce_info, bb, t, f);
if (!t || !f)
goto fail;
#endif
true_expr = t;
false_expr = f;
}
while (bb != last_test_bb);
if (!then_bb && !rtx_equal_p (true_expr, test_expr))
if (any_condjump_p (BB_END (bb)))
validate_change (BB_END (bb), &XEXP (SET_SRC (pc_set (BB_END (bb))), 0),
true_expr, 1);
}
then_mod_ok = (else_bb == NULL_BLOCK);
if (then_bb && then_end
&& (! false_expr
|| ! cond_exec_process_insns (ce_info, then_start, then_end,
false_expr, false_prob_val,
then_mod_ok)))
goto fail;
if (else_bb && else_end
&& ! cond_exec_process_insns (ce_info, else_start, else_end,
true_expr, true_prob_val, TRUE))
goto fail;
if (! apply_change_group ())
{
#ifdef IFCVT_MODIFY_CANCEL
IFCVT_MODIFY_CANCEL (ce_info);
#endif
return FALSE;
}
#ifdef IFCVT_MODIFY_FINAL
IFCVT_MODIFY_FINAL (ce_info);
#endif
if (dump_file)
fprintf (dump_file, "%d insn%s converted to conditional execution.\n",
n_insns, (n_insns == 1) ? " was" : "s were");
merge_if_block (ce_info);
cond_exec_changed_p = TRUE;
return TRUE;
fail:
#ifdef IFCVT_MODIFY_CANCEL
IFCVT_MODIFY_CANCEL (ce_info);
#endif
cancel_changes (0);
return FALSE;
}
struct noce_if_info
{
basic_block test_bb;
rtx insn_a, insn_b;
rtx x, a, b;
rtx jump, cond, cond_earliest;
bool b_unconditional;
};
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_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_store_flag_mask (struct noce_if_info *);
static rtx noce_emit_cmove (struct noce_if_info *, rtx, enum rtx_code, rtx,
rtx, rtx, rtx);
static int noce_try_cmove (struct noce_if_info *);
static int noce_try_cmove_arith (struct noce_if_info *);
static rtx noce_get_alt_condition (struct noce_if_info *, rtx, rtx *);
static int noce_try_minmax (struct noce_if_info *);
static int noce_try_abs (struct noce_if_info *);
static int noce_try_sign_mask (struct noce_if_info *);
static rtx
noce_emit_store_flag (struct noce_if_info *if_info, rtx x, int reversep,
int normalize)
{
rtx cond = if_info->cond;
int cond_complex;
enum rtx_code code;
cond_complex = (! general_operand (XEXP (cond, 0), VOIDmode)
|| ! general_operand (XEXP (cond, 1), VOIDmode));
if (cond_complex)
cond = XEXP (SET_SRC (pc_set (if_info->jump)), 0);
if (reversep)
code = reversed_comparison_code (cond, if_info->jump);
else
code = GET_CODE (cond);
if ((if_info->cond_earliest == if_info->jump || cond_complex)
&& (normalize == 0 || STORE_FLAG_VALUE == normalize))
{
rtx tmp;
tmp = gen_rtx_fmt_ee (code, GET_MODE (x), XEXP (cond, 0),
XEXP (cond, 1));
tmp = gen_rtx_SET (VOIDmode, x, tmp);
start_sequence ();
tmp = emit_insn (tmp);
if (recog_memoized (tmp) >= 0)
{
tmp = get_insns ();
end_sequence ();
emit_insn (tmp);
if_info->cond_earliest = if_info->jump;
return x;
}
end_sequence ();
}
if (cond_complex || !SCALAR_INT_MODE_P (GET_MODE (x)))
return NULL_RTX;
return emit_store_flag (x, code, XEXP (cond, 0),
XEXP (cond, 1), VOIDmode,
(code == LTU || code == LEU
|| code == GEU || code == GTU), normalize);
}
static void
noce_emit_move_insn (rtx x, rtx y)
{
enum machine_mode outmode;
rtx outer, inner;
int bitpos;
if (GET_CODE (x) != STRICT_LOW_PART)
{
rtx seq, insn, target;
optab ot;
start_sequence ();
insn = (OBJECT_P (y) || CONSTANT_P (y) || GET_CODE (y) == SUBREG)
? emit_move_insn (x, y)
: emit_insn (gen_rtx_SET (VOIDmode, x, y));
seq = get_insns ();
end_sequence();
if (recog_memoized (insn) <= 0)
{
if (GET_CODE (x) == ZERO_EXTRACT)
{
rtx op = XEXP (x, 0);
unsigned HOST_WIDE_INT size = INTVAL (XEXP (x, 1));
unsigned HOST_WIDE_INT start = INTVAL (XEXP (x, 2));
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
{
if (MEM_P (op))
start = BITS_PER_UNIT - start - size;
else
{
gcc_assert (REG_P (op));
start = BITS_PER_WORD - start - size;
}
}
gcc_assert (start < (MEM_P (op) ? BITS_PER_UNIT : BITS_PER_WORD));
store_bit_field (op, size, start, GET_MODE (x), y, NULL_TREE);
return;
}
switch (GET_RTX_CLASS (GET_CODE (y)))
{
case RTX_UNARY:
ot = code_to_optab[GET_CODE (y)];
if (ot)
{
start_sequence ();
target = expand_unop (GET_MODE (y), ot, XEXP (y, 0), x, 0);
if (target != NULL_RTX)
{
if (target != x)
emit_move_insn (x, target);
seq = get_insns ();
}
end_sequence ();
}
break;
case RTX_BIN_ARITH:
case RTX_COMM_ARITH:
ot = code_to_optab[GET_CODE (y)];
if (ot)
{
start_sequence ();
target = expand_binop (GET_MODE (y), ot,
XEXP (y, 0), XEXP (y, 1),
x, 0, OPTAB_DIRECT);
if (target != NULL_RTX)
{
if (target != x)
emit_move_insn (x, target);
seq = get_insns ();
}
end_sequence ();
}
break;
default:
break;
}
}
emit_insn (seq);
return;
}
outer = XEXP (x, 0);
inner = XEXP (outer, 0);
outmode = GET_MODE (outer);
bitpos = SUBREG_BYTE (outer) * BITS_PER_UNIT;
store_bit_field (inner, GET_MODE_BITSIZE (outmode), bitpos, outmode, y,
NULL_TREE);
}
static rtx
end_ifcvt_sequence (struct noce_if_info *if_info)
{
rtx insn;
rtx seq = get_insns ();
set_used_flags (if_info->x);
set_used_flags (if_info->cond);
unshare_all_rtl_in_chain (seq);
end_sequence ();
for (insn = seq; insn; insn = NEXT_INSN (insn))
if (JUMP_P (insn)
|| recog_memoized (insn) == -1)
return NULL_RTX;
return seq;
}
static int
noce_try_move (struct noce_if_info *if_info)
{
rtx cond = if_info->cond;
enum rtx_code code = GET_CODE (cond);
rtx y, seq;
if (code != NE && code != EQ)
return FALSE;
if (HONOR_NANS (GET_MODE (if_info->x))
|| HONOR_SIGNED_ZEROS (GET_MODE (if_info->x)))
return FALSE;
if ((rtx_equal_p (if_info->a, XEXP (cond, 0))
&& rtx_equal_p (if_info->b, XEXP (cond, 1)))
|| (rtx_equal_p (if_info->a, XEXP (cond, 1))
&& rtx_equal_p (if_info->b, XEXP (cond, 0))))
{
y = (code == EQ) ? if_info->a : if_info->b;
if (! rtx_equal_p (if_info->x, y))
{
start_sequence ();
noce_emit_move_insn (if_info->x, y);
seq = end_ifcvt_sequence (if_info);
if (!seq)
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATOR (if_info->insn_a));
}
return TRUE;
}
return FALSE;
}
static int
noce_try_store_flag (struct noce_if_info *if_info)
{
int reversep;
rtx target, seq;
if (GET_CODE (if_info->b) == CONST_INT
&& INTVAL (if_info->b) == STORE_FLAG_VALUE
&& if_info->a == const0_rtx)
reversep = 0;
else if (if_info->b == const0_rtx
&& GET_CODE (if_info->a) == CONST_INT
&& INTVAL (if_info->a) == STORE_FLAG_VALUE
&& (reversed_comparison_code (if_info->cond, if_info->jump)
!= UNKNOWN))
reversep = 1;
else
return FALSE;
start_sequence ();
target = noce_emit_store_flag (if_info, if_info->x, reversep, 0);
if (target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
else
{
end_sequence ();
return FALSE;
}
}
static int
noce_try_store_flag_constants (struct noce_if_info *if_info)
{
rtx target, seq;
int reversep;
HOST_WIDE_INT itrue, ifalse, diff, tmp;
int normalize, can_reverse;
enum machine_mode mode;
if (! no_new_pseudos
&& GET_CODE (if_info->a) == CONST_INT
&& GET_CODE (if_info->b) == CONST_INT)
{
mode = GET_MODE (if_info->x);
ifalse = INTVAL (if_info->a);
itrue = INTVAL (if_info->b);
if ((itrue - ifalse > 0)
!= ((ifalse < 0) != (itrue < 0) ? ifalse < 0 : ifalse < itrue))
return FALSE;
diff = trunc_int_for_mode (itrue - ifalse, mode);
can_reverse = (reversed_comparison_code (if_info->cond, if_info->jump)
!= UNKNOWN);
reversep = 0;
if (diff == STORE_FLAG_VALUE || diff == -STORE_FLAG_VALUE)
normalize = 0;
else if (ifalse == 0 && exact_log2 (itrue) >= 0
&& (STORE_FLAG_VALUE == 1
|| BRANCH_COST >= 2))
normalize = 1;
else if (itrue == 0 && exact_log2 (ifalse) >= 0 && can_reverse
&& (STORE_FLAG_VALUE == 1 || BRANCH_COST >= 2))
normalize = 1, reversep = 1;
else if (itrue == -1
&& (STORE_FLAG_VALUE == -1
|| BRANCH_COST >= 2))
normalize = -1;
else if (ifalse == -1 && can_reverse
&& (STORE_FLAG_VALUE == -1 || BRANCH_COST >= 2))
normalize = -1, reversep = 1;
else if ((BRANCH_COST >= 2 && STORE_FLAG_VALUE == -1)
|| BRANCH_COST >= 3)
normalize = -1;
else
return FALSE;
if (reversep)
{
tmp = itrue; itrue = ifalse; ifalse = tmp;
diff = trunc_int_for_mode (-diff, mode);
}
start_sequence ();
target = noce_emit_store_flag (if_info, if_info->x, reversep, normalize);
if (! target)
{
end_sequence ();
return FALSE;
}
if (diff == STORE_FLAG_VALUE || diff == -STORE_FLAG_VALUE)
{
target = expand_simple_binop (mode,
(diff == STORE_FLAG_VALUE
? PLUS : MINUS),
GEN_INT (ifalse), target, if_info->x, 0,
OPTAB_WIDEN);
}
else if (ifalse == 0 && (tmp = exact_log2 (itrue)) >= 0)
{
target = expand_simple_binop (mode, ASHIFT,
target, GEN_INT (tmp), if_info->x, 0,
OPTAB_WIDEN);
}
else if (itrue == -1)
{
target = expand_simple_binop (mode, IOR,
target, GEN_INT (ifalse), if_info->x, 0,
OPTAB_WIDEN);
}
else
{
target = expand_simple_binop (mode, AND,
target, GEN_INT (diff), if_info->x, 0,
OPTAB_WIDEN);
if (target)
target = expand_simple_binop (mode, PLUS,
target, GEN_INT (ifalse),
if_info->x, 0, OPTAB_WIDEN);
}
if (! target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
return FALSE;
}
static int
noce_try_addcc (struct noce_if_info *if_info)
{
rtx target, seq;
int subtract, normalize;
if (! no_new_pseudos
&& GET_CODE (if_info->a) == PLUS
&& rtx_equal_p (XEXP (if_info->a, 0), if_info->b)
&& (reversed_comparison_code (if_info->cond, if_info->jump)
!= UNKNOWN))
{
rtx cond = if_info->cond;
enum rtx_code code = reversed_comparison_code (cond, if_info->jump);
if (general_operand (XEXP (cond, 0), VOIDmode)
&& general_operand (XEXP (cond, 1), VOIDmode))
{
start_sequence ();
target = emit_conditional_add (if_info->x, code,
XEXP (cond, 0),
XEXP (cond, 1),
VOIDmode,
if_info->b,
XEXP (if_info->a, 1),
GET_MODE (if_info->x),
(code == LTU || code == GEU
|| code == LEU || code == GTU));
if (target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
end_sequence ();
}
if (BRANCH_COST >= 2
&& (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)))
subtract = 0, normalize = 0;
else if (-STORE_FLAG_VALUE == INTVAL (XEXP (if_info->a, 1)))
subtract = 1, normalize = 0;
else
subtract = 0, normalize = INTVAL (XEXP (if_info->a, 1));
target = noce_emit_store_flag (if_info,
gen_reg_rtx (GET_MODE (if_info->x)),
1, normalize);
if (target)
target = expand_simple_binop (GET_MODE (if_info->x),
subtract ? MINUS : PLUS,
if_info->b, target, if_info->x,
0, OPTAB_WIDEN);
if (target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
end_sequence ();
}
}
return FALSE;
}
static int
noce_try_store_flag_mask (struct noce_if_info *if_info)
{
rtx target, seq;
int reversep;
reversep = 0;
if (! no_new_pseudos
&& (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))))
{
start_sequence ();
target = noce_emit_store_flag (if_info,
gen_reg_rtx (GET_MODE (if_info->x)),
reversep, -1);
if (target)
target = expand_simple_binop (GET_MODE (if_info->x), AND,
if_info->x,
target, if_info->x, 0,
OPTAB_WIDEN);
if (target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
end_sequence ();
}
return FALSE;
}
static rtx
noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
rtx cmp_a, rtx cmp_b, rtx vfalse, rtx vtrue)
{
if (if_info->cond_earliest == if_info->jump)
{
rtx tmp;
tmp = gen_rtx_fmt_ee (code, GET_MODE (if_info->cond), cmp_a, cmp_b);
tmp = gen_rtx_IF_THEN_ELSE (GET_MODE (x), tmp, vtrue, vfalse);
tmp = gen_rtx_SET (VOIDmode, x, tmp);
start_sequence ();
tmp = emit_insn (tmp);
if (recog_memoized (tmp) >= 0)
{
tmp = get_insns ();
end_sequence ();
emit_insn (tmp);
return x;
}
end_sequence ();
}
if (! general_operand (cmp_a, GET_MODE (cmp_a))
|| ! general_operand (cmp_b, GET_MODE (cmp_b)))
return NULL_RTX;
#if HAVE_conditional_move
return emit_conditional_move (x, code, cmp_a, cmp_b, VOIDmode,
vtrue, vfalse, GET_MODE (x),
(code == LTU || code == GEU
|| code == LEU || code == GTU));
#else
return NULL_RTX;
#endif
}
static int
noce_try_cmove (struct noce_if_info *if_info)
{
enum rtx_code code;
rtx target, seq;
if ((CONSTANT_P (if_info->a) || register_operand (if_info->a, VOIDmode))
&& (CONSTANT_P (if_info->b) || register_operand (if_info->b, VOIDmode)))
{
start_sequence ();
code = GET_CODE (if_info->cond);
target = noce_emit_cmove (if_info, if_info->x, code,
XEXP (if_info->cond, 0),
XEXP (if_info->cond, 1),
if_info->a, if_info->b);
if (target)
{
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_LOCATOR (if_info->insn_a));
return TRUE;
}
else
{
end_sequence ();
return FALSE;
}
}
return FALSE;
}
static int
noce_try_cmove_arith (struct noce_if_info *if_info)
{
rtx a = if_info->a;
rtx b = if_info->b;
rtx x = if_info->x;
rtx orig_a, orig_b;
rtx insn_a, insn_b;
rtx tmp, target;
int is_mem = 0;
int insn_cost;
enum rtx_code code;
if (! no_new_pseudos && cse_not_expected
&& MEM_P (a) && MEM_P (b)
&& BRANCH_COST >= 5)
{
a = XEXP (a, 0);
b = XEXP (b, 0);
x = gen_reg_rtx (Pmode);
is_mem = 1;
}
else if (may_trap_after_code_motion_p (a) || may_trap_after_code_motion_p (b))
return FALSE;
code = GET_CODE (if_info->cond);
insn_a = if_info->insn_a;
insn_b = if_info->insn_b;
if (insn_a)
{
insn_cost = insn_rtx_cost (PATTERN (insn_a));
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (BRANCH_COST))
return FALSE;
}
else
{
insn_cost = 0;
}
if (insn_b) {
insn_cost += insn_rtx_cost (PATTERN (insn_b));
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (BRANCH_COST))
return FALSE;
}
if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN)
{
int reversep = 0;
if (rtx_equal_p (b, x))
reversep = 1;
else if (general_operand (b, GET_MODE (b)))
reversep = 1;
if (reversep)
{
code = reversed_comparison_code (if_info->cond, if_info->jump);
tmp = a, a = b, b = tmp;
tmp = insn_a, insn_a = insn_b, insn_b = tmp;
}
}
start_sequence ();
orig_a = a;
orig_b = b;
if (! general_operand (a, GET_MODE (a)))
{
rtx set;
if (no_new_pseudos)
goto end_seq_and_fail;
if (is_mem)
{
tmp = gen_reg_rtx (GET_MODE (a));
tmp = emit_insn (gen_rtx_SET (VOIDmode, tmp, a));
}
else if (! insn_a)
goto end_seq_and_fail;
else
{
a = gen_reg_rtx (GET_MODE (a));
tmp = copy_rtx (insn_a);
set = single_set (tmp);
SET_DEST (set) = a;
tmp = emit_insn (PATTERN (tmp));
}
if (recog_memoized (tmp) < 0)
goto end_seq_and_fail;
}
if (! general_operand (b, GET_MODE (b)))
{
rtx set, last;
if (no_new_pseudos)
goto end_seq_and_fail;
if (is_mem)
{
tmp = gen_reg_rtx (GET_MODE (b));
tmp = gen_rtx_SET (VOIDmode, tmp, b);
}
else if (! insn_b)
goto end_seq_and_fail;
else
{
b = gen_reg_rtx (GET_MODE (b));
tmp = copy_rtx (insn_b);
set = single_set (tmp);
SET_DEST (set) = b;
tmp = PATTERN (tmp);
}
last = get_last_insn ();
if (last && modified_in_p (orig_b, last))
{
tmp = emit_insn_before (tmp, get_insns ());
if (modified_in_p (orig_a, tmp))
goto end_seq_and_fail;
}
else
tmp = emit_insn (tmp);
if (recog_memoized (tmp) < 0)
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);
if (! target)
goto end_seq_and_fail;
if (is_mem)
{
tmp = gen_rtx_MEM (GET_MODE (if_info->x), target);
if (MEM_VOLATILE_P (if_info->a) || MEM_VOLATILE_P (if_info->b))
MEM_VOLATILE_P (tmp) = 1;
if (MEM_IN_STRUCT_P (if_info->a) && MEM_IN_STRUCT_P (if_info->b))
MEM_IN_STRUCT_P (tmp) = 1;
if (MEM_SCALAR_P (if_info->a) && MEM_SCALAR_P (if_info->b))
MEM_SCALAR_P (tmp) = 1;
if (MEM_ALIAS_SET (if_info->a) == MEM_ALIAS_SET (if_info->b))
set_mem_alias_set (tmp, MEM_ALIAS_SET (if_info->a));
set_mem_align (tmp,
MIN (MEM_ALIGN (if_info->a), MEM_ALIGN (if_info->b)));
noce_emit_move_insn (if_info->x, tmp);
}
else if (target != x)
noce_emit_move_insn (x, target);
tmp = end_ifcvt_sequence (if_info);
if (!tmp)
return FALSE;
emit_insn_before_setloc (tmp, if_info->jump, INSN_LOCATOR (if_info->insn_a));
return TRUE;
end_seq_and_fail:
end_sequence ();
return FALSE;
}
static rtx
noce_get_alt_condition (struct noce_if_info *if_info, rtx target,
rtx *earliest)
{
rtx cond, set, insn;
int reverse;
if (reg_mentioned_p (target, if_info->cond))
{
*earliest = if_info->cond_earliest;
return if_info->cond;
}
set = pc_set (if_info->jump);
cond = XEXP (SET_SRC (set), 0);
reverse
= GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
&& XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (if_info->jump);
if (GET_CODE (target) == CONST_INT)
{
enum rtx_code code = GET_CODE (if_info->cond);
rtx op_a = XEXP (if_info->cond, 0);
rtx op_b = XEXP (if_info->cond, 1);
rtx prev_insn;
prev_insn = prev_nonnote_insn (if_info->cond_earliest);
if (prev_insn
&& INSN_P (prev_insn)
&& GET_CODE (PATTERN (prev_insn)) == SET)
{
rtx src = find_reg_equal_equiv_note (prev_insn);
if (!src)
src = SET_SRC (PATTERN (prev_insn));
if (GET_CODE (src) == CONST_INT)
{
if (rtx_equal_p (op_a, SET_DEST (PATTERN (prev_insn))))
op_a = src;
else if (rtx_equal_p (op_b, SET_DEST (PATTERN (prev_insn))))
op_b = src;
if (GET_CODE (op_a) == CONST_INT)
{
rtx tmp = op_a;
op_a = op_b;
op_b = tmp;
code = swap_condition (code);
}
}
}
if (GET_CODE (op_b) == CONST_INT)
{
HOST_WIDE_INT desired_val = INTVAL (target);
HOST_WIDE_INT actual_val = INTVAL (op_b);
switch (code)
{
case LT:
if (actual_val == desired_val + 1)
{
code = LE;
op_b = GEN_INT (desired_val);
}
break;
case LE:
if (actual_val == desired_val - 1)
{
code = LT;
op_b = GEN_INT (desired_val);
}
break;
case GT:
if (actual_val == desired_val - 1)
{
code = GE;
op_b = GEN_INT (desired_val);
}
break;
case GE:
if (actual_val == desired_val + 1)
{
code = GT;
op_b = GEN_INT (desired_val);
}
break;
default:
break;
}
}
if (code != GET_CODE (if_info->cond)
|| op_a != XEXP (if_info->cond, 0)
|| op_b != XEXP (if_info->cond, 1))
{
cond = gen_rtx_fmt_ee (code, GET_MODE (cond), op_a, op_b);
*earliest = if_info->cond_earliest;
return cond;
}
}
cond = canonicalize_condition (if_info->jump, cond, reverse,
earliest, target, false, true);
if (! cond || ! reg_mentioned_p (target, cond))
return NULL;
for (insn = if_info->jump; insn != *earliest; insn = PREV_INSN (insn))
if (INSN_P (insn) && reg_overlap_mentioned_p (if_info->x, PATTERN (insn)))
return NULL;
for (insn = *earliest; insn != if_info->jump; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& (modified_in_p (if_info->a, insn)
|| modified_in_p (if_info->b, insn)))
return NULL;
return cond;
}
static int
noce_try_minmax (struct noce_if_info *if_info)
{
rtx cond, earliest, target, seq;
enum rtx_code code, op;
int unsignedp;
if (no_new_pseudos)
return FALSE;
if (HONOR_SIGNED_ZEROS (GET_MODE (if_info->x))
|| HONOR_NANS (GET_MODE (if_info->x)))
return FALSE;
cond = noce_get_alt_condition (if_info, if_info->a, &earliest);
if (!cond)
return FALSE;
code = GET_CODE (cond);
if (rtx_equal_p (XEXP (cond, 0), if_info->a))
{
if (! rtx_equal_p (XEXP (cond, 1), if_info->b))
return FALSE;
}
else if (rtx_equal_p (XEXP (cond, 1), if_info->a))
{
if (! rtx_equal_p (XEXP (cond, 0), if_info->b))
return FALSE;
code = swap_condition (code);
}
else
return FALSE;
switch (code)
{
case LT:
case LE:
case UNLT:
case UNLE:
op = SMAX;
unsignedp = 0;
break;
case GT:
case GE:
case UNGT:
case UNGE:
op = SMIN;
unsignedp = 0;
break;
case LTU:
case LEU:
op = UMAX;
unsignedp = 1;
break;
case GTU:
case GEU:
op = UMIN;
unsignedp = 1;
break;
default:
return FALSE;
}
start_sequence ();
target = expand_simple_binop (GET_MODE (if_info->x), op,
if_info->a, if_info->b,
if_info->x, unsignedp, OPTAB_WIDEN);
if (! target)
{
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_LOCATOR (if_info->insn_a));
if_info->cond = cond;
if_info->cond_earliest = earliest;
return TRUE;
}
static int
noce_try_abs (struct noce_if_info *if_info)
{
rtx cond, earliest, target, seq, a, b, c;
int negate;
if (no_new_pseudos)
return FALSE;
a = if_info->a;
b = if_info->b;
if (GET_CODE (a) == NEG && rtx_equal_p (XEXP (a, 0), b))
negate = 0;
else if (GET_CODE (b) == NEG && rtx_equal_p (XEXP (b, 0), a))
{
c = a; a = b; b = c;
negate = 1;
}
else
return FALSE;
cond = noce_get_alt_condition (if_info, b, &earliest);
if (!cond)
return FALSE;
if (rtx_equal_p (XEXP (cond, 0), b))
c = XEXP (cond, 1);
else if (rtx_equal_p (XEXP (cond, 1), b))
{
c = XEXP (cond, 0);
negate = !negate;
}
else
return FALSE;
if (REG_P (c))
{
rtx set, insn = prev_nonnote_insn (earliest);
if (insn
&& (set = single_set (insn))
&& rtx_equal_p (SET_DEST (set), c))
{
rtx note = find_reg_equal_equiv_note (insn);
if (note)
c = XEXP (note, 0);
else
c = SET_SRC (set);
}
else
return FALSE;
}
if (MEM_P (c)
&& GET_CODE (XEXP (c, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (c, 0)))
c = get_pool_constant (XEXP (c, 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)))
return FALSE;
switch (GET_CODE (cond))
{
case LT:
case LE:
case UNLT:
case UNLE:
negate = !negate;
break;
case GT:
case GE:
case UNGT:
case UNGE:
break;
default:
return FALSE;
}
start_sequence ();
target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1);
if (target && negate)
target = expand_simple_unop (GET_MODE (target), NEG, target, if_info->x, 0);
if (! target)
{
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_LOCATOR (if_info->insn_a));
if_info->cond = cond;
if_info->cond_earliest = earliest;
return TRUE;
}
static int
noce_try_sign_mask (struct noce_if_info *if_info)
{
rtx cond, t, m, c, seq;
enum machine_mode mode;
enum rtx_code code;
if (no_new_pseudos)
return FALSE;
cond = if_info->cond;
code = GET_CODE (cond);
m = XEXP (cond, 0);
c = XEXP (cond, 1);
t = NULL_RTX;
if (if_info->a == const0_rtx)
{
if ((code == LT && c == const0_rtx)
|| (code == LE && c == constm1_rtx))
t = if_info->b;
}
else if (if_info->b == const0_rtx)
{
if ((code == GE && c == const0_rtx)
|| (code == GT && c == constm1_rtx))
t = if_info->a;
}
if (! t || side_effects_p (t))
return FALSE;
mode = GET_MODE (t);
if (GET_MODE (m) != mode)
return FALSE;
if (rtx_cost (t, SET) >= COSTS_N_INSNS (2)
&& (!if_info->b_unconditional
|| t != if_info->b))
return FALSE;
start_sequence ();
m = emit_store_flag (gen_reg_rtx (mode), LT, m, const0_rtx, mode, 0, -1);
t = m ? expand_binop (mode, and_optab, m, t, NULL_RTX, 0, OPTAB_DIRECT)
: NULL_RTX;
if (!t)
{
end_sequence ();
return FALSE;
}
noce_emit_move_insn (if_info->x, t);
seq = end_ifcvt_sequence (if_info);
if (!seq)
return FALSE;
emit_insn_before_setloc (seq, if_info->jump, INSN_LOCATOR (if_info->insn_a));
return TRUE;
}
static int
noce_try_bitop (struct noce_if_info *if_info)
{
rtx cond, x, a, result, seq;
enum machine_mode mode;
enum rtx_code code;
int bitnum;
x = if_info->x;
cond = if_info->cond;
code = GET_CODE (cond);
if (! rtx_equal_p (x, if_info->b))
return FALSE;
if (code != NE && code != EQ)
return FALSE;
if (XEXP (cond, 1) != const0_rtx)
return FALSE;
cond = XEXP (cond, 0);
if (GET_CODE (cond) == ZERO_EXTRACT)
{
if (XEXP (cond, 1) != const1_rtx
|| GET_CODE (XEXP (cond, 2)) != CONST_INT
|| ! rtx_equal_p (x, XEXP (cond, 0)))
return FALSE;
bitnum = INTVAL (XEXP (cond, 2));
mode = GET_MODE (x);
if (BITS_BIG_ENDIAN)
bitnum = GET_MODE_BITSIZE (mode) - 1 - bitnum;
if (bitnum < 0 || bitnum >= HOST_BITS_PER_WIDE_INT)
return FALSE;
}
else
return FALSE;
a = if_info->a;
if (GET_CODE (a) == IOR || GET_CODE (a) == XOR)
{
if (! rtx_equal_p (x, XEXP (a, 0))
|| GET_CODE (XEXP (a, 1)) != CONST_INT
|| (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
!= (unsigned HOST_WIDE_INT) 1 << bitnum)
return FALSE;
if (GET_CODE (a) == IOR)
result = (code == NE) ? a : NULL_RTX;
else if (code == NE)
{
result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode);
result = simplify_gen_binary (IOR, mode, x, result);
}
else
{
result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode);
result = simplify_gen_binary (AND, mode, x, result);
}
}
else if (GET_CODE (a) == AND)
{
if (! rtx_equal_p (x, XEXP (a, 0))
|| GET_CODE (XEXP (a, 1)) != CONST_INT
|| (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
!= (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode)))
return FALSE;
result = (code == EQ) ? a : NULL_RTX;
}
else
return FALSE;
if (result)
{
start_sequence ();
noce_emit_move_insn (x, result);
seq = end_ifcvt_sequence (if_info);
if (!seq)
return FALSE;
emit_insn_before_setloc (seq, if_info->jump,
INSN_LOCATOR (if_info->insn_a));
}
return TRUE;
}
static rtx
noce_get_condition (rtx jump, rtx *earliest)
{
rtx cond, set, tmp;
bool reverse;
if (! any_condjump_p (jump))
return NULL_RTX;
set = pc_set (jump);
reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
&& XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
cond = XEXP (SET_SRC (set), 0);
tmp = XEXP (cond, 0);
if (REG_P (tmp) && GET_MODE_CLASS (GET_MODE (tmp)) == MODE_INT)
{
*earliest = jump;
if (reverse)
cond = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond)),
GET_MODE (cond), tmp, XEXP (cond, 1));
return cond;
}
return canonicalize_condition (jump, cond, reverse, earliest,
NULL_RTX, false, true);
}
static int
noce_init_if_info (struct ce_if_block *ce_info, struct noce_if_info *if_info)
{
basic_block test_bb = ce_info->test_bb;
rtx cond, jump;
if (ce_info->num_multiple_test_blocks)
{
if (ce_info->else_bb || !ce_info->and_and_p)
return FALSE;
ce_info->test_bb = test_bb = ce_info->last_test_bb;
ce_info->num_multiple_test_blocks = 0;
ce_info->num_and_and_blocks = 0;
ce_info->num_or_or_blocks = 0;
}
jump = BB_END (test_bb);
cond = noce_get_condition (jump, &if_info->cond_earliest);
if (!cond)
return FALSE;
if (! onlyjump_p (jump))
return FALSE;
if (GET_MODE (XEXP (cond, 0)) == BLKmode)
return FALSE;
if_info->test_bb = test_bb;
if_info->cond = cond;
if_info->jump = jump;
return TRUE;
}
static int
noce_operand_ok (rtx op)
{
if (MEM_P (op))
return ! side_effects_p (XEXP (op, 0));
if (side_effects_p (op))
return FALSE;
return ! may_trap_p (op);
}
static bool
noce_mem_write_may_trap_or_fault_p (rtx mem)
{
rtx addr;
if (MEM_READONLY_P (mem))
return true;
if (may_trap_or_fault_p (mem))
return true;
addr = XEXP (mem, 0);
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 (GET_CODE (XEXP (addr, 1)) == CONST_INT)
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;
}
static int
noce_process_if_block (struct ce_if_block * ce_info)
{
basic_block test_bb = ce_info->test_bb;
basic_block then_bb = ce_info->then_bb;
basic_block else_bb = ce_info->else_bb;
struct noce_if_info if_info;
rtx insn_a, insn_b;
rtx set_a, set_b;
rtx orig_x, x, a, b;
rtx jump, cond;
if (!noce_init_if_info (ce_info, &if_info))
return FALSE;
cond = if_info.cond;
jump = if_info.jump;
insn_a = first_active_insn (then_bb);
if (! insn_a
|| insn_a != last_active_insn (then_bb, FALSE)
|| (set_a = single_set (insn_a)) == NULL_RTX)
return FALSE;
x = SET_DEST (set_a);
a = SET_SRC (set_a);
set_b = NULL_RTX;
if (else_bb)
{
insn_b = first_active_insn (else_bb);
if (! insn_b
|| insn_b != last_active_insn (else_bb, FALSE)
|| (set_b = single_set (insn_b)) == NULL_RTX
|| ! rtx_equal_p (x, SET_DEST (set_b)))
return FALSE;
}
else
{
insn_b = prev_nonnote_insn (if_info.cond_earliest);
if (! insn_b
|| !NONJUMP_INSN_P (insn_b)
|| (set_b = single_set (insn_b)) == NULL_RTX
|| ! rtx_equal_p (x, SET_DEST (set_b))
|| reg_overlap_mentioned_p (x, SET_SRC (set_b))
|| modified_between_p (SET_SRC (set_b),
PREV_INSN (if_info.cond_earliest), jump)
|| reg_overlap_mentioned_p (x, cond)
|| reg_overlap_mentioned_p (x, a)
|| modified_between_p (x, PREV_INSN (if_info.cond_earliest), jump))
insn_b = set_b = NULL_RTX;
}
if (side_effects_p (x))
return FALSE;
b = (set_b ? SET_SRC (set_b) : x);
orig_x = x;
if (!REG_P (x)
|| (SMALL_REGISTER_CLASSES
&& REGNO (x) < FIRST_PSEUDO_REGISTER))
{
if (no_new_pseudos || GET_MODE (x) == BLKmode)
return FALSE;
if (GET_MODE (x) == ZERO_EXTRACT
&& (GET_CODE (XEXP (x, 1)) != CONST_INT
|| GET_CODE (XEXP (x, 2)) != CONST_INT))
return FALSE;
x = gen_reg_rtx (GET_MODE (GET_CODE (x) == STRICT_LOW_PART
? XEXP (x, 0) : x));
}
if (! noce_operand_ok (a) || ! noce_operand_ok (b))
return FALSE;
if_info.insn_a = insn_a;
if_info.insn_b = insn_b;
if_info.x = x;
if_info.a = a;
if_info.b = b;
if_info.b_unconditional = else_bb == 0;
if (rtx_equal_p (a, b))
{
if (insn_b && else_bb)
{
rtx note;
if (else_bb && insn_b == BB_END (else_bb))
BB_END (else_bb) = PREV_INSN (insn_b);
reorder_insns (insn_b, insn_b, PREV_INSN (jump));
if ((note = find_reg_note (insn_b, REG_EQUAL, NULL_RTX)) != 0)
remove_note (insn_b, note);
insn_b = NULL_RTX;
}
else if (insn_b && side_effects_p (orig_x))
return FALSE;
x = orig_x;
goto success;
}
if (!set_b && MEM_P (orig_x) && noce_mem_write_may_trap_or_fault_p (orig_x))
return FALSE;
if (noce_try_move (&if_info))
goto success;
if (noce_try_store_flag (&if_info))
goto success;
if (noce_try_bitop (&if_info))
goto success;
if (noce_try_minmax (&if_info))
goto success;
if (noce_try_abs (&if_info))
goto success;
if (HAVE_conditional_move
&& noce_try_cmove (&if_info))
goto success;
if (! HAVE_conditional_execution)
{
if (noce_try_store_flag_constants (&if_info))
goto success;
if (noce_try_addcc (&if_info))
goto success;
if (noce_try_store_flag_mask (&if_info))
goto success;
if (HAVE_conditional_move
&& noce_try_cmove_arith (&if_info))
goto success;
if (noce_try_sign_mask (&if_info))
goto success;
}
return FALSE;
success:
delete_insn (insn_a);
if (insn_b && else_bb)
delete_insn (insn_b);
delete_insn (jump);
if (orig_x != x)
{
start_sequence ();
noce_emit_move_insn (orig_x, x);
insn_b = get_insns ();
set_used_flags (orig_x);
unshare_all_rtl_in_chain (insn_b);
end_sequence ();
emit_insn_after_setloc (insn_b, BB_END (test_bb), INSN_LOCATOR (insn_a));
}
merge_if_block (ce_info);
return TRUE;
}
static int
check_cond_move_block (basic_block bb, rtx *vals, rtx cond)
{
rtx insn;
FOR_BB_INSNS (bb, insn)
{
rtx set, dest, src;
if (!INSN_P (insn) || JUMP_P (insn))
continue;
set = single_set (insn);
if (!set)
return FALSE;
dest = SET_DEST (set);
src = SET_SRC (set);
if (!REG_P (dest)
|| (SMALL_REGISTER_CLASSES && HARD_REGISTER_P (dest)))
return FALSE;
if (!CONSTANT_P (src) && !register_operand (src, VOIDmode))
return FALSE;
if (side_effects_p (src) || side_effects_p (dest))
return FALSE;
if (may_trap_p (src) || may_trap_p (dest))
return FALSE;
if ((REG_P (src)
&& vals[REGNO (src)] != NULL)
|| (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src))
&& vals[REGNO (SUBREG_REG (src))] != NULL))
return FALSE;
if (vals[REGNO (dest)] != NULL)
return FALSE;
if (reg_overlap_mentioned_p (dest, cond))
return FALSE;
vals[REGNO (dest)] = src;
if (!CONSTANT_P (src)
&& modified_between_p (src, insn, NEXT_INSN (BB_END (bb))))
return FALSE;
}
return TRUE;
}
static int
cond_move_process_if_block (struct ce_if_block *ce_info)
{
basic_block then_bb = ce_info->then_bb;
basic_block else_bb = ce_info->else_bb;
struct noce_if_info if_info;
rtx jump, cond, insn, seq, cond_arg0, cond_arg1, loc_insn;
int max_reg, size, c, i;
rtx *then_vals;
rtx *else_vals;
enum rtx_code code;
if (!HAVE_conditional_move || no_new_pseudos)
return FALSE;
memset (&if_info, 0, sizeof if_info);
if (!noce_init_if_info (ce_info, &if_info))
return FALSE;
cond = if_info.cond;
jump = if_info.jump;
max_reg = max_reg_num ();
size = (max_reg + 1) * sizeof (rtx);
then_vals = (rtx *) alloca (size);
else_vals = (rtx *) alloca (size);
memset (then_vals, 0, size);
memset (else_vals, 0, size);
if (!check_cond_move_block (then_bb, then_vals, cond)
|| (else_bb && !check_cond_move_block (else_bb, else_vals, cond)))
return FALSE;
c = 0;
for (i = 0; i <= max_reg; ++i)
{
if (!then_vals[i] && !else_vals[i])
continue;
if (!then_vals[i] || !else_vals[i])
++c;
else
{
if (!CONSTANT_P (then_vals[i])
&& !CONSTANT_P (else_vals[i])
&& !rtx_equal_p (then_vals[i], else_vals[i]))
return FALSE;
}
}
if (c > MAX_CONDITIONAL_EXECUTE)
return FALSE;
code = GET_CODE (cond);
cond_arg0 = XEXP (cond, 0);
cond_arg1 = XEXP (cond, 1);
start_sequence ();
FOR_BB_INSNS (then_bb, insn)
{
rtx set, target, dest, t, e;
unsigned int regno;
if (!INSN_P (insn) || JUMP_P (insn))
continue;
set = single_set (insn);
gcc_assert (set && REG_P (SET_DEST (set)));
dest = SET_DEST (set);
regno = REGNO (dest);
t = then_vals[regno];
e = else_vals[regno];
gcc_assert (t);
if (!e)
e = dest;
target = noce_emit_cmove (&if_info, dest, code, cond_arg0, cond_arg1,
t, e);
if (!target)
{
end_sequence ();
return FALSE;
}
if (target != dest)
noce_emit_move_insn (dest, target);
}
if (else_bb)
{
FOR_BB_INSNS (else_bb, insn)
{
rtx set, target, dest;
unsigned int regno;
if (!INSN_P (insn) || JUMP_P (insn))
continue;
set = single_set (insn);
gcc_assert (set && REG_P (SET_DEST (set)));
dest = SET_DEST (set);
regno = REGNO (dest);
if (then_vals[regno])
continue;
gcc_assert (else_vals[regno]);
target = noce_emit_cmove (&if_info, dest, code, cond_arg0, cond_arg1,
dest, else_vals[regno]);
if (!target)
{
end_sequence ();
return FALSE;
}
if (target != dest)
noce_emit_move_insn (dest, target);
}
}
seq = end_ifcvt_sequence (&if_info);
if (!seq)
return FALSE;
loc_insn = first_active_insn (then_bb);
if (!loc_insn)
{
loc_insn = first_active_insn (else_bb);
gcc_assert (loc_insn);
}
emit_insn_before_setloc (seq, jump, INSN_LOCATOR (loc_insn));
FOR_BB_INSNS (then_bb, insn)
if (INSN_P (insn) && !JUMP_P (insn))
delete_insn (insn);
if (else_bb)
{
FOR_BB_INSNS (else_bb, insn)
if (INSN_P (insn) && !JUMP_P (insn))
delete_insn (insn);
}
delete_insn (jump);
merge_if_block (ce_info);
return TRUE;
}
static int
process_if_block (struct ce_if_block * ce_info)
{
if (! reload_completed
&& noce_process_if_block (ce_info))
return TRUE;
if (HAVE_conditional_move
&& cond_move_process_if_block (ce_info))
return TRUE;
if (HAVE_conditional_execution && reload_completed)
{
if (cond_exec_process_if_block (ce_info, TRUE))
return TRUE;
if (ce_info->num_multiple_test_blocks && ce_info->then_bb)
{
cancel_changes (0);
if (cond_exec_process_if_block (ce_info, FALSE))
return TRUE;
}
}
return FALSE;
}
static void
merge_if_block (struct ce_if_block * ce_info)
{
basic_block test_bb = ce_info->test_bb;
basic_block then_bb = ce_info->then_bb;
basic_block else_bb = ce_info->else_bb;
basic_block join_bb = ce_info->join_bb;
basic_block combo_bb;
combo_bb = test_bb;
if (ce_info->num_multiple_test_blocks > 0)
{
basic_block bb = test_bb;
basic_block last_test_bb = ce_info->last_test_bb;
basic_block fallthru = block_fallthru (bb);
do
{
bb = fallthru;
fallthru = block_fallthru (bb);
merge_blocks (combo_bb, bb);
num_true_changes++;
}
while (bb != last_test_bb);
}
if (then_bb)
{
if (combo_bb->il.rtl->global_live_at_end)
COPY_REG_SET (combo_bb->il.rtl->global_live_at_end,
then_bb->il.rtl->global_live_at_end);
merge_blocks (combo_bb, then_bb);
num_true_changes++;
}
if (else_bb)
{
merge_blocks (combo_bb, else_bb);
num_true_changes++;
}
if (! join_bb)
{
rtx last = BB_END (combo_bb);
if (EDGE_COUNT (combo_bb->succs) == 0)
gcc_assert (find_reg_note (last, REG_NORETURN, NULL)
|| (NONJUMP_INSN_P (last)
&& GET_CODE (PATTERN (last)) == TRAP_IF
&& (TRAP_CONDITION (PATTERN (last))
== const_true_rtx)));
else
gcc_assert (JUMP_P (last)
|| (EDGE_SUCC (combo_bb, 0)->dest == EXIT_BLOCK_PTR
&& CALL_P (last)
&& SIBLING_CALL_P (last))
|| ((EDGE_SUCC (combo_bb, 0)->flags & EDGE_EH)
&& can_throw_internal (last)));
}
else if (EDGE_COUNT (join_bb->preds) < 2
&& join_bb != EXIT_BLOCK_PTR)
{
if (combo_bb->il.rtl->global_live_at_end)
COPY_REG_SET (combo_bb->il.rtl->global_live_at_end,
join_bb->il.rtl->global_live_at_end);
merge_blocks (combo_bb, join_bb);
num_true_changes++;
}
else
{
gcc_assert (single_succ_p (combo_bb)
&& single_succ (combo_bb) == join_bb);
if (join_bb != EXIT_BLOCK_PTR)
tidy_fallthru_edge (single_succ_edge (combo_bb));
}
num_updated_if_blocks++;
}
static basic_block
find_if_header (basic_block test_bb, int pass)
{
ce_if_block_t ce_info;
edge then_edge;
edge else_edge;
if (EDGE_COUNT (test_bb->succs) != 2)
return NULL;
then_edge = EDGE_SUCC (test_bb, 0);
else_edge = EDGE_SUCC (test_bb, 1);
if ((then_edge->flags & EDGE_COMPLEX)
|| (else_edge->flags & EDGE_COMPLEX))
return NULL;
if ((then_edge->flags & EDGE_LOOP_EXIT)
|| (else_edge->flags & EDGE_LOOP_EXIT))
return NULL;
if (then_edge->flags & EDGE_FALLTHRU)
;
else if (else_edge->flags & EDGE_FALLTHRU)
{
edge e = else_edge;
else_edge = then_edge;
then_edge = e;
}
else
return NULL;
memset (&ce_info, '\0', sizeof (ce_info));
ce_info.test_bb = test_bb;
ce_info.then_bb = then_edge->dest;
ce_info.else_bb = else_edge->dest;
ce_info.pass = pass;
#ifdef IFCVT_INIT_EXTRA_FIELDS
IFCVT_INIT_EXTRA_FIELDS (&ce_info);
#endif
if (find_if_block (&ce_info))
goto success;
if (HAVE_trap && HAVE_conditional_trap
&& find_cond_trap (test_bb, then_edge, else_edge))
goto success;
if (dom_computed[CDI_POST_DOMINATORS] >= DOM_NO_FAST_QUERY
&& (! HAVE_conditional_execution || reload_completed))
{
if (find_if_case_1 (test_bb, then_edge, else_edge))
goto success;
if (find_if_case_2 (test_bb, then_edge, else_edge))
goto success;
}
return NULL;
success:
if (dump_file)
fprintf (dump_file, "Conversion succeeded on pass %d.\n", pass);
return ce_info.test_bb;
}
static int
block_jumps_and_fallthru_p (basic_block cur_bb, basic_block target_bb)
{
edge cur_edge;
int fallthru_p = FALSE;
int jump_p = FALSE;
rtx insn;
rtx end;
int n_insns = 0;
edge_iterator ei;
if (!cur_bb || !target_bb)
return -1;
if (EDGE_COUNT (cur_bb->succs) == 0)
return FALSE;
FOR_EACH_EDGE (cur_edge, ei, cur_bb->succs)
{
if (cur_edge->flags & EDGE_COMPLEX)
return -1;
else if (cur_edge->flags & EDGE_FALLTHRU)
fallthru_p = TRUE;
else if (cur_edge->dest == target_bb)
jump_p = TRUE;
else
return -1;
}
if ((jump_p & fallthru_p) == 0)
return -1;
end = BB_END (cur_bb);
insn = BB_HEAD (cur_bb);
while (insn != NULL_RTX)
{
if (CALL_P (insn))
return -1;
if (INSN_P (insn)
&& !JUMP_P (insn)
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
n_insns++;
if (insn == end)
break;
insn = NEXT_INSN (insn);
}
return n_insns;
}
static int
find_if_block (struct ce_if_block * ce_info)
{
basic_block test_bb = ce_info->test_bb;
basic_block then_bb = ce_info->then_bb;
basic_block else_bb = ce_info->else_bb;
basic_block join_bb = NULL_BLOCK;
edge cur_edge;
basic_block next;
edge_iterator ei;
ce_info->last_test_bb = test_bb;
if (HAVE_conditional_execution && reload_completed
&& single_pred_p (test_bb)
&& single_pred_edge (test_bb)->flags == EDGE_FALLTHRU)
{
basic_block bb = single_pred (test_bb);
basic_block target_bb;
int max_insns = MAX_CONDITIONAL_EXECUTE;
int n_insns;
if ((n_insns = block_jumps_and_fallthru_p (bb, else_bb)) >= 0)
{
ce_info->and_and_p = TRUE;
target_bb = else_bb;
}
else if ((n_insns = block_jumps_and_fallthru_p (bb, then_bb)) >= 0)
{
ce_info->and_and_p = FALSE;
target_bb = then_bb;
}
else
target_bb = NULL_BLOCK;
if (target_bb && n_insns <= max_insns)
{
int total_insns = 0;
int blocks = 0;
ce_info->last_test_bb = test_bb;
do
{
ce_info->test_bb = test_bb = bb;
total_insns += n_insns;
blocks++;
if (!single_pred_p (bb))
break;
bb = single_pred (bb);
n_insns = block_jumps_and_fallthru_p (bb, target_bb);
}
while (n_insns >= 0 && (total_insns + n_insns) <= max_insns);
ce_info->num_multiple_test_blocks = blocks;
ce_info->num_multiple_test_insns = total_insns;
if (ce_info->and_and_p)
ce_info->num_and_and_blocks = blocks;
else
ce_info->num_or_or_blocks = blocks;
}
}
if ((EDGE_COUNT (then_bb->preds) - ce_info->num_or_or_blocks) != 1)
goto combine_and_and_only;
FOR_EACH_EDGE (cur_edge, ei, then_bb->preds)
{
if (cur_edge->flags & EDGE_COMPLEX)
goto combine_and_and_only;
}
FOR_EACH_EDGE (cur_edge, ei, else_bb->preds)
{
if (cur_edge->flags & EDGE_COMPLEX)
goto combine_and_and_only;
}
if (EDGE_COUNT (then_bb->succs) > 0
&& (!single_succ_p (then_bb)
|| (single_succ_edge (then_bb)->flags & EDGE_COMPLEX)
|| (flow2_completed && tablejump_p (BB_END (then_bb), NULL, NULL))))
goto combine_and_and_only;
if (EDGE_COUNT (then_bb->succs) == 0)
{
if (single_pred_p (else_bb))
{
rtx last_insn = BB_END (then_bb);
while (last_insn
&& NOTE_P (last_insn)
&& last_insn != BB_HEAD (then_bb))
last_insn = PREV_INSN (last_insn);
if (last_insn
&& JUMP_P (last_insn)
&& ! simplejump_p (last_insn))
goto combine_and_and_only;
join_bb = else_bb;
else_bb = NULL_BLOCK;
}
else
goto combine_and_and_only;
}
else if (single_succ (then_bb) == else_bb)
{
join_bb = else_bb;
else_bb = NULL_BLOCK;
}
else if (single_succ_p (else_bb)
&& single_succ (then_bb) == single_succ (else_bb)
&& single_pred_p (else_bb)
&& ! (single_succ_edge (else_bb)->flags & EDGE_COMPLEX)
&& ! (flow2_completed && tablejump_p (BB_END (else_bb), NULL, NULL)))
join_bb = single_succ (else_bb);
else
goto combine_and_and_only;
goto if_block_found;
combine_and_and_only:;
if (ce_info->num_and_and_blocks || ce_info->num_or_or_blocks)
{
join_bb = else_bb = NULL_BLOCK;
then_bb = ce_info->then_bb = NULL_BLOCK;
}
else
return FALSE;
if_block_found:;
num_possible_if_blocks++;
if (dump_file)
{
fprintf (dump_file,
"\nIF%s%s block found, pass %d, start block %d "
"[insn %d]",
(then_bb) ? "-THEN" : "",
(else_bb) ? "-ELSE" : "",
ce_info->pass,
test_bb->index,
BB_HEAD (test_bb) ? (int)INSN_UID (BB_HEAD (test_bb)) : -1);
if (then_bb)
fprintf (dump_file, ", then %d [%d]",
then_bb->index,
BB_HEAD (then_bb) ? (int)INSN_UID (BB_HEAD (then_bb)) : -1);
if (else_bb)
fprintf (dump_file, ", else %d [%d]",
else_bb->index,
BB_HEAD (else_bb) ? (int)INSN_UID (BB_HEAD (else_bb)) : -1);
if (join_bb)
fprintf (dump_file, ", join %d [%d]",
join_bb->index,
BB_HEAD (join_bb) ? (int)INSN_UID (BB_HEAD (join_bb)) : -1);
if (ce_info->num_multiple_test_blocks > 0)
fprintf (dump_file, ", %d %s block%s last test %d [%d]",
ce_info->num_multiple_test_blocks,
(ce_info->and_and_p) ? "&&" : "||",
(ce_info->num_multiple_test_blocks == 1) ? "" : "s",
ce_info->last_test_bb->index,
((BB_HEAD (ce_info->last_test_bb))
? (int)INSN_UID (BB_HEAD (ce_info->last_test_bb))
: -1));
fputc ('\n', dump_file);
}
if (then_bb)
{
next = then_bb;
if (else_bb && (next = next->next_bb) != else_bb)
return FALSE;
if ((next = next->next_bb) != join_bb && join_bb != EXIT_BLOCK_PTR)
{
if (else_bb)
join_bb = NULL;
else
return FALSE;
}
}
ce_info->else_bb = else_bb;
ce_info->join_bb = join_bb;
return process_if_block (ce_info);
}
static int
find_cond_trap (basic_block test_bb, edge then_edge, edge else_edge)
{
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest;
basic_block other_bb, trap_bb;
rtx trap, jump, cond, cond_earliest, seq;
enum rtx_code code;
if ((trap = block_has_only_trap (then_bb)) != NULL)
trap_bb = then_bb, other_bb = else_bb;
else if ((trap = block_has_only_trap (else_bb)) != NULL)
trap_bb = else_bb, other_bb = then_bb;
else
return FALSE;
if (dump_file)
{
fprintf (dump_file, "\nTRAP-IF block found, start %d, trap %d\n",
test_bb->index, trap_bb->index);
}
jump = BB_END (test_bb);
cond = noce_get_condition (jump, &cond_earliest);
if (! cond)
return FALSE;
if (! onlyjump_p (jump))
return FALSE;
if (GET_MODE (XEXP (cond, 0)) == BLKmode)
return FALSE;
code = GET_CODE (cond);
if (then_bb == trap_bb)
{
code = reversed_comparison_code (cond, jump);
if (code == UNKNOWN)
return FALSE;
}
seq = gen_cond_trap (code, XEXP (cond, 0),
XEXP (cond, 1),
TRAP_CODE (PATTERN (trap)));
if (seq == NULL)
return FALSE;
num_true_changes++;
emit_insn_before_setloc (seq, cond_earliest, INSN_LOCATOR (trap));
remove_edge (trap_bb == then_bb ? then_edge : else_edge);
if (EDGE_COUNT (trap_bb->preds) == 0)
delete_basic_block (trap_bb);
if (test_bb->next_bb == other_bb)
{
struct ce_if_block new_ce_info;
delete_insn (jump);
memset (&new_ce_info, '\0', sizeof (new_ce_info));
new_ce_info.test_bb = test_bb;
new_ce_info.then_bb = NULL;
new_ce_info.else_bb = NULL;
new_ce_info.join_bb = other_bb;
merge_if_block (&new_ce_info);
}
else
{
rtx lab, newjump;
lab = JUMP_LABEL (jump);
newjump = emit_jump_insn_after (gen_jump (lab), jump);
LABEL_NUSES (lab) += 1;
JUMP_LABEL (newjump) = lab;
emit_barrier_after (newjump);
delete_insn (jump);
}
return TRUE;
}
static rtx
block_has_only_trap (basic_block bb)
{
rtx trap;
if (bb == EXIT_BLOCK_PTR)
return NULL_RTX;
if (EDGE_COUNT (bb->succs) > 0)
return NULL_RTX;
trap = first_active_insn (bb);
if (! (trap == BB_END (bb)
&& GET_CODE (PATTERN (trap)) == TRAP_IF
&& TRAP_CONDITION (PATTERN (trap)) == const_true_rtx))
return NULL_RTX;
return trap;
}
static int
find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge)
{
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest, new_bb;
int then_bb_index;
if ((BB_END (then_bb)
&& find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
|| (BB_END (test_bb)
&& find_reg_note (BB_END (test_bb), REG_CROSSING_JUMP, NULL_RTX))
|| (BB_END (else_bb)
&& find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
NULL_RTX)))
return FALSE;
if (!single_succ_p (then_bb))
return FALSE;
if (single_succ_edge (then_bb)->flags & (EDGE_COMPLEX | EDGE_FALLTHRU))
return FALSE;
if (!single_pred_p (then_bb))
return FALSE;
if (forwarder_block_p (then_bb))
return FALSE;
num_possible_if_blocks++;
if (dump_file)
fprintf (dump_file,
"\nIF-CASE-1 found, start %d, then %d\n",
test_bb->index, then_bb->index);
#ifdef TARGET_POWERPC
if (! cheap_bb_rtx_cost_p (then_bb, COSTS_N_INSNS (BRANCH_COST + 1)))
#else
if (! cheap_bb_rtx_cost_p (then_bb, COSTS_N_INSNS (BRANCH_COST)))
#endif
return FALSE;
if (! dead_or_predicable (test_bb, then_bb, else_bb,
single_succ (then_bb), 1))
return FALSE;
bitmap_ior (test_bb->il.rtl->global_live_at_end,
else_bb->il.rtl->global_live_at_start,
then_bb->il.rtl->global_live_at_end);
if (then_bb->next_bb == else_bb
&& then_bb->prev_bb == test_bb
&& else_bb != EXIT_BLOCK_PTR)
{
redirect_edge_succ (FALLTHRU_EDGE (test_bb), else_bb);
new_bb = 0;
}
else
new_bb = redirect_edge_and_branch_force (FALLTHRU_EDGE (test_bb),
else_bb);
then_bb_index = then_bb->index;
delete_basic_block (then_bb);
if (new_bb)
{
new_bb->index = then_bb_index;
SET_BASIC_BLOCK (then_bb_index, new_bb);
BB_COPY_PARTITION (new_bb, test_bb);
}
num_true_changes++;
num_updated_if_blocks++;
return TRUE;
}
static int
find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge)
{
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest;
edge else_succ;
rtx note;
if ((BB_END (then_bb)
&& find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
|| (BB_END (test_bb)
&& find_reg_note (BB_END (test_bb), REG_CROSSING_JUMP, NULL_RTX))
|| (BB_END (else_bb)
&& find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
NULL_RTX)))
return FALSE;
if (!single_succ_p (else_bb))
return FALSE;
else
else_succ = single_succ_edge (else_bb);
if (else_succ->flags & EDGE_COMPLEX)
return FALSE;
if (!single_pred_p (else_bb))
return FALSE;
if (then_bb->index < NUM_FIXED_BLOCKS)
return FALSE;
note = find_reg_note (BB_END (test_bb), REG_BR_PROB, NULL_RTX);
if (note && INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2)
;
else if (else_succ->dest->index < NUM_FIXED_BLOCKS
|| dominated_by_p (CDI_POST_DOMINATORS, then_bb,
else_succ->dest))
;
else
return FALSE;
num_possible_if_blocks++;
if (dump_file)
fprintf (dump_file,
"\nIF-CASE-2 found, start %d, else %d\n",
test_bb->index, else_bb->index);
#ifdef TARGET_POWERPC
if (! cheap_bb_rtx_cost_p (else_bb, COSTS_N_INSNS (BRANCH_COST + 1)))
#else
if (! cheap_bb_rtx_cost_p (else_bb, COSTS_N_INSNS (BRANCH_COST)))
#endif
return FALSE;
if (! dead_or_predicable (test_bb, else_bb, then_bb, else_succ->dest, 0))
return FALSE;
bitmap_ior (test_bb->il.rtl->global_live_at_end,
then_bb->il.rtl->global_live_at_start,
else_bb->il.rtl->global_live_at_end);
delete_basic_block (else_bb);
num_true_changes++;
num_updated_if_blocks++;
return TRUE;
}
static int
find_memory (rtx *px, void *data ATTRIBUTE_UNUSED)
{
return MEM_P (*px);
}
static int
dead_or_predicable (basic_block test_bb, basic_block merge_bb,
basic_block other_bb, basic_block new_dest, int reversep)
{
rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX;
jump = BB_END (test_bb);
head = BB_HEAD (merge_bb);
end = BB_END (merge_bb);
if (tablejump_p (end, NULL, NULL))
return FALSE;
if (LABEL_P (head))
head = NEXT_INSN (head);
if (NOTE_P (head))
{
if (head == end)
{
head = end = NULL_RTX;
goto no_body;
}
head = NEXT_INSN (head);
}
if (JUMP_P (end))
{
if (head == end)
{
head = end = NULL_RTX;
goto no_body;
}
end = PREV_INSN (end);
}
#ifndef IFCVT_MODIFY_TESTS
if (HAVE_conditional_execution)
{
rtx cond, prob_val;
cond = cond_exec_get_condition (jump);
if (! cond)
return FALSE;
prob_val = find_reg_note (jump, REG_BR_PROB, NULL_RTX);
if (prob_val)
prob_val = XEXP (prob_val, 0);
if (reversep)
{
enum rtx_code rev = reversed_comparison_code (cond, jump);
if (rev == UNKNOWN)
return FALSE;
cond = gen_rtx_fmt_ee (rev, GET_MODE (cond), XEXP (cond, 0),
XEXP (cond, 1));
if (prob_val)
prob_val = GEN_INT (REG_BR_PROB_BASE - INTVAL (prob_val));
}
if (! cond_exec_process_insns ((ce_if_block_t *)0, head, end, cond,
prob_val, 0))
goto cancel;
earliest = jump;
}
else
#endif
{
rtx insn, cond, prev;
regset merge_set, tmp, test_live, test_set;
struct propagate_block_info *pbi;
unsigned i, fail = 0;
bitmap_iterator bi;
for (insn = head; ; insn = NEXT_INSN (insn))
{
if (CALL_P (insn))
return FALSE;
if (INSN_P (insn))
{
if (may_trap_p (PATTERN (insn)))
return FALSE;
if (for_each_rtx (&PATTERN (insn), find_memory, NULL))
return FALSE;
}
if (insn == end)
break;
}
if (! any_condjump_p (jump))
return FALSE;
cond = noce_get_condition (jump, &earliest);
if (! cond)
return FALSE;
tmp = ALLOC_REG_SET (®_obstack);
merge_set = ALLOC_REG_SET (®_obstack);
test_live = ALLOC_REG_SET (®_obstack);
test_set = ALLOC_REG_SET (®_obstack);
if (max_regno < max_reg_num ())
{
max_regno = max_reg_num ();
allocate_reg_info (max_regno, FALSE, FALSE);
}
propagate_block (merge_bb, tmp, merge_set, merge_set, 0);
if (SMALL_REGISTER_CLASSES && ! reload_completed)
{
EXECUTE_IF_SET_IN_BITMAP (merge_set, 0, i, bi)
{
if (i < FIRST_PSEUDO_REGISTER
&& ! fixed_regs[i]
&& ! global_regs[i])
fail = 1;
}
}
COPY_REG_SET (test_live, other_bb->il.rtl->global_live_at_start);
pbi = init_propagate_block_info (test_bb, test_live, test_set, test_set,
0);
for (insn = jump; ; insn = prev)
{
prev = propagate_one_insn (pbi, insn);
if (insn == earliest)
break;
}
free_propagate_block_info (pbi);
if (bitmap_intersect_p (test_set, merge_set)
|| bitmap_intersect_p (test_live, merge_set)
|| bitmap_intersect_p (test_set,
merge_bb->il.rtl->global_live_at_start))
fail = 1;
FREE_REG_SET (tmp);
FREE_REG_SET (merge_set);
FREE_REG_SET (test_live);
FREE_REG_SET (test_set);
if (fail)
return FALSE;
}
no_body:
old_dest = JUMP_LABEL (jump);
if (other_bb != new_dest)
{
new_label = block_label (new_dest);
if (reversep
? ! invert_jump_1 (jump, new_label)
: ! redirect_jump_1 (jump, new_label))
goto cancel;
}
if (! apply_change_group ())
return FALSE;
if (other_bb != new_dest)
{
redirect_jump_2 (jump, old_dest, new_label, -1, reversep);
redirect_edge_succ (BRANCH_EDGE (test_bb), new_dest);
if (reversep)
{
gcov_type count, probability;
count = BRANCH_EDGE (test_bb)->count;
BRANCH_EDGE (test_bb)->count = FALLTHRU_EDGE (test_bb)->count;
FALLTHRU_EDGE (test_bb)->count = count;
probability = BRANCH_EDGE (test_bb)->probability;
BRANCH_EDGE (test_bb)->probability
= FALLTHRU_EDGE (test_bb)->probability;
FALLTHRU_EDGE (test_bb)->probability = probability;
update_br_prob_note (test_bb);
}
}
if (head != NULL)
{
rtx insn;
if (end == BB_END (merge_bb))
BB_END (merge_bb) = PREV_INSN (head);
if (squeeze_notes (&head, &end))
return TRUE;
insn = head;
do
{
rtx note, set;
if (! INSN_P (insn))
continue;
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (! note)
continue;
set = single_set (insn);
if (!set || !function_invariant_p (SET_SRC (set)))
remove_note (insn, note);
} while (insn != end && (insn = NEXT_INSN (insn)));
reorder_insns (head, end, PREV_INSN (earliest));
}
if (other_bb == new_dest)
{
delete_insn (jump);
remove_edge (BRANCH_EDGE (test_bb));
}
return TRUE;
cancel:
cancel_changes (0);
return FALSE;
}
static void
if_convert (int x_life_data_ok)
{
basic_block bb;
int pass;
num_possible_if_blocks = 0;
num_updated_if_blocks = 0;
num_true_changes = 0;
life_data_ok = (x_life_data_ok != 0);
if ((! targetm.cannot_modify_jumps_p ())
&& (!flag_reorder_blocks_and_partition || !no_new_pseudos
|| !targetm.have_named_sections))
{
struct loops loops;
flow_loops_find (&loops);
mark_loop_exit_edges (&loops);
flow_loops_free (&loops);
free_dominance_info (CDI_DOMINATORS);
}
if (HAVE_conditional_execution || life_data_ok)
calculate_dominance_info (CDI_POST_DOMINATORS);
if (life_data_ok)
clear_bb_flags ();
pass = 0;
do
{
cond_exec_changed_p = FALSE;
pass++;
#ifdef IFCVT_MULTIPLE_DUMPS
if (dump_file && pass > 1)
fprintf (dump_file, "\n\n========== Pass %d ==========\n", pass);
#endif
FOR_EACH_BB (bb)
{
basic_block new_bb;
while ((new_bb = find_if_header (bb, pass)))
bb = new_bb;
}
#ifdef IFCVT_MULTIPLE_DUMPS
if (dump_file && cond_exec_changed_p)
print_rtl_with_bb (dump_file, get_insns ());
#endif
}
while (cond_exec_changed_p);
#ifdef IFCVT_MULTIPLE_DUMPS
if (dump_file)
fprintf (dump_file, "\n\n========== no more changes\n");
#endif
free_dominance_info (CDI_POST_DOMINATORS);
if (dump_file)
fflush (dump_file);
clear_aux_for_blocks ();
if (num_true_changes && life_data_ok)
{
if (max_regno < max_reg_num ())
{
max_regno = max_reg_num ();
allocate_reg_info (max_regno, FALSE, FALSE);
}
update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE
| PROP_KILL_DEAD_CODE);
}
if (dump_file && num_possible_if_blocks > 0)
{
fprintf (dump_file,
"\n%d possible IF blocks searched.\n",
num_possible_if_blocks);
fprintf (dump_file,
"%d IF blocks converted.\n",
num_updated_if_blocks);
fprintf (dump_file,
"%d true changes made.\n\n\n",
num_true_changes);
}
#ifdef ENABLE_CHECKING
verify_flow_info ();
#endif
}
static bool
gate_handle_if_conversion (void)
{
return (optimize > 0);
}
static unsigned int
rest_of_handle_if_conversion (void)
{
if (flag_if_conversion)
{
if (dump_file)
dump_flow_info (dump_file, dump_flags);
cleanup_cfg (CLEANUP_EXPENSIVE);
reg_scan (get_insns (), max_reg_num ());
if_convert (0);
}
timevar_push (TV_JUMP);
cleanup_cfg (CLEANUP_EXPENSIVE);
reg_scan (get_insns (), max_reg_num ());
timevar_pop (TV_JUMP);
return 0;
}
struct tree_opt_pass pass_rtl_ifcvt =
{
"ce1",
gate_handle_if_conversion,
rest_of_handle_if_conversion,
NULL,
NULL,
0,
TV_IFCVT,
0,
0,
0,
0,
TODO_dump_func,
'C'
};
static bool
gate_handle_if_after_combine (void)
{
return (optimize > 0 && flag_if_conversion);
}
static unsigned int
rest_of_handle_if_after_combine (void)
{
no_new_pseudos = 0;
if_convert (1);
no_new_pseudos = 1;
return 0;
}
struct tree_opt_pass pass_if_after_combine =
{
"ce2",
gate_handle_if_after_combine,
rest_of_handle_if_after_combine,
NULL,
NULL,
0,
TV_IFCVT,
0,
0,
0,
0,
TODO_dump_func |
TODO_ggc_collect,
'C'
};
static bool
gate_handle_if_after_reload (void)
{
return (optimize > 0);
}
static unsigned int
rest_of_handle_if_after_reload (void)
{
cleanup_cfg (CLEANUP_EXPENSIVE
| CLEANUP_UPDATE_LIFE
| (flag_crossjumping ? CLEANUP_CROSSJUMP : 0));
if (flag_if_conversion2)
if_convert (1);
return 0;
}
struct tree_opt_pass pass_if_after_reload =
{
"ce3",
gate_handle_if_after_reload,
rest_of_handle_if_after_reload,
NULL,
NULL,
0,
TV_IFCVT2,
0,
0,
0,
0,
TODO_dump_func |
TODO_ggc_collect,
'E'
};