/* If-conversion support.
- Copyright (C) 2000 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
- This file is part of GNU CC.
+ This file is part of GCC.
- GNU CC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
- GNU CC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
You should have received a copy of the GNU General Public License
- along with GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ 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. */
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "regs.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 "toplev.h"
#include "tm_p.h"
#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 EDGE_COMPLEX (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH)
-
#define NULL_EDGE ((struct edge_def *)NULL)
#define NULL_BLOCK ((struct basic_block_def *)NULL)
/* # of basic blocks that were removed. */
static int num_removed_blocks;
+/* Whether conditional execution changes were made. */
+static int cond_exec_changed_p;
+
+/* True if life data ok at present. */
+static bool life_data_ok;
+
/* The post-dominator relation on the original block numbers. */
-static sbitmap *post_dominators;
+static dominance_info post_dominators;
/* Forward references. */
static int count_bb_insns PARAMS ((basic_block));
static rtx first_active_insn PARAMS ((basic_block));
-static int last_active_insn_p PARAMS ((basic_block, rtx));
+static rtx last_active_insn PARAMS ((basic_block, int));
static int seq_contains_jump PARAMS ((rtx));
-
-static int cond_exec_process_insns PARAMS ((rtx, rtx, rtx, rtx, int));
+static basic_block block_fallthru PARAMS ((basic_block));
+static int cond_exec_process_insns PARAMS ((ce_if_block_t *,
+ rtx, rtx, rtx, rtx, int));
static rtx cond_exec_get_condition PARAMS ((rtx));
-static int cond_exec_process_if_block PARAMS ((basic_block, basic_block,
- basic_block, basic_block));
-
+static int cond_exec_process_if_block PARAMS ((ce_if_block_t *, int));
static rtx noce_get_condition PARAMS ((rtx, rtx *));
-static int noce_process_if_block PARAMS ((basic_block, basic_block,
- basic_block, basic_block));
-
-static int process_if_block PARAMS ((basic_block, basic_block,
- basic_block, basic_block));
-static void merge_if_block PARAMS ((basic_block, basic_block,
- basic_block, basic_block));
-
-static int find_if_header PARAMS ((basic_block));
-static int find_if_block PARAMS ((basic_block, edge, edge));
+static int noce_operand_ok PARAMS ((rtx));
+static int noce_process_if_block PARAMS ((ce_if_block_t *));
+static int process_if_block PARAMS ((ce_if_block_t *));
+static void merge_if_block PARAMS ((ce_if_block_t *));
+static int find_cond_trap PARAMS ((basic_block, edge, edge));
+static basic_block find_if_header PARAMS ((basic_block, int));
+static int block_jumps_and_fallthru_p PARAMS ((basic_block, basic_block));
+static int find_if_block PARAMS ((ce_if_block_t *));
static int find_if_case_1 PARAMS ((basic_block, edge, edge));
static int find_if_case_2 PARAMS ((basic_block, edge, edge));
static int find_memory PARAMS ((rtx *, void *));
static int dead_or_predicable PARAMS ((basic_block, basic_block,
- basic_block, rtx, int));
-\f
-/* Abuse the basic_block AUX field to store the original block index,
- as well as a flag indicating that the block should be rescaned for
- life analysis. */
-
-#define SET_ORIG_INDEX(BB,I) ((BB)->aux = (void *)((size_t)(I) << 1))
-#define ORIG_INDEX(BB) ((size_t)(BB)->aux >> 1)
-#define SET_UPDATE_LIFE(BB) ((BB)->aux = (void *)((size_t)(BB)->aux | 1))
-#define UPDATE_LIFE(BB) ((size_t)(BB)->aux & 1)
-
+ basic_block, basic_block, int));
+static void noce_emit_move_insn PARAMS ((rtx, rtx));
+static rtx block_has_only_trap PARAMS ((basic_block));
\f
/* Count the number of non-jump active insns in BB. */
return insn;
}
-/* Return true if INSN is the last active non-jump insn in BB. */
+/* Return the last non-jump active (non-jump) insn in the basic block. */
-static int
-last_active_insn_p (bb, insn)
+static rtx
+last_active_insn (bb, skip_use_p)
basic_block bb;
- rtx insn;
+ int skip_use_p;
{
- do
+ rtx insn = bb->end;
+ rtx head = bb->head;
+
+ while (GET_CODE (insn) == NOTE
+ || GET_CODE (insn) == JUMP_INSN
+ || (skip_use_p
+ && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == USE))
{
- if (insn == bb->end)
- return TRUE;
- insn = NEXT_INSN (insn);
+ if (insn == head)
+ return NULL_RTX;
+ insn = PREV_INSN (insn);
}
- while (GET_CODE (insn) == NOTE);
- return GET_CODE (insn) == JUMP_INSN;
+ if (GET_CODE (insn) == CODE_LABEL)
+ return NULL_RTX;
+
+ return insn;
}
/* It is possible, especially when having dealt with multi-word
}
return 0;
}
+
+static basic_block
+block_fallthru (bb)
+ basic_block bb;
+{
+ edge e;
+
+ for (e = bb->succ;
+ e != NULL_EDGE && (e->flags & EDGE_FALLTHRU) == 0;
+ e = e->succ_next)
+ ;
+
+ return (e) ? e->dest : NULL_BLOCK;
+}
\f
/* Go through a bunch of insns, converting them to conditional
execution format if possible. Return TRUE if all of the non-note
insns were processed. */
static int
-cond_exec_process_insns (start, end, test, prob_val, mod_ok)
+cond_exec_process_insns (ce_info, start, end, test, prob_val, mod_ok)
+ ce_if_block_t *ce_info ATTRIBUTE_UNUSED; /* if block information */
rtx start; /* first insn to look at */
rtx end; /* last insn to look at */
rtx test; /* conditional execution test */
{
int must_be_last = FALSE;
rtx insn;
+ rtx xtest;
rtx pattern;
+ if (!start || !end)
+ return FALSE;
+
for (insn = start; ; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
/* Now build the conditional form of the instruction. */
pattern = PATTERN (insn);
+ xtest = copy_rtx (test);
+
+ /* If this is already a COND_EXEC, rewrite the test to be an AND of the
+ two conditions. */
+ 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);
/* If the machine needs to modify the insn being conditionally executed,
say for example to force a constant integer operand into a temp
register, do so here. */
#ifdef IFCVT_MODIFY_INSN
- IFCVT_MODIFY_INSN (pattern, insn);
+ IFCVT_MODIFY_INSN (ce_info, pattern, insn);
if (! pattern)
return FALSE;
#endif
- validate_change (insn, &PATTERN (insn),
- gen_rtx_COND_EXEC (VOIDmode, copy_rtx (test),
- pattern), 1);
+ validate_change (insn, &PATTERN (insn), pattern, 1);
if (GET_CODE (insn) == CALL_INSN && prob_val)
validate_change (insn, ®_NOTES (insn),
reverse the condition. */
if (GET_CODE (XEXP (test_if, 2)) == LABEL_REF
&& XEXP (XEXP (test_if, 2), 0) == JUMP_LABEL (jump))
- cond = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond)),
- GET_MODE (cond), XEXP (cond, 0),
- XEXP (cond, 1));
+ {
+ 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;
}
/* Given a simple IF-THEN or IF-THEN-ELSE block, attempt to convert it
to conditional execution. Return TRUE if we were successful at
- converting the the block. */
+ converting the block. */
static int
-cond_exec_process_if_block (test_bb, then_bb, else_bb, join_bb)
- basic_block test_bb; /* Basic block test is in */
- basic_block then_bb; /* Basic block for THEN block */
- basic_block else_bb; /* Basic block for ELSE block */
- basic_block join_bb; /* Basic block the join label is in */
+cond_exec_process_if_block (ce_info, do_multiple_p)
+ ce_if_block_t * ce_info; /* if block information */
+ int do_multiple_p; /* != 0 if we should handle && and || blocks */
{
+ basic_block test_bb = ce_info->test_bb; /* last test block */
+ basic_block then_bb = ce_info->then_bb; /* THEN */
+ basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
rtx test_expr; /* expression in IF_THEN_ELSE that is tested */
rtx then_start; /* first insn in THEN block */
rtx then_end; /* last insn + 1 in THEN block */
rtx else_start = NULL_RTX; /* first insn in ELSE block or NULL */
rtx else_end = NULL_RTX; /* last insn + 1 in ELSE block */
- int max; /* max # of insns to convert. */
+ int max; /* max # of insns to convert. */
int then_mod_ok; /* whether conditional mods are ok in THEN */
rtx true_expr; /* test for else block insns */
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;
+ enum rtx_code false_code;
+
+ /* If test is comprised of && or || elements, and we've failed at handling
+ all of them together, just use the last test if it is the special case of
+ && elements without an ELSE block. */
+ 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;
+ }
/* Find the conditional jump to the ELSE or JOIN part, and isolate
the test. */
/* If the conditional jump is more than just a conditional jump,
then we can not do conditional execution conversion on this block. */
- if (!onlyjump_p (test_bb->end))
+ if (! onlyjump_p (test_bb->end))
return FALSE;
- /* Collect the bounds of where we're to search. */
-
- then_start = then_bb->head;
- then_end = then_bb->end;
-
- /* Skip a label heading THEN block. */
- if (GET_CODE (then_start) == CODE_LABEL)
- then_start = NEXT_INSN (then_start);
-
- /* Skip a (use (const_int 0)) or branch as the final insn. */
- if (GET_CODE (then_end) == INSN
- && GET_CODE (PATTERN (then_end)) == USE
- && GET_CODE (XEXP (PATTERN (then_end), 0)) == CONST_INT)
- then_end = PREV_INSN (then_end);
- else if (GET_CODE (then_end) == JUMP_INSN)
- then_end = PREV_INSN (then_end);
+ /* Collect the bounds of where we're to search, skipping any labels, jumps
+ and notes at the beginning and end of the block. Then count the total
+ 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);
+ max = MAX_CONDITIONAL_EXECUTE;
if (else_bb)
{
- /* Skip the ELSE block's label. */
- else_start = NEXT_INSN (else_bb->head);
- else_end = else_bb->end;
-
- /* Skip a (use (const_int 0)) or branch as the final insn. */
- if (GET_CODE (else_end) == INSN
- && GET_CODE (PATTERN (else_end)) == USE
- && GET_CODE (XEXP (PATTERN (else_end), 0)) == CONST_INT)
- else_end = PREV_INSN (else_end);
- else if (GET_CODE (else_end) == JUMP_INSN)
- else_end = PREV_INSN (else_end);
+ 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);
}
- /* How many instructions should we convert in total? */
- n_insns = 0;
- if (else_bb)
- {
- max = 2 * MAX_CONDITIONAL_EXECUTE;
- n_insns = count_bb_insns (else_bb);
- }
- else
- max = MAX_CONDITIONAL_EXECUTE;
- n_insns += count_bb_insns (then_bb);
if (n_insns > max)
return FALSE;
the conditionally executed code. */
true_expr = test_expr;
- false_expr = gen_rtx_fmt_ee (reverse_condition (GET_CODE (true_expr)),
- GET_MODE (true_expr), XEXP (true_expr, 0),
- XEXP (true_expr, 1));
+
+ false_code = reversed_comparison_code (true_expr, test_bb->end);
+ 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
/* If the machine description needs to modify the tests, such as setting a
conditional execution register from a comparison, it can do so here. */
- IFCVT_MODIFY_TESTS (true_expr, false_expr, test_bb, then_bb, else_bb,
- join_bb);
+ IFCVT_MODIFY_TESTS (ce_info, true_expr, false_expr);
/* See if the conversion failed */
if (!true_expr || !false_expr)
else
false_prob_val = NULL_RTX;
+ /* If we have && or || tests, do them here. These tests are in the adjacent
+ blocks after the first block containing the test. */
+ 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
+ {
+ rtx start, end;
+ rtx t, f;
+
+ bb = block_fallthru (bb);
+ start = first_active_insn (bb);
+ end = last_active_insn (bb, TRUE);
+ if (start
+ && ! cond_exec_process_insns (ce_info, start, end, false_expr,
+ false_prob_val, FALSE))
+ goto fail;
+
+ /* If the conditional jump is more than just a conditional jump, then
+ we can not do conditional execution conversion on this block. */
+ if (! onlyjump_p (bb->end))
+ goto fail;
+
+ /* Find the conditional jump and isolate the test. */
+ t = cond_exec_get_condition (bb->end);
+ if (! t)
+ goto fail;
+
+ f = gen_rtx_fmt_ee (reverse_condition (GET_CODE (t)),
+ GET_MODE (t),
+ XEXP (t, 0),
+ XEXP (t, 1));
+
+ if (ce_info->and_and_p)
+ {
+ t = gen_rtx_AND (GET_MODE (t), true_expr, t);
+ f = gen_rtx_IOR (GET_MODE (t), false_expr, f);
+ }
+ else
+ {
+ t = gen_rtx_IOR (GET_MODE (t), true_expr, t);
+ f = gen_rtx_AND (GET_MODE (t), false_expr, f);
+ }
+
+ /* If the machine description needs to modify the tests, such as
+ setting a conditional execution register from a comparison, it can
+ do so here. */
+#ifdef IFCVT_MODIFY_MULTIPLE_TESTS
+ IFCVT_MODIFY_MULTIPLE_TESTS (ce_info, bb, t, f);
+
+ /* See if the conversion failed */
+ if (!t || !f)
+ goto fail;
+#endif
+
+ true_expr = t;
+ false_expr = f;
+ }
+ while (bb != last_test_bb);
+ }
+
/* For IF-THEN-ELSE blocks, we don't allow modifications of the test
on then THEN block. */
then_mod_ok = (else_bb == NULL_BLOCK);
to conditional execution. */
if (then_end
- && ! cond_exec_process_insns (then_start, then_end,
- false_expr, false_prob_val, then_mod_ok))
+ && (! 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
- && ! cond_exec_process_insns (else_start, else_end,
+ if (else_bb && else_end
+ && ! cond_exec_process_insns (ce_info, else_start, else_end,
true_expr, true_prob_val, TRUE))
goto fail;
+ /* If we cannot apply the changes, fail. Do not go through the normal fail
+ processing, since apply_change_group will call cancel_changes. */
if (! apply_change_group ())
- return FALSE;
+ {
+#ifdef IFCVT_MODIFY_CANCEL
+ /* Cancel any machine dependent changes. */
+ IFCVT_MODIFY_CANCEL (ce_info);
+#endif
+ return FALSE;
+ }
#ifdef IFCVT_MODIFY_FINAL
/* Do any machine dependent final modifications */
- IFCVT_MODIFY_FINAL (test_bb, then_bb, else_bb, join_bb);
+ IFCVT_MODIFY_FINAL (ce_info);
#endif
/* Conversion succeeded. */
n_insns, (n_insns == 1) ? " was" : "s were");
/* Merge the blocks! */
- merge_if_block (test_bb, then_bb, else_bb, join_bb);
+ merge_if_block (ce_info);
+ cond_exec_changed_p = TRUE;
return TRUE;
fail:
#ifdef IFCVT_MODIFY_CANCEL
/* Cancel any machine dependent changes. */
- IFCVT_MODIFY_CANCEL (test_bb, then_bb, else_bb, join_bb);
+ IFCVT_MODIFY_CANCEL (ce_info);
#endif
cancel_changes (0);
struct noce_if_info
{
+ basic_block test_bb;
rtx insn_a, insn_b;
rtx x, a, b;
rtx jump, cond, cond_earliest;
rtx, rtx, rtx));
static int noce_try_cmove PARAMS ((struct noce_if_info *));
static int noce_try_cmove_arith PARAMS ((struct noce_if_info *));
+static rtx noce_get_alt_condition PARAMS ((struct noce_if_info *,
+ rtx, rtx *));
+static int noce_try_minmax PARAMS ((struct noce_if_info *));
+static int noce_try_abs PARAMS ((struct noce_if_info *));
/* Helper function for noce_try_store_flag*. */
build the store_flag insn directly. */
if (cond_complex)
- cond = XEXP (SET_SRC (PATTERN (if_info->jump)), 0);
+ 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;
- code = GET_CODE (cond);
- if (reversep)
- code = reverse_condition (code);
-
tmp = gen_rtx_fmt_ee (code, GET_MODE (x), XEXP (cond, 0),
XEXP (cond, 1));
tmp = gen_rtx_SET (VOIDmode, x, tmp);
{
tmp = get_insns ();
end_sequence ();
- emit_insns (tmp);
+ emit_insn (tmp);
if_info->cond_earliest = if_info->jump;
if (cond_complex)
return NULL_RTX;
- code = GET_CODE (cond);
- if (reversep)
- code = reverse_condition (code);
-
return emit_store_flag (x, code, XEXP (cond, 0),
XEXP (cond, 1), VOIDmode,
(code == LTU || code == LEU
|| code == GEU || code == GTU), normalize);
}
+/* Emit instruction to move an rtx into STRICT_LOW_PART. */
+static void
+noce_emit_move_insn (x, y)
+ rtx x, y;
+{
+ enum machine_mode outmode, inmode;
+ rtx outer, inner;
+ int bitpos;
+
+ if (GET_CODE (x) != STRICT_LOW_PART)
+ {
+ emit_move_insn (x, y);
+ 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,
+ GET_MODE_BITSIZE (inmode));
+}
+
/* Convert "if (test) x = 1; else x = 0".
Only try 0 and STORE_FLAG_VALUE here. Other combinations will be
else if (if_info->b == const0_rtx
&& GET_CODE (if_info->a) == CONST_INT
&& INTVAL (if_info->a) == STORE_FLAG_VALUE
- && can_reverse_comparison_p (if_info->cond, if_info->jump))
+ && (reversed_comparison_code (if_info->cond, if_info->jump)
+ != UNKNOWN))
reversep = 1;
else
return FALSE;
if (target)
{
if (target != if_info->x)
- emit_move_insn (if_info->x, target);
+ noce_emit_move_insn (if_info->x, target);
seq = get_insns ();
end_sequence ();
- emit_insns_before (seq, if_info->cond_earliest);
+ emit_insn_before_scope (seq, if_info->jump, INSN_SCOPE (if_info->insn_a));
return TRUE;
}
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);
- diff = itrue - ifalse;
- can_reverse = can_reverse_comparison_p (if_info->cond, if_info->jump);
+ /* Make sure we can represent the difference between the two values. */
+ 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)
if (reversep)
{
tmp = itrue; itrue = ifalse; ifalse = tmp;
- diff = -diff;
+ diff = trunc_int_for_mode (-diff, mode);
}
start_sequence ();
=> x = 3 + (test == 0); */
if (diff == STORE_FLAG_VALUE || diff == -STORE_FLAG_VALUE)
{
- target = expand_binop (GET_MODE (if_info->x),
- (diff == STORE_FLAG_VALUE
- ? add_optab : sub_optab),
- GEN_INT (ifalse), target, if_info->x, 0,
- OPTAB_WIDEN);
+ target = expand_simple_binop (mode,
+ (diff == STORE_FLAG_VALUE
+ ? PLUS : MINUS),
+ GEN_INT (ifalse), target, if_info->x, 0,
+ OPTAB_WIDEN);
}
/* if (test) x = 8; else x = 0;
=> x = (test != 0) << 3; */
else if (ifalse == 0 && (tmp = exact_log2 (itrue)) >= 0)
{
- target = expand_binop (GET_MODE (if_info->x), ashl_optab,
- target, GEN_INT (tmp), if_info->x, 0,
- OPTAB_WIDEN);
+ target = expand_simple_binop (mode, ASHIFT,
+ target, GEN_INT (tmp), if_info->x, 0,
+ OPTAB_WIDEN);
}
/* if (test) x = -1; else x = b;
=> x = -(test != 0) | b; */
else if (itrue == -1)
{
- target = expand_binop (GET_MODE (if_info->x), ior_optab,
- target, GEN_INT (ifalse), if_info->x, 0,
- OPTAB_WIDEN);
+ target = expand_simple_binop (mode, IOR,
+ target, GEN_INT (ifalse), if_info->x, 0,
+ OPTAB_WIDEN);
}
/* if (test) x = a; else x = b;
=> x = (-(test != 0) & (b - a)) + a; */
else
{
- target = expand_binop (GET_MODE (if_info->x), and_optab,
- target, GEN_INT (diff), if_info->x, 0,
- OPTAB_WIDEN);
+ target = expand_simple_binop (mode, AND,
+ target, GEN_INT (diff), if_info->x, 0,
+ OPTAB_WIDEN);
if (target)
- target = expand_binop (GET_MODE (if_info->x), add_optab,
- target, GEN_INT (ifalse), if_info->x, 0,
- OPTAB_WIDEN);
+ target = expand_simple_binop (mode, PLUS,
+ target, GEN_INT (ifalse),
+ if_info->x, 0, OPTAB_WIDEN);
}
if (! target)
}
if (target != if_info->x)
- emit_move_insn (if_info->x, target);
+ noce_emit_move_insn (if_info->x, target);
seq = get_insns ();
end_sequence ();
if (seq_contains_jump (seq))
return FALSE;
- emit_insns_before (seq, if_info->cond_earliest);
+ emit_insn_before_scope (seq, if_info->jump, INSN_SCOPE (if_info->insn_a));
return TRUE;
}
&& (XEXP (if_info->a, 1) == const1_rtx
|| XEXP (if_info->a, 1) == constm1_rtx)
&& rtx_equal_p (XEXP (if_info->a, 0), if_info->x)
- && can_reverse_comparison_p (if_info->cond, if_info->jump))
+ && (reversed_comparison_code (if_info->cond, if_info->jump)
+ != UNKNOWN))
{
if (STORE_FLAG_VALUE == INTVAL (XEXP (if_info->a, 1)))
subtract = 0, normalize = 0;
1, normalize);
if (target)
- target = expand_binop (GET_MODE (if_info->x),
- subtract ? sub_optab : add_optab,
- if_info->x, target, if_info->x, 0, OPTAB_WIDEN);
+ target = expand_simple_binop (GET_MODE (if_info->x),
+ subtract ? MINUS : PLUS,
+ if_info->x, target, if_info->x,
+ 0, OPTAB_WIDEN);
if (target)
{
if (target != if_info->x)
- emit_move_insn (if_info->x, target);
+ noce_emit_move_insn (if_info->x, target);
seq = get_insns ();
end_sequence ();
if (seq_contains_jump (seq))
return FALSE;
- emit_insns_before (seq, if_info->cond_earliest);
+ emit_insn_before_scope (seq, if_info->jump,
+ INSN_SCOPE (if_info->insn_a));
return TRUE;
}
|| STORE_FLAG_VALUE == -1)
&& ((if_info->a == const0_rtx
&& rtx_equal_p (if_info->b, if_info->x))
- || ((reversep = can_reverse_comparison_p (if_info->cond,
- if_info->jump))
+ || ((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))))
{
gen_reg_rtx (GET_MODE (if_info->x)),
reversep, -1);
if (target)
- target = expand_binop (GET_MODE (if_info->x), and_optab,
- if_info->x, target, if_info->x, 0,
- OPTAB_WIDEN);
+ 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)
- emit_move_insn (if_info->x, target);
+ noce_emit_move_insn (if_info->x, target);
seq = get_insns ();
end_sequence ();
if (seq_contains_jump (seq))
return FALSE;
- emit_insns_before (seq, if_info->cond_earliest);
+ emit_insn_before_scope (seq, if_info->jump,
+ INSN_SCOPE (if_info->insn_a));
return TRUE;
}
{
tmp = get_insns ();
end_sequence ();
- emit_insns (tmp);
+ emit_insn (tmp);
return x;
}
if (target)
{
if (target != if_info->x)
- emit_move_insn (if_info->x, target);
+ noce_emit_move_insn (if_info->x, target);
seq = get_insns ();
end_sequence ();
- emit_insns_before (seq, if_info->cond_earliest);
+ emit_insn_before_scope (seq, if_info->jump,
+ INSN_SCOPE (if_info->insn_a));
return TRUE;
}
else
insn_b = if_info->insn_b;
/* Possibly rearrange operands to make things come out more natural. */
- if (can_reverse_comparison_p (if_info->cond, if_info->jump))
+ if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN)
{
int reversep = 0;
if (rtx_equal_p (b, x))
if (reversep)
{
- code = reverse_condition (code);
+ 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;
}
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))
- MEM_ALIAS_SET (tmp) = MEM_ALIAS_SET (if_info->a);
+ 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)));
- emit_move_insn (if_info->x, tmp);
+ noce_emit_move_insn (if_info->x, tmp);
}
else if (target != x)
- emit_move_insn (x, target);
+ noce_emit_move_insn (x, target);
tmp = get_insns ();
end_sequence ();
- emit_insns_before (tmp, if_info->cond_earliest);
+ emit_insn_before_scope (tmp, if_info->jump, INSN_SCOPE (if_info->insn_a));
return TRUE;
end_seq_and_fail:
return FALSE;
}
-/* Look for the condition for the jump first. We'd prefer to avoid
- get_condition if we can -- it tries to look back for the contents
- of an original compare. On targets that use normal integers for
- comparisons, e.g. alpha, this is wasteful. */
+/* For most cases, the simplified condition we found is the best
+ choice, but this is not the case for the min/max/abs transforms.
+ For these we wish to know that it is A or B in the condition. */
+
+static rtx
+noce_get_alt_condition (if_info, target, earliest)
+ struct noce_if_info *if_info;
+ rtx target;
+ rtx *earliest;
+{
+ rtx cond, set, insn;
+ int reverse;
+
+ /* If target is already mentioned in the known condition, return it. */
+ 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 we're looking for a constant, try to make the conditional
+ have that constant in it. There are two reasons why it may
+ not have the constant we want:
+
+ 1. GCC may have needed to put the constant in a register, because
+ the target can't compare directly against that constant. For
+ this case, we look for a SET immediately before the comparison
+ that puts a constant in that register.
+
+ 2. GCC may have canonicalized the conditional, for example
+ replacing "if x < 4" with "if x <= 3". We can undo that (or
+ make equivalent types of changes) to get the constants we need
+ if they're off by one in the right direction. */
+
+ 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;
+
+ /* First, look to see if we put a constant in a register. */
+ prev_insn = PREV_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);
+ }
+ }
+ }
+
+ /* Now, look to see if we can get the right constant by
+ adjusting the conditional. */
+ 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 we made any changes, generate a new conditional that is
+ equivalent to what we started with, but has the right
+ constants in it. */
+ 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);
+ if (! cond || ! reg_mentioned_p (target, cond))
+ return NULL;
+
+ /* We almost certainly searched back to a different place.
+ Need to re-verify correct lifetimes. */
+
+ /* X may not be mentioned in the range (cond_earliest, jump]. */
+ 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;
+
+ /* A and B may not be modified in the range [cond_earliest, jump). */
+ 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;
+}
+
+/* Convert "if (a < b) x = a; else x = b;" to "x = min(a, b);", etc. */
+
+static int
+noce_try_minmax (if_info)
+ struct noce_if_info *if_info;
+{
+ rtx cond, earliest, target, seq;
+ enum rtx_code code, op;
+ int unsignedp;
+
+ /* ??? Can't guarantee that expand_binop won't create pseudos. */
+ if (no_new_pseudos)
+ return FALSE;
+
+ /* ??? Reject modes with NaNs or signed zeros since we don't know how
+ they will be resolved with an SMIN/SMAX. It wouldn't be too hard
+ to get the target to tell us... */
+ 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;
+
+ /* Verify the condition is of the form we expect, and canonicalize
+ the comparison code. */
+ 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;
+
+ /* Determine what sort of operation this is. Note that the code is for
+ a taken branch, so the code->operation mapping appears backwards. */
+ 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 = get_insns ();
+ end_sequence ();
+
+ if (seq_contains_jump (seq))
+ return FALSE;
+
+ emit_insn_before_scope (seq, if_info->jump, INSN_SCOPE (if_info->insn_a));
+ if_info->cond = cond;
+ if_info->cond_earliest = earliest;
+
+ return TRUE;
+}
+
+/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", etc. */
+
+static int
+noce_try_abs (if_info)
+ struct noce_if_info *if_info;
+{
+ rtx cond, earliest, target, seq, a, b, c;
+ int negate;
+
+ /* ??? Can't guarantee that expand_binop won't create pseudos. */
+ if (no_new_pseudos)
+ return FALSE;
+
+ /* Recognize A and B as constituting an ABS or NABS. */
+ 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;
+
+ /* Verify the condition is of the form we expect. */
+ 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);
+ else
+ return FALSE;
+
+ /* Verify that C is zero. Search backward through the block for
+ a REG_EQUAL note if necessary. */
+ if (REG_P (c))
+ {
+ rtx insn, note = NULL;
+ for (insn = earliest;
+ insn != if_info->test_bb->head;
+ 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)
+ return FALSE;
+ c = XEXP (note, 0);
+ }
+ if (GET_CODE (c) == MEM
+ && GET_CODE (XEXP (c, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (c, 0)))
+ c = get_pool_constant (XEXP (c, 0));
+
+ /* Work around funny ideas get_condition has wrt canonicalization.
+ Note that these rtx constants are known to be CONST_INT, and
+ therefore imply integer comparisons. */
+ 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;
+
+ /* Determine what sort of operation this is. */
+ 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_simple_unop (GET_MODE (if_info->x), ABS, b, if_info->x, 0);
+
+ /* ??? It's a quandry 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 (! target)
+ {
+ end_sequence ();
+ return FALSE;
+ }
+
+ if (target != if_info->x)
+ noce_emit_move_insn (if_info->x, target);
+
+ seq = get_insns ();
+ end_sequence ();
+
+ if (seq_contains_jump (seq))
+ return FALSE;
+
+ emit_insn_before_scope (seq, if_info->jump, INSN_SCOPE (if_info->insn_a));
+ if_info->cond = cond;
+ if_info->cond_earliest = earliest;
+
+ return TRUE;
+}
+
+/* Similar to get_condition, only the resulting condition must be
+ valid at JUMP, instead of at EARLIEST. */
static rtx
noce_get_condition (jump, earliest)
rtx jump;
rtx *earliest;
{
- rtx cond;
- rtx set;
-
- /* If the condition variable is a register and is MODE_INT, accept it.
- Otherwise, fall back on get_condition. */
+ rtx cond, set, tmp, insn;
+ bool reverse;
if (! any_condjump_p (jump))
return NULL_RTX;
set = pc_set (jump);
+ /* If this branches to JUMP_LABEL when the condition is false,
+ reverse the condition. */
+ reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+ && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
+
+ /* If the condition variable is a register and is MODE_INT, accept it. */
+
cond = XEXP (SET_SRC (set), 0);
- if (GET_CODE (XEXP (cond, 0)) == REG
- && GET_MODE_CLASS (GET_MODE (XEXP (cond, 0))) == MODE_INT)
+ tmp = XEXP (cond, 0);
+ if (REG_P (tmp) && GET_MODE_CLASS (GET_MODE (tmp)) == MODE_INT)
{
*earliest = jump;
- /* If this branches to JUMP_LABEL when the condition is false,
- reverse the condition. */
- if (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
- && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump))
+ if (reverse)
cond = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond)),
- GET_MODE (cond), XEXP (cond, 0),
- XEXP (cond, 1));
+ GET_MODE (cond), tmp, XEXP (cond, 1));
+ return cond;
}
- else
- cond = get_condition (jump, earliest);
- return cond;
+ /* Otherwise, fall back on canonicalize_condition to do the dirty
+ work of manipulating MODE_CC values and COMPARE rtx codes. */
+
+ tmp = canonicalize_condition (jump, cond, reverse, earliest, NULL_RTX);
+ if (!tmp)
+ return NULL_RTX;
+
+ /* We are going to insert code before JUMP, not before EARLIEST.
+ We must therefore be certain that the given condition is valid
+ at JUMP by virtue of not having been modified since. */
+ for (insn = *earliest; insn != jump; insn = NEXT_INSN (insn))
+ if (INSN_P (insn) && modified_in_p (tmp, insn))
+ break;
+ if (insn == jump)
+ return tmp;
+
+ /* The condition was modified. See if we can get a partial result
+ that doesn't follow all the reversals. Perhaps combine can fold
+ them together later. */
+ tmp = XEXP (tmp, 0);
+ if (!REG_P (tmp) || GET_MODE_CLASS (GET_MODE (tmp)) != MODE_INT)
+ return NULL_RTX;
+ tmp = canonicalize_condition (jump, cond, reverse, earliest, tmp);
+ if (!tmp)
+ return NULL_RTX;
+
+ /* For sanity's sake, re-validate the new result. */
+ for (insn = *earliest; insn != jump; insn = NEXT_INSN (insn))
+ if (INSN_P (insn) && modified_in_p (tmp, insn))
+ return NULL_RTX;
+
+ return tmp;
+}
+
+/* Return true if OP is ok for if-then-else processing. */
+
+static int
+noce_operand_ok (op)
+ rtx op;
+{
+ /* We special-case memories, so handle any of them with
+ no address side effects. */
+ if (GET_CODE (op) == MEM)
+ return ! side_effects_p (XEXP (op, 0));
+
+ if (side_effects_p (op))
+ return FALSE;
+
+ return ! may_trap_p (op);
}
/* 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 the block. */
+ successful at converting the block. */
static int
-noce_process_if_block (test_bb, then_bb, else_bb, join_bb)
- basic_block test_bb; /* Basic block test is in */
- basic_block then_bb; /* Basic block for THEN block */
- basic_block else_bb; /* Basic block for ELSE block */
- basic_block join_bb; /* Basic block the join label is in */
+noce_process_if_block (ce_info)
+ struct ce_if_block * ce_info;
{
+ 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 */
+ 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;
+
/* We're looking for patterns of the form
(1) if (...) x = a; else x = b;
??? For future expansion, look for multiple X in such patterns. */
- 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, insn;
+ /* 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 = test_bb->end;
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 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;
/* Look for one of the potential sets. */
insn_a = first_active_insn (then_bb);
if (! insn_a
- || ! last_active_insn_p (then_bb, insn_a)
+ || insn_a != last_active_insn (then_bb, FALSE)
|| (set_a = single_set (insn_a)) == NULL_RTX)
return FALSE;
{
insn_b = first_active_insn (else_bb);
if (! insn_b
- || ! last_active_insn_p (else_bb, 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;
|| GET_CODE (insn_b) != INSN
|| (set_b = single_set (insn_b)) == NULL_RTX
|| ! rtx_equal_p (x, SET_DEST (set_b))
- || reg_mentioned_p (x, cond)
- || reg_mentioned_p (x, a)
- || reg_mentioned_p (x, SET_SRC (set_b)))
+ || reg_overlap_mentioned_p (x, cond)
+ || reg_overlap_mentioned_p (x, a)
+ || reg_overlap_mentioned_p (x, SET_SRC (set_b))
+ || modified_between_p (x, if_info.cond_earliest, NEXT_INSN (jump)))
insn_b = set_b = NULL_RTX;
}
b = (set_b ? SET_SRC (set_b) : x);
- /* X may not be mentioned in the range (cond_earliest, jump]. */
- for (insn = jump; insn != if_info.cond_earliest; insn = PREV_INSN (insn))
- if (INSN_P (insn) && reg_mentioned_p (x, insn))
- return FALSE;
-
- /* A and B may not be modified in the range [cond_earliest, jump). */
- for (insn = if_info.cond_earliest; insn != jump; insn = NEXT_INSN (insn))
- if (INSN_P (insn)
- && (modified_in_p (a, insn) || modified_in_p (b, insn)))
- return FALSE;
-
/* Only operate on register destinations, and even then avoid extending
the lifetime of hard registers on small register class machines. */
orig_x = x;
{
if (no_new_pseudos)
return FALSE;
- x = gen_reg_rtx (GET_MODE (x));
+ x = gen_reg_rtx (GET_MODE (GET_CODE (x) == STRICT_LOW_PART
+ ? XEXP (x, 0) : x));
}
/* Don't operate on sources that may trap or are volatile. */
- if (side_effects_p (a) || side_effects_p (b)
- || (GET_CODE (a) != MEM && may_trap_p (a))
- || (GET_CODE (b) != MEM && may_trap_p (b)))
+ if (! noce_operand_ok (a) || ! noce_operand_ok (b))
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;
that case don't do anything and let the code below delete INSN_A. */
if (insn_b && else_bb)
{
+ rtx note;
+
if (else_bb && insn_b == else_bb->end)
else_bb->end = PREV_INSN (insn_b);
- reorder_insns (insn_b, insn_b, PREV_INSN (if_info.cond_earliest));
+ reorder_insns (insn_b, insn_b, PREV_INSN (jump));
+
+ /* If there was a REG_EQUAL note, delete it since it may have been
+ true due to this insn being after a jump. */
+ if ((note = find_reg_note (insn_b, REG_EQUAL, NULL_RTX)) != 0)
+ remove_note (insn_b, note);
+
insn_b = NULL_RTX;
}
+ /* If we have "x = b; if (...) x = a;", and x has side-effects, then
+ x must be executed twice. */
+ else if (insn_b && side_effects_p (orig_x))
+ return FALSE;
+
x = orig_x;
goto success;
}
if (noce_try_store_flag (&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;
success:
/* The original sets may now be killed. */
- if (insn_a == then_bb->end)
- then_bb->end = PREV_INSN (insn_a);
- flow_delete_insn (insn_a);
+ 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
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)
- {
- if (insn_b == else_bb->end)
- else_bb->end = PREV_INSN (insn_b);
- flow_delete_insn (insn_b);
- }
+ delete_insn (insn_b);
- /* The new insns will have been inserted before cond_earliest. 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. */
- test_bb->end = PREV_INSN (jump);
- flow_delete_insn (jump);
+ /* 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)
{
start_sequence ();
- emit_move_insn (orig_x, x);
- insn_b = gen_sequence ();
+ noce_emit_move_insn (copy_rtx (orig_x), x);
+ insn_b = get_insns ();
end_sequence ();
- test_bb->end = emit_insn_after (insn_b, test_bb->end);
+ emit_insn_after_scope (insn_b, test_bb->end, INSN_SCOPE (insn_a));
}
/* Merge the blocks! */
- merge_if_block (test_bb, then_bb, else_bb, join_bb);
+ merge_if_block (ce_info);
return TRUE;
}
straight line code. Return true if successful. */
static int
-process_if_block (test_bb, then_bb, else_bb, join_bb)
- basic_block test_bb; /* Basic block test is in */
- basic_block then_bb; /* Basic block for THEN block */
- basic_block else_bb; /* Basic block for ELSE block */
- basic_block join_bb; /* Basic block the join label is in */
+process_if_block (ce_info)
+ struct ce_if_block * ce_info;
{
if (! reload_completed
- && noce_process_if_block (test_bb, then_bb, else_bb, join_bb))
+ && noce_process_if_block (ce_info))
return TRUE;
- if (HAVE_conditional_execution
- && reload_completed
- && cond_exec_process_if_block (test_bb, then_bb, else_bb, join_bb))
- return TRUE;
+ if (HAVE_conditional_execution && reload_completed)
+ {
+ /* If we have && and || tests, try to first handle combining the && and
+ || tests into the conditional code, and if that fails, go back and
+ handle it without the && and ||, which at present handles the && case
+ if there was no ELSE block. */
+ if (cond_exec_process_if_block (ce_info, TRUE))
+ return TRUE;
+
+ if (ce_info->num_multiple_test_blocks)
+ {
+ cancel_changes (0);
+
+ if (cond_exec_process_if_block (ce_info, FALSE))
+ return TRUE;
+ }
+ }
return FALSE;
}
/* Merge the blocks and mark for local life update. */
static void
-merge_if_block (test_bb, then_bb, else_bb, join_bb)
- basic_block test_bb; /* Basic block test is in */
- basic_block then_bb; /* Basic block for THEN block */
- basic_block else_bb; /* Basic block for ELSE block */
- basic_block join_bb; /* Basic block the join label is in */
+merge_if_block (ce_info)
+ struct ce_if_block * ce_info;
{
+ basic_block test_bb = ce_info->test_bb; /* last 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 = ce_info->join_bb; /* join block */
basic_block combo_bb;
/* All block merging is done into the lower block numbers. */
combo_bb = test_bb;
- /* First merge TEST block into THEN block. This is a no-brainer since
- the THEN block did not have a code label to begin with. */
+ /* Merge any basic blocks to handle && and || subtests. Each of
+ the blocks are on the fallthru path from the predecessor block. */
+ 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);
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, bb);
+ merge_blocks_nomove (combo_bb, bb);
+ num_removed_blocks++;
+ }
+ while (bb != last_test_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_nomove (combo_bb, then_bb);
- num_removed_blocks++;
+ /* Merge TEST block into THEN block. Normally the THEN block won't have a
+ label, but it might if there were || tests. That label's count should be
+ zero, and it normally should be removed. */
+
+ 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);
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, then_bb);
+ merge_blocks_nomove (combo_bb, then_bb);
+ num_removed_blocks++;
+ }
/* The ELSE block, if it existed, had a label. That label count
will almost always be zero, but odd things can happen when labels
get their addresses taken. */
if (else_bb)
{
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, else_bb);
merge_blocks_nomove (combo_bb, else_bb);
num_removed_blocks++;
}
if (! join_bb)
{
+ rtx last = combo_bb->end;
+
/* The outgoing edge for the current COMBO block should already
be correct. Verify this. */
if (combo_bb->succ == NULL_EDGE)
- abort ();
+ {
+ if (find_reg_note (last, REG_NORETURN, NULL))
+ ;
+ else if (GET_CODE (last) == INSN
+ && GET_CODE (PATTERN (last)) == TRAP_IF
+ && TRAP_CONDITION (PATTERN (last)) == const_true_rtx)
+ ;
+ else
+ abort ();
+ }
- /* There should sill be a branch at the end of the THEN or ELSE
+ /* There should still be something at the end of the THEN or ELSE
blocks taking us to our final destination. */
- if (! simplejump_p (combo_bb->end)
- && ! returnjump_p (combo_bb->end))
+ else if (GET_CODE (last) == JUMP_INSN)
+ ;
+ else if (combo_bb->succ->dest == EXIT_BLOCK_PTR
+ && GET_CODE (last) == CALL_INSN
+ && SIBLING_CALL_P (last))
+ ;
+ else if ((combo_bb->succ->flags & EDGE_EH)
+ && can_throw_internal (last))
+ ;
+ else
abort ();
}
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). */
- else if (join_bb->pred == NULL || join_bb->pred->pred_next == NULL)
+ else if ((join_bb->pred == NULL
+ || join_bb->pred->pred_next == NULL)
+ && 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);
+
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, join_bb);
merge_blocks_nomove (combo_bb, join_bb);
num_removed_blocks++;
}
abort ();
/* Remove the jump and cruft from the end of the COMBO block. */
- tidy_fallthru_edge (combo_bb->succ, combo_bb, join_bb);
+ if (join_bb != EXIT_BLOCK_PTR)
+ tidy_fallthru_edge (combo_bb->succ, combo_bb, join_bb);
}
- /* Make sure we update life info properly. */
- SET_UPDATE_LIFE (combo_bb);
-
num_updated_if_blocks++;
}
\f
-/* Find a block ending in a simple IF condition. Return TRUE if
- we were able to transform it in some way. */
+/* Find a block ending in a simple IF condition and try to transform it
+ in some way. When converting a multi-block condition, put the new code
+ in the first such block and delete the rest. Return a pointer to this
+ first block if some transformation was done. Return NULL otherwise. */
-static int
-find_if_header (test_bb)
+static basic_block
+find_if_header (test_bb, pass)
basic_block test_bb;
+ int pass;
{
+ ce_if_block_t ce_info;
edge then_edge;
edge else_edge;
if ((then_edge = test_bb->succ) == NULL_EDGE
|| (else_edge = then_edge->succ_next) == NULL_EDGE
|| else_edge->succ_next != NULL_EDGE)
- return FALSE;
+ return NULL;
/* Neither edge should be abnormal. */
if ((then_edge->flags & EDGE_COMPLEX)
|| (else_edge->flags & EDGE_COMPLEX))
- return FALSE;
+ return NULL;
/* The THEN edge is canonically the one that falls through. */
if (then_edge->flags & EDGE_FALLTHRU)
}
else
/* Otherwise this must be a multiway branch of some sort. */
- return FALSE;
+ return NULL;
- if (find_if_block (test_bb, then_edge, else_edge))
+ memset ((PTR) &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 (post_dominators
&& (! HAVE_conditional_execution || reload_completed))
{
goto success;
}
- return FALSE;
+ return NULL;
success:
if (rtl_dump_file)
- fprintf (rtl_dump_file, "Conversion succeeded.\n");
- return TRUE;
+ fprintf (rtl_dump_file, "Conversion succeeded on pass %d.\n", pass);
+ return ce_info.test_bb;
+}
+
+/* Return true if a block has two edges, one of which falls through to the next
+ block, and the other jumps to a specific block, so that we can tell if the
+ block is part of an && test or an || test. Returns either -1 or the number
+ of non-note, non-jump, non-USE/CLOBBER insns in the block. */
+
+static int
+block_jumps_and_fallthru_p (cur_bb, target_bb)
+ 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;
+
+ if (!cur_bb || !target_bb)
+ return -1;
+
+ /* If no edges, obviously it doesn't jump or fallthru. */
+ if (cur_bb->succ == NULL_EDGE)
+ return FALSE;
+
+ for (cur_edge = cur_bb->succ;
+ cur_edge != NULL_EDGE;
+ cur_edge = cur_edge->succ_next)
+ {
+ if (cur_edge->flags & EDGE_COMPLEX)
+ /* Anything complex isn't what we want. */
+ 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;
+
+ /* Don't allow calls in the block, since this is used to group && and ||
+ together for conditional execution support. ??? we should support
+ conditional execution support across calls for IA-64 some day, but
+ for now it makes the code simpler. */
+ end = cur_bb->end;
+ insn = cur_bb->head;
+
+ while (insn != NULL_RTX)
+ {
+ if (GET_CODE (insn) == CALL_INSN)
+ return -1;
+
+ if (INSN_P (insn)
+ && GET_CODE (insn) != JUMP_INSN
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ n_insns++;
+
+ if (insn == end)
+ break;
+
+ insn = NEXT_INSN (insn);
+ }
+
+ return n_insns;
}
/* Determine if a given basic block heads a simple IF-THEN or IF-THEN-ELSE
block. If so, we'll try to convert the insns to not require the branch.
- Return TRUE if we were successful at converting the the block. */
+ Return TRUE if we were successful at converting the block. */
static int
-find_if_block (test_bb, then_edge, else_edge)
- basic_block test_bb;
- edge then_edge, else_edge;
+find_if_block (ce_info)
+ struct ce_if_block * ce_info;
{
- basic_block then_bb = then_edge->dest;
- basic_block else_bb = else_edge->dest;
+ 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 then_succ = then_bb->succ;
edge else_succ = else_bb->succ;
- int next_index;
+ int then_predecessors;
+ int else_predecessors;
+ edge cur_edge;
+ basic_block next;
+
+ ce_info->last_test_bb = test_bb;
+
+ /* 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
+ the then block). */
+ if (HAVE_conditional_execution && reload_completed
+ && test_bb->pred != NULL_EDGE
+ && test_bb->pred->pred_next == NULL_EDGE
+ && test_bb->pred->flags == EDGE_FALLTHRU)
+ {
+ basic_block bb = test_bb->pred->src;
+ basic_block target_bb;
+ int max_insns = MAX_CONDITIONAL_EXECUTE;
+ int n_insns;
- /* The THEN block of an IF-THEN combo must have exactly one predecessor. */
- if (then_bb->pred->pred_next != NULL_EDGE)
+ /* Determine if the preceeding block is an && or || block. */
+ 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;
+
+ /* Found at least one && or || block, look for more. */
+ do
+ {
+ ce_info->test_bb = test_bb = bb;
+ total_insns += n_insns;
+ blocks++;
+
+ if (bb->pred == NULL_EDGE || bb->pred->pred_next != NULL_EDGE)
+ break;
+
+ bb = bb->pred->src;
+ 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;
+ }
+ }
+
+ /* Count the number of edges the THEN and ELSE blocks have. */
+ then_predecessors = 0;
+ for (cur_edge = then_bb->pred;
+ cur_edge != NULL_EDGE;
+ cur_edge = cur_edge->pred_next)
+ {
+ then_predecessors++;
+ if (cur_edge->flags & EDGE_COMPLEX)
+ return FALSE;
+ }
+
+ else_predecessors = 0;
+ for (cur_edge = else_bb->pred;
+ cur_edge != NULL_EDGE;
+ cur_edge = cur_edge->pred_next)
+ {
+ else_predecessors++;
+ if (cur_edge->flags & EDGE_COMPLEX)
+ return FALSE;
+ }
+
+ /* The THEN block of an IF-THEN combo must have exactly one predecessor,
+ other than any || blocks which jump to the THEN block. */
+ if ((then_predecessors - ce_info->num_or_or_blocks) != 1)
return FALSE;
/* The THEN block of an IF-THEN combo must have zero or one successors. */
/* If the THEN block has no successors, conditional execution can still
make a conditional call. Don't do this unless the ELSE block has
- only one incoming edge -- the CFG manipulation is too ugly otherwise. */
+ only one incoming edge -- the CFG manipulation is too ugly otherwise.
+ Check for the last insn of the THEN block being an indirect jump, which
+ is listed as not having any successors, but confuses the rest of the CE
+ code processing. ??? we should fix this in the future. */
if (then_succ == NULL)
{
if (else_bb->pred->pred_next == NULL_EDGE)
{
+ rtx last_insn = then_bb->end;
+
+ while (last_insn
+ && GET_CODE (last_insn) == NOTE
+ && last_insn != then_bb->head)
+ last_insn = PREV_INSN (last_insn);
+
+ if (last_insn
+ && GET_CODE (last_insn) == JUMP_INSN
+ && ! simplejump_p (last_insn))
+ return FALSE;
+
join_bb = else_bb;
else_bb = NULL_BLOCK;
}
if (rtl_dump_file)
{
+ fprintf (rtl_dump_file, "\nIF-THEN%s block found, pass %d, start block %d [insn %d], then %d [%d]",
+ (else_bb) ? "-ELSE" : "",
+ ce_info->pass,
+ test_bb->index, (test_bb->head) ? (int)INSN_UID (test_bb->head) : -1,
+ then_bb->index, (then_bb->head) ? (int)INSN_UID (then_bb->head) : -1);
+
if (else_bb)
- fprintf (rtl_dump_file,
- "\nIF-THEN-ELSE block found, start %d, then %d, else %d, join %d\n",
- test_bb->index, then_bb->index, else_bb->index,
- join_bb->index);
- else
- fprintf (rtl_dump_file,
- "\nIF-THEN block found, start %d, then %d, join %d\n",
- test_bb->index, then_bb->index, join_bb->index);
+ fprintf (rtl_dump_file, ", else %d [%d]",
+ else_bb->index, (else_bb->head) ? (int)INSN_UID (else_bb->head) : -1);
+
+ fprintf (rtl_dump_file, ", join %d [%d]",
+ join_bb->index, (join_bb->head) ? (int)INSN_UID (join_bb->head) : -1);
+
+ if (ce_info->num_multiple_test_blocks > 0)
+ fprintf (rtl_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,
+ ((ce_info->last_test_bb->head)
+ ? (int)INSN_UID (ce_info->last_test_bb->head)
+ : -1));
+
+ fputc ('\n', rtl_dump_file);
}
- /* Make sure IF, THEN, and ELSE, blocks are adjacent. Actually, we
- get the first condition for free, since we've already asserted that
- there's a fallthru edge from IF to THEN. */
- /* ??? As an enhancement, move the ELSE block. Have to deal with EH and
+ /* Make sure IF, THEN, and ELSE, blocks are adjacent. Actually, we get the
+ first condition for free, since we've already asserted that there's a
+ fallthru edge from IF to THEN. Likewise for the && and || blocks, since
+ 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
exist. Sticky enough I don't want to think about it now. */
- next_index = then_bb->index;
- if (else_bb && ++next_index != else_bb->index)
+ next = then_bb;
+ if (else_bb && (next = next->next_bb) != else_bb)
return FALSE;
- if (++next_index != join_bb->index)
+ if ((next = next->next_bb) != join_bb && join_bb != EXIT_BLOCK_PTR)
{
if (else_bb)
join_bb = NULL;
}
/* Do the real work. */
- return process_if_block (test_bb, then_bb, else_bb, join_bb);
+ ce_info->else_bb = else_bb;
+ ce_info->join_bb = join_bb;
+
+ return process_if_block (ce_info);
+}
+
+/* Convert a branch over a trap, or a branch
+ to a trap, into a conditional trap. */
+
+static int
+find_cond_trap (test_bb, then_edge, else_edge)
+ basic_block test_bb;
+ edge then_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;
+
+ /* Locate the block with the trap instruction. */
+ /* ??? While we look for no successors, we really ought to allow
+ EH successors. Need to fix merge_if_block for that to work. */
+ 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 (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file, "\nTRAP-IF block found, start %d, trap %d\n",
+ test_bb->index, trap_bb->index);
+ }
+
+ /* If this is not a standard conditional jump, we can't parse it. */
+ jump = test_bb->end;
+ cond = noce_get_condition (jump, &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;
+
+ /* Reverse the comparison code, if necessary. */
+ code = GET_CODE (cond);
+ if (then_bb == trap_bb)
+ {
+ code = reversed_comparison_code (cond, jump);
+ if (code == UNKNOWN)
+ return FALSE;
+ }
+
+ /* Attempt to generate the conditional trap. */
+ seq = gen_cond_trap (code, XEXP (cond, 0), XEXP (cond, 1),
+ TRAP_CODE (PATTERN (trap)));
+ if (seq == NULL)
+ return FALSE;
+
+ /* Emit the new insns before cond_earliest. */
+ emit_insn_before_scope (seq, cond_earliest, INSN_SCOPE (trap));
+
+ /* Delete the trap block if possible. */
+ remove_edge (trap_bb == then_bb ? then_edge : else_edge);
+ if (trap_bb->pred == NULL)
+ {
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, trap_bb);
+ flow_delete_block (trap_bb);
+ num_removed_blocks++;
+ }
+
+ /* 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 ((PTR) &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;
+}
+
+/* Subroutine of find_cond_trap: if BB contains only a trap insn,
+ return it. */
+
+static rtx
+block_has_only_trap (bb)
+ basic_block bb;
+{
+ rtx trap;
+
+ /* We're not the exit block. */
+ if (bb == EXIT_BLOCK_PTR)
+ return NULL_RTX;
+
+ /* The block must have no successors. */
+ if (bb->succ)
+ return NULL_RTX;
+
+ /* The only instruction in the THEN block must be the trap. */
+ trap = first_active_insn (bb);
+ if (! (trap == bb->end
+ && GET_CODE (PATTERN (trap)) == TRAP_IF
+ && TRAP_CONDITION (PATTERN (trap)) == const_true_rtx))
+ return NULL_RTX;
+
+ return trap;
}
/* Look for IF-THEN-ELSE cases in which one of THEN or ELSE is
transformable, but not necessarily the other. There need be no
JOIN block.
- Return TRUE if we were successful at converting the the block.
+ Return TRUE if we were successful at converting the block.
Cases we'd like to look at:
edge then_edge, else_edge;
{
basic_block then_bb = then_edge->dest;
- basic_block else_bb = else_edge->dest;
+ basic_block else_bb = else_edge->dest, new_bb;
edge then_succ = then_bb->succ;
- rtx new_lab;
+ int then_bb_index;
/* THEN has one successor. */
if (!then_succ || then_succ->succ_next != NULL)
if (then_bb->pred->pred_next != NULL)
return FALSE;
- /* ELSE follows THEN. (??? could be moved) */
- if (else_bb->index != then_bb->index + 1)
+ /* THEN must do something. */
+ if (forwarder_block_p (then_bb))
return FALSE;
num_possible_if_blocks++;
if (count_bb_insns (then_bb) > BRANCH_COST)
return FALSE;
- /* Find the label for THEN's destination. */
- if (then_succ->dest == EXIT_BLOCK_PTR)
- new_lab = NULL_RTX;
- else
- {
- new_lab = JUMP_LABEL (then_bb->end);
- if (! new_lab)
- abort ();
- }
-
/* Registers set are dead, or are predicable. */
- if (! dead_or_predicable (test_bb, then_bb, else_bb, new_lab, 1))
+ if (! dead_or_predicable (test_bb, then_bb, else_bb,
+ then_bb->succ->dest, 1))
return FALSE;
/* Conversion went ok, including moving the insns and fixing up the
jump. Adjust the CFG to match. */
- SET_UPDATE_LIFE (test_bb);
bitmap_operation (test_bb->global_live_at_end,
else_bb->global_live_at_start,
then_bb->global_live_at_end, BITMAP_IOR);
- make_edge (NULL, test_bb, then_succ->dest, 0);
+ new_bb = redirect_edge_and_branch_force (FALLTHRU_EDGE (test_bb), else_bb);
+ then_bb_index = then_bb->index;
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, then_bb);
flow_delete_block (then_bb);
- tidy_fallthru_edge (else_edge, test_bb, else_bb);
+
+ /* Make rest of code believe that the newly created block is the THEN_BB
+ block we removed. */
+ if (new_bb)
+ {
+ new_bb->index = then_bb_index;
+ BASIC_BLOCK (then_bb_index) = new_bb;
+ }
+ /* We've possibly created jump to next insn, cleanup_cfg will solve that
+ later. */
num_removed_blocks++;
num_updated_if_blocks++;
basic_block then_bb = then_edge->dest;
basic_block else_bb = else_edge->dest;
edge else_succ = else_bb->succ;
- rtx new_lab, note;
+ rtx note;
/* ELSE has one successor. */
if (!else_succ || else_succ->succ_next != NULL)
if (note && INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2)
;
else if (else_succ->dest->index < 0
- || TEST_BIT (post_dominators[ORIG_INDEX (then_bb)],
- ORIG_INDEX (else_succ->dest)))
+ || dominated_by_p (post_dominators, then_bb,
+ else_succ->dest))
;
else
return FALSE;
test_bb->index, else_bb->index);
/* ELSE is small. */
- if (count_bb_insns (then_bb) > BRANCH_COST)
+ if (count_bb_insns (else_bb) > BRANCH_COST)
return FALSE;
- /* Find the label for ELSE's destination. */
- if (else_succ->dest == EXIT_BLOCK_PTR)
- new_lab = NULL_RTX;
- else
- {
- if (else_succ->flags & EDGE_FALLTHRU)
- {
- new_lab = else_succ->dest->head;
- if (GET_CODE (new_lab) != CODE_LABEL)
- abort ();
- }
- else
- {
- new_lab = JUMP_LABEL (else_bb->end);
- if (! new_lab)
- abort ();
- }
- }
-
/* Registers set are dead, or are predicable. */
- if (! dead_or_predicable (test_bb, else_bb, then_bb, new_lab, 0))
+ if (! dead_or_predicable (test_bb, else_bb, then_bb, else_succ->dest, 0))
return FALSE;
/* Conversion went ok, including moving the insns and fixing up the
jump. Adjust the CFG to match. */
- SET_UPDATE_LIFE (test_bb);
bitmap_operation (test_bb->global_live_at_end,
then_bb->global_live_at_start,
else_bb->global_live_at_end, BITMAP_IOR);
- remove_edge (else_edge);
- make_edge (NULL, test_bb, else_succ->dest, 0);
+ if (post_dominators)
+ delete_from_dominance_info (post_dominators, else_bb);
flow_delete_block (else_bb);
num_removed_blocks++;
static int
dead_or_predicable (test_bb, merge_bb, other_bb, new_dest, reversep)
basic_block test_bb, merge_bb, other_bb;
- rtx new_dest;
+ basic_block new_dest;
int reversep;
{
- rtx head, end, jump, earliest, old_dest;
+ rtx head, end, jump, earliest, old_dest, new_label = NULL_RTX;
jump = test_bb->end;
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)
if (reversep)
{
- cond = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond)),
- GET_MODE (cond), XEXP (cond, 0),
+ 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 (head, end, cond, prob_val, 0))
+ if (! cond_exec_process_insns ((ce_if_block_t *)0, head, end, cond,
+ prob_val, 0))
goto cancel;
earliest = jump;
/* ??? Even non-trapping memories such as stack frame
references must be avoided. For stores, we collect
no lifetime info; for reads, we'd have to assert
- true_dependance false against every store in the
+ true_dependence false against every store in the
TEST range. */
if (for_each_rtx (&PATTERN (insn), find_memory, NULL))
return FALSE;
/* ??? 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. */
- propagate_block (merge_bb, tmp, merge_set, 0);
+ propagate_block (merge_bb, tmp, merge_set, merge_set, 0);
/* For small register class machines, don't lengthen lifetimes of
hard registers before reload. */
Moreover, we're interested in the insns live from OTHER_BB. */
COPY_REG_SET (test_live, other_bb->global_live_at_start);
- pbi = init_propagate_block_info (test_bb, test_live, test_set, 0);
+ pbi = init_propagate_block_info (test_bb, test_live, test_set, test_set,
+ 0);
for (insn = jump; ; insn = prev)
{
change group management. */
old_dest = JUMP_LABEL (jump);
- if (reversep
- ? ! invert_jump_1 (jump, new_dest)
- : ! redirect_jump_1 (jump, new_dest))
- goto cancel;
+ 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 (old_dest)
- LABEL_NUSES (old_dest) -= 1;
- if (new_dest)
- LABEL_NUSES (new_dest) += 1;
- JUMP_LABEL (jump) = new_dest;
-
- if (reversep)
+ if (other_bb != new_dest)
{
- rtx note = find_reg_note (jump, REG_BR_PROB, NULL_RTX);
- if (note)
- XEXP (note, 0) = GEN_INT (REG_BR_PROB_BASE - INTVAL (XEXP (note, 0)));
+ 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_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);
+ }
}
/* Move the insns out of MERGE_BB to before the branch. */
if (end == merge_bb->end)
merge_bb->end = PREV_INSN (head);
- head = squeeze_notes (head, end);
- if (GET_CODE (end) == NOTE
- && (NOTE_LINE_NUMBER (end) == NOTE_INSN_BLOCK_END
- || NOTE_LINE_NUMBER (end) == NOTE_INSN_BLOCK_BEG
- || NOTE_LINE_NUMBER (end) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (end) == NOTE_INSN_LOOP_END
- || NOTE_LINE_NUMBER (end) == NOTE_INSN_LOOP_CONT
- || NOTE_LINE_NUMBER (end) == NOTE_INSN_LOOP_VTOP))
- {
- if (head == end)
- return TRUE;
- end = PREV_INSN (end);
- }
+ if (squeeze_notes (&head, &end))
+ return TRUE;
reorder_insns (head, end, PREV_INSN (earliest));
}
+
+ /* Remove the jump and edge if we can. */
+ if (other_bb == new_dest)
+ {
+ delete_insn (jump);
+ remove_edge (BRANCH_EDGE (test_bb));
+ /* ??? Can't merge blocks here, as then_bb is still in use.
+ At minimum, the merge will get done just before bb-reorder. */
+ }
+
return TRUE;
cancel:
/* Main entry point for all if-conversion. */
void
-if_convert (life_data_ok)
- int life_data_ok;
+if_convert (x_life_data_ok)
+ int x_life_data_ok;
{
- int block_num;
+ basic_block bb;
+ int pass;
num_possible_if_blocks = 0;
num_updated_if_blocks = 0;
num_removed_blocks = 0;
+ life_data_ok = (x_life_data_ok != 0);
- /* Free up basic_block_for_insn so that we don't have to keep it
+ /* Free up basic_block_for_insn so that we don't have to keep it
up to date, either here or in merge_blocks_nomove. */
free_basic_block_vars (1);
post_dominators = NULL;
if (HAVE_conditional_execution || life_data_ok)
{
- post_dominators = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
- compute_flow_dominators (NULL, post_dominators);
+ post_dominators = calculate_dominance_info (CDI_POST_DOMINATORS);
}
+ if (life_data_ok)
+ clear_bb_flags ();
- /* Record initial block numbers. */
- for (block_num = 0; block_num < n_basic_blocks; block_num++)
- SET_ORIG_INDEX (BASIC_BLOCK (block_num), block_num);
-
- /* Go through each of the basic blocks looking for things to convert. */
- for (block_num = 0; block_num < n_basic_blocks; )
+ /* Go through each of the basic blocks looking for things to convert. If we
+ have conditional execution, we make multiple passes to allow us to handle
+ IF-THEN{-ELSE} blocks within other IF-THEN{-ELSE} blocks. */
+ pass = 0;
+ do
{
- basic_block bb = BASIC_BLOCK (block_num);
- if (find_if_header (bb))
- block_num = bb->index;
- else
- block_num++;
+ cond_exec_changed_p = FALSE;
+ pass++;
+
+#ifdef IFCVT_MULTIPLE_DUMPS
+ if (rtl_dump_file && pass > 1)
+ fprintf (rtl_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 (rtl_dump_file && cond_exec_changed_p)
+ print_rtl_with_bb (rtl_dump_file, get_insns ());
+#endif
}
+ while (cond_exec_changed_p);
+
+#ifdef IFCVT_MULTIPLE_DUMPS
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "\n\n========== no more changes\n");
+#endif
if (post_dominators)
- sbitmap_vector_free (post_dominators);
+ free_dominance_info (post_dominators);
if (rtl_dump_file)
fflush (rtl_dump_file);
- /* Rebuild basic_block_for_insn for update_life_info and for gcse. */
- compute_bb_for_insn (get_max_uid ());
+ clear_aux_for_blocks ();
/* Rebuild life info for basic blocks that require it. */
if (num_removed_blocks && life_data_ok)
{
- sbitmap update_life_blocks = sbitmap_alloc (n_basic_blocks);
- sbitmap_zero (update_life_blocks);
-
/* If we allocated new pseudos, we must resize the array for sched1. */
if (max_regno < max_reg_num ())
{
max_regno = max_reg_num ();
allocate_reg_info (max_regno, FALSE, FALSE);
}
-
- for (block_num = 0; block_num < n_basic_blocks; block_num++)
- if (UPDATE_LIFE (BASIC_BLOCK (block_num)))
- SET_BIT (update_life_blocks, block_num);
-
- count_or_remove_death_notes (update_life_blocks, 1);
- /* ??? See about adding a mode that verifies that the initial
- set of blocks don't let registers come live. */
- update_life_info (update_life_blocks, UPDATE_LIFE_GLOBAL,
- PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE
- | PROP_KILL_DEAD_CODE);
-
- sbitmap_free (update_life_blocks);
+ update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_DEATH_NOTES | PROP_SCAN_DEAD_CODE
+ | PROP_KILL_DEAD_CODE);
}
/* Write the final stats. */