summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-15 02:49:17 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-15 02:49:17 +0000
commit268de9b99fd9a7440549cf09781af0566ff076a1 (patch)
tree0f3b6840427ac80db771374ab36bf699a7b5f066
parent6e4c5e2028d021dcd6c553a301ba4c4f9933193b (diff)
downloadgcc-268de9b99fd9a7440549cf09781af0566ff076a1.tar.gz
./:
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@109717 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog9
-rw-r--r--gcc/ifcvt.c310
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.target/i386/cmov6.c22
4 files changed, 317 insertions, 28 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index dc23fabcc29..f6a9c931c44 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c
index 2390f2f3c89..d72b86d976e 100644
--- a/gcc/ifcvt.c
+++ b/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)
- 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))
+ if (!noce_init_if_info (ce_info, &if_info))
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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index bf07e66e2a5..d776ab5ee0c 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
diff --git a/gcc/testsuite/gcc.target/i386/cmov6.c b/gcc/testsuite/gcc.target/i386/cmov6.c
new file mode 100644
index 00000000000..177a0cee02f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cmov6.c
@@ -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);
+}