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