Hello, This patch adds basic support for utilizing the SH div0s instruction to simplify some integer sign comparisons such as '(a < 0) == (b < 0)'. Tested on rev 190332 with make -k check RUNTESTFLAGS="--target_board=sh-sim \{-m2/-ml,-m2/-mb,-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}"
and no new failures. OK? Cheers, Oleg ChangeLog: PR target/52933 * config/sh/sh.md (cmp_div0s_0, cmp_div0s_1, *cmp_div0s_0, *cmp_div0s_1, *cbranch_div0s, *movsicc_div0s): New insns. * config/sh/sh.c (sh_rtx_costs): Handle div0s patterns. testsuite/ChangeLog: PR target/52933 * gcc.target/sh/pr52933-1.c: New. * gcc.target/sh/pr52933-2.c: New.
Index: gcc/testsuite/gcc.target/sh/pr52933-1.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr52933-1.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr52933-1.c (revision 0) @@ -0,0 +1,168 @@ +/* Check that the div0s instruction is used for integer sign comparisons. + Each test case is expected to emit at least one div0s insn. + Problems when combining the div0s comparison result with surrounding + logic usually show up as redundant tst insns. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */ +/* { dg-final { scan-assembler-times "div0s" 25 } } */ +/* { dg-final { scan-assembler-not "tst" } } */ + +typedef unsigned char bool; + +int other_func_a (int, int); +int other_func_b (int, int); + +bool +test_00 (int a, int b) +{ + return (a ^ b) >= 0; +} + +bool +test_01 (int a, int b) +{ + return (a ^ b) < 0; +} + +int +test_02 (int a, int b, int c, int d) +{ + if ((a ^ b) < 0) + return other_func_a (a, c); + else + return other_func_b (d, b); +} + +int +test_03 (int a, int b, int c, int d) +{ + if ((a ^ b) >= 0) + return other_func_a (a, c); + else + return other_func_b (d, b); +} + +int +test_04 (int a, int b) +{ + return (a ^ b) >= 0 ? -20 : -40; +} + +bool +test_05 (int a, int b) +{ + return (a ^ b) < 0; +} + +int +test_06 (int a, int b) +{ + return (a ^ b) < 0 ? -20 : -40; +} + +bool +test_07 (int a, int b) +{ + return (a < 0) == (b < 0); +} + +int +test_08 (int a, int b) +{ + return (a < 0) == (b < 0) ? -20 : -40; +} + +bool +test_09 (int a, int b) +{ + return (a < 0) != (b < 0); +} + +int +test_10 (int a, int b) +{ + return (a < 0) != (b < 0) ? -20 : -40; +} + +bool +test_11 (int a, int b) +{ + return (a >= 0) ^ (b < 0); +} + +int +test_12 (int a, int b) +{ + return (a >= 0) ^ (b < 0) ? -20 : -40; +} + +bool +test_13 (int a, int b) +{ + return !((a >= 0) ^ (b < 0)); +} + +int +test_14 (int a, int b) +{ + return !((a >= 0) ^ (b < 0)) ? -20 : -40; +} + +bool +test_15 (int a, int b) +{ + return (a & 0x80000000) == (b & 0x80000000); +} + +int +test_16 (int a, int b) +{ + return (a & 0x80000000) == (b & 0x80000000) ? -20 : -40; +} + +bool +test_17 (int a, int b) +{ + return (a & 0x80000000) != (b & 0x80000000); +} + +int +test_18 (int a, int b) +{ + return (a & 0x80000000) != (b & 0x80000000) ? -20 : -40; +} + +int +test_19 (unsigned int a, unsigned int b) +{ + return (a ^ b) >> 31; +} + +int +test_20 (unsigned int a, unsigned int b) +{ + return (a >> 31) ^ (b >> 31); +} + +int +test_21 (int a, int b) +{ + return ((a & 0x80000000) ^ (b & 0x80000000)) >> 31 ? -30 : -10; +} + +int +test_22 (int a, int b, int c, int d) +{ + if ((a < 0) == (b < 0)) + return other_func_a (a, b); + else + return other_func_b (c, d); +} + +bool +test_23 (int a, int b, int c, int d) +{ + /* Should emit 2x div0s. */ + return ((a < 0) == (b < 0)) | ((c < 0) == (d < 0)); +} Index: gcc/testsuite/gcc.target/sh/pr52933-2.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr52933-2.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr52933-2.c (revision 0) @@ -0,0 +1,12 @@ +/* Check that the div0s instruction is used for integer sign comparisons + when -mpretend-cmove is enabled. + Each test case is expected to emit at least one div0s insn. + Problems when combining the div0s comparison result with surrounding + logic usually show up as redundant tst insns. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2 -mpretend-cmove" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */ +/* { dg-final { scan-assembler-times "div0s" 25 } } */ +/* { dg-final { scan-assembler-not "tst" } } */ + +#include "pr52933-1.c" Index: gcc/config/sh/sh.md =================================================================== --- gcc/config/sh/sh.md (revision 190332) +++ gcc/config/sh/sh.md (working copy) @@ -801,6 +801,70 @@ "cmp/pl %0" [(set_attr "type" "mt_group")]) +;; Some integer sign comparison patterns can be realized with the div0s insn. +;; div0s Rm,Rn T = (Rm >> 31) ^ (Rn >> 31) +(define_insn "cmp_div0s_0" + [(set (reg:SI T_REG) + (lshiftrt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r") + (match_operand:SI 1 "arith_reg_operand" "r")) + (const_int 31)))] + "TARGET_SH1" + "div0s %0,%1" + [(set_attr "type" "arith")]) + +(define_insn "cmp_div0s_1" + [(set (reg:SI T_REG) + (lt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r") + (match_operand:SI 1 "arith_reg_operand" "r")) + (const_int 0)))] + "TARGET_SH1" + "div0s %0,%1" + [(set_attr "type" "arith")]) + +(define_insn_and_split "*cmp_div0s_0" + [(set (match_operand:SI 0 "arith_reg_dest" "") + (lshiftrt:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "") + (match_operand:SI 2 "arith_reg_operand" "")) + (const_int 31))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& 1" + [(set (reg:SI T_REG) + (lshiftrt:SI (xor:SI (match_dup 1) (match_dup 2)) (const_int 31))) + (set (match_dup 0) (reg:SI T_REG))]) + +(define_insn_and_split "*cmp_div0s_1" + [(set (match_operand:SI 0 "arith_reg_dest" "") + (ge:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "") + (match_operand:SI 2 "arith_reg_operand" "")) + (const_int 0))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& can_create_pseudo_p ()" + [(const_int 0)] +;; We have to go through the movnegt expander here which will handle the +;; SH2A vs non-SH2A cases. +{ + emit_insn (gen_cmp_div0s_1 (operands[1], operands[2])); + emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ())); + DONE; +}) + +(define_insn_and_split "*cmp_div0s_1" + [(set (reg:SI T_REG) + (ge:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "") + (match_operand:SI 1 "arith_reg_operand" "")) + (const_int 0)))] + "TARGET_SH1" + "#" + "&& can_create_pseudo_p ()" + [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 0) (match_dup 1)) + (const_int 0))) + (set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))]) + + ;; ------------------------------------------------------------------------- ;; SImode compare and branch ;; ------------------------------------------------------------------------- @@ -918,6 +982,63 @@ (label_ref (match_dup 2)) (pc)))]) +;; Compare and branch combine patterns for div0s comparisons. +(define_insn_and_split "*cbranch_div0s" + [(set (pc) + (if_then_else (lt (xor:SI (match_operand:SI 0 "arith_reg_operand" "") + (match_operand:SI 1 "arith_reg_operand" "")) + (const_int 0)) + (label_ref (match_operand 2)) + (pc))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& 1" + [(set (reg:SI T_REG) + (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0))) + (set (pc) + (if_then_else (ne (reg:SI T_REG) (const_int 0)) + (label_ref (match_dup 2)) + (pc)))]) + +(define_insn_and_split "*cbranch_div0s" + [(set (pc) + (if_then_else (ge (xor:SI (match_operand:SI 0 "arith_reg_operand" "") + (match_operand:SI 1 "arith_reg_operand" "")) + (const_int 0)) + (label_ref (match_operand 2)) + (pc))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& 1" + [(set (reg:SI T_REG) + (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0))) + (set (pc) + (if_then_else (eq (reg:SI T_REG) (const_int 0)) + (label_ref (match_dup 2)) + (pc)))]) + +;; Conditional move combine pattern for div0s comparisons. +;; This is used when TARGET_PRETEND_CMOVE is in effect. +(define_insn_and_split "*movsicc_div0s" + [(set (match_operand:SI 0 "arith_reg_dest" "") + (if_then_else:SI (ge (xor:SI (match_operand:SI 1 "arith_reg_operand" "") + (match_operand:SI 2 "arith_reg_operand" "")) + (const_int 0)) + (match_operand:SI 3 "arith_reg_operand" "") + (match_operand:SI 4 "general_movsrc_operand" ""))) + (clobber (reg:SI T_REG))] + "TARGET_PRETEND_CMOVE" + "#" + "&& 1" + [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 1) (match_dup 2)) + (const_int 0))) + (set (match_dup 0) + (if_then_else (ne (reg:SI T_REG) (const_int 0)) + (match_dup 4) + (match_dup 3)))]) + ;; ------------------------------------------------------------------------- ;; SImode unsigned integer comparisons ;; ------------------------------------------------------------------------- Index: gcc/config/sh/sh.c =================================================================== --- gcc/config/sh/sh.c (revision 190332) +++ gcc/config/sh/sh.c (working copy) @@ -3186,9 +3186,33 @@ *total = COSTS_N_INSNS (multcosts (x)); return true; + case LT: + case GE: + /* div0s sign comparison. */ + if (GET_CODE (XEXP (x, 0)) == XOR + && REG_P ((XEXP (XEXP (x, 0), 0))) + && REG_P ((XEXP (XEXP (x, 0), 1))) + && satisfies_constraint_Z (XEXP (x, 1))) + { + *total = COSTS_N_INSNS (1); + return true; + } + else + return false; + + case LSHIFTRT: + /* div0s sign comparison. */ + if (GET_CODE (XEXP (x, 0)) == XOR + && REG_P ((XEXP (XEXP (x, 0), 0))) + && REG_P ((XEXP (XEXP (x, 0), 1))) + && CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 31) + { + *total = COSTS_N_INSNS (1); + return true; + } + /* Fall through to shiftcosts. */ case ASHIFT: case ASHIFTRT: - case LSHIFTRT: { int cost = shiftcosts (x); if (cost < 0)