ifcvt.c (noce_init_if_info): New static function, broken out of noce_process_if_block.
./: * ifcvt.c (noce_init_if_info): New static function, broken out of noce_process_if_block. (noce_process_if_block): Call noce_init_if_info. (check_cond_move_block): New static function. (cond_move_process_if_block): New static function. (process_if_block): Call cond_move_process_if_block. testsuite/: * gcc.target/i386/cmov6.c: New test. From-SVN: r109717
This commit is contained in:
parent
7c514720e4
commit
5d1dcaa2c3
4 changed files with 317 additions and 28 deletions
|
@ -1,3 +1,12 @@
|
|||
2006-01-14 Ian Lance Taylor <ian@airs.com>
|
||||
|
||||
* ifcvt.c (noce_init_if_info): New static function, broken out of
|
||||
noce_process_if_block.
|
||||
(noce_process_if_block): Call noce_init_if_info.
|
||||
(check_cond_move_block): New static function.
|
||||
(cond_move_process_if_block): New static function.
|
||||
(process_if_block): Call cond_move_process_if_block.
|
||||
|
||||
2006-01-15 Kazu Hirata <kazu@codesourcery.com>
|
||||
|
||||
* cselib.c (hash_table): Rename to cselib_hash_table.
|
||||
|
|
310
gcc/ifcvt.c
310
gcc/ifcvt.c
|
@ -2016,6 +2016,52 @@ noce_get_condition (rtx jump, rtx *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
|
||||
|
@ -2111,33 +2157,11 @@ noce_process_if_block (struct ce_if_block * ce_info)
|
|||
|
||||
??? 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);
|
||||
|
@ -2216,9 +2240,6 @@ noce_process_if_block (struct ce_if_block * ce_info)
|
|||
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;
|
||||
|
@ -2340,6 +2361,235 @@ noce_process_if_block (struct ce_if_block * ce_info)
|
|||
|
||||
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. COND is the condition we will
|
||||
test. */
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
vals[REGNO (dest)] = src;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
/* 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, cond)
|
||||
|| (else_bb && !check_cond_move_block (else_bb, else_vals, 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; 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Emit the conditional moves. First do the then block, then do
|
||||
anything left in the else blocks. */
|
||||
|
||||
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 this register was set in the then block, we already
|
||||
handled this case above. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Attempt to convert an IF-THEN or IF-THEN-ELSE block into
|
||||
straight line code. Return true if successful. */
|
||||
|
@ -2351,6 +2601,10 @@ process_if_block (struct ce_if_block * ce_info)
|
|||
&& 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 we have && and || tests, try to first handle combining the && and
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2006-01-14 Ian Lance Taylor <ian@airs.com>
|
||||
|
||||
* gcc.target/i386/cmov6.c: New test.
|
||||
|
||||
2006-01-14 Mark Mitchell <mark@codesourcery.com>
|
||||
|
||||
PR c++/25663
|
||||
|
|
22
gcc/testsuite/gcc.target/i386/cmov6.c
Normal file
22
gcc/testsuite/gcc.target/i386/cmov6.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-options "-O2 -march=i686" } */
|
||||
/* { dg-final { scan-assembler "cmov\[^6\]" } } */
|
||||
|
||||
/* Verify that blocks are converted to conditional moves. */
|
||||
extern int bar (int, int);
|
||||
int foo (int c, int d, int e)
|
||||
{
|
||||
int a, b;
|
||||
|
||||
if (c)
|
||||
{
|
||||
a = 10;
|
||||
b = d;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = e;
|
||||
b = 20;
|
||||
}
|
||||
return bar (a, b);
|
||||
}
|
Loading…
Add table
Reference in a new issue