From: Mihailo Stojanovic <[email protected]>

Create an additional case for if-conversion which expands the following
sequence: "if (test) x ^= C;" as

a = 0;
if (test) a = C;
x ^= a;

This reduces the number of necessary conditional moves on some targets
(most notably MIPS).

gcc/ChangeLog:

        * config/mips/mips.cc (mips_rtx_costs): Increase the cost of
        conditional moves which allow both operands to be registers on
        mips64r6.
        * ifcvt.cc (noce_try_synthesized_xor_ok): New function.  Do not
        try the XOR/IOR conversion if the target has a conditional move
        which accepts two registers.
        (noce_try_synthesized_xor): New function.  Discover the sequence
        of instructions which fit the description and expand them
        accordingly.

gcc/testsuite/ChangeLog:

        * gcc.target/mips/cond_xor.c: New test.
        * gcc.target/mips/cond_xor1.c: New test.
        * gcc.target/mips/cond_xor2.c: New test. Skip -Os.

Cherry-picked 5409eee7c24688cd73df92d83a6844a041545c2f,
31d6d46912ad3cbb56c6fc251418c2624b4bb07f and
ff607fa78b23b8e1d753a6e836419e3fe46e3045
from https://github.com/MIPS/gcc

Signed-off-by: Mihailo Stojanovic <[email protected]>
Signed-off-by: Faraz Shahbazker <[email protected]>
Signed-off-by: Chao-ying Fu <[email protected]>
Signed-off-by: Aleksandar Rakic <[email protected]>
Signed-off-by: Eldar Osmanovic <[email protected]>
---
 gcc/config/mips/mips.cc                    |  16 +++
 gcc/ifcvt.cc                               | 134 +++++++++++++++++++++
 gcc/testsuite/gcc.target/mips/cond_xor-1.c |  16 +++
 gcc/testsuite/gcc.target/mips/cond_xor-2.c |  15 +++
 gcc/testsuite/gcc.target/mips/cond_xor.c   |  16 +++
 5 files changed, 197 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor-1.c
 create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor-2.c
 create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor.c

diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index 1c79d980790..e371f1a1de4 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -5573,6 +5573,22 @@ mips_rtx_costs (rtx x, machine_mode mode, int outer_code,
       *total = mips_cost->fp_add;
       return false;
 
+    case IF_THEN_ELSE:
+      if (reg_or_0_operand (XEXP (x, 1), VOIDmode)
+         || reg_or_0_operand (XEXP (x, 2), VOIDmode))
+       *total = 0;
+      if (outer_code == SET)
+       {
+         /* Conditional moves on r6 only allow one parameter to be a register
+         (the other parameter is zero).  Increase the cost of conditional
+         moves which allow both parameters to be registers.  */
+         if (mips_isa_rev == 6
+             && register_operand (XEXP (x, 1), VOIDmode)
+             && register_operand (XEXP (x, 2), VOIDmode))
+           *total = 1;
+       }
+       return false;
+
     case SET:
       if (register_operand (SET_DEST (x), VOIDmode)
          && reg_or_0_operand (SET_SRC (x), VOIDmode))
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index 1df5d54efa3..619d9efa0bd 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -2223,6 +2223,136 @@ noce_try_cmove (struct noce_if_info *if_info)
   return false;
 }
 
+/* If the target has a conditional move which accepts two registers, do not
+   try synthesized conditional XOR/IOR, as it will not yield any benefits.  */
+
+static bool
+noce_try_synthesized_xor_ok (struct noce_if_info *if_info)
+{
+  machine_mode mode = GET_MODE (if_info->x);
+  rtx testreg = gen_rtx_REG (mode, LAST_VIRTUAL_REGISTER + 1);
+
+  rtx if_then_else = gen_rtx_IF_THEN_ELSE (mode,
+                                           if_info->cond,
+                                           const0_rtx, if_info->x);
+
+  rtx if_then_else_2 = gen_rtx_IF_THEN_ELSE (mode,
+                                             if_info->cond,
+                                             testreg, if_info->x);
+
+  return rtx_cost (if_then_else_2, mode, SET, 1, true)
+         > rtx_cost (if_then_else, mode, SET, 1, true);
+}
+
+/* Expand "if (test) x ^= C;" as
+
+   a = 0;
+   if (test) a = C;
+   x ^= a;
+
+   This lowers the number of necessary conditional moves on some targets.
+
+   We allow for maximum of three instructions in the then block.
+   First one loads the constant into a register.  Second one is an actual
+   XOR/IOR instruction.  Third one is a zero or sign extend.  */
+
+static bool
+noce_try_synthesized_xor (struct noce_if_info *if_info)
+{
+  enum rtx_code code = GET_CODE (if_info->cond);
+
+  if (code != NE && code != EQ)
+    return false;
+  if (if_info->else_bb)
+    return false;
+
+  rtx a = if_info->a;
+  rtx_insn *insn_a = if_info->insn_a;
+  rtx_insn *prev = prev_nonnote_nondebug_insn (insn_a);
+  if ((GET_CODE (a) == ZERO_EXTEND
+      || GET_CODE (a) == SIGN_EXTEND)
+      && prev
+      && single_set (prev))
+    {
+      insn_a = prev;
+      a = SET_SRC (single_set (insn_a));
+    }
+
+  enum rtx_code opcode = GET_CODE (a);
+  if (opcode != XOR && opcode != IOR)
+    return false;
+
+  rtx xor_src = XEXP (a, 0);
+  rtx xor_const = XEXP (a, 1);
+  if (GET_CODE (xor_src) == SUBREG)
+    xor_src = SUBREG_REG (xor_src);
+
+  prev = prev_nonnote_nondebug_insn (insn_a);
+  if (prev && BLOCK_FOR_INSN (insn_a) == BLOCK_FOR_INSN (prev))
+    {
+      if (!REG_P (xor_const))
+             return false;
+
+      a = single_set (prev);
+      if (a != NULL_RTX
+         && rtx_equal_p (SET_DEST (a), xor_const)
+         && CONST_INT_P (SET_SRC (a)))
+       xor_const = SET_SRC (a);
+      else
+       return false;
+
+      rtx_insn *prev_prev = prev_nonnote_nondebug_insn  (prev);
+      if (prev_prev && BLOCK_FOR_INSN (prev) == BLOCK_FOR_INSN (prev_prev))
+       return false;
+    }
+  else if (!CONST_INT_P (xor_const))
+    return false;
+
+  if (!rtx_equal_p (xor_src, if_info->x))
+    return false;
+
+  start_sequence ();
+
+  machine_mode mode = GET_MODE (if_info->x);
+  rtx const_reg = gen_reg_rtx (mode);
+  rtx target = gen_reg_rtx (mode);
+
+  noce_emit_move_insn (const_reg, xor_const);
+  target = noce_emit_cmove (if_info, target, code,
+                            XEXP (if_info->cond, 0),
+                            XEXP (if_info->cond, 1),
+                            const_reg, const0_rtx);
+  if (!target)
+    {
+      end_sequence ();
+      return false;
+    }
+
+  target = expand_simple_binop (GET_MODE (if_info->x), opcode,
+                                if_info->x, target, if_info->x,
+                                0, OPTAB_WIDEN);
+  if (!target)
+    {
+      end_sequence ();
+      return false;
+    }
+
+  rtx_insn* seq = end_ifcvt_sequence (if_info);
+  if (!seq)
+    return false;
+
+  if (!targetm.noce_conversion_profitable_p (seq, if_info)
+      && (seq_cost (seq, if_info->speed_p)
+       > if_info->max_seq_cost + COSTS_N_INSNS (1)))
+    return false;
+
+  emit_insn_before_setloc (seq, if_info->jump,
+                           INSN_LOCATION (if_info->insn_a));
+  if_info->transform_name = "noce_try_synthesized_xor";
+
+  return true;
+}
+
 /* Return true if X contains a conditional code mode rtx.  */
 
 static bool
@@ -4558,6 +4688,10 @@ noce_process_if_block (struct noce_if_info *if_info)
   if (HAVE_conditional_move
       && noce_try_cmove (if_info))
     goto success;
+  if (HAVE_conditional_move
+      && noce_try_synthesized_xor_ok (if_info)
+      && noce_try_synthesized_xor (if_info))
+    goto success;
   if (! targetm.have_conditional_execution ())
     {
       if (noce_try_addcc (if_info))
diff --git a/gcc/testsuite/gcc.target/mips/cond_xor-1.c 
b/gcc/testsuite/gcc.target/mips/cond_xor-1.c
new file mode 100644
index 00000000000..2c6ea68fcfd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/cond_xor-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=64 -march=mips64r6" } */
+/* { dg-additional-options "--param max-rtl-if-conversion-insns=1" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } } */
+
+NOMIPS16 unsigned int
+foo (unsigned int x, unsigned int y, unsigned z)
+{
+  if (x == z)
+    y ^= 0xabcd;
+
+  return y;
+}
+
+/* { dg-final { scan-assembler-times "seleqz" 1 } } */
+/* { dg-final { scan-assembler-not "selnez" } } */
diff --git a/gcc/testsuite/gcc.target/mips/cond_xor-2.c 
b/gcc/testsuite/gcc.target/mips/cond_xor-2.c
new file mode 100644
index 00000000000..fc0c6affe8e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/cond_xor-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=64 -march=mips64r6" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" "-Os" } } */
+
+NOMIPS16 unsigned int
+foo (unsigned int x, unsigned int y, unsigned int z)
+{
+  if (x == 1)
+    y ^= z;
+
+  return y;
+}
+
+/* { dg-final { scan-assembler-times "seleqz" 1 } } */
+/* { dg-final { scan-assembler-not "selnez" } } */
diff --git a/gcc/testsuite/gcc.target/mips/cond_xor.c 
b/gcc/testsuite/gcc.target/mips/cond_xor.c
new file mode 100644
index 00000000000..a5788c246e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/cond_xor.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=64 -march=mips64r6" } */
+/* { dg-additional-options "--param max-rtl-if-conversion-insns=1" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } } */
+
+NOMIPS16 unsigned int
+foo (unsigned int x, unsigned int y)
+{
+  if (x == 1)
+    y ^= 0xabcd;
+
+  return y;
+}
+
+/* { dg-final { scan-assembler-times "seleqz" 1 } } */
+/* { dg-final { scan-assembler-not "selnez" } } */
-- 
2.43.0

Reply via email to