This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 8/9] ifcvt: Handle swap-style idioms differently.
- From: Robin Dapp <rdapp at linux dot ibm dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: james dot greenhalgh at arm dot com, rdapp at linux dot ibm dot com
- Date: Fri, 2 Aug 2019 17:10:27 +0200
- Subject: [PATCH 8/9] ifcvt: Handle swap-style idioms differently.
- References: <20190802151028.15590-1-rdapp@linux.ibm.com>
A swap-style idiom like
tmp = a
a = b
b = tmp
would be transformed like
tmp_tmp = cond ? a : tmp
tmp_a = cond ? b : a
tmp_b = cond ? tmp_tmp : b
[...]
including rewiring the first source operand to previous writes (e.g. tmp ->
tmp_tmp).
The code would recognize this, though, and change the last line to
tmp_b = cond ? a : b.
Without additional temporaries we can now emit the following sequence:
tmp = a // (no condition here)
a = cond ? b : a
b = cond ? tmp : b
avoiding any rewiring which would break things now.
check_need_cmovs () finds swap-style idioms and marks the first of the
three instructions as not needing a cmove.
---
gcc/ifcvt.c | 97 ++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 78 insertions(+), 19 deletions(-)
diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c
index 955f9541f60..09bf443656c 100644
--- a/gcc/ifcvt.c
+++ b/gcc/ifcvt.c
@@ -103,6 +103,7 @@ static rtx_insn *block_has_only_trap (basic_block);
static void check_need_temps (basic_block bb,
hash_map<rtx, bool> *need_temp,
rtx cond);
+static void check_need_cmovs (basic_block bb, hash_map<rtx, bool> *need_cmov);
/* Count the number of non-jump active insns in BB. */
@@ -3207,6 +3208,7 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
int count = 0;
hash_map<rtx, bool> need_temps;
+ hash_map<rtx, bool> need_no_cmovs;
/* If we newly set a CC before a cmov, we might need a temporary
even though the compare will be removed by a later pass. Costing
@@ -3214,6 +3216,9 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
check_need_temps (then_bb, &need_temps, cond);
+ /* Identify swap-style idioms. */
+ check_need_cmovs (then_bb, &need_no_cmovs);
+
hash_map<rtx, bool> temps_created;
FOR_BB_INSNS (then_bb, insn)
@@ -3229,10 +3234,8 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
rtx new_val = SET_SRC (set);
rtx old_val = target;
- rtx dest = SET_DEST (set);
-
rtx temp;
- if (need_temps.get (dest))
+ if (need_temps.get (target))
{
temp = gen_reg_rtx (GET_MODE (target));
temps_created.put (target, true);
@@ -3241,18 +3244,11 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
temp = target;
/* 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. */
+ we've changed the register allocation. Rewire the read. */
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
+ if (find_reg_note (insn, REG_DEAD, new_val) == NULL_RTX)
new_val = temporaries[i];
break;
}
@@ -3324,8 +3320,11 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
{
start_sequence ();
- temp_dest1 = noce_emit_cmove (if_info, temp, cond_code,
- x, y, new_val, old_val, NULL_RTX);
+ if (!need_no_cmovs.get (insn))
+ temp_dest1 = noce_emit_cmove (if_info, temp, cond_code,
+ x, y, new_val, old_val, NULL_RTX);
+ else
+ noce_emit_move_insn (target, new_val);
/* If we failed to expand the conditional move, drop out and don't
try to continue. */
@@ -3346,13 +3345,16 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
end_sequence ();
}
- /* Now try emitting one passing a non-canonicalized cc comparison.
- This allows the backend to emit a cmov directly without additional
+ /* Now try emitting a cmov passing a non-canonicalized cc comparison.
+ This allows backends to emit a cmov directly without additional
compare. */
{
start_sequence ();
- temp_dest2 = noce_emit_cmove (if_info, target, cond_code,
- x, y, new_val, old_val, cc_cmp);
+ if (!need_no_cmovs.get (insn))
+ temp_dest2 = noce_emit_cmove (if_info, target, cond_code,
+ x, y, new_val, old_val, cc_cmp);
+ else
+ noce_emit_move_insn (target, new_val);
/* If we failed to expand the conditional move, drop out and don't
try to continue. */
@@ -3931,6 +3933,7 @@ check_cond_move_block (basic_block bb,
/* Check for which sets we need to emit temporaries to hold the destination of
a conditional move. */
+
static void
check_need_temps (basic_block bb, hash_map<rtx, bool> *need_temp, rtx cond)
{
@@ -3938,7 +3941,7 @@ check_need_temps (basic_block bb, hash_map<rtx, bool> *need_temp, rtx cond)
FOR_BB_INSNS (bb, insn)
{
- rtx set, dest;
+ rtx set, src, dest;
if (!active_insn_p (insn))
continue;
@@ -3947,12 +3950,68 @@ check_need_temps (basic_block bb, hash_map<rtx, bool> *need_temp, rtx cond)
if (set == NULL_RTX)
continue;
+ src = SET_SRC (set);
dest = SET_DEST (set);
/* noce_emit_cmove will emit the condition check every time it is called
so we need a temp register if the destination is modified. */
if (reg_overlap_mentioned_p (dest, cond))
need_temp->put (dest, 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
+ -->
+
+ load tmp,a
+ cmov a,b
+ cmov b,tmp */
+
+static void
+check_need_cmovs (basic_block bb, hash_map<rtx, bool> *need_cmov)
+{
+ rtx_insn *insn;
+ int count = 0;
+ auto_vec<rtx> insns;
+ auto_vec<rtx> dests;
+
+ 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);
+ dest = SET_DEST (set);
+
+ for (int i = count - 1; i >= 0; --i)
+ {
+ if (reg_overlap_mentioned_p (src, dests[i])
+ && find_reg_note (insn, REG_DEAD, src) != NULL_RTX)
+ {
+ need_cmov->put (insns[i], false);
+ }
+ }
+
+ insns.safe_push (insn);
+ dests.safe_push (dest);
+
+ count++;
}
}
--
2.17.0