/* If-conversion support.
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010
Free Software Foundation, Inc.
This file is part of GCC.
#include "vecprim.h"
#include "dbgcnt.h"
-#ifndef HAVE_conditional_execution
-#define HAVE_conditional_execution 0
-#endif
#ifndef HAVE_conditional_move
#define HAVE_conditional_move 0
#endif
insn = NEXT_INSN (insn);
}
- while (NOTE_P (insn))
+ while (NOTE_P (insn) || DEBUG_INSN_P (insn))
{
if (insn == BB_END (bb))
return NULL_RTX;
while (NOTE_P (insn)
|| JUMP_P (insn)
+ || DEBUG_INSN_P (insn)
|| (skip_use_p
&& NONJUMP_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == USE))
for (insn = start; ; insn = NEXT_INSN (insn))
{
- if (NOTE_P (insn))
+ if (NOTE_P (insn) || DEBUG_INSN_P (insn))
goto insn_done;
gcc_assert(NONJUMP_INSN_P (insn) || CALL_P (insn));
rtx false_expr; /* test for then block insns */
rtx true_prob_val; /* probability of else block */
rtx false_prob_val; /* probability of then block */
- int n_insns;
+ rtx then_last_head = NULL_RTX; /* Last match at the head of THEN */
+ rtx else_last_head = NULL_RTX; /* Last match at the head of ELSE */
+ rtx then_first_tail = NULL_RTX; /* First match at the tail of THEN */
+ rtx else_first_tail = NULL_RTX; /* First match at the tail of ELSE */
+ int then_n_insns, else_n_insns, n_insns;
enum rtx_code false_code;
/* If test is comprised of && or || elements, and we've failed at handling
number of insns and see if it is small enough to convert. */
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);
+ then_n_insns = ce_info->num_then_insns = count_bb_insns (then_bb);
+ n_insns = then_n_insns;
max = MAX_CONDITIONAL_EXECUTE;
if (else_bb)
{
+ int n_matching;
+
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);
+ else_n_insns = ce_info->num_else_insns = count_bb_insns (else_bb);
+ n_insns += else_n_insns;
+
+ /* Look for matching sequences at the head and tail of the two blocks,
+ and limit the range of insns to be converted if possible. */
+ n_matching = flow_find_cross_jump (then_bb, else_bb,
+ &then_first_tail, &else_first_tail);
+ if (then_first_tail == BB_HEAD (then_bb))
+ then_start = then_end = NULL_RTX;
+ if (else_first_tail == BB_HEAD (else_bb))
+ else_start = else_end = NULL_RTX;
+
+ if (n_matching > 0)
+ {
+ if (then_end)
+ then_end = prev_active_insn (then_first_tail);
+ if (else_end)
+ else_end = prev_active_insn (else_first_tail);
+ n_insns -= 2 * n_matching;
+ }
+
+ if (then_start && else_start)
+ {
+ int longest_match = MIN (then_n_insns - n_matching,
+ else_n_insns - n_matching);
+ n_matching
+ = flow_find_head_matching_sequence (then_bb, else_bb,
+ &then_last_head,
+ &else_last_head,
+ longest_match);
+
+ if (n_matching > 0)
+ {
+ rtx insn;
+
+ /* We won't pass the insns in the head sequence to
+ cond_exec_process_insns, so we need to test them here
+ to make sure that they don't clobber the condition. */
+ for (insn = BB_HEAD (then_bb);
+ insn != NEXT_INSN (then_last_head);
+ insn = NEXT_INSN (insn))
+ if (!LABEL_P (insn) && !NOTE_P (insn)
+ && !DEBUG_INSN_P (insn)
+ && modified_in_p (test_expr, insn))
+ return FALSE;
+ }
+
+ if (then_last_head == then_end)
+ then_start = then_end = NULL_RTX;
+ if (else_last_head == else_end)
+ else_start = else_end = NULL_RTX;
+
+ if (n_matching > 0)
+ {
+ if (then_start)
+ then_start = next_active_insn (then_last_head);
+ if (else_start)
+ else_start = next_active_insn (else_last_head);
+ n_insns -= 2 * n_matching;
+ }
+ }
}
if (n_insns > max)
fprintf (dump_file, "%d insn%s converted to conditional execution.\n",
n_insns, (n_insns == 1) ? " was" : "s were");
- /* Merge the blocks! */
+ /* Merge the blocks! If we had matching sequences, make sure to delete one
+ copy at the appropriate location first: delete the copy in the THEN branch
+ for a tail sequence so that the remaining one is executed last for both
+ branches, and delete the copy in the ELSE branch for a head sequence so
+ that the remaining one is executed first for both branches. */
+ if (then_first_tail)
+ {
+ rtx from = then_first_tail;
+ if (!INSN_P (from))
+ from = next_active_insn (from);
+ delete_insn_chain (from, BB_END (then_bb), false);
+ }
+ if (else_last_head)
+ delete_insn_chain (first_active_insn (else_bb), else_last_head, false);
+
merge_if_block (ce_info);
cond_exec_changed_p = TRUE;
return TRUE;
/* ??? 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)
{
+ enum machine_mode address_mode
+ = targetm.addr_space.address_mode (MEM_ADDR_SPACE (a));
+
a = XEXP (a, 0);
b = XEXP (b, 0);
- x = gen_reg_rtx (Pmode);
+ x = gen_reg_rtx (address_mode);
is_mem = 1;
}
if insn_rtx_cost can't be estimated. */
if (insn_a)
{
- insn_cost = insn_rtx_cost (PATTERN (insn_a),
- optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_a)));
+ insn_cost
+ = insn_rtx_cost (PATTERN (insn_a),
+ optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_a)));
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (if_info->branch_cost))
return FALSE;
}
if (insn_b)
{
- insn_cost += insn_rtx_cost (PATTERN (insn_b),
- optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_b)));
+ insn_cost
+ += insn_rtx_cost (PATTERN (insn_b),
+ optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_b)));
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (if_info->branch_cost))
return FALSE;
}
set_mem_align (tmp,
MIN (MEM_ALIGN (if_info->a), MEM_ALIGN (if_info->b)));
+ gcc_assert (MEM_ADDR_SPACE (if_info->a) == MEM_ADDR_SPACE (if_info->b));
+ set_mem_addr_space (tmp, MEM_ADDR_SPACE (if_info->a));
+
noce_emit_move_insn (if_info->x, tmp);
}
else if (target != x)
/* First, look to see if we put a constant in a register. */
prev_insn = prev_nonnote_insn (if_info->cond_earliest);
if (prev_insn
- && BLOCK_NUM (prev_insn) == BLOCK_NUM (if_info->cond_earliest)
+ && BLOCK_FOR_INSN (prev_insn)
+ == BLOCK_FOR_INSN (if_info->cond_earliest)
&& INSN_P (prev_insn)
&& GET_CODE (PATTERN (prev_insn)) == SET)
{
return TRUE;
}
-/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", etc. */
+/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);",
+ "if (a < 0) x = ~a; else x = a;" to "x = one_cmpl_abs(a);",
+ etc. */
static int
noce_try_abs (struct noce_if_info *if_info)
{
rtx cond, earliest, target, seq, a, b, c;
int negate;
+ bool one_cmpl = false;
/* Reject modes with signed zeros. */
if (HONOR_SIGNED_ZEROS (GET_MODE (if_info->x)))
c = a; a = b; b = c;
negate = 1;
}
+ else if (GET_CODE (a) == NOT && rtx_equal_p (XEXP (a, 0), b))
+ {
+ negate = 0;
+ one_cmpl = true;
+ }
+ else if (GET_CODE (b) == NOT && rtx_equal_p (XEXP (b, 0), a))
+ {
+ c = a; a = b; b = c;
+ negate = 1;
+ one_cmpl = true;
+ }
else
return FALSE;
{
rtx set, insn = prev_nonnote_insn (earliest);
if (insn
- && BLOCK_NUM (insn) == BLOCK_NUM (earliest)
+ && BLOCK_FOR_INSN (insn) == BLOCK_FOR_INSN (earliest)
&& (set = single_set (insn))
&& rtx_equal_p (SET_DEST (set), c))
{
}
start_sequence ();
-
- target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1);
+ if (one_cmpl)
+ target = expand_one_cmpl_abs_nojump (GET_MODE (if_info->x), b,
+ if_info->x);
+ else
+ target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1);
/* ??? It's a quandary whether cmove would be better here, especially
for integers. Perhaps combine will clean things up. */
if (target && negate)
- target = expand_simple_unop (GET_MODE (target), NEG, target, if_info->x, 0);
+ {
+ if (one_cmpl)
+ target = expand_simple_unop (GET_MODE (target), NOT, target,
+ if_info->x, 0);
+ else
+ target = expand_simple_unop (GET_MODE (target), NEG, target,
+ if_info->x, 0);
+ }
if (! target)
{
else
{
insn_b = prev_nonnote_insn (if_info->cond_earliest);
+ while (insn_b && DEBUG_INSN_P (insn_b))
+ insn_b = prev_nonnote_insn (insn_b);
/* We're going to be moving the evaluation of B down from above
COND_EARLIEST to JUMP. Make sure the relevant data is still
intact. */
if (! insn_b
- || BLOCK_NUM (insn_b) != BLOCK_NUM (if_info->cond_earliest)
+ || BLOCK_FOR_INSN (insn_b) != BLOCK_FOR_INSN (if_info->cond_earliest)
|| !NONJUMP_INSN_P (insn_b)
|| (set_b = single_set (insn_b)) == NULL_RTX
|| ! rtx_equal_p (x, SET_DEST (set_b))
|| ! noce_operand_ok (SET_SRC (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)
+ || modified_between_p (SET_SRC (set_b), insn_b, jump)
/* Likewise with X. In particular this can happen when
noce_get_condition looks farther back in the instruction
stream than one might expect. */
|| reg_overlap_mentioned_p (x, cond)
|| reg_overlap_mentioned_p (x, a)
- || modified_between_p (x, PREV_INSN (if_info->cond_earliest), jump))
+ || modified_between_p (x, insn_b, jump))
insn_b = set_b = NULL_RTX;
}
if (HAVE_conditional_move
&& noce_try_cmove (if_info))
goto success;
- if (! HAVE_conditional_execution)
+ if (! targetm.have_conditional_execution ())
{
if (noce_try_store_flag_constants (if_info))
goto success;
REGS. COND is the condition we will test. */
static int
-check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs, rtx cond)
+check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
+ rtx cond)
{
rtx insn;
{
rtx set, dest, src;
- if (!INSN_P (insn) || JUMP_P (insn))
+ if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
continue;
set = single_set (insn);
if (!set)
rtx set, target, dest, t, e;
unsigned int regno;
- if (!INSN_P (insn) || JUMP_P (insn))
+ /* ??? Maybe emit conditional debug insn? */
+ if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
continue;
set = single_set (insn);
gcc_assert (set && REG_P (SET_DEST (set)));
/* Make sure the blocks are suitable. */
if (!check_cond_move_block (then_bb, then_vals, &then_regs, cond)
- || (else_bb && !check_cond_move_block (else_bb, else_vals, &else_regs, cond)))
+ || (else_bb
+ && !check_cond_move_block (else_bb, else_vals, &else_regs, cond)))
{
VEC_free (int, heap, then_regs);
VEC_free (int, heap, else_regs);
Return TRUE if we were successful at converting the block. */
static int
-noce_find_if_block (basic_block test_bb,
- edge then_edge, edge else_edge,
+noce_find_if_block (basic_block test_bb, edge then_edge, edge else_edge,
int pass)
{
basic_block then_bb, else_bb, join_bb;
return FALSE;
/* If this is not a standard conditional jump, we can't parse it. */
- cond = noce_get_condition (jump,
- &cond_earliest,
- then_else_reversed);
+ cond = noce_get_condition (jump, &cond_earliest, then_else_reversed);
if (!cond)
return FALSE;
/* Otherwise this must be a multiway branch of some sort. */
return NULL;
- memset (&ce_info, '\0', sizeof (ce_info));
+ 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;
IFCVT_INIT_EXTRA_FIELDS (&ce_info);
#endif
- if (! reload_completed
+ if (!reload_completed
&& noce_find_if_block (test_bb, then_edge, else_edge, pass))
goto success;
- if (HAVE_conditional_execution && reload_completed
+ if (reload_completed
+ && targetm.have_conditional_execution ()
&& cond_exec_find_if_block (&ce_info))
goto success;
goto success;
if (dom_info_state (CDI_POST_DOMINATORS) >= DOM_NO_FAST_QUERY
- && (! HAVE_conditional_execution || reload_completed))
+ && (reload_completed || !targetm.have_conditional_execution ()))
{
if (find_if_case_1 (test_bb, then_edge, else_edge))
goto success;
if (INSN_P (insn)
&& !JUMP_P (insn)
+ && !DEBUG_INSN_P (insn)
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
n_insns++;
ce_info->last_test_bb = test_bb;
/* We only ever should get here after reload,
- and only if we have conditional execution. */
- gcc_assert (HAVE_conditional_execution && reload_completed);
+ and if we have conditional execution. */
+ gcc_assert (reload_completed && targetm.have_conditional_execution ());
/* Discover if any fall through predecessors of the current test basic block
were && tests (which jump to the else block) or || tests (which jump to
if (EDGE_COUNT (then_bb->succs) > 0
&& (!single_succ_p (then_bb)
|| (single_succ_edge (then_bb)->flags & EDGE_COMPLEX)
- || (epilogue_completed && tablejump_p (BB_END (then_bb), NULL, NULL))))
+ || (epilogue_completed
+ && tablejump_p (BB_END (then_bb), NULL, NULL))))
return FALSE;
/* If the THEN block has no successors, conditional execution can still
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)
- && ! (epilogue_completed && tablejump_p (BB_END (else_bb), NULL, NULL)))
+ && !(single_succ_edge (else_bb)->flags & EDGE_COMPLEX)
+ && !(epilogue_completed
+ && tablejump_p (BB_END (else_bb), NULL, NULL)))
join_bb = single_succ (else_bb);
/* Otherwise it is not an IF-THEN or IF-THEN-ELSE combination. */
test_bb->index, else_bb->index);
/* ELSE is small. */
- if (! cheap_bb_rtx_cost_p (else_bb,
+ if (! cheap_bb_rtx_cost_p (else_bb,
COSTS_N_INSNS (BRANCH_COST (optimize_bb_for_speed_p (else_edge->src),
predictable_edge_p (else_edge)))))
return FALSE;
head = BB_HEAD (merge_bb);
end = BB_END (merge_bb);
+ while (DEBUG_INSN_P (end) && end != head)
+ end = PREV_INSN (end);
+
/* If merge_bb ends with a tablejump, predicating/moving insn's
into test_bb and then deleting merge_bb will result in the jumptable
that follows merge_bb being removed along with merge_bb and then we
if (LABEL_P (head))
head = NEXT_INSN (head);
+ while (DEBUG_INSN_P (head) && head != end)
+ head = NEXT_INSN (head);
if (NOTE_P (head))
{
if (head == end)
goto no_body;
}
head = NEXT_INSN (head);
+ while (DEBUG_INSN_P (head) && head != end)
+ head = NEXT_INSN (head);
}
if (JUMP_P (end))
goto no_body;
}
end = PREV_INSN (end);
+ while (DEBUG_INSN_P (end) && end != head)
+ end = PREV_INSN (end);
}
/* Disable handling dead code by conditional execution if the machine needs
to do anything funny with the tests, etc. */
#ifndef IFCVT_MODIFY_TESTS
- if (HAVE_conditional_execution)
+ if (targetm.have_conditional_execution ())
{
/* In the conditional execution case, we have things easy. We know
the condition is reversible. We don't have to check life info
{
if (CALL_P (insn))
return FALSE;
- if (INSN_P (insn))
+ if (NONDEBUG_INSN_P (insn))
{
if (may_trap_p (PATTERN (insn)))
return FALSE;
FOR_BB_INSNS (merge_bb, insn)
{
- if (INSN_P (insn))
+ if (NONDEBUG_INSN_P (insn))
{
unsigned int uid = INSN_UID (insn);
df_ref *def_rec;
fail = 1;
}
}
-
+
/* For TEST, we're interested in a range of insns, not a whole block.
Moreover, we're interested in the insns live from OTHER_BB. */
-
- /* The loop below takes the set of live registers
+
+ /* The loop below takes the set of live registers
after JUMP, and calculates the live set before EARLIEST. */
bitmap_copy (test_live, df_get_live_in (other_bb));
df_simulate_initialize_backwards (test_bb, test_live);
if (! note)
continue;
set = single_set (insn);
- if (!set || !function_invariant_p (SET_SRC (set)))
+ if (!set || !function_invariant_p (SET_SRC (set))
+ || !function_invariant_p (XEXP (note, 0)))
remove_note (insn, note);
} while (insn != end && (insn = NEXT_INSN (insn)));
FOR_EACH_BB (bb)
{
basic_block new_bb;
- while (!df_get_bb_dirty (bb)
+ while (!df_get_bb_dirty (bb)
&& (new_bb = find_if_header (bb, pass)) != NULL)
bb = new_bb;
}
#ifdef IFCVT_MULTIPLE_DUMPS
if (dump_file && cond_exec_changed_p)
- print_rtl_with_bb (dump_file, get_insns ());
+ {
+ if (dump_flags & TDF_SLIM)
+ print_rtl_slim_with_bb (dump_file, get_insns (), dump_flags);
+ else
+ print_rtl_with_bb (dump_file, get_insns ());
+ }
#endif
}
while (cond_exec_changed_p);