From: Mihailo Stojanovic <mihailo.stojano...@rt-rk.com> 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/ * 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/ * 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 <mistojano...@wavecomp.com> Signed-off-by: Faraz Shahbazker <fshahbaz...@wavecomp.com> Signed-off-by: Chao-ying Fu <c...@mips.com> Signed-off-by: Aleksandar Rakic <aleksandar.ra...@htecgroup.com> --- gcc/config/mips/mips.cc | 18 ++- gcc/ifcvt.cc | 135 ++++++++++++++++++++++ gcc/testsuite/gcc.target/mips/cond_xor.c | 15 +++ gcc/testsuite/gcc.target/mips/cond_xor1.c | 15 +++ gcc/testsuite/gcc.target/mips/cond_xor2.c | 15 +++ 5 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor.c create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor1.c create mode 100644 gcc/testsuite/gcc.target/mips/cond_xor2.c diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc index 19d428e6ed6..63b7bdd255c 100644 --- a/gcc/config/mips/mips.cc +++ b/gcc/config/mips/mips.cc @@ -5784,10 +5784,20 @@ mips_rtx_costs (rtx x, machine_mode mode, int outer_code, 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; - return false; + 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; default: return false; diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index 74f13a637b2..297ccd470dc 100644 --- a/gcc/ifcvt.cc +++ b/gcc/ifcvt.cc @@ -1962,6 +1962,137 @@ 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) +{ + rtx testreg = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 1); + + rtx if_then_else = gen_rtx_IF_THEN_ELSE (word_mode, + if_info->cond, + const0_rtx, if_info->x); + + rtx if_then_else_2 = gen_rtx_IF_THEN_ELSE (word_mode, + if_info->cond, + testreg, if_info->x); + + return rtx_cost (if_then_else_2, word_mode, SET, 1, true) + > rtx_cost (if_then_else, word_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; + + /* Fail if there is an else block. */ + if (if_info->else_bb) + return FALSE; + + /* We allow for the final instruction in the basic block to be sign or + zero extend. */ + rtx a = if_info->a; + rtx_insn *insn_a = if_info->insn_a; + if ((GET_CODE (a) == ZERO_EXTEND + || GET_CODE (a) == SIGN_EXTEND) + && single_set (prev_nonnote_nondebug_insn (insn_a))) + { + a = SET_SRC (single_set (prev_nonnote_nondebug_insn (insn_a))); + insn_a = prev_nonnote_nondebug_insn (insn_a); + } + + /* Check that the operation is indeed XOR or IOR. Also check that we don't + have any more instructions in the then block. */ + 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); + xor_src = GET_CODE (xor_src) == SUBREG ? SUBREG_REG (xor_src) : xor_src; + + /* Check if the instruction prior to XOR or IOR loads the constant into + the register. */ + rtx_insn *prev = prev_nonnote_nondebug_insn (insn_a); + if (BLOCK_FOR_INSN (insn_a) == BLOCK_FOR_INSN (prev)) + { + if (GET_CODE (xor_const) != REG) + return FALSE; + + a = single_set (prev); + if (a != NULL_RTX + && rtx_equal_p (SET_DEST (a), xor_const) + && GET_CODE (SET_SRC (a)) == CONST_INT) + xor_const = SET_SRC (a); + else + return FALSE; + + /* This must be the first instruction of the basic block. */ + if (BLOCK_FOR_INSN (prev) + == BLOCK_FOR_INSN (prev_nonnote_nondebug_insn (prev))) + return FALSE; + } + else if (GET_CODE (xor_const) != CONST_INT) + 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 || !targetm.noce_conversion_profitable_p (seq, if_info)) + 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 @@ -4246,6 +4377,10 @@ noce_process_if_block (struct noce_if_info *if_info) if (HAVE_conditional_move && noce_try_cond_zero_arith (if_info)) goto success; + if (HAVE_conditional_move + && noce_try_synthesized_xor_ok (if_info) + && noce_try_synthesized_xor (if_info)) + goto success; if (HAVE_conditional_move && noce_try_cmove_arith (if_info)) goto success; 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..c0635c9dc53 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/cond_xor.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=64 -march=mips64r6" } */ +/* { 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" } } */ diff --git a/gcc/testsuite/gcc.target/mips/cond_xor1.c b/gcc/testsuite/gcc.target/mips/cond_xor1.c new file mode 100644 index 00000000000..6a09465cecd --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/cond_xor1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=64 -march=mips64r6" } */ +/* { 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_xor2.c b/gcc/testsuite/gcc.target/mips/cond_xor2.c new file mode 100644 index 00000000000..a64cc189116 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/cond_xor2.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-times "selnez" 1 } } */ -- 2.34.1