mirror of git://gcc.gnu.org/git/gcc.git
[RTL-ifcvt] Make non-conditional execution if-conversion more aggressive
* ifcvt.c (struct noce_if_info): Add then_simple, else_simple, then_cost, else_cost fields. Change branch_cost field to unsigned int. (end_ifcvt_sequence): Call set_used_flags on each insn in the sequence. Include rtl-iter.h. (noce_simple_bbs): New function. (noce_try_move): Bail if basic blocks are not simple. (noce_try_store_flag): Likewise. (noce_try_store_flag_constants): Likewise. (noce_try_addcc): Likewise. (noce_try_store_flag_mask): Likewise. (noce_try_cmove): Likewise. (noce_try_minmax): Likewise. (noce_try_abs): Likewise. (noce_try_sign_mask): Likewise. (noce_try_bitop): Likewise. (bbs_ok_for_cmove_arith): New function. (noce_emit_all_but_last): Likewise. (noce_emit_insn): Likewise. (noce_emit_bb): Likewise. (noce_try_cmove_arith): Handle non-simple basic blocks. (insn_valid_noce_process_p): New function. (contains_mem_rtx_p): Likewise. (bb_valid_for_noce_process_p): Likewise. (noce_process_if_block): Allow non-simple basic blocks where appropriate. * gcc.dg/ifcvt-1.c: New test. * gcc.dg/ifcvt-2.c: Likewise. * gcc.dg/ifcvt-3.c: Likewise. From-SVN: r227368
This commit is contained in:
parent
2674213872
commit
e65bf4e814
|
|
@ -1,3 +1,33 @@
|
||||||
|
2015-09-01 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
|
||||||
|
|
||||||
|
* ifcvt.c (struct noce_if_info): Add then_simple, else_simple,
|
||||||
|
then_cost, else_cost fields. Change branch_cost field to unsigned
|
||||||
|
int.
|
||||||
|
(end_ifcvt_sequence): Call set_used_flags on each insn in the
|
||||||
|
sequence.
|
||||||
|
Include rtl-iter.h.
|
||||||
|
(noce_simple_bbs): New function.
|
||||||
|
(noce_try_move): Bail if basic blocks are not simple.
|
||||||
|
(noce_try_store_flag): Likewise.
|
||||||
|
(noce_try_store_flag_constants): Likewise.
|
||||||
|
(noce_try_addcc): Likewise.
|
||||||
|
(noce_try_store_flag_mask): Likewise.
|
||||||
|
(noce_try_cmove): Likewise.
|
||||||
|
(noce_try_minmax): Likewise.
|
||||||
|
(noce_try_abs): Likewise.
|
||||||
|
(noce_try_sign_mask): Likewise.
|
||||||
|
(noce_try_bitop): Likewise.
|
||||||
|
(bbs_ok_for_cmove_arith): New function.
|
||||||
|
(noce_emit_all_but_last): Likewise.
|
||||||
|
(noce_emit_insn): Likewise.
|
||||||
|
(noce_emit_bb): Likewise.
|
||||||
|
(noce_try_cmove_arith): Handle non-simple basic blocks.
|
||||||
|
(insn_valid_noce_process_p): New function.
|
||||||
|
(contains_mem_rtx_p): Likewise.
|
||||||
|
(bb_valid_for_noce_process_p): Likewise.
|
||||||
|
(noce_process_if_block): Allow non-simple basic blocks
|
||||||
|
where appropriate.
|
||||||
|
|
||||||
2015-08-31 Alan Lawrence <alan.lawrence@arm.com>
|
2015-08-31 Alan Lawrence <alan.lawrence@arm.com>
|
||||||
|
|
||||||
* tree-ssa-dom.c (record_equivalences_from_phis,
|
* tree-ssa-dom.c (record_equivalences_from_phis,
|
||||||
|
|
|
||||||
468
gcc/ifcvt.c
468
gcc/ifcvt.c
|
|
@ -53,6 +53,7 @@
|
||||||
#include "tree-pass.h"
|
#include "tree-pass.h"
|
||||||
#include "dbgcnt.h"
|
#include "dbgcnt.h"
|
||||||
#include "shrink-wrap.h"
|
#include "shrink-wrap.h"
|
||||||
|
#include "rtl-iter.h"
|
||||||
#include "ifcvt.h"
|
#include "ifcvt.h"
|
||||||
|
|
||||||
#ifndef MAX_CONDITIONAL_EXECUTE
|
#ifndef MAX_CONDITIONAL_EXECUTE
|
||||||
|
|
@ -808,8 +809,17 @@ struct noce_if_info
|
||||||
form as well. */
|
form as well. */
|
||||||
bool then_else_reversed;
|
bool then_else_reversed;
|
||||||
|
|
||||||
|
/* True if the contents of then_bb and else_bb are a
|
||||||
|
simple single set instruction. */
|
||||||
|
bool then_simple;
|
||||||
|
bool else_simple;
|
||||||
|
|
||||||
|
/* The total rtx cost of the instructions in then_bb and else_bb. */
|
||||||
|
unsigned int then_cost;
|
||||||
|
unsigned int else_cost;
|
||||||
|
|
||||||
/* Estimated cost of the particular branch instruction. */
|
/* Estimated cost of the particular branch instruction. */
|
||||||
int branch_cost;
|
unsigned int branch_cost;
|
||||||
};
|
};
|
||||||
|
|
||||||
static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
|
static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
|
||||||
|
|
@ -1029,6 +1039,10 @@ end_ifcvt_sequence (struct noce_if_info *if_info)
|
||||||
set_used_flags (if_info->cond);
|
set_used_flags (if_info->cond);
|
||||||
set_used_flags (if_info->a);
|
set_used_flags (if_info->a);
|
||||||
set_used_flags (if_info->b);
|
set_used_flags (if_info->b);
|
||||||
|
|
||||||
|
for (insn = seq; insn; insn = NEXT_INSN (insn))
|
||||||
|
set_used_flags (insn);
|
||||||
|
|
||||||
unshare_all_rtl_in_chain (seq);
|
unshare_all_rtl_in_chain (seq);
|
||||||
end_sequence ();
|
end_sequence ();
|
||||||
|
|
||||||
|
|
@ -1046,6 +1060,21 @@ end_ifcvt_sequence (struct noce_if_info *if_info)
|
||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true iff the then and else basic block (if it exists)
|
||||||
|
consist of a single simple set instruction. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
noce_simple_bbs (struct noce_if_info *if_info)
|
||||||
|
{
|
||||||
|
if (!if_info->then_simple)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (if_info->else_bb)
|
||||||
|
return if_info->else_simple;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert "if (a != b) x = a; else x = b" into "x = a" and
|
/* Convert "if (a != b) x = a; else x = b" into "x = a" and
|
||||||
"if (a == b) x = a; else x = b" into "x = b". */
|
"if (a == b) x = a; else x = b" into "x = b". */
|
||||||
|
|
||||||
|
|
@ -1060,6 +1089,9 @@ noce_try_move (struct noce_if_info *if_info)
|
||||||
if (code != NE && code != EQ)
|
if (code != NE && code != EQ)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* This optimization isn't valid if either A or B could be a NaN
|
/* This optimization isn't valid if either A or B could be a NaN
|
||||||
or a signed zero. */
|
or a signed zero. */
|
||||||
if (HONOR_NANS (if_info->x)
|
if (HONOR_NANS (if_info->x)
|
||||||
|
|
@ -1108,6 +1140,9 @@ noce_try_store_flag (struct noce_if_info *if_info)
|
||||||
rtx target;
|
rtx target;
|
||||||
rtx_insn *seq;
|
rtx_insn *seq;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (CONST_INT_P (if_info->b)
|
if (CONST_INT_P (if_info->b)
|
||||||
&& INTVAL (if_info->b) == STORE_FLAG_VALUE
|
&& INTVAL (if_info->b) == STORE_FLAG_VALUE
|
||||||
&& if_info->a == const0_rtx)
|
&& if_info->a == const0_rtx)
|
||||||
|
|
@ -1177,6 +1212,9 @@ noce_try_store_flag_constants (struct noce_if_info *if_info)
|
||||||
b = XEXP (b, 1);
|
b = XEXP (b, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (CONST_INT_P (a)
|
if (CONST_INT_P (a)
|
||||||
&& CONST_INT_P (b))
|
&& CONST_INT_P (b))
|
||||||
{
|
{
|
||||||
|
|
@ -1365,6 +1403,9 @@ noce_try_addcc (struct noce_if_info *if_info)
|
||||||
rtx_insn *seq;
|
rtx_insn *seq;
|
||||||
int subtract, normalize;
|
int subtract, normalize;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if (GET_CODE (if_info->a) == PLUS
|
if (GET_CODE (if_info->a) == PLUS
|
||||||
&& rtx_equal_p (XEXP (if_info->a, 0), if_info->b)
|
&& rtx_equal_p (XEXP (if_info->a, 0), if_info->b)
|
||||||
&& (reversed_comparison_code (if_info->cond, if_info->jump)
|
&& (reversed_comparison_code (if_info->cond, if_info->jump)
|
||||||
|
|
@ -1456,6 +1497,9 @@ noce_try_store_flag_mask (struct noce_if_info *if_info)
|
||||||
rtx_insn *seq;
|
rtx_insn *seq;
|
||||||
int reversep;
|
int reversep;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
reversep = 0;
|
reversep = 0;
|
||||||
if ((if_info->branch_cost >= 2
|
if ((if_info->branch_cost >= 2
|
||||||
|| STORE_FLAG_VALUE == -1)
|
|| STORE_FLAG_VALUE == -1)
|
||||||
|
|
@ -1624,6 +1668,9 @@ noce_try_cmove (struct noce_if_info *if_info)
|
||||||
rtx target;
|
rtx target;
|
||||||
rtx_insn *seq;
|
rtx_insn *seq;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
if ((CONSTANT_P (if_info->a) || register_operand (if_info->a, VOIDmode))
|
if ((CONSTANT_P (if_info->a) || register_operand (if_info->a, VOIDmode))
|
||||||
&& (CONSTANT_P (if_info->b) || register_operand (if_info->b, VOIDmode)))
|
&& (CONSTANT_P (if_info->b) || register_operand (if_info->b, VOIDmode)))
|
||||||
{
|
{
|
||||||
|
|
@ -1714,6 +1761,152 @@ noce_try_cmove (struct noce_if_info *if_info)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper for bb_valid_for_noce_process_p. Validate that
|
||||||
|
the rtx insn INSN is a single set that does not set
|
||||||
|
the conditional register CC and is in general valid for
|
||||||
|
if-conversion. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
insn_valid_noce_process_p (rtx_insn *insn, rtx cc)
|
||||||
|
{
|
||||||
|
if (!insn
|
||||||
|
|| !NONJUMP_INSN_P (insn)
|
||||||
|
|| (cc && set_of (cc, insn)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rtx sset = single_set (insn);
|
||||||
|
|
||||||
|
/* Currently support only simple single sets in test_bb. */
|
||||||
|
if (!sset
|
||||||
|
|| !noce_operand_ok (SET_DEST (sset))
|
||||||
|
|| !noce_operand_ok (SET_SRC (sset)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return true iff the registers that the insns in BB_A set do not
|
||||||
|
get used in BB_B. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
bbs_ok_for_cmove_arith (basic_block bb_a, basic_block bb_b)
|
||||||
|
{
|
||||||
|
rtx_insn *a_insn;
|
||||||
|
bitmap bba_sets = BITMAP_ALLOC (®_obstack);
|
||||||
|
|
||||||
|
df_ref def;
|
||||||
|
df_ref use;
|
||||||
|
|
||||||
|
FOR_BB_INSNS (bb_a, a_insn)
|
||||||
|
{
|
||||||
|
if (!active_insn_p (a_insn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rtx sset_a = single_set (a_insn);
|
||||||
|
|
||||||
|
if (!sset_a)
|
||||||
|
{
|
||||||
|
BITMAP_FREE (bba_sets);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record all registers that BB_A sets. */
|
||||||
|
FOR_EACH_INSN_DEF (def, a_insn)
|
||||||
|
bitmap_set_bit (bba_sets, DF_REF_REGNO (def));
|
||||||
|
}
|
||||||
|
|
||||||
|
rtx_insn *b_insn;
|
||||||
|
|
||||||
|
FOR_BB_INSNS (bb_b, b_insn)
|
||||||
|
{
|
||||||
|
if (!active_insn_p (b_insn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rtx sset_b = single_set (b_insn);
|
||||||
|
|
||||||
|
if (!sset_b)
|
||||||
|
{
|
||||||
|
BITMAP_FREE (bba_sets);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure this is a REG and not some instance
|
||||||
|
of ZERO_EXTRACT or SUBREG or other dangerous stuff. */
|
||||||
|
if (!REG_P (SET_DEST (sset_b)))
|
||||||
|
{
|
||||||
|
BITMAP_FREE (bba_sets);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the insn uses a reg set in BB_A return false. */
|
||||||
|
FOR_EACH_INSN_USE (use, b_insn)
|
||||||
|
{
|
||||||
|
if (bitmap_bit_p (bba_sets, DF_REF_REGNO (use)))
|
||||||
|
{
|
||||||
|
BITMAP_FREE (bba_sets);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BITMAP_FREE (bba_sets);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit copies of all the active instructions in BB except the last.
|
||||||
|
This is a helper for noce_try_cmove_arith. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
noce_emit_all_but_last (basic_block bb)
|
||||||
|
{
|
||||||
|
rtx_insn *last = last_active_insn (bb, FALSE);
|
||||||
|
rtx_insn *insn;
|
||||||
|
FOR_BB_INSNS (bb, insn)
|
||||||
|
{
|
||||||
|
if (insn != last && active_insn_p (insn))
|
||||||
|
{
|
||||||
|
rtx_insn *to_emit = as_a <rtx_insn *> (copy_rtx (insn));
|
||||||
|
|
||||||
|
emit_insn (PATTERN (to_emit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper for noce_try_cmove_arith. Emit the pattern TO_EMIT and return
|
||||||
|
the resulting insn or NULL if it's not a valid insn. */
|
||||||
|
|
||||||
|
static rtx_insn *
|
||||||
|
noce_emit_insn (rtx to_emit)
|
||||||
|
{
|
||||||
|
gcc_assert (to_emit);
|
||||||
|
rtx_insn *insn = emit_insn (to_emit);
|
||||||
|
|
||||||
|
if (recog_memoized (insn) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper for noce_try_cmove_arith. Emit a copy of the insns up to
|
||||||
|
and including the penultimate one in BB if it is not simple
|
||||||
|
(as indicated by SIMPLE). Then emit LAST_INSN as the last
|
||||||
|
insn in the block. The reason for that is that LAST_INSN may
|
||||||
|
have been modified by the preparation in noce_try_cmove_arith. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
noce_emit_bb (rtx last_insn, basic_block bb, bool simple)
|
||||||
|
{
|
||||||
|
if (bb && !simple)
|
||||||
|
noce_emit_all_but_last (bb);
|
||||||
|
|
||||||
|
if (last_insn && !noce_emit_insn (last_insn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Try more complex cases involving conditional_move. */
|
/* Try more complex cases involving conditional_move. */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -1724,9 +1917,12 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
||||||
rtx x = if_info->x;
|
rtx x = if_info->x;
|
||||||
rtx orig_a, orig_b;
|
rtx orig_a, orig_b;
|
||||||
rtx_insn *insn_a, *insn_b;
|
rtx_insn *insn_a, *insn_b;
|
||||||
|
bool a_simple = if_info->then_simple;
|
||||||
|
bool b_simple = if_info->else_simple;
|
||||||
|
basic_block then_bb = if_info->then_bb;
|
||||||
|
basic_block else_bb = if_info->else_bb;
|
||||||
rtx target;
|
rtx target;
|
||||||
int is_mem = 0;
|
int is_mem = 0;
|
||||||
int insn_cost;
|
|
||||||
enum rtx_code code;
|
enum rtx_code code;
|
||||||
rtx_insn *ifcvt_seq;
|
rtx_insn *ifcvt_seq;
|
||||||
|
|
||||||
|
|
@ -1765,27 +1961,22 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
||||||
insn_a = if_info->insn_a;
|
insn_a = if_info->insn_a;
|
||||||
insn_b = if_info->insn_b;
|
insn_b = if_info->insn_b;
|
||||||
|
|
||||||
/* Total insn_rtx_cost should be smaller than branch cost. Exit
|
unsigned int then_cost;
|
||||||
if insn_rtx_cost can't be estimated. */
|
unsigned int else_cost;
|
||||||
if (insn_a)
|
if (insn_a)
|
||||||
{
|
then_cost = if_info->then_cost;
|
||||||
insn_cost
|
|
||||||
= insn_rtx_cost (PATTERN (insn_a),
|
|
||||||
optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_a)));
|
|
||||||
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (if_info->branch_cost))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
insn_cost = 0;
|
then_cost = 0;
|
||||||
|
|
||||||
if (insn_b)
|
if (insn_b)
|
||||||
{
|
else_cost = if_info->else_cost;
|
||||||
insn_cost
|
else
|
||||||
+= insn_rtx_cost (PATTERN (insn_b),
|
else_cost = 0;
|
||||||
optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn_b)));
|
|
||||||
if (insn_cost == 0 || insn_cost > COSTS_N_INSNS (if_info->branch_cost))
|
/* We're going to execute one of the basic blocks anyway, so
|
||||||
return FALSE;
|
bail out if the most expensive of the two blocks is unacceptable. */
|
||||||
}
|
if (MAX (then_cost, else_cost) > COSTS_N_INSNS (if_info->branch_cost))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* Possibly rearrange operands to make things come out more natural. */
|
/* Possibly rearrange operands to make things come out more natural. */
|
||||||
if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN)
|
if (reversed_comparison_code (if_info->cond, if_info->jump) != UNKNOWN)
|
||||||
|
|
@ -1801,26 +1992,36 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
||||||
code = reversed_comparison_code (if_info->cond, if_info->jump);
|
code = reversed_comparison_code (if_info->cond, if_info->jump);
|
||||||
std::swap (a, b);
|
std::swap (a, b);
|
||||||
std::swap (insn_a, insn_b);
|
std::swap (insn_a, insn_b);
|
||||||
|
std::swap (a_simple, b_simple);
|
||||||
|
std::swap (then_bb, else_bb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!a_simple && then_bb && !b_simple && else_bb
|
||||||
|
&& (!bbs_ok_for_cmove_arith (then_bb, else_bb)
|
||||||
|
|| !bbs_ok_for_cmove_arith (else_bb, then_bb)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
start_sequence ();
|
start_sequence ();
|
||||||
|
|
||||||
orig_a = a;
|
orig_a = a;
|
||||||
orig_b = b;
|
orig_b = b;
|
||||||
|
|
||||||
|
rtx emit_a = NULL_RTX;
|
||||||
|
rtx emit_b = NULL_RTX;
|
||||||
|
|
||||||
/* If either operand is complex, load it into a register first.
|
/* If either operand is complex, load it into a register first.
|
||||||
The best way to do this is to copy the original insn. In this
|
The best way to do this is to copy the original insn. In this
|
||||||
way we preserve any clobbers etc that the insn may have had.
|
way we preserve any clobbers etc that the insn may have had.
|
||||||
This is of course not possible in the IS_MEM case. */
|
This is of course not possible in the IS_MEM case. */
|
||||||
|
|
||||||
if (! general_operand (a, GET_MODE (a)))
|
if (! general_operand (a, GET_MODE (a)))
|
||||||
{
|
{
|
||||||
rtx_insn *insn;
|
|
||||||
|
|
||||||
if (is_mem)
|
if (is_mem)
|
||||||
{
|
{
|
||||||
rtx reg = gen_reg_rtx (GET_MODE (a));
|
rtx reg = gen_reg_rtx (GET_MODE (a));
|
||||||
insn = emit_insn (gen_rtx_SET (reg, a));
|
emit_a = gen_rtx_SET (reg, a);
|
||||||
}
|
}
|
||||||
else if (! insn_a)
|
else if (! insn_a)
|
||||||
goto end_seq_and_fail;
|
goto end_seq_and_fail;
|
||||||
|
|
@ -1830,50 +2031,59 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
||||||
rtx_insn *copy_of_a = as_a <rtx_insn *> (copy_rtx (insn_a));
|
rtx_insn *copy_of_a = as_a <rtx_insn *> (copy_rtx (insn_a));
|
||||||
rtx set = single_set (copy_of_a);
|
rtx set = single_set (copy_of_a);
|
||||||
SET_DEST (set) = a;
|
SET_DEST (set) = a;
|
||||||
insn = emit_insn (PATTERN (copy_of_a));
|
|
||||||
|
emit_a = PATTERN (copy_of_a);
|
||||||
}
|
}
|
||||||
if (recog_memoized (insn) < 0)
|
|
||||||
goto end_seq_and_fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! general_operand (b, GET_MODE (b)))
|
if (! general_operand (b, GET_MODE (b)))
|
||||||
{
|
{
|
||||||
rtx pat;
|
|
||||||
rtx_insn *last;
|
|
||||||
rtx_insn *new_insn;
|
|
||||||
|
|
||||||
if (is_mem)
|
if (is_mem)
|
||||||
{
|
{
|
||||||
rtx reg = gen_reg_rtx (GET_MODE (b));
|
rtx reg = gen_reg_rtx (GET_MODE (b));
|
||||||
pat = gen_rtx_SET (reg, b);
|
emit_b = gen_rtx_SET (reg, b);
|
||||||
}
|
}
|
||||||
else if (! insn_b)
|
else if (! insn_b)
|
||||||
goto end_seq_and_fail;
|
goto end_seq_and_fail;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
b = gen_reg_rtx (GET_MODE (b));
|
b = gen_reg_rtx (GET_MODE (b));
|
||||||
rtx_insn *copy_of_insn_b = as_a <rtx_insn *> (copy_rtx (insn_b));
|
rtx_insn *copy_of_b = as_a <rtx_insn *> (copy_rtx (insn_b));
|
||||||
rtx set = single_set (copy_of_insn_b);
|
rtx set = single_set (copy_of_b);
|
||||||
|
|
||||||
SET_DEST (set) = b;
|
SET_DEST (set) = b;
|
||||||
pat = PATTERN (copy_of_insn_b);
|
emit_b = PATTERN (copy_of_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If insn to set up A clobbers any registers B depends on, try to
|
|
||||||
swap insn that sets up A with the one that sets up B. If even
|
|
||||||
that doesn't help, punt. */
|
|
||||||
last = get_last_insn ();
|
|
||||||
if (last && modified_in_p (orig_b, last))
|
|
||||||
{
|
|
||||||
new_insn = emit_insn_before (pat, get_insns ());
|
|
||||||
if (modified_in_p (orig_a, new_insn))
|
|
||||||
goto end_seq_and_fail;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new_insn = emit_insn (pat);
|
|
||||||
|
|
||||||
if (recog_memoized (new_insn) < 0)
|
|
||||||
goto end_seq_and_fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If insn to set up A clobbers any registers B depends on, try to
|
||||||
|
swap insn that sets up A with the one that sets up B. If even
|
||||||
|
that doesn't help, punt. */
|
||||||
|
|
||||||
|
if (emit_a && modified_in_p (orig_b, emit_a))
|
||||||
|
{
|
||||||
|
if (modified_in_p (orig_a, emit_b))
|
||||||
|
goto end_seq_and_fail;
|
||||||
|
|
||||||
|
if (else_bb && !b_simple)
|
||||||
|
{
|
||||||
|
if (!noce_emit_bb (emit_b, else_bb, b_simple))
|
||||||
|
goto end_seq_and_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noce_emit_bb (emit_a, then_bb, a_simple))
|
||||||
|
goto end_seq_and_fail;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!noce_emit_bb (emit_a, then_bb, a_simple))
|
||||||
|
goto end_seq_and_fail;
|
||||||
|
|
||||||
|
if (!noce_emit_bb (emit_b, else_bb, b_simple))
|
||||||
|
goto end_seq_and_fail;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0),
|
target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0),
|
||||||
XEXP (if_info->cond, 1), a, b);
|
XEXP (if_info->cond, 1), a, b);
|
||||||
|
|
||||||
|
|
@ -2076,6 +2286,9 @@ noce_try_minmax (struct noce_if_info *if_info)
|
||||||
enum rtx_code code, op;
|
enum rtx_code code, op;
|
||||||
int unsignedp;
|
int unsignedp;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* ??? Reject modes with NaNs or signed zeros since we don't know how
|
/* ??? 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
|
they will be resolved with an SMIN/SMAX. It wouldn't be too hard
|
||||||
to get the target to tell us... */
|
to get the target to tell us... */
|
||||||
|
|
@ -2172,6 +2385,9 @@ noce_try_abs (struct noce_if_info *if_info)
|
||||||
int negate;
|
int negate;
|
||||||
bool one_cmpl = false;
|
bool one_cmpl = false;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* Reject modes with signed zeros. */
|
/* Reject modes with signed zeros. */
|
||||||
if (HONOR_SIGNED_ZEROS (if_info->x))
|
if (HONOR_SIGNED_ZEROS (if_info->x))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -2320,6 +2536,9 @@ noce_try_sign_mask (struct noce_if_info *if_info)
|
||||||
enum rtx_code code;
|
enum rtx_code code;
|
||||||
bool t_unconditional;
|
bool t_unconditional;
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
cond = if_info->cond;
|
cond = if_info->cond;
|
||||||
code = GET_CODE (cond);
|
code = GET_CODE (cond);
|
||||||
m = XEXP (cond, 0);
|
m = XEXP (cond, 0);
|
||||||
|
|
@ -2403,6 +2622,9 @@ noce_try_bitop (struct noce_if_info *if_info)
|
||||||
cond = if_info->cond;
|
cond = if_info->cond;
|
||||||
code = GET_CODE (cond);
|
code = GET_CODE (cond);
|
||||||
|
|
||||||
|
if (!noce_simple_bbs (if_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* Check for no else condition. */
|
/* Check for no else condition. */
|
||||||
if (! rtx_equal_p (x, if_info->b))
|
if (! rtx_equal_p (x, if_info->b))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -2653,6 +2875,113 @@ noce_can_store_speculate_p (basic_block top_bb, const_rtx mem)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if X contains a MEM subrtx. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
contains_mem_rtx_p (rtx x)
|
||||||
|
{
|
||||||
|
subrtx_iterator::array_type array;
|
||||||
|
FOR_EACH_SUBRTX (iter, array, x, ALL)
|
||||||
|
if (MEM_P (*iter))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true iff basic block TEST_BB is valid for noce if-conversion.
|
||||||
|
The condition used in this if-conversion is in COND.
|
||||||
|
In practice, check that TEST_BB ends with a single set
|
||||||
|
x := a and all previous computations
|
||||||
|
in TEST_BB don't produce any values that are live after TEST_BB.
|
||||||
|
In other words, all the insns in TEST_BB are there only
|
||||||
|
to compute a value for x. Put the rtx cost of the insns
|
||||||
|
in TEST_BB into COST. Record whether TEST_BB is a single simple
|
||||||
|
set instruction in SIMPLE_P. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
bb_valid_for_noce_process_p (basic_block test_bb, rtx cond,
|
||||||
|
unsigned int *cost, bool *simple_p)
|
||||||
|
{
|
||||||
|
if (!test_bb)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rtx_insn *last_insn = last_active_insn (test_bb, FALSE);
|
||||||
|
rtx last_set = NULL_RTX;
|
||||||
|
|
||||||
|
rtx cc = cc_in_cond (cond);
|
||||||
|
|
||||||
|
if (!insn_valid_noce_process_p (last_insn, cc))
|
||||||
|
return false;
|
||||||
|
last_set = single_set (last_insn);
|
||||||
|
|
||||||
|
rtx x = SET_DEST (last_set);
|
||||||
|
rtx_insn *first_insn = first_active_insn (test_bb);
|
||||||
|
rtx first_set = single_set (first_insn);
|
||||||
|
|
||||||
|
if (!first_set)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* We have a single simple set, that's okay. */
|
||||||
|
bool speed_p = optimize_bb_for_speed_p (test_bb);
|
||||||
|
|
||||||
|
if (first_insn == last_insn)
|
||||||
|
{
|
||||||
|
*simple_p = noce_operand_ok (SET_DEST (first_set));
|
||||||
|
*cost = insn_rtx_cost (first_set, speed_p);
|
||||||
|
return *simple_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtx_insn *prev_last_insn = PREV_INSN (last_insn);
|
||||||
|
gcc_assert (prev_last_insn);
|
||||||
|
|
||||||
|
/* For now, disallow setting x multiple times in test_bb. */
|
||||||
|
if (REG_P (x) && reg_set_between_p (x, first_insn, prev_last_insn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bitmap test_bb_temps = BITMAP_ALLOC (®_obstack);
|
||||||
|
|
||||||
|
/* The regs that are live out of test_bb. */
|
||||||
|
bitmap test_bb_live_out = df_get_live_out (test_bb);
|
||||||
|
|
||||||
|
int potential_cost = insn_rtx_cost (last_set, speed_p);
|
||||||
|
rtx_insn *insn;
|
||||||
|
FOR_BB_INSNS (test_bb, insn)
|
||||||
|
{
|
||||||
|
if (insn != last_insn)
|
||||||
|
{
|
||||||
|
if (!active_insn_p (insn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!insn_valid_noce_process_p (insn, cc))
|
||||||
|
goto free_bitmap_and_fail;
|
||||||
|
|
||||||
|
rtx sset = single_set (insn);
|
||||||
|
gcc_assert (sset);
|
||||||
|
|
||||||
|
if (contains_mem_rtx_p (SET_SRC (sset))
|
||||||
|
|| !REG_P (SET_DEST (sset)))
|
||||||
|
goto free_bitmap_and_fail;
|
||||||
|
|
||||||
|
potential_cost += insn_rtx_cost (sset, speed_p);
|
||||||
|
bitmap_set_bit (test_bb_temps, REGNO (SET_DEST (sset)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If any of the intermediate results in test_bb are live after test_bb
|
||||||
|
then fail. */
|
||||||
|
if (bitmap_intersect_p (test_bb_live_out, test_bb_temps))
|
||||||
|
goto free_bitmap_and_fail;
|
||||||
|
|
||||||
|
BITMAP_FREE (test_bb_temps);
|
||||||
|
*cost = potential_cost;
|
||||||
|
*simple_p = false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
free_bitmap_and_fail:
|
||||||
|
BITMAP_FREE (test_bb_temps);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
|
/* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
|
||||||
it without using conditional execution. Return TRUE if we were successful
|
it without using conditional execution. Return TRUE if we were successful
|
||||||
at converting the block. */
|
at converting the block. */
|
||||||
|
|
@ -2669,7 +2998,6 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||||||
rtx_insn *insn_a, *insn_b;
|
rtx_insn *insn_a, *insn_b;
|
||||||
rtx set_a, set_b;
|
rtx set_a, set_b;
|
||||||
rtx orig_x, x, a, b;
|
rtx orig_x, x, a, b;
|
||||||
rtx cc;
|
|
||||||
|
|
||||||
/* We're looking for patterns of the form
|
/* We're looking for patterns of the form
|
||||||
|
|
||||||
|
|
@ -2678,15 +3006,23 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||||||
(3) if (...) x = a; // as if with an initial x = x.
|
(3) if (...) x = a; // as if with an initial x = x.
|
||||||
|
|
||||||
The later patterns require jumps to be more expensive.
|
The later patterns require jumps to be more expensive.
|
||||||
|
For the if (...) x = a; else x = b; case we allow multiple insns
|
||||||
|
inside the then and else blocks as long as their only effect is
|
||||||
|
to calculate a value for x.
|
||||||
??? For future expansion, look for multiple X in such patterns. */
|
??? For future expansion, look for multiple X in such patterns. */
|
||||||
|
|
||||||
/* Look for one of the potential sets. */
|
if (! bb_valid_for_noce_process_p (then_bb, cond, &if_info->then_cost,
|
||||||
insn_a = first_active_insn (then_bb);
|
&if_info->then_simple))
|
||||||
if (! insn_a
|
return false;
|
||||||
|| insn_a != last_active_insn (then_bb, FALSE)
|
|
||||||
|| (set_a = single_set (insn_a)) == NULL_RTX)
|
if (else_bb
|
||||||
return FALSE;
|
&& ! bb_valid_for_noce_process_p (else_bb, cond, &if_info->else_cost,
|
||||||
|
&if_info->else_simple))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
insn_a = last_active_insn (then_bb, FALSE);
|
||||||
|
set_a = single_set (insn_a);
|
||||||
|
gcc_assert (set_a);
|
||||||
|
|
||||||
x = SET_DEST (set_a);
|
x = SET_DEST (set_a);
|
||||||
a = SET_SRC (set_a);
|
a = SET_SRC (set_a);
|
||||||
|
|
@ -2701,11 +3037,11 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||||||
set_b = NULL_RTX;
|
set_b = NULL_RTX;
|
||||||
if (else_bb)
|
if (else_bb)
|
||||||
{
|
{
|
||||||
insn_b = first_active_insn (else_bb);
|
insn_b = last_active_insn (else_bb, FALSE);
|
||||||
if (! insn_b
|
set_b = single_set (insn_b);
|
||||||
|| insn_b != last_active_insn (else_bb, FALSE)
|
gcc_assert (set_b);
|
||||||
|| (set_b = single_set (insn_b)) == NULL_RTX
|
|
||||||
|| ! rtx_interchangeable_p (x, SET_DEST (set_b)))
|
if (!rtx_interchangeable_p (x, SET_DEST (set_b)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -2781,20 +3117,14 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||||||
if_info->a = a;
|
if_info->a = a;
|
||||||
if_info->b = b;
|
if_info->b = b;
|
||||||
|
|
||||||
/* Skip it if the instruction to be moved might clobber CC. */
|
|
||||||
cc = cc_in_cond (cond);
|
|
||||||
if (cc
|
|
||||||
&& (set_of (cc, insn_a)
|
|
||||||
|| (insn_b && set_of (cc, insn_b))))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Try optimizations in some approximation of a useful order. */
|
/* Try optimizations in some approximation of a useful order. */
|
||||||
/* ??? Should first look to see if X is live incoming at all. If it
|
/* ??? Should first look to see if X is live incoming at all. If it
|
||||||
isn't, we don't need anything but an unconditional set. */
|
isn't, we don't need anything but an unconditional set. */
|
||||||
|
|
||||||
/* Look and see if A and B are really the same. Avoid creating silly
|
/* Look and see if A and B are really the same. Avoid creating silly
|
||||||
cmove constructs that no one will fix up later. */
|
cmove constructs that no one will fix up later. */
|
||||||
if (rtx_interchangeable_p (a, b))
|
if (noce_simple_bbs (if_info)
|
||||||
|
&& rtx_interchangeable_p (a, b))
|
||||||
{
|
{
|
||||||
/* If we have an INSN_B, we don't have to create any new rtl. Just
|
/* If we have an INSN_B, we don't have to create any new rtl. Just
|
||||||
move the instruction that we already have. If we don't have an
|
move the instruction that we already have. If we don't have an
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
2015-09-01 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
|
||||||
|
|
||||||
|
* gcc.dg/ifcvt-1.c: New test.
|
||||||
|
* gcc.dg/ifcvt-2.c: Likewise.
|
||||||
|
* gcc.dg/ifcvt-3.c: Likewise.
|
||||||
|
|
||||||
2015-09-01 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
|
2015-09-01 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
|
||||||
|
|
||||||
PR libfortran/67412
|
PR libfortran/67412
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/* { dg-do compile { target aarch64*-*-* x86_64-*-* } } */
|
||||||
|
/* { dg-options "-fdump-rtl-ce1 -O2" } */
|
||||||
|
|
||||||
|
int
|
||||||
|
foo (int x)
|
||||||
|
{
|
||||||
|
return x > 100 ? x - 2 : x - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* { dg-final { scan-rtl-dump "3 true changes made" "ce1" } } */
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* { dg-do compile { target aarch64*-*-* x86_64-*-* } } */
|
||||||
|
/* { dg-options "-fdump-rtl-ce1 -O2" } */
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned int uint16_t;
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
_xtime (const uint8_t byte, const uint16_t generator)
|
||||||
|
{
|
||||||
|
if (byte & 0x80)
|
||||||
|
return byte ^ generator;
|
||||||
|
else
|
||||||
|
return byte << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* { dg-final { scan-rtl-dump "3 true changes made" "ce1" } } */
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* { dg-do compile { target aarch64*-*-* x86_64-*-* } } */
|
||||||
|
/* { dg-options "-fdump-rtl-ce1 -O2" } */
|
||||||
|
|
||||||
|
typedef long long s64;
|
||||||
|
|
||||||
|
int
|
||||||
|
foo (s64 a, s64 b, s64 c)
|
||||||
|
{
|
||||||
|
s64 d = a - b;
|
||||||
|
|
||||||
|
if (d == 0)
|
||||||
|
return a + c;
|
||||||
|
else
|
||||||
|
return b + d + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This test can be reduced to just return a + c; */
|
||||||
|
/* { dg-final { scan-rtl-dump "3 true changes made" "ce1" } } */
|
||||||
|
/* { dg-final { scan-assembler-not "sub\.*\tx\[0-9\]+, x\[0-9\]+, x\[0-9\]+\.*" { target { aarch64*-*-* } } } } */
|
||||||
Loading…
Reference in New Issue