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

Reply via email to