/* If-conversion support.
- Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This file is part of GCC.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "tm_p.h"
#include "cfgloop.h"
#include "target.h"
+#include "timevar.h"
+#include "tree-pass.h"
+#include "vec.h"
+#include "vecprim.h"
#ifndef HAVE_conditional_execution
#define MAX_CONDITIONAL_EXECUTE (BRANCH_COST + 1)
#endif
-#define NULL_EDGE ((edge) NULL)
#define NULL_BLOCK ((basic_block) NULL)
/* # of IF-THEN or IF-THEN-ELSE blocks we looked at */
/* Forward references. */
static int count_bb_insns (basic_block);
-static int total_bb_rtx_cost (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);
basic_block, int);
static void noce_emit_move_insn (rtx, rtx);
static rtx block_has_only_trap (basic_block);
-static void mark_loop_exit_edges (void);
\f
-/* Sets EDGE_LOOP_EXIT flag for all loop exits. */
-static void
-mark_loop_exit_edges (void)
-{
- struct loops loops;
- basic_block bb;
- edge e;
-
- flow_loops_find (&loops, LOOP_TREE);
- free_dominance_info (CDI_DOMINATORS);
-
- if (loops.num > 1)
- {
- FOR_EACH_BB (bb)
- {
- edge_iterator ei;
- FOR_EACH_EDGE (e, ei, bb->succs)
- {
- if (find_common_loop (bb->loop_father, e->dest->loop_father)
- != bb->loop_father)
- e->flags |= EDGE_LOOP_EXIT;
- else
- e->flags &= ~EDGE_LOOP_EXIT;
- }
- }
- }
-
- flow_loops_free (&loops);
-}
-
/* Count the number of non-jump active insns in BB. */
static int
return count;
}
-/* Count the total insn_rtx_cost of non-jump active insns in BB.
- This function returns -1, if the cost of any instruction could
- not be estimated. */
+/* Determine whether the total insn_rtx_cost on non-jump insns in
+ basic block BB is less than MAX_COST. This function returns
+ false if the cost of any instruction could not be estimated. */
-static int
-total_bb_rtx_cost (basic_block bb)
+static bool
+cheap_bb_rtx_cost_p (basic_block bb, int max_cost)
{
int count = 0;
rtx insn = BB_HEAD (bb);
{
int cost = insn_rtx_cost (PATTERN (insn));
if (cost == 0)
- return -1;
+ return false;
+
+ /* If this instruction is the load or set of a "stack" register,
+ such as a floating point register on x87, then the cost of
+ speculatively executing this insn may need to include
+ the additional cost of popping its result off of the
+ register stack. Unfortunately, correctly recognizing and
+ accounting for this additional overhead is tricky, so for
+ now we simply prohibit such speculative execution. */
+#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 -1;
-
+ return false;
+
if (insn == BB_END (bb))
break;
insn = NEXT_INSN (insn);
}
- return count;
+ return true;
}
/* Return the first non-jump active insn in the basic block. */
if (NOTE_P (insn))
goto insn_done;
- if (!NONJUMP_INSN_P (insn) && !CALL_P (insn))
- abort ();
+ gcc_assert(NONJUMP_INSN_P (insn) || CALL_P (insn));
/* Remove USE insns that get in the way. */
if (reload_completed && GET_CODE (PATTERN (insn)) == USE)
struct noce_if_info
{
+ /* A basic block that ends in a simple conditional jump. */
basic_block test_bb;
+
+ /* The jump that ends TEST_BB. */
+ rtx jump;
+
+ /* The jump condition. */
+ rtx cond;
+
+ /* New insns should be inserted before this one. */
+ rtx cond_earliest;
+
+ /* Insns in the THEN and ELSE block. There is always just this
+ one insns in those blocks. The insns are single_set insns.
+ If there was no ELSE block, INSN_B is the last insn before
+ COND_EARLIEST, or NULL_RTX. In the former case, the insn
+ operands are still valid, as if INSN_B was moved down below
+ the jump. */
rtx insn_a, insn_b;
- rtx x, a, b;
- rtx jump, cond, cond_earliest;
- /* True if "b" was originally evaluated unconditionally. */
- bool b_unconditional;
+
+ /* The SET_SRC of INSN_A and INSN_B. */
+ rtx a, b;
+
+ /* The SET_DEST of INSN_A. */
+ rtx x;
};
static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
static void
noce_emit_move_insn (rtx x, rtx y)
{
- enum machine_mode outmode, inmode;
+ enum machine_mode outmode;
rtx outer, inner;
int bitpos;
if (GET_CODE (x) != STRICT_LOW_PART)
{
- emit_move_insn (x, y);
+ rtx seq, insn, target;
+ optab ot;
+
+ start_sequence ();
+ /* Check that the SET_SRC is reasonable before calling emit_move_insn,
+ otherwise construct a suitable SET pattern ourselves. */
+ 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));
+
+ /* store_bit_field expects START to be relative to
+ BYTES_BIG_ENDIAN and adjusts this value for machines with
+ BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN. In order to be able to
+ invoke store_bit_field again it is necessary to have the START
+ value from the first call. */
+ 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);
+ 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);
- inmode = GET_MODE (inner);
bitpos = SUBREG_BYTE (outer) * BITS_PER_UNIT;
store_bit_field (inner, GET_MODE_BITSIZE (outmode), bitpos, outmode, y);
}
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;
return FALSE;
}
else
+ insn_cost = 0;
+
+ if (insn_b)
{
- insn_cost = 0;
+ insn_cost += insn_rtx_cost (PATTERN (insn_b));
+ if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (BRANCH_COST))
+ return FALSE;
}
- if (insn_b) {
- insn_cost += insn_rtx_cost (PATTERN (insn_b));
- if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (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)
{
start_sequence ();
+ orig_a = a;
+ orig_b = b;
+
/* If either operand is complex, load it into a register first.
The best way to do this is to copy the original insn. In this
way we preserve any clobbers etc that the insn may have had.
}
if (! general_operand (b, GET_MODE (b)))
{
- rtx set;
+ rtx set, last;
if (no_new_pseudos)
goto end_seq_and_fail;
if (is_mem)
{
tmp = gen_reg_rtx (GET_MODE (b));
- tmp = emit_insn (gen_rtx_SET (VOIDmode,
- tmp,
- b));
+ tmp = gen_rtx_SET (VOIDmode, tmp, b);
}
else if (! insn_b)
goto end_seq_and_fail;
tmp = copy_rtx (insn_b);
set = single_set (tmp);
SET_DEST (set) = b;
- tmp = emit_insn (PATTERN (tmp));
+ tmp = PATTERN (tmp);
+ }
+
+ /* 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. */
+ 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;
}
rtx prev_insn;
/* First, look to see if we put a constant in a register. */
- prev_insn = PREV_INSN (if_info->cond_earliest);
+ prev_insn = prev_nonnote_insn (if_info->cond_earliest);
if (prev_insn
&& INSN_P (prev_insn)
&& GET_CODE (PATTERN (prev_insn)) == SET)
if (no_new_pseudos)
return FALSE;
- /* Recognize A and B as constituting an ABS or NABS. */
+ /* Recognize A and B as constituting an ABS or NABS. The canonical
+ form is a branch around the negation, taken when the object is the
+ first operand of a comparison against 0 that evaluates to true. */
a = if_info->a;
b = if_info->b;
if (GET_CODE (a) == NEG && rtx_equal_p (XEXP (a, 0), b))
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);
+ {
+ c = XEXP (cond, 0);
+ negate = !negate;
+ }
else
return FALSE;
- /* Verify that C is zero. Search backward through the block for
- a REG_EQUAL note if necessary. */
+ /* Verify that C is zero. Search one step backward for a
+ REG_EQUAL note or a simple source if necessary. */
if (REG_P (c))
{
- rtx insn, note = NULL;
- for (insn = earliest;
- insn != BB_HEAD (if_info->test_bb);
- insn = PREV_INSN (insn))
- if (INSN_P (insn)
- && ((note = find_reg_note (insn, REG_EQUAL, c))
- || (note = find_reg_note (insn, REG_EQUIV, c))))
- break;
- if (! note)
+ 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;
- c = XEXP (note, 0);
}
if (MEM_P (c)
&& GET_CODE (XEXP (c, 0)) == SYMBOL_REF
rtx cond, t, m, c, seq;
enum machine_mode mode;
enum rtx_code code;
+ bool b_unconditional;
if (no_new_pseudos)
return FALSE;
return FALSE;
/* This is only profitable if T is cheap, or T is unconditionally
- executed/evaluated in the original insn sequence. */
+ executed/evaluated in the original insn sequence. The latter
+ happens if INSN_B was taken from TEST_BB, or if there was no
+ INSN_B which can happen for e.g. conditional stores to memory. */
+ b_unconditional = (if_info->insn_b == NULL_RTX
+ || BLOCK_FOR_INSN (if_info->insn_b) == if_info->test_bb);
if (rtx_cost (t, SET) >= COSTS_N_INSNS (2)
- && (!if_info->b_unconditional
+ && (!b_unconditional
|| t != if_info->b))
return FALSE;
}
+/* Optimize away "if (x & C) x |= C" and similar bit manipulation
+ transformations. */
+
+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);
+
+ /* Check for no else condition. */
+ if (! rtx_equal_p (x, if_info->b))
+ return FALSE;
+
+ /* Check for a suitable condition. */
+ if (code != NE && code != EQ)
+ return FALSE;
+ if (XEXP (cond, 1) != const0_rtx)
+ return FALSE;
+ cond = XEXP (cond, 0);
+
+ /* ??? We could also handle AND here. */
+ 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)
+ {
+ /* Check for "if (X & C) x = x op C". */
+ 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 ((x & C) == 0) x |= C; is transformed to x |= C. */
+ /* if ((x & C) != 0) x |= C; is transformed to nothing. */
+ if (GET_CODE (a) == IOR)
+ result = (code == NE) ? a : NULL_RTX;
+ 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 = 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 = simplify_gen_binary (AND, mode, x, result);
+ }
+ }
+ else if (GET_CODE (a) == AND)
+ {
+ /* Check for "if (X & C) x &= ~C". */
+ 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;
+
+ /* if ((x & C) == 0) x &= ~C; is transformed to nothing. */
+ /* if ((x & C) != 0) x &= ~C; is transformed to x &= ~C. */
+ 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;
+}
+
+
/* Similar to get_condition, only the resulting condition must be
valid at JUMP, instead of at EARLIEST. */
NULL_RTX, false, true);
}
+/* Initialize for a simple IF-THEN or IF-THEN-ELSE block. We will not
+ be using conditional execution. Set some fields of IF_INFO based
+ on CE_INFO: test_bb, cond, jump, cond_earliest. Return TRUE if
+ things look OK. */
+
+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 test is comprised of && or || elements, don't handle it unless
+ it is the special case of && elements without an ELSE block. */
+ 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;
+ }
+
+ /* If this is not a standard conditional jump, we can't parse it. */
+ jump = BB_END (test_bb);
+ cond = noce_get_condition (jump, &if_info->cond_earliest);
+ if (!cond)
+ 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))
+ return FALSE;
+
+ /* We must be comparing objects whose modes imply the size. */
+ 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;
+}
+
/* Return true if OP is ok for if-then-else processing. */
static int
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 (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 (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;
+}
+
/* Given a simple IF-THEN or IF-THEN-ELSE block, attempt to convert it
without using conditional execution. Return TRUE if we were
successful at converting the block. */
basic_block test_bb = ce_info->test_bb; /* test block */
basic_block then_bb = ce_info->then_bb; /* THEN */
basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
+ basic_block join_bb;
struct noce_if_info if_info;
rtx insn_a, insn_b;
rtx set_a, set_b;
??? For future expansion, look for multiple X in such patterns. */
- /* If test is comprised of && or || elements, don't handle it unless it is
- the special case of && elements without an ELSE block. */
- if (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;
- }
-
- /* If this is not a standard conditional jump, we can't parse it. */
- jump = BB_END (test_bb);
- cond = noce_get_condition (jump, &if_info.cond_earliest);
- if (! cond)
+ if (!noce_init_if_info (ce_info, &if_info))
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))
- return FALSE;
-
- /* We must be comparing objects whose modes imply the size. */
- if (GET_MODE (XEXP (cond, 0)) == BLKmode)
- return FALSE;
+ cond = if_info.cond;
+ jump = if_info.jump;
/* Look for one of the potential sets. */
insn_a = first_active_insn (then_bb);
{
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));
}
return FALSE;
/* Set up the info block for our subroutines. */
- if_info.test_bb = test_bb;
- if_info.cond = cond;
- if_info.jump = jump;
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;
/* Try optimizations in some approximation of a useful order. */
/* ??? Should first look to see if X is live incoming at all. If it
}
/* Disallow the "if (...) x = a;" form (with an implicit "else x = x;")
- for most optimizations if writing to x may trap, i.e. it's a memory
- other than a static var or a stack slot. */
- if (! set_b
- && MEM_P (orig_x)
- && ! MEM_NOTRAP_P (orig_x)
- && rtx_addr_can_trap_p (XEXP (orig_x, 0)))
- {
- if (HAVE_conditional_move)
- {
- if (noce_try_cmove (&if_info))
- goto success;
- if (! HAVE_conditional_execution
- && noce_try_cmove_arith (&if_info))
- goto success;
- }
- return FALSE;
- }
+ 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 (!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))
return FALSE;
success:
- /* The original sets may now be killed. */
- delete_insn (insn_a);
-
- /* Several special cases here: First, we may have reused insn_b above,
- in which case insn_b is now NULL. Second, we want to delete insn_b
- if it came from the ELSE block, because follows the now correct
- write that appears in the TEST block. However, if we got insn_b from
- the TEST block, it may in fact be loading data needed for the comparison.
- We'll let life_analysis remove the insn if it's really dead. */
- if (insn_b && else_bb)
- delete_insn (insn_b);
-
- /* The new insns will have been inserted immediately before the jump. We
- should be able to remove the jump with impunity, but the condition itself
- may have been modified by gcse to be shared across basic blocks. */
- delete_insn (jump);
/* If we used a temporary, fix it up now. */
if (orig_x != x)
{
+ rtx seq;
+
start_sequence ();
noce_emit_move_insn (orig_x, x);
- insn_b = get_insns ();
+ seq = get_insns ();
set_used_flags (orig_x);
- unshare_all_rtl_in_chain (insn_b);
+ unshare_all_rtl_in_chain (seq);
end_sequence ();
- emit_insn_after_setloc (insn_b, BB_END (test_bb), INSN_LOCATOR (insn_a));
+ emit_insn_before_setloc (seq, BB_END (test_bb), INSN_LOCATOR (insn_a));
}
- /* Merge the blocks! */
- merge_if_block (ce_info);
+ /* The original THEN and ELSE blocks may now be removed. The test block
+ must now jump to the join block. If the test block and the join block
+ can be merged, do so. */
+
+ join_bb = single_succ (then_bb);
+ if (else_bb)
+ {
+ delete_basic_block (else_bb);
+ num_true_changes++;
+ }
+ else
+ remove_edge (find_edge (test_bb, join_bb));
+
+ remove_edge (find_edge (then_bb, join_bb));
+ redirect_edge_and_branch_force (single_succ_edge (test_bb), join_bb);
+ delete_basic_block (then_bb);
+ num_true_changes++;
+
+ if (can_merge_blocks_p (test_bb, join_bb))
+ {
+ merge_blocks (test_bb, join_bb);
+ num_true_changes++;
+ }
+
+ num_updated_if_blocks++;
+ return TRUE;
+}
+
+/* Check whether a block is suitable for conditional move conversion.
+ Every insn must be a simple set of a register to a constant or a
+ register. For each assignment, store the value in the array VALS,
+ indexed by register number, then store the register number in
+ 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)
+{
+ rtx insn;
+
+ /* We can only handle simple jumps at the end of the basic block.
+ It is almost impossible to update the CFG otherwise. */
+ insn = BB_END (bb);
+ if (JUMP_P (insn) && !onlyjump_p (insn))
+ return FALSE;
+
+ 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;
+
+ /* Don't try to handle this if the source register was
+ modified earlier in the block. */
+ 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;
+
+ /* Don't try to handle this if the destination register was
+ modified earlier in the block. */
+ if (vals[REGNO (dest)] != NULL)
+ return FALSE;
+
+ /* Don't try to handle this if the condition uses the
+ destination register. */
+ if (reg_overlap_mentioned_p (dest, cond))
+ return FALSE;
+
+ /* Don't try to handle this if the source register is modified
+ later in the block. */
+ if (!CONSTANT_P (src)
+ && modified_between_p (src, insn, NEXT_INSN (BB_END (bb))))
+ return FALSE;
+
+ vals[REGNO (dest)] = src;
+
+ VEC_safe_push (int, heap, regs, REGNO (dest));
+ }
return TRUE;
}
+
+/* Given a basic block BB suitable for conditional move conversion,
+ a condition COND, and arrays THEN_VALS and ELSE_VALS containing the
+ register values depending on COND, emit the insns in the block as
+ conditional moves. If ELSE_BLOCK is true, THEN_BB was already
+ processed. The caller has started a sequence for the conversion.
+ Return true if successful, false if something goes wrong. */
+
+static bool
+cond_move_convert_if_block (struct noce_if_info *if_infop,
+ basic_block bb, rtx cond,
+ rtx *then_vals, rtx *else_vals,
+ bool else_block_p)
+{
+ enum rtx_code code;
+ rtx insn, cond_arg0, cond_arg1;
+
+ code = GET_CODE (cond);
+ cond_arg0 = XEXP (cond, 0);
+ cond_arg1 = XEXP (cond, 1);
+
+ FOR_BB_INSNS (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];
+
+ if (else_block_p)
+ {
+ /* If this register was set in the then block, we already
+ handled this case there. */
+ if (t)
+ continue;
+ t = dest;
+ gcc_assert (e);
+ }
+ else
+ {
+ gcc_assert (t);
+ if (!e)
+ e = dest;
+ }
+
+ target = noce_emit_cmove (if_infop, dest, code, cond_arg0, cond_arg1,
+ t, e);
+ if (!target)
+ return false;
+
+ if (target != dest)
+ noce_emit_move_insn (dest, target);
+ }
+
+ return true;
+}
+
+/* Given a simple IF-THEN or IF-THEN-ELSE block, attempt to convert it
+ using only conditional moves. Return TRUE if we were successful at
+ converting the block. */
+
+static int
+cond_move_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;
+ basic_block join_bb;
+ struct noce_if_info if_info;
+ rtx jump, cond, seq, loc_insn;
+ int max_reg, size, c, reg;
+ rtx *then_vals;
+ rtx *else_vals;
+ VEC (int, heap) *then_regs = NULL;
+ VEC (int, heap) *else_regs = NULL;
+ unsigned int i;
+
+ 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;
+
+ /* Build a mapping for each block to the value used for each
+ register. */
+ 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);
+
+ /* 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)))
+ return FALSE;
+
+ /* Make sure the blocks can be used together. If the same register
+ is set in both blocks, and is not set to a constant in both
+ cases, then both blocks must set it to the same register. We
+ have already verified that if it is set to a register, that the
+ source register does not change after the assignment. Also count
+ the number of registers set in only one of the blocks. */
+ c = 0;
+ for (i = 0; VEC_iterate (int, then_regs, i, reg); i++)
+ {
+ if (!then_vals[reg] && !else_vals[reg])
+ continue;
+
+ if (!else_vals[reg])
+ ++c;
+ else
+ {
+ if (!CONSTANT_P (then_vals[reg])
+ && !CONSTANT_P (else_vals[reg])
+ && !rtx_equal_p (then_vals[reg], else_vals[reg]))
+ return FALSE;
+ }
+ }
+
+ /* Finish off c for MAX_CONDITIONAL_EXECUTE. */
+ for (i = 0; VEC_iterate (int, else_regs, i, reg); ++i)
+ if (!then_vals[reg])
+ ++c;
+
+ /* Make sure it is reasonable to convert this block. What matters
+ 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)
+ return FALSE;
+
+ /* Try to emit the conditional moves. First do the then block,
+ then do anything left in the else blocks. */
+ start_sequence ();
+ if (!cond_move_convert_if_block (&if_info, then_bb, cond,
+ then_vals, else_vals, false)
+ || (else_bb
+ && !cond_move_convert_if_block (&if_info, else_bb, cond,
+ then_vals, else_vals, true)))
+ {
+ end_sequence ();
+ return FALSE;
+ }
+ 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));
+
+ join_bb = single_succ (then_bb);
+ if (else_bb)
+ {
+ delete_basic_block (else_bb);
+ num_true_changes++;
+ }
+ else
+ remove_edge (find_edge (test_bb, join_bb));
+
+ remove_edge (find_edge (then_bb, join_bb));
+ redirect_edge_and_branch_force (single_succ_edge (test_bb), join_bb);
+ delete_basic_block (then_bb);
+ num_true_changes++;
+
+ if (can_merge_blocks_p (test_bb, join_bb))
+ {
+ merge_blocks (test_bb, join_bb);
+ num_true_changes++;
+ }
+
+ num_updated_if_blocks++;
+
+ VEC_free (int, heap, then_regs);
+ VEC_free (int, heap, else_regs);
+
+ return TRUE;
+}
+
\f
/* Attempt to convert an IF-THEN or IF-THEN-ELSE block into
straight line code. Return true if successful. */
static int
process_if_block (struct ce_if_block * ce_info)
{
- if (! reload_completed
- && noce_process_if_block (ce_info))
- return TRUE;
+ /* Only perform the noce transformations before register allocation.
+ They could be made to run later, but this would require a lot of
+ work, and it doesn't seem to be worth it. */
+ if (! reload_completed)
+ {
+ if (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 (then_bb)
{
- if (combo_bb->global_live_at_end)
- COPY_REG_SET (combo_bb->global_live_at_end,
- then_bb->global_live_at_end);
merge_blocks (combo_bb, then_bb);
num_true_changes++;
}
/* The outgoing edge for the current COMBO block should already
be correct. Verify this. */
if (EDGE_COUNT (combo_bb->succs) == 0)
- {
- if (find_reg_note (last, REG_NORETURN, NULL))
- ;
- else if (NONJUMP_INSN_P (last)
- && GET_CODE (PATTERN (last)) == TRAP_IF
- && TRAP_CONDITION (PATTERN (last)) == const_true_rtx)
- ;
- else
- abort ();
- }
+ 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
/* There should still be something at the end of the THEN or ELSE
blocks taking us to our final destination. */
- else if (JUMP_P (last))
- ;
- else if (EDGE_SUCC (combo_bb, 0)->dest == EXIT_BLOCK_PTR
- && CALL_P (last)
- && SIBLING_CALL_P (last))
- ;
- else if ((EDGE_SUCC (combo_bb, 0)->flags & EDGE_EH)
- && can_throw_internal (last))
- ;
- else
- abort ();
+ 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)));
}
/* The JOIN block may have had quite a number of other predecessors too.
have only one remaining edge from our if-then-else diamond. If there
is more than one remaining edge, it must come from elsewhere. There
may be zero incoming edges if the THEN block didn't actually join
- back up (as with a call to abort). */
+ back up (as with a call to a non-return function). */
else if (EDGE_COUNT (join_bb->preds) < 2
&& join_bb != EXIT_BLOCK_PTR)
{
/* We can merge the JOIN. */
- if (combo_bb->global_live_at_end)
- COPY_REG_SET (combo_bb->global_live_at_end,
- join_bb->global_live_at_end);
-
merge_blocks (combo_bb, join_bb);
num_true_changes++;
}
/* The outgoing edge for the current COMBO block should already
be correct. Verify this. */
- if (EDGE_COUNT (combo_bb->succs) > 1
- || EDGE_SUCC (combo_bb, 0)->dest != join_bb)
- abort ();
+ gcc_assert (single_succ_p (combo_bb)
+ && single_succ (combo_bb) == join_bb);
/* Remove the jump and cruft from the end of the COMBO block. */
if (join_bb != EXIT_BLOCK_PTR)
- tidy_fallthru_edge (EDGE_SUCC (combo_bb, 0));
+ tidy_fallthru_edge (single_succ_edge (combo_bb));
}
num_updated_if_blocks++;
were && tests (which jump to the else block) or || tests (which jump to
the then block). */
if (HAVE_conditional_execution && reload_completed
- && EDGE_COUNT (test_bb->preds) == 1
- && EDGE_PRED (test_bb, 0)->flags == EDGE_FALLTHRU)
+ && single_pred_p (test_bb)
+ && single_pred_edge (test_bb)->flags == EDGE_FALLTHRU)
{
- basic_block bb = EDGE_PRED (test_bb, 0)->src;
+ basic_block bb = single_pred (test_bb);
basic_block target_bb;
int max_insns = MAX_CONDITIONAL_EXECUTE;
int n_insns;
total_insns += n_insns;
blocks++;
- if (EDGE_COUNT (bb->preds) != 1)
+ if (!single_pred_p (bb))
break;
- bb = EDGE_PRED (bb, 0)->src;
+ bb = single_pred (bb);
n_insns = block_jumps_and_fallthru_p (bb, target_bb);
}
while (n_insns >= 0 && (total_insns + n_insns) <= max_insns);
other than any || blocks which jump to the THEN block. */
if ((EDGE_COUNT (then_bb->preds) - ce_info->num_or_or_blocks) != 1)
return FALSE;
-
+
/* The edges of the THEN and ELSE blocks cannot have complex edges. */
FOR_EACH_EDGE (cur_edge, ei, then_bb->preds)
{
/* The THEN block of an IF-THEN combo must have zero or one successors. */
if (EDGE_COUNT (then_bb->succs) > 0
- && (EDGE_COUNT (then_bb->succs) > 1
- || (EDGE_SUCC (then_bb, 0)->flags & EDGE_COMPLEX)
+ && (!single_succ_p (then_bb)
+ || (single_succ_edge (then_bb)->flags & EDGE_COMPLEX)
|| (flow2_completed && tablejump_p (BB_END (then_bb), NULL, NULL))))
return FALSE;
code processing. ??? we should fix this in the future. */
if (EDGE_COUNT (then_bb->succs) == 0)
{
- if (EDGE_COUNT (else_bb->preds) == 1)
+ if (single_pred_p (else_bb))
{
rtx last_insn = BB_END (then_bb);
/* If the THEN block's successor is the other edge out of the TEST block,
then we have an IF-THEN combo without an ELSE. */
- else if (EDGE_SUCC (then_bb, 0)->dest == else_bb)
+ else if (single_succ (then_bb) == else_bb)
{
join_bb = else_bb;
else_bb = NULL_BLOCK;
/* If the THEN and ELSE block meet in a subsequent block, and the ELSE
has exactly one predecessor and one successor, and the outgoing edge
is not complex, then we have an IF-THEN-ELSE combo. */
- else if (EDGE_COUNT (else_bb->succs) == 1
- && EDGE_SUCC (then_bb, 0)->dest == EDGE_SUCC (else_bb, 0)->dest
- && EDGE_COUNT (else_bb->preds) == 1
- && ! (EDGE_SUCC (else_bb, 0)->flags & EDGE_COMPLEX)
+ 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 = EDGE_SUCC (else_bb, 0)->dest;
+ join_bb = single_succ (else_bb);
/* Otherwise it is not an IF-THEN or IF-THEN-ELSE combination. */
else
we checked the FALLTHRU flag, those are already adjacent to the last IF
block. */
/* ??? As an enhancement, move the ELSE block. Have to deal with
- BLOCK notes, if by no other means than aborting the merge if they
+ BLOCK notes, if by no other means than backing out the merge if they
exist. Sticky enough I don't want to think about it now. */
next = then_bb;
if (else_bb && (next = next->next_bb) != else_bb)
if (seq == NULL)
return FALSE;
- num_true_changes++;
-
/* Emit the new insns before cond_earliest. */
emit_insn_before_setloc (seq, cond_earliest, INSN_LOCATOR (trap));
/* Delete the trap block if possible. */
remove_edge (trap_bb == then_bb ? then_edge : else_edge);
if (EDGE_COUNT (trap_bb->preds) == 0)
- delete_basic_block (trap_bb);
-
- /* If the non-trap block and the test are now adjacent, merge them.
- Otherwise we must insert a direct branch. */
- 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);
+ delete_basic_block (trap_bb);
+ num_true_changes++;
}
+
+ /* Wire together the blocks again. */
+ if (current_ir_type () == IR_RTL_CFGLAYOUT)
+ single_succ_edge (test_bb)->flags |= EDGE_FALLTHRU;
else
{
rtx lab, newjump;
LABEL_NUSES (lab) += 1;
JUMP_LABEL (newjump) = lab;
emit_barrier_after (newjump);
+ }
+ delete_insn (jump);
- delete_insn (jump);
+ if (can_merge_blocks_p (test_bb, other_bb))
+ {
+ merge_blocks (test_bb, other_bb);
+ num_true_changes++;
}
+ num_updated_if_blocks++;
return TRUE;
}
{
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest, new_bb;
- int then_bb_index, bb_cost;
+ int then_bb_index;
/* If we are partitioning hot/cold basic blocks, we don't want to
mess up unconditional or indirect jumps that cross between hot
and cold sections.
-
+
Basic block partitioning may result in some jumps that appear to
- be optimizable (or blocks that appear to be mergeable), but which really
- must be left untouched (they are required to make it safely across
- partition boundaries). See the comments at the top of
+ be optimizable (or blocks that appear to be mergeable), but which really
+ must be left untouched (they are required to make it safely across
+ partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && ((BB_END (then_bb)
- && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
- || (BB_END (else_bb)
- && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
- NULL_RTX))))
+ 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;
/* THEN has one successor. */
- if (EDGE_COUNT (then_bb->succs) != 1)
+ if (!single_succ_p (then_bb))
return FALSE;
/* THEN does not fall through, but is not strange either. */
- if (EDGE_SUCC (then_bb, 0)->flags & (EDGE_COMPLEX | EDGE_FALLTHRU))
+ if (single_succ_edge (then_bb)->flags & (EDGE_COMPLEX | EDGE_FALLTHRU))
return FALSE;
/* THEN has one predecessor. */
- if (EDGE_COUNT (then_bb->preds) != 1)
+ if (!single_pred_p (then_bb))
return FALSE;
/* THEN must do something. */
test_bb->index, then_bb->index);
/* THEN is small. */
- bb_cost = total_bb_rtx_cost (then_bb);
- if (bb_cost < 0 || bb_cost >= COSTS_N_INSNS (BRANCH_COST))
+ if (! cheap_bb_rtx_cost_p (then_bb, COSTS_N_INSNS (BRANCH_COST)))
return FALSE;
/* Registers set are dead, or are predicable. */
if (! dead_or_predicable (test_bb, then_bb, else_bb,
- EDGE_SUCC (then_bb, 0)->dest, 1))
+ single_succ (then_bb), 1))
return FALSE;
/* Conversion went ok, including moving the insns and fixing up the
jump. Adjust the CFG to match. */
- bitmap_ior (test_bb->global_live_at_end,
- else_bb->global_live_at_start,
- then_bb->global_live_at_end);
+ 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);
+
+
+ /* We can avoid creating a new basic block if then_bb is immediately
+ followed by else_bb, i.e. deleting then_bb allows test_bb to fall
+ thru to else_bb. */
+
+ 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);
- 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;
- BASIC_BLOCK (then_bb_index) = new_bb;
+ SET_BASIC_BLOCK (then_bb_index, new_bb);
/* Since the fallthru edge was redirected from test_bb to new_bb,
we need to ensure that new_bb is in the same partition as
test bb (you can not fall through across section boundaries). */
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest;
edge else_succ;
- int bb_cost;
rtx note;
/* If we are partitioning hot/cold basic blocks, we don't want to
mess up unconditional or indirect jumps that cross between hot
and cold sections.
-
+
Basic block partitioning may result in some jumps that appear to
- be optimizable (or blocks that appear to be mergeable), but which really
- must be left untouched (they are required to make it safely across
- partition boundaries). See the comments at the top of
+ be optimizable (or blocks that appear to be mergeable), but which really
+ must be left untouched (they are required to make it safely across
+ partition boundaries). See the comments at the top of
bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */
- if (flag_reorder_blocks_and_partition
- && ((BB_END (then_bb)
- && find_reg_note (BB_END (then_bb), REG_CROSSING_JUMP, NULL_RTX))
- || (BB_END (else_bb)
- && find_reg_note (BB_END (else_bb), REG_CROSSING_JUMP,
- NULL_RTX))))
+ 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;
/* ELSE has one successor. */
- if (EDGE_COUNT (else_bb->succs) != 1)
+ if (!single_succ_p (else_bb))
return FALSE;
else
- else_succ = EDGE_SUCC (else_bb, 0);
+ else_succ = single_succ_edge (else_bb);
/* ELSE outgoing edge is not complex. */
if (else_succ->flags & EDGE_COMPLEX)
return FALSE;
/* ELSE has one predecessor. */
- if (EDGE_COUNT (else_bb->preds) != 1)
+ if (!single_pred_p (else_bb))
return FALSE;
/* THEN is not EXIT. */
- if (then_bb->index < 0)
+ if (then_bb->index < NUM_FIXED_BLOCKS)
return FALSE;
/* ELSE is predicted or SUCC(ELSE) postdominates THEN. */
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 < 0
+ else if (else_succ->dest->index < NUM_FIXED_BLOCKS
|| dominated_by_p (CDI_POST_DOMINATORS, then_bb,
else_succ->dest))
;
test_bb->index, else_bb->index);
/* ELSE is small. */
- bb_cost = total_bb_rtx_cost (else_bb);
- if (bb_cost < 0 || bb_cost >= COSTS_N_INSNS (BRANCH_COST))
+ if (! cheap_bb_rtx_cost_p (else_bb, COSTS_N_INSNS (BRANCH_COST)))
return FALSE;
/* Registers set are dead, or are predicable. */
/* Conversion went ok, including moving the insns and fixing up the
jump. Adjust the CFG to match. */
- bitmap_ior (test_bb->global_live_at_end,
- then_bb->global_live_at_start,
- else_bb->global_live_at_end);
+ 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);
head = BB_HEAD (merge_bb);
end = BB_END (merge_bb);
+ /* 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
+ get an unresolved reference to the jumptable. */
+ if (tablejump_p (end, NULL, NULL))
+ return FALSE;
+
if (LABEL_P (head))
head = NEXT_INSN (head);
if (NOTE_P (head))
TEST_SET = set of registers set between EARLIEST and the
end of the block. */
- tmp = OBSTACK_ALLOC_REG_SET (®_obstack);
- merge_set = OBSTACK_ALLOC_REG_SET (®_obstack);
- test_live = OBSTACK_ALLOC_REG_SET (®_obstack);
- test_set = OBSTACK_ALLOC_REG_SET (®_obstack);
+ tmp = ALLOC_REG_SET (®_obstack);
+ merge_set = ALLOC_REG_SET (®_obstack);
+ test_live = ALLOC_REG_SET (®_obstack);
+ test_set = ALLOC_REG_SET (®_obstack);
/* ??? bb->local_set is only valid during calculate_global_regs_live,
so we must recompute usage for MERGE_BB. Not so bad, I suppose,
since we've already asserted that MERGE_BB is small. */
+ /* If we allocated new pseudos (e.g. in the conditional move
+ expander called from noce_emit_cmove), we must resize the
+ array first. */
+ 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);
/* For small register class machines, don't lengthen lifetimes of
/* 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. */
- COPY_REG_SET (test_live, other_bb->global_live_at_start);
+ 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);
/* We can perform the transformation if
MERGE_SET & (TEST_SET | TEST_LIVE)
and
- TEST_SET & merge_bb->global_live_at_start
+ TEST_SET & merge_bb->il.rtl->global_live_at_start
are empty. */
if (bitmap_intersect_p (test_set, merge_set)
|| bitmap_intersect_p (test_live, merge_set)
- || bitmap_intersect_p (test_set, merge_bb->global_live_at_start))
+ || bitmap_intersect_p (test_set,
+ merge_bb->il.rtl->global_live_at_start))
fail = 1;
FREE_REG_SET (tmp);
if (other_bb != new_dest)
{
- if (old_dest)
- LABEL_NUSES (old_dest) -= 1;
- if (new_label)
- LABEL_NUSES (new_label) += 1;
- JUMP_LABEL (jump) = new_label;
- if (reversep)
- invert_br_probabilities (jump);
+ redirect_jump_2 (jump, old_dest, new_label, 0, reversep);
redirect_edge_succ (BRANCH_EDGE (test_bb), new_dest);
if (reversep)
/* Move the insns out of MERGE_BB to before the branch. */
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;
+ /* PR 21767: When moving insns above a conditional branch, REG_EQUAL
+ notes might become invalid. */
+ 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));
}
\f
/* Main entry point for all if-conversion. */
-void
+static void
if_convert (int x_life_data_ok)
{
basic_block bb;
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))
- mark_loop_exit_edges ();
+ loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
+ if (current_loops)
+ {
+ mark_loop_exit_edges ();
+ loop_optimizer_finalize ();
+ }
+ free_dominance_info (CDI_DOMINATORS);
/* Compute postdominators if we think we'll use them. */
if (HAVE_conditional_execution || life_data_ok)
verify_flow_info ();
#endif
}
+\f
+static bool
+gate_handle_if_conversion (void)
+{
+ return (optimize > 0);
+}
+
+/* If-conversion and CFG cleanup. */
+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", /* name */
+ gate_handle_if_conversion, /* gate */
+ rest_of_handle_if_conversion, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IFCVT, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func, /* todo_flags_finish */
+ 'C' /* letter */
+};
+
+static bool
+gate_handle_if_after_combine (void)
+{
+ return (optimize > 0 && flag_if_conversion);
+}
+
+
+/* Rerun if-conversion, as combine may have simplified things enough
+ to now meet sequence length restrictions. */
+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", /* name */
+ gate_handle_if_after_combine, /* gate */
+ rest_of_handle_if_after_combine, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IFCVT, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'C' /* letter */
+};
+
+
+static bool
+gate_handle_if_after_reload (void)
+{
+ return (optimize > 0);
+}
+
+static unsigned int
+rest_of_handle_if_after_reload (void)
+{
+ /* Last attempt to optimize CFG, as scheduling, peepholing and insn
+ splitting possibly introduced more crossjumping opportunities. */
+ 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", /* name */
+ gate_handle_if_after_reload, /* gate */
+ rest_of_handle_if_after_reload, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_IFCVT2, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'E' /* letter */
+};