1055 lines
35 KiB
Diff
1055 lines
35 KiB
Diff
Revert r12-674{3,4,5,6,7,8,9}, as there are several PRs reported against
|
||
those changes: PR104153, PR104154 and PR104198 at least.
|
||
|
||
--- gcc/config/rs6000/rs6000.cc
|
||
+++ gcc/config/rs6000/rs6000.cc
|
||
@@ -16373,10 +16373,10 @@ rs6000_emit_minmax (rtx dest, enum rtx_code code, rtx op0, rtx op1)
|
||
c = GEU;
|
||
|
||
if (code == SMAX || code == UMAX)
|
||
- target = emit_conditional_move (dest, { c, op0, op1, mode },
|
||
+ target = emit_conditional_move (dest, c, op0, op1, mode,
|
||
op0, op1, mode, 0);
|
||
else
|
||
- target = emit_conditional_move (dest, { c, op0, op1, mode },
|
||
+ target = emit_conditional_move (dest, c, op0, op1, mode,
|
||
op1, op0, mode, 0);
|
||
gcc_assert (target);
|
||
if (target != dest)
|
||
@@ -22769,7 +22769,7 @@ rs6000_emit_swsqrt (rtx dst, rtx src, bool recip)
|
||
|
||
if (mode == SFmode)
|
||
{
|
||
- rtx target = emit_conditional_move (e, { GT, src, zero, mode },
|
||
+ rtx target = emit_conditional_move (e, GT, src, zero, mode,
|
||
e, zero, mode, 0);
|
||
if (target != e)
|
||
emit_move_insn (e, target);
|
||
--- gcc/expmed.cc
|
||
+++ gcc/expmed.cc
|
||
@@ -4124,8 +4124,8 @@ expand_sdiv_pow2 (scalar_int_mode mode, rtx op0, HOST_WIDE_INT d)
|
||
temp = force_reg (mode, temp);
|
||
|
||
/* Construct "temp2 = (temp2 < 0) ? temp : temp2". */
|
||
- temp2 = emit_conditional_move (temp2, { LT, temp2, const0_rtx, mode },
|
||
- temp, temp2, mode, 0);
|
||
+ temp2 = emit_conditional_move (temp2, LT, temp2, const0_rtx,
|
||
+ mode, temp, temp2, mode, 0);
|
||
if (temp2)
|
||
{
|
||
rtx_insn *seq = get_insns ();
|
||
@@ -6127,10 +6127,10 @@ emit_store_flag (rtx target, enum rtx_code code, rtx op0, rtx op1,
|
||
return 0;
|
||
|
||
if (and_them)
|
||
- tem = emit_conditional_move (target, { code, op0, op1, mode },
|
||
+ tem = emit_conditional_move (target, code, op0, op1, mode,
|
||
tem, const0_rtx, GET_MODE (tem), 0);
|
||
else
|
||
- tem = emit_conditional_move (target, { code, op0, op1, mode },
|
||
+ tem = emit_conditional_move (target, code, op0, op1, mode,
|
||
trueval, tem, GET_MODE (tem), 0);
|
||
|
||
if (tem == 0)
|
||
--- gcc/expr.cc
|
||
+++ gcc/expr.cc
|
||
@@ -8824,9 +8824,8 @@ expand_cond_expr_using_cmove (tree treeop0 ATTRIBUTE_UNUSED,
|
||
op2 = gen_lowpart (mode, op2);
|
||
|
||
/* Try to emit the conditional move. */
|
||
- insn = emit_conditional_move (temp,
|
||
- { comparison_code, op00, op01,
|
||
- comparison_mode },
|
||
+ insn = emit_conditional_move (temp, comparison_code,
|
||
+ op00, op01, comparison_mode,
|
||
op1, op2, mode,
|
||
unsignedp);
|
||
|
||
@@ -9717,9 +9716,8 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
|
||
start_sequence ();
|
||
|
||
/* Try to emit the conditional move. */
|
||
- insn = emit_conditional_move (target,
|
||
- { comparison_code,
|
||
- op0, cmpop1, mode },
|
||
+ insn = emit_conditional_move (target, comparison_code,
|
||
+ op0, cmpop1, mode,
|
||
op0, op1, mode,
|
||
unsignedp);
|
||
|
||
--- gcc/ifcvt.cc
|
||
+++ gcc/ifcvt.cc
|
||
@@ -83,7 +83,7 @@ static rtx_insn *last_active_insn (basic_block, int);
|
||
static rtx_insn *find_active_insn_before (basic_block, rtx_insn *);
|
||
static rtx_insn *find_active_insn_after (basic_block, rtx_insn *);
|
||
static basic_block block_fallthru (basic_block);
|
||
-static rtx cond_exec_get_condition (rtx_insn *, bool);
|
||
+static rtx cond_exec_get_condition (rtx_insn *);
|
||
static rtx noce_get_condition (rtx_insn *, rtx_insn **, bool);
|
||
static int noce_operand_ok (const_rtx);
|
||
static void merge_if_block (ce_if_block *);
|
||
@@ -98,14 +98,6 @@ static int dead_or_predicable (basic_block, basic_block, basic_block,
|
||
edge, int);
|
||
static void noce_emit_move_insn (rtx, rtx);
|
||
static rtx_insn *block_has_only_trap (basic_block);
|
||
-static void need_cmov_or_rewire (basic_block, hash_set<rtx_insn *> *,
|
||
- hash_map<rtx_insn *, int> *);
|
||
-static bool noce_convert_multiple_sets_1 (struct noce_if_info *,
|
||
- hash_set<rtx_insn *> *,
|
||
- hash_map<rtx_insn *, int> *,
|
||
- auto_vec<rtx> *,
|
||
- auto_vec<rtx> *,
|
||
- auto_vec<rtx_insn *> *, int *);
|
||
|
||
/* Count the number of non-jump active insns in BB. */
|
||
|
||
@@ -433,7 +425,7 @@ cond_exec_process_insns (ce_if_block *ce_info ATTRIBUTE_UNUSED,
|
||
/* Return the condition for a jump. Do not do any special processing. */
|
||
|
||
static rtx
|
||
-cond_exec_get_condition (rtx_insn *jump, bool get_reversed = false)
|
||
+cond_exec_get_condition (rtx_insn *jump)
|
||
{
|
||
rtx test_if, cond;
|
||
|
||
@@ -445,10 +437,8 @@ cond_exec_get_condition (rtx_insn *jump, bool get_reversed = false)
|
||
|
||
/* If this branches to JUMP_LABEL when the condition is false,
|
||
reverse the condition. */
|
||
- if (get_reversed
|
||
- || (GET_CODE (XEXP (test_if, 2)) == LABEL_REF
|
||
- && label_ref_label (XEXP (test_if, 2))
|
||
- == JUMP_LABEL (jump)))
|
||
+ if (GET_CODE (XEXP (test_if, 2)) == LABEL_REF
|
||
+ && label_ref_label (XEXP (test_if, 2)) == JUMP_LABEL (jump))
|
||
{
|
||
enum rtx_code rev = reversed_comparison_code (cond, jump);
|
||
if (rev == UNKNOWN)
|
||
@@ -780,7 +770,7 @@ static int noce_try_addcc (struct noce_if_info *);
|
||
static int noce_try_store_flag_constants (struct noce_if_info *);
|
||
static int noce_try_store_flag_mask (struct noce_if_info *);
|
||
static rtx noce_emit_cmove (struct noce_if_info *, rtx, enum rtx_code, rtx,
|
||
- rtx, rtx, rtx, rtx = NULL, rtx = NULL);
|
||
+ rtx, rtx, rtx);
|
||
static int noce_try_cmove (struct noce_if_info *);
|
||
static int noce_try_cmove_arith (struct noce_if_info *);
|
||
static rtx noce_get_alt_condition (struct noce_if_info *, rtx, rtx_insn **);
|
||
@@ -1719,8 +1709,7 @@ noce_try_store_flag_mask (struct noce_if_info *if_info)
|
||
|
||
static rtx
|
||
noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
|
||
- rtx cmp_a, rtx cmp_b, rtx vfalse, rtx vtrue, rtx cc_cmp,
|
||
- rtx rev_cc_cmp)
|
||
+ rtx cmp_a, rtx cmp_b, rtx vfalse, rtx vtrue)
|
||
{
|
||
rtx target ATTRIBUTE_UNUSED;
|
||
int unsignedp ATTRIBUTE_UNUSED;
|
||
@@ -1752,30 +1741,23 @@ noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
|
||
end_sequence ();
|
||
}
|
||
|
||
- unsignedp = (code == LTU || code == GEU
|
||
- || code == LEU || code == GTU);
|
||
-
|
||
- if (cc_cmp != NULL_RTX && rev_cc_cmp != NULL_RTX)
|
||
- target = emit_conditional_move (x, cc_cmp, rev_cc_cmp,
|
||
- vtrue, vfalse, GET_MODE (x));
|
||
- else
|
||
+ /* Don't even try if the comparison operands are weird
|
||
+ except that the target supports cbranchcc4. */
|
||
+ if (! general_operand (cmp_a, GET_MODE (cmp_a))
|
||
+ || ! general_operand (cmp_b, GET_MODE (cmp_b)))
|
||
{
|
||
- /* Don't even try if the comparison operands are weird
|
||
- except that the target supports cbranchcc4. */
|
||
- if (! general_operand (cmp_a, GET_MODE (cmp_a))
|
||
- || ! general_operand (cmp_b, GET_MODE (cmp_b)))
|
||
- {
|
||
- if (!have_cbranchcc4
|
||
- || GET_MODE_CLASS (GET_MODE (cmp_a)) != MODE_CC
|
||
- || cmp_b != const0_rtx)
|
||
- return NULL_RTX;
|
||
- }
|
||
-
|
||
- target = emit_conditional_move (x, { code, cmp_a, cmp_b, VOIDmode },
|
||
- vtrue, vfalse, GET_MODE (x),
|
||
- unsignedp);
|
||
+ if (!have_cbranchcc4
|
||
+ || GET_MODE_CLASS (GET_MODE (cmp_a)) != MODE_CC
|
||
+ || cmp_b != const0_rtx)
|
||
+ return NULL_RTX;
|
||
}
|
||
|
||
+ unsignedp = (code == LTU || code == GEU
|
||
+ || code == LEU || code == GTU);
|
||
+
|
||
+ target = emit_conditional_move (x, code, cmp_a, cmp_b, VOIDmode,
|
||
+ vtrue, vfalse, GET_MODE (x),
|
||
+ unsignedp);
|
||
if (target)
|
||
return target;
|
||
|
||
@@ -1811,9 +1793,8 @@ noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
|
||
|
||
promoted_target = gen_reg_rtx (GET_MODE (reg_vtrue));
|
||
|
||
- target = emit_conditional_move (promoted_target,
|
||
- { code, cmp_a, cmp_b, VOIDmode },
|
||
- reg_vtrue, reg_vfalse,
|
||
+ target = emit_conditional_move (promoted_target, code, cmp_a, cmp_b,
|
||
+ VOIDmode, reg_vtrue, reg_vfalse,
|
||
GET_MODE (reg_vtrue), unsignedp);
|
||
/* Nope, couldn't do it in that mode either. */
|
||
if (!target)
|
||
@@ -3160,50 +3141,6 @@ bb_valid_for_noce_process_p (basic_block test_bb, rtx cond,
|
||
return false;
|
||
}
|
||
|
||
-/* Helper function to emit a cmov sequence encapsulated in
|
||
- start_sequence () and end_sequence (). If NEED_CMOV is true
|
||
- we call noce_emit_cmove to create a cmove sequence. Otherwise emit
|
||
- a simple move. If successful, store the first instruction of the
|
||
- sequence in TEMP_DEST and the sequence costs in SEQ_COST. */
|
||
-
|
||
-static rtx_insn*
|
||
-try_emit_cmove_seq (struct noce_if_info *if_info, rtx temp,
|
||
- rtx cond, rtx new_val, rtx old_val, bool need_cmov,
|
||
- unsigned *cost, rtx *temp_dest,
|
||
- rtx cc_cmp = NULL, rtx rev_cc_cmp = NULL)
|
||
-{
|
||
- rtx_insn *seq = NULL;
|
||
- *cost = 0;
|
||
-
|
||
- rtx x = XEXP (cond, 0);
|
||
- rtx y = XEXP (cond, 1);
|
||
- rtx_code cond_code = GET_CODE (cond);
|
||
-
|
||
- start_sequence ();
|
||
-
|
||
- if (need_cmov)
|
||
- *temp_dest = noce_emit_cmove (if_info, temp, cond_code,
|
||
- x, y, new_val, old_val, cc_cmp, rev_cc_cmp);
|
||
- else
|
||
- {
|
||
- *temp_dest = temp;
|
||
- if (if_info->then_else_reversed)
|
||
- noce_emit_move_insn (temp, old_val);
|
||
- else
|
||
- noce_emit_move_insn (temp, new_val);
|
||
- }
|
||
-
|
||
- if (*temp_dest != NULL_RTX)
|
||
- {
|
||
- seq = get_insns ();
|
||
- *cost = seq_cost (seq, if_info->speed_p);
|
||
- }
|
||
-
|
||
- end_sequence ();
|
||
-
|
||
- return seq;
|
||
-}
|
||
-
|
||
/* We have something like:
|
||
|
||
if (x > y)
|
||
@@ -3261,6 +3198,7 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
|
||
rtx cond = noce_get_condition (jump, &cond_earliest, false);
|
||
rtx x = XEXP (cond, 0);
|
||
rtx y = XEXP (cond, 1);
|
||
+ rtx_code cond_code = GET_CODE (cond);
|
||
|
||
/* The true targets for a conditional move. */
|
||
auto_vec<rtx> targets;
|
||
@@ -3269,139 +3207,8 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
|
||
auto_vec<rtx> temporaries;
|
||
/* The insns we've emitted. */
|
||
auto_vec<rtx_insn *> unmodified_insns;
|
||
-
|
||
- hash_set<rtx_insn *> need_no_cmov;
|
||
- hash_map<rtx_insn *, int> rewired_src;
|
||
-
|
||
- need_cmov_or_rewire (then_bb, &need_no_cmov, &rewired_src);
|
||
-
|
||
- int last_needs_comparison = -1;
|
||
-
|
||
- bool ok = noce_convert_multiple_sets_1
|
||
- (if_info, &need_no_cmov, &rewired_src, &targets, &temporaries,
|
||
- &unmodified_insns, &last_needs_comparison);
|
||
- if (!ok)
|
||
- return false;
|
||
-
|
||
- /* If there are insns that overwrite part of the initial
|
||
- comparison, we can still omit creating temporaries for
|
||
- the last of them.
|
||
- As the second try will always create a less expensive,
|
||
- valid sequence, we do not need to compare and can discard
|
||
- the first one. */
|
||
- if (last_needs_comparison != -1)
|
||
- {
|
||
- end_sequence ();
|
||
- start_sequence ();
|
||
- ok = noce_convert_multiple_sets_1
|
||
- (if_info, &need_no_cmov, &rewired_src, &targets, &temporaries,
|
||
- &unmodified_insns, &last_needs_comparison);
|
||
- /* Actually we should not fail anymore if we reached here,
|
||
- but better still check. */
|
||
- if (!ok)
|
||
- return false;
|
||
- }
|
||
-
|
||
- /* We must have seen some sort of insn to insert, otherwise we were
|
||
- given an empty BB to convert, and we can't handle that. */
|
||
- gcc_assert (!unmodified_insns.is_empty ());
|
||
-
|
||
- /* Now fixup the assignments. */
|
||
- for (unsigned i = 0; i < targets.length (); i++)
|
||
- if (targets[i] != temporaries[i])
|
||
- noce_emit_move_insn (targets[i], temporaries[i]);
|
||
-
|
||
- /* Actually emit the sequence if it isn't too expensive. */
|
||
- rtx_insn *seq = get_insns ();
|
||
-
|
||
- if (!targetm.noce_conversion_profitable_p (seq, if_info))
|
||
- {
|
||
- end_sequence ();
|
||
- return FALSE;
|
||
- }
|
||
-
|
||
- for (insn = seq; insn; insn = NEXT_INSN (insn))
|
||
- set_used_flags (insn);
|
||
-
|
||
- /* Mark all our temporaries and targets as used. */
|
||
- for (unsigned i = 0; i < targets.length (); i++)
|
||
- {
|
||
- set_used_flags (temporaries[i]);
|
||
- set_used_flags (targets[i]);
|
||
- }
|
||
-
|
||
- set_used_flags (cond);
|
||
- set_used_flags (x);
|
||
- set_used_flags (y);
|
||
-
|
||
- unshare_all_rtl_in_chain (seq);
|
||
- end_sequence ();
|
||
-
|
||
- if (!seq)
|
||
- return FALSE;
|
||
-
|
||
- for (insn = seq; insn; insn = NEXT_INSN (insn))
|
||
- if (JUMP_P (insn)
|
||
- || recog_memoized (insn) == -1)
|
||
- return FALSE;
|
||
-
|
||
- emit_insn_before_setloc (seq, if_info->jump,
|
||
- INSN_LOCATION (unmodified_insns.last ()));
|
||
-
|
||
- /* Clean up THEN_BB and the edges in and out of it. */
|
||
- remove_edge (find_edge (test_bb, join_bb));
|
||
- remove_edge (find_edge (then_bb, join_bb));
|
||
- redirect_edge_and_branch_force (single_succ_edge (test_bb), join_bb);
|
||
- delete_basic_block (then_bb);
|
||
- num_true_changes++;
|
||
-
|
||
- /* Maybe merge blocks now the jump is simple enough. */
|
||
- if (can_merge_blocks_p (test_bb, join_bb))
|
||
- {
|
||
- merge_blocks (test_bb, join_bb);
|
||
- num_true_changes++;
|
||
- }
|
||
-
|
||
- num_updated_if_blocks++;
|
||
- if_info->transform_name = "noce_convert_multiple_sets";
|
||
- return TRUE;
|
||
-}
|
||
-
|
||
-/* This goes through all relevant insns of IF_INFO->then_bb and tries to
|
||
- create conditional moves. In case a simple move sufficis the insn
|
||
- should be listed in NEED_NO_CMOV. The rewired-src cases should be
|
||
- specified via REWIRED_SRC. TARGETS, TEMPORARIES and UNMODIFIED_INSNS
|
||
- are specified and used in noce_convert_multiple_sets and should be passed
|
||
- to this function.. */
|
||
-
|
||
-static bool
|
||
-noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
||
- hash_set<rtx_insn *> *need_no_cmov,
|
||
- hash_map<rtx_insn *, int> *rewired_src,
|
||
- auto_vec<rtx> *targets,
|
||
- auto_vec<rtx> *temporaries,
|
||
- auto_vec<rtx_insn *> *unmodified_insns,
|
||
- int *last_needs_comparison)
|
||
-{
|
||
- basic_block then_bb = if_info->then_bb;
|
||
- rtx_insn *jump = if_info->jump;
|
||
- rtx_insn *cond_earliest;
|
||
-
|
||
- /* Decompose the condition attached to the jump. */
|
||
- rtx cond = noce_get_condition (jump, &cond_earliest, false);
|
||
-
|
||
- rtx cc_cmp = cond_exec_get_condition (jump);
|
||
- rtx rev_cc_cmp = cond_exec_get_condition (jump, /* get_reversed */ true);
|
||
-
|
||
- rtx_insn *insn;
|
||
int count = 0;
|
||
|
||
- targets->truncate (0);
|
||
- temporaries->truncate (0);
|
||
- unmodified_insns->truncate (0);
|
||
-
|
||
- bool second_try = *last_needs_comparison != -1;
|
||
-
|
||
FOR_BB_INSNS (then_bb, insn)
|
||
{
|
||
/* Skip over non-insns. */
|
||
@@ -3412,53 +3219,26 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
||
gcc_checking_assert (set);
|
||
|
||
rtx target = SET_DEST (set);
|
||
- rtx temp;
|
||
-
|
||
+ rtx temp = gen_reg_rtx (GET_MODE (target));
|
||
rtx new_val = SET_SRC (set);
|
||
- if (int *ii = rewired_src->get (insn))
|
||
- new_val = simplify_replace_rtx (new_val, (*targets)[*ii],
|
||
- (*temporaries)[*ii]);
|
||
rtx old_val = target;
|
||
|
||
- /* As we are transforming
|
||
- if (x > y)
|
||
- {
|
||
- a = b;
|
||
- c = d;
|
||
- }
|
||
- into
|
||
- a = (x > y) ...
|
||
- c = (x > y) ...
|
||
-
|
||
- we potentially check x > y before every set.
|
||
- Even though the check might be removed by subsequent passes, this means
|
||
- that we cannot transform
|
||
- if (x > y)
|
||
- {
|
||
- x = y;
|
||
- ...
|
||
- }
|
||
- into
|
||
- x = (x > y) ...
|
||
- ...
|
||
- since this would invalidate x and the following to-be-removed checks.
|
||
- Therefore we introduce a temporary every time we are about to
|
||
- overwrite a variable used in the check. Costing of a sequence with
|
||
- these is going to be inaccurate so only use temporaries when
|
||
- needed.
|
||
-
|
||
- If performing a second try, we know how many insns require a
|
||
- temporary. For the last of these, we can omit creating one. */
|
||
- if (reg_overlap_mentioned_p (target, cond)
|
||
- && (!second_try || count < *last_needs_comparison))
|
||
- temp = gen_reg_rtx (GET_MODE (target));
|
||
- else
|
||
- temp = target;
|
||
-
|
||
- /* We have identified swap-style idioms before. A normal
|
||
- set will need to be a cmov while the first instruction of a swap-style
|
||
- idiom can be a regular move. This helps with costing. */
|
||
- bool need_cmov = !need_no_cmov->contains (insn);
|
||
+ /* If we were supposed to read from an earlier write in this block,
|
||
+ we've changed the register allocation. Rewire the read. While
|
||
+ we are looking, also try to catch a swap idiom. */
|
||
+ for (int i = count - 1; i >= 0; --i)
|
||
+ if (reg_overlap_mentioned_p (new_val, targets[i]))
|
||
+ {
|
||
+ /* Catch a "swap" style idiom. */
|
||
+ if (find_reg_note (insn, REG_DEAD, new_val) != NULL_RTX)
|
||
+ /* The write to targets[i] is only live until the read
|
||
+ here. As the condition codes match, we can propagate
|
||
+ the set to here. */
|
||
+ new_val = SET_SRC (single_set (unmodified_insns[i]));
|
||
+ else
|
||
+ new_val = temporaries[i];
|
||
+ break;
|
||
+ }
|
||
|
||
/* If we had a non-canonical conditional jump (i.e. one where
|
||
the fallthrough is to the "else" case) we need to reverse
|
||
@@ -3478,9 +3258,7 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
||
we'll end up trying to emit r4:HI = cond ? (r1:SI) : (r3:HI).
|
||
Wrap the two cmove operands into subregs if appropriate to prevent
|
||
that. */
|
||
-
|
||
- if (!CONSTANT_P (new_val)
|
||
- && GET_MODE (new_val) != GET_MODE (temp))
|
||
+ if (GET_MODE (new_val) != GET_MODE (temp))
|
||
{
|
||
machine_mode src_mode = GET_MODE (new_val);
|
||
machine_mode dst_mode = GET_MODE (temp);
|
||
@@ -3491,8 +3269,7 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
||
}
|
||
new_val = lowpart_subreg (dst_mode, new_val, src_mode);
|
||
}
|
||
- if (!CONSTANT_P (old_val)
|
||
- && GET_MODE (old_val) != GET_MODE (temp))
|
||
+ if (GET_MODE (old_val) != GET_MODE (temp))
|
||
{
|
||
machine_mode src_mode = GET_MODE (old_val);
|
||
machine_mode dst_mode = GET_MODE (temp);
|
||
@@ -3504,80 +3281,101 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
||
old_val = lowpart_subreg (dst_mode, old_val, src_mode);
|
||
}
|
||
|
||
- /* Try emitting a conditional move passing the backend the
|
||
- canonicalized comparison. The backend is then able to
|
||
- recognize expressions like
|
||
-
|
||
- if (x > y)
|
||
- y = x;
|
||
-
|
||
- as min/max and emit an insn, accordingly. */
|
||
- unsigned cost1 = 0, cost2 = 0;
|
||
- rtx_insn *seq, *seq1, *seq2;
|
||
- rtx temp_dest = NULL_RTX, temp_dest1 = NULL_RTX, temp_dest2 = NULL_RTX;
|
||
+ /* Actually emit the conditional move. */
|
||
+ rtx temp_dest = noce_emit_cmove (if_info, temp, cond_code,
|
||
+ x, y, new_val, old_val);
|
||
|
||
- seq1 = try_emit_cmove_seq (if_info, temp, cond,
|
||
- new_val, old_val, need_cmov,
|
||
- &cost1, &temp_dest1);
|
||
-
|
||
- /* Here, we try to pass the backend a non-canonicalized cc comparison
|
||
- as well. This allows the backend to emit a cmov directly without
|
||
- creating an additional compare for each. If successful, costing
|
||
- is easier and this sequence is usually preferred. */
|
||
- seq2 = try_emit_cmove_seq (if_info, target, cond,
|
||
- new_val, old_val, need_cmov,
|
||
- &cost2, &temp_dest2, cc_cmp, rev_cc_cmp);
|
||
-
|
||
- /* Check which version is less expensive. */
|
||
- if (seq1 != NULL_RTX && (cost1 <= cost2 || seq2 == NULL_RTX))
|
||
- {
|
||
- seq = seq1;
|
||
- temp_dest = temp_dest1;
|
||
- if (!second_try)
|
||
- *last_needs_comparison = count;
|
||
- }
|
||
- else if (seq2 != NULL_RTX)
|
||
- {
|
||
- seq = seq2;
|
||
- temp_dest = temp_dest2;
|
||
- }
|
||
- else
|
||
+ /* If we failed to expand the conditional move, drop out and don't
|
||
+ try to continue. */
|
||
+ if (temp_dest == NULL_RTX)
|
||
{
|
||
- /* Nothing worked, bail out. */
|
||
end_sequence ();
|
||
return FALSE;
|
||
}
|
||
|
||
- /* End the sub sequence and emit to the main sequence. */
|
||
- emit_insn (seq);
|
||
-
|
||
/* Bookkeeping. */
|
||
count++;
|
||
- targets->safe_push (target);
|
||
- temporaries->safe_push (temp_dest);
|
||
- unmodified_insns->safe_push (insn);
|
||
+ targets.safe_push (target);
|
||
+ temporaries.safe_push (temp_dest);
|
||
+ unmodified_insns.safe_push (insn);
|
||
}
|
||
|
||
- return true;
|
||
-}
|
||
+ /* We must have seen some sort of insn to insert, otherwise we were
|
||
+ given an empty BB to convert, and we can't handle that. */
|
||
+ gcc_assert (!unmodified_insns.is_empty ());
|
||
+
|
||
+ /* Now fixup the assignments. */
|
||
+ for (int i = 0; i < count; i++)
|
||
+ noce_emit_move_insn (targets[i], temporaries[i]);
|
||
+
|
||
+ /* Actually emit the sequence if it isn't too expensive. */
|
||
+ rtx_insn *seq = get_insns ();
|
||
+
|
||
+ if (!targetm.noce_conversion_profitable_p (seq, if_info))
|
||
+ {
|
||
+ end_sequence ();
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ for (insn = seq; insn; insn = NEXT_INSN (insn))
|
||
+ set_used_flags (insn);
|
||
+
|
||
+ /* Mark all our temporaries and targets as used. */
|
||
+ for (int i = 0; i < count; i++)
|
||
+ {
|
||
+ set_used_flags (temporaries[i]);
|
||
+ set_used_flags (targets[i]);
|
||
+ }
|
||
|
||
+ set_used_flags (cond);
|
||
+ set_used_flags (x);
|
||
+ set_used_flags (y);
|
||
|
||
+ unshare_all_rtl_in_chain (seq);
|
||
+ end_sequence ();
|
||
+
|
||
+ if (!seq)
|
||
+ return FALSE;
|
||
+
|
||
+ for (insn = seq; insn; insn = NEXT_INSN (insn))
|
||
+ if (JUMP_P (insn)
|
||
+ || recog_memoized (insn) == -1)
|
||
+ return FALSE;
|
||
+
|
||
+ emit_insn_before_setloc (seq, if_info->jump,
|
||
+ INSN_LOCATION (unmodified_insns.last ()));
|
||
+
|
||
+ /* Clean up THEN_BB and the edges in and out of it. */
|
||
+ remove_edge (find_edge (test_bb, join_bb));
|
||
+ remove_edge (find_edge (then_bb, join_bb));
|
||
+ redirect_edge_and_branch_force (single_succ_edge (test_bb), join_bb);
|
||
+ delete_basic_block (then_bb);
|
||
+ num_true_changes++;
|
||
+
|
||
+ /* Maybe merge blocks now the jump is simple enough. */
|
||
+ if (can_merge_blocks_p (test_bb, join_bb))
|
||
+ {
|
||
+ merge_blocks (test_bb, join_bb);
|
||
+ num_true_changes++;
|
||
+ }
|
||
+
|
||
+ num_updated_if_blocks++;
|
||
+ if_info->transform_name = "noce_convert_multiple_sets";
|
||
+ return TRUE;
|
||
+}
|
||
|
||
/* Return true iff basic block TEST_BB is comprised of only
|
||
(SET (REG) (REG)) insns suitable for conversion to a series
|
||
of conditional moves. Also check that we have more than one set
|
||
(other routines can handle a single set better than we would), and
|
||
- fewer than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. While going
|
||
- through the insns store the sum of their potential costs in COST. */
|
||
+ fewer than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. */
|
||
|
||
static bool
|
||
-bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost)
|
||
+bb_ok_for_noce_convert_multiple_sets (basic_block test_bb)
|
||
{
|
||
rtx_insn *insn;
|
||
unsigned count = 0;
|
||
unsigned param = param_max_rtl_if_conversion_insns;
|
||
- bool speed_p = optimize_bb_for_speed_p (test_bb);
|
||
- unsigned potential_cost = 0;
|
||
|
||
FOR_BB_INSNS (test_bb, insn)
|
||
{
|
||
@@ -3600,9 +3398,9 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost)
|
||
if (!REG_P (dest))
|
||
return false;
|
||
|
||
- if (!((REG_P (src) || CONSTANT_P (src))
|
||
- || (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src))
|
||
- && subreg_lowpart_p (src))))
|
||
+ if (!(REG_P (src)
|
||
+ || (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src))
|
||
+ && subreg_lowpart_p (src))))
|
||
return false;
|
||
|
||
/* Destination must be appropriate for a conditional write. */
|
||
@@ -3613,13 +3411,9 @@ bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost)
|
||
if (!can_conditionally_move_p (GET_MODE (dest)))
|
||
return false;
|
||
|
||
- potential_cost += insn_cost (insn, speed_p);
|
||
-
|
||
count++;
|
||
}
|
||
|
||
- *cost += potential_cost;
|
||
-
|
||
/* If we would only put out one conditional move, the other strategies
|
||
this pass tries are better optimized and will be more appropriate.
|
||
Some targets want to strictly limit the number of conditional moves
|
||
@@ -3667,24 +3461,11 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||
to calculate a value for x.
|
||
??? For future expansion, further expand the "multiple X" rules. */
|
||
|
||
- /* First look for multiple SETS. The original costs already include
|
||
- a base cost of COSTS_N_INSNS (2): one instruction for the compare
|
||
- (which we will be needing either way) and one instruction for the
|
||
- branch. When comparing costs we want to use the branch instruction
|
||
- cost and the sets vs. the cmovs generated here. Therefore subtract
|
||
- the costs of the compare before checking.
|
||
- ??? Actually, instead of the branch instruction costs we might want
|
||
- to use COSTS_N_INSNS (BRANCH_COST ()) as in other places. */
|
||
-
|
||
- unsigned potential_cost = if_info->original_cost - COSTS_N_INSNS (1);
|
||
- unsigned old_cost = if_info->original_cost;
|
||
+ /* First look for multiple SETS. */
|
||
if (!else_bb
|
||
&& HAVE_conditional_move
|
||
- && bb_ok_for_noce_convert_multiple_sets (then_bb, &potential_cost))
|
||
+ && bb_ok_for_noce_convert_multiple_sets (then_bb))
|
||
{
|
||
- /* Temporarily set the original costs to what we estimated so
|
||
- we can determine if the transformation is worth it. */
|
||
- if_info->original_cost = potential_cost;
|
||
if (noce_convert_multiple_sets (if_info))
|
||
{
|
||
if (dump_file && if_info->transform_name)
|
||
@@ -3692,9 +3473,6 @@ noce_process_if_block (struct noce_if_info *if_info)
|
||
if_info->transform_name);
|
||
return TRUE;
|
||
}
|
||
-
|
||
- /* Restore the original costs. */
|
||
- if_info->original_cost = old_cost;
|
||
}
|
||
|
||
bool speed_p = optimize_bb_for_speed_p (test_bb);
|
||
@@ -4036,89 +3814,6 @@ check_cond_move_block (basic_block bb,
|
||
return TRUE;
|
||
}
|
||
|
||
-/* Find local swap-style idioms in BB and mark the first insn (1)
|
||
- that is only a temporary as not needing a conditional move as
|
||
- it is going to be dead afterwards anyway.
|
||
-
|
||
- (1) int tmp = a;
|
||
- a = b;
|
||
- b = tmp;
|
||
-
|
||
- ifcvt
|
||
- -->
|
||
-
|
||
- tmp = a;
|
||
- a = cond ? b : a_old;
|
||
- b = cond ? tmp : b_old;
|
||
-
|
||
- Additionally, store the index of insns like (2) when a subsequent
|
||
- SET reads from their destination.
|
||
-
|
||
- (2) int c = a;
|
||
- int d = c;
|
||
-
|
||
- ifcvt
|
||
- -->
|
||
-
|
||
- c = cond ? a : c_old;
|
||
- d = cond ? d : c; // Need to use c rather than c_old here.
|
||
-*/
|
||
-
|
||
-static void
|
||
-need_cmov_or_rewire (basic_block bb,
|
||
- hash_set<rtx_insn *> *need_no_cmov,
|
||
- hash_map<rtx_insn *, int> *rewired_src)
|
||
-{
|
||
- rtx_insn *insn;
|
||
- int count = 0;
|
||
- auto_vec<rtx_insn *> insns;
|
||
- auto_vec<rtx> dests;
|
||
-
|
||
- /* Iterate over all SETs, storing the destinations
|
||
- in DEST.
|
||
- - If we hit a SET that reads from a destination
|
||
- that we have seen before and the corresponding register
|
||
- is dead afterwards, the register does not need to be
|
||
- moved conditionally.
|
||
- - If we encounter a previously changed register,
|
||
- rewire the read to the original source. */
|
||
- FOR_BB_INSNS (bb, insn)
|
||
- {
|
||
- rtx set, src, dest;
|
||
-
|
||
- if (!active_insn_p (insn))
|
||
- continue;
|
||
-
|
||
- set = single_set (insn);
|
||
- if (set == NULL_RTX)
|
||
- continue;
|
||
-
|
||
- src = SET_SRC (set);
|
||
- if (SUBREG_P (src))
|
||
- src = SUBREG_REG (src);
|
||
- dest = SET_DEST (set);
|
||
-
|
||
- /* Check if the current SET's source is the same
|
||
- as any previously seen destination.
|
||
- This is quadratic but the number of insns in BB
|
||
- is bounded by PARAM_MAX_RTL_IF_CONVERSION_INSNS. */
|
||
- if (REG_P (src))
|
||
- for (int i = count - 1; i >= 0; --i)
|
||
- if (reg_overlap_mentioned_p (src, dests[i]))
|
||
- {
|
||
- if (find_reg_note (insn, REG_DEAD, src) != NULL_RTX)
|
||
- need_no_cmov->add (insns[i]);
|
||
- else
|
||
- rewired_src->put (insn, i);
|
||
- }
|
||
-
|
||
- insns.safe_push (insn);
|
||
- dests.safe_push (dest);
|
||
-
|
||
- count++;
|
||
- }
|
||
-}
|
||
-
|
||
/* Given a basic block BB suitable for conditional move conversion,
|
||
a condition COND, and pointer maps THEN_VALS and ELSE_VALS containing
|
||
the register values depending on COND, emit the insns in the block as
|
||
--- gcc/optabs.cc
|
||
+++ gcc/optabs.cc
|
||
@@ -52,8 +52,6 @@ static void prepare_float_lib_cmp (rtx, rtx, enum rtx_code, rtx *,
|
||
static rtx expand_unop_direct (machine_mode, optab, rtx, rtx, int);
|
||
static void emit_libcall_block_1 (rtx_insn *, rtx, rtx, rtx, bool);
|
||
|
||
-static rtx emit_conditional_move_1 (rtx, rtx, rtx, rtx, machine_mode);
|
||
-
|
||
/* Debug facility for use in GDB. */
|
||
void debug_optab_libfuncs (void);
|
||
|
||
@@ -626,13 +624,12 @@ expand_doubleword_shift_condmove (scalar_int_mode op1_mode, optab binoptab,
|
||
|
||
/* Select between them. Do the INTO half first because INTO_SUPERWORD
|
||
might be the current value of OUTOF_TARGET. */
|
||
- if (!emit_conditional_move (into_target, { cmp_code, cmp1, cmp2, op1_mode },
|
||
+ if (!emit_conditional_move (into_target, cmp_code, cmp1, cmp2, op1_mode,
|
||
into_target, into_superword, word_mode, false))
|
||
return false;
|
||
|
||
if (outof_target != 0)
|
||
- if (!emit_conditional_move (outof_target,
|
||
- { cmp_code, cmp1, cmp2, op1_mode },
|
||
+ if (!emit_conditional_move (outof_target, cmp_code, cmp1, cmp2, op1_mode,
|
||
outof_target, outof_superword,
|
||
word_mode, false))
|
||
return false;
|
||
@@ -4854,8 +4851,8 @@ emit_indirect_jump (rtx loc)
|
||
is not supported. */
|
||
|
||
rtx
|
||
-emit_conditional_move (rtx target, struct rtx_comparison comp,
|
||
- rtx op2, rtx op3,
|
||
+emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
|
||
+ machine_mode cmode, rtx op2, rtx op3,
|
||
machine_mode mode, int unsignedp)
|
||
{
|
||
rtx comparison;
|
||
@@ -4877,33 +4874,31 @@ emit_conditional_move (rtx target, struct rtx_comparison comp,
|
||
/* If one operand is constant, make it the second one. Only do this
|
||
if the other operand is not constant as well. */
|
||
|
||
- if (swap_commutative_operands_p (comp.op0, comp.op1))
|
||
+ if (swap_commutative_operands_p (op0, op1))
|
||
{
|
||
- std::swap (comp.op0, comp.op1);
|
||
- comp.code = swap_condition (comp.code);
|
||
+ std::swap (op0, op1);
|
||
+ code = swap_condition (code);
|
||
}
|
||
|
||
/* get_condition will prefer to generate LT and GT even if the old
|
||
comparison was against zero, so undo that canonicalization here since
|
||
comparisons against zero are cheaper. */
|
||
+ if (code == LT && op1 == const1_rtx)
|
||
+ code = LE, op1 = const0_rtx;
|
||
+ else if (code == GT && op1 == constm1_rtx)
|
||
+ code = GE, op1 = const0_rtx;
|
||
|
||
- if (comp.code == LT && comp.op1 == const1_rtx)
|
||
- comp.code = LE, comp.op1 = const0_rtx;
|
||
- else if (comp.code == GT && comp.op1 == constm1_rtx)
|
||
- comp.code = GE, comp.op1 = const0_rtx;
|
||
-
|
||
- if (comp.mode == VOIDmode)
|
||
- comp.mode = GET_MODE (comp.op0);
|
||
+ if (cmode == VOIDmode)
|
||
+ cmode = GET_MODE (op0);
|
||
|
||
- enum rtx_code orig_code = comp.code;
|
||
+ enum rtx_code orig_code = code;
|
||
bool swapped = false;
|
||
if (swap_commutative_operands_p (op2, op3)
|
||
- && ((reversed =
|
||
- reversed_comparison_code_parts (comp.code, comp.op0, comp.op1, NULL))
|
||
- != UNKNOWN))
|
||
+ && ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL))
|
||
+ != UNKNOWN))
|
||
{
|
||
std::swap (op2, op3);
|
||
- comp.code = reversed;
|
||
+ code = reversed;
|
||
swapped = true;
|
||
}
|
||
|
||
@@ -4920,10 +4915,8 @@ emit_conditional_move (rtx target, struct rtx_comparison comp,
|
||
|
||
for (int pass = 0; ; pass++)
|
||
{
|
||
- comp.code = unsignedp ? unsigned_condition (comp.code) : comp.code;
|
||
- comparison =
|
||
- simplify_gen_relational (comp.code, VOIDmode,
|
||
- comp.mode, comp.op0, comp.op1);
|
||
+ code = unsignedp ? unsigned_condition (code) : code;
|
||
+ comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
|
||
|
||
/* We can get const0_rtx or const_true_rtx in some circumstances. Just
|
||
punt and let the caller figure out how best to deal with this
|
||
@@ -4934,16 +4927,24 @@ emit_conditional_move (rtx target, struct rtx_comparison comp,
|
||
save_pending_stack_adjust (&save);
|
||
last = get_last_insn ();
|
||
do_pending_stack_adjust ();
|
||
- machine_mode cmpmode = comp.mode;
|
||
+ machine_mode cmpmode = cmode;
|
||
prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
|
||
GET_CODE (comparison), NULL_RTX, unsignedp,
|
||
OPTAB_WIDEN, &comparison, &cmpmode);
|
||
if (comparison)
|
||
{
|
||
- rtx res = emit_conditional_move_1 (target, comparison,
|
||
- op2, op3, mode);
|
||
- if (res != NULL_RTX)
|
||
- return res;
|
||
+ class expand_operand ops[4];
|
||
+
|
||
+ create_output_operand (&ops[0], target, mode);
|
||
+ create_fixed_operand (&ops[1], comparison);
|
||
+ create_input_operand (&ops[2], op2, mode);
|
||
+ create_input_operand (&ops[3], op3, mode);
|
||
+ if (maybe_expand_insn (icode, 4, ops))
|
||
+ {
|
||
+ if (ops[0].value != target)
|
||
+ convert_move (target, ops[0].value, false);
|
||
+ return target;
|
||
+ }
|
||
}
|
||
delete_insns_since (last);
|
||
restore_pending_stack_adjust (&save);
|
||
@@ -4955,88 +4956,17 @@ emit_conditional_move (rtx target, struct rtx_comparison comp,
|
||
/* If the preferred op2/op3 order is not usable, retry with other
|
||
operand order, perhaps it will expand successfully. */
|
||
if (swapped)
|
||
- comp.code = orig_code;
|
||
- else if ((reversed =
|
||
- reversed_comparison_code_parts (orig_code, comp.op0, comp.op1,
|
||
+ code = orig_code;
|
||
+ else if ((reversed = reversed_comparison_code_parts (orig_code, op0, op1,
|
||
NULL))
|
||
!= UNKNOWN)
|
||
- comp.code = reversed;
|
||
+ code = reversed;
|
||
else
|
||
return NULL_RTX;
|
||
std::swap (op2, op3);
|
||
}
|
||
}
|
||
|
||
-/* Helper function that, in addition to COMPARISON, also tries
|
||
- the reversed REV_COMPARISON with swapped OP2 and OP3. As opposed
|
||
- to when we pass the specific constituents of a comparison, no
|
||
- additional insns are emitted for it. It might still be necessary
|
||
- to emit more than one insn for the final conditional move, though. */
|
||
-
|
||
-rtx
|
||
-emit_conditional_move (rtx target, rtx comparison, rtx rev_comparison,
|
||
- rtx op2, rtx op3, machine_mode mode)
|
||
-{
|
||
- rtx res = emit_conditional_move_1 (target, comparison, op2, op3, mode);
|
||
-
|
||
- if (res != NULL_RTX)
|
||
- return res;
|
||
-
|
||
- return emit_conditional_move_1 (target, rev_comparison, op3, op2, mode);
|
||
-}
|
||
-
|
||
-/* Helper for emitting a conditional move. */
|
||
-
|
||
-static rtx
|
||
-emit_conditional_move_1 (rtx target, rtx comparison,
|
||
- rtx op2, rtx op3, machine_mode mode)
|
||
-{
|
||
- enum insn_code icode;
|
||
-
|
||
- if (comparison == NULL_RTX || !COMPARISON_P (comparison))
|
||
- return NULL_RTX;
|
||
-
|
||
- /* If the two source operands are identical, that's just a move.
|
||
- As the comparison comes in non-canonicalized, we must make
|
||
- sure not to discard any possible side effects. If there are
|
||
- side effects, just let the target handle it. */
|
||
- if (!side_effects_p (comparison) && rtx_equal_p (op2, op3))
|
||
- {
|
||
- if (!target)
|
||
- target = gen_reg_rtx (mode);
|
||
-
|
||
- emit_move_insn (target, op3);
|
||
- return target;
|
||
- }
|
||
-
|
||
- if (mode == VOIDmode)
|
||
- mode = GET_MODE (op2);
|
||
-
|
||
- icode = direct_optab_handler (movcc_optab, mode);
|
||
-
|
||
- if (icode == CODE_FOR_nothing)
|
||
- return NULL_RTX;
|
||
-
|
||
- if (!target)
|
||
- target = gen_reg_rtx (mode);
|
||
-
|
||
- class expand_operand ops[4];
|
||
-
|
||
- create_output_operand (&ops[0], target, mode);
|
||
- create_fixed_operand (&ops[1], comparison);
|
||
- create_input_operand (&ops[2], op2, mode);
|
||
- create_input_operand (&ops[3], op3, mode);
|
||
-
|
||
- if (maybe_expand_insn (icode, 4, ops))
|
||
- {
|
||
- if (ops[0].value != target)
|
||
- convert_move (target, ops[0].value, false);
|
||
- return target;
|
||
- }
|
||
-
|
||
- return NULL_RTX;
|
||
-}
|
||
-
|
||
|
||
/* Emit a conditional negate or bitwise complement using the
|
||
negcc or notcc optabs if available. Return NULL_RTX if such operations
|
||
--- gcc/optabs.h
|
||
+++ gcc/optabs.h
|
||
@@ -279,8 +279,8 @@ extern void emit_indirect_jump (rtx);
|
||
#endif
|
||
|
||
/* Emit a conditional move operation. */
|
||
-rtx emit_conditional_move (rtx, rtx_comparison, rtx, rtx, machine_mode, int);
|
||
-rtx emit_conditional_move (rtx, rtx, rtx, rtx, rtx, machine_mode);
|
||
+rtx emit_conditional_move (rtx, enum rtx_code, rtx, rtx, machine_mode,
|
||
+ rtx, rtx, machine_mode, int);
|
||
|
||
/* Emit a conditional negate or bitwise complement operation. */
|
||
rtx emit_conditional_neg_or_complement (rtx, rtx_code, machine_mode, rtx,
|
||
--- gcc/rtl.h
|
||
+++ gcc/rtl.h
|
||
@@ -4604,16 +4604,7 @@ word_register_operation_p (const_rtx x)
|
||
return true;
|
||
}
|
||
}
|
||
-
|
||
-/* Holds an rtx comparison to simplify passing many parameters pertaining to a
|
||
- single comparison. */
|
||
-
|
||
-struct rtx_comparison {
|
||
- rtx_code code;
|
||
- rtx op0, op1;
|
||
- machine_mode mode;
|
||
-};
|
||
-
|
||
+
|
||
/* gtype-desc.cc. */
|
||
extern void gt_ggc_mx (rtx &);
|
||
extern void gt_pch_nx (rtx &);
|