From ebbeaf490c56e04d2e9be25caf9522ef5fba6c72 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Fri, 22 Aug 2025 11:53:27 -0600 Subject: [PATCH] [PR rtl-optimization/120553] Improve selecting between constants based on sign bit test While working to remove mvconst_internal I stumbled over a regression in the code to handle signed division by a power of two. In that sequence we want to select between 0, 2^n-1 by pairing a sign bit splat with a subsequent logical right shift. This can be done without branches or conditional moves. Playing with it a bit made me realize there's a handful of selections we can do based on a sign bit test. Essentially there's two broad cases. Clearing bits after the sign bit splat. So we have 0, -1, if we clear bits the 0 stays as-is, but the -1 could easily turn into 2^n-1, ~2^n-1, or some small constants. Setting bits after the sign bit splat. If we have 0, -1, setting bits the -1 stays as-is, but the 0 can turn into 2^n, a small constant, etc. Shreya and I originally started looking at target patterns to do this, essentially discovering conditional move forms of the selects and rewriting them into something more efficient. That got out of control pretty quickly and it relied on if-conversion to initially create the conditional move. The better solution is to actually discover the cases during if-conversion itself. That catches cases that were previously being missed, checks cost models, and is actually simpler since we don't have to distinguish between things like ori and bseti, instead we just emit the natural RTL and let the target figure it out. In the ifcvt implementation we put these cases just before trying the traditional conditional move sequences. Essentially these are a last attempt before trying the generalized conditional move sequence. This as been bootstrapped and regression tested on aarch64, riscv, ppc64le, s390x, alpha, m68k, sh4eb, x86_64 and probably a couple others I've forgotten. It's also been tested on the other embedded targets. Obviously the new tests are risc-v specific, so that testing was primarily to make sure we didn't ICE, generate incorrect code or regress target existing specific tests. Raphael has some changes to attack this from the gimple direction as well. I think the latest version of those is on me to push through internal review. PR rtl-optimization/120553 gcc/ * ifcvt.cc (noce_try_sign_bit_splat): New function. (noce_process_if_block): Use it. gcc/testsuite/ * gcc.target/riscv/pr120553-1.c: New test. * gcc.target/riscv/pr120553-2.c: New test. * gcc.target/riscv/pr120553-3.c: New test. * gcc.target/riscv/pr120553-4.c: New test. * gcc.target/riscv/pr120553-5.c: New test. * gcc.target/riscv/pr120553-6.c: New test. * gcc.target/riscv/pr120553-7.c: New test. * gcc.target/riscv/pr120553-8.c: New test. --- gcc/ifcvt.cc | 203 ++++++++++++++++++++ gcc/testsuite/gcc.target/riscv/pr120553-1.c | 90 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-2.c | 90 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-3.c | 90 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-4.c | 90 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-5.c | 91 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-6.c | 91 +++++++++ gcc/testsuite/gcc.target/riscv/pr120553-7.c | 19 ++ gcc/testsuite/gcc.target/riscv/pr120553-8.c | 19 ++ 9 files changed, 783 insertions(+) create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-1.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-2.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-3.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-4.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-5.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-6.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-7.c create mode 100644 gcc/testsuite/gcc.target/riscv/pr120553-8.c diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index a0c6575e4e4..1181b44ff3e 100644 --- a/gcc/ifcvt.cc +++ b/gcc/ifcvt.cc @@ -1179,6 +1179,207 @@ noce_try_move (struct noce_if_info *if_info) return false; } +/* If a sign bit test is selecting across constants, we may be able + to generate efficient code utilizing the -1/0 result of a sign + bit splat idiom. + + Do this before trying the generalized conditional move as these + (when applicable) are hopefully faster than a conditional move. */ + +static bool +noce_try_sign_bit_splat (struct noce_if_info *if_info) +{ + rtx cond = if_info->cond; + enum rtx_code code = GET_CODE (cond); + + /* We're looking for sign bit tests, so only a few cases are + interesting. LT/GE 0 LE/GT -1. */ + if (((code == LT || code == GE) + && XEXP (cond, 1) == CONST0_RTX (GET_MODE (cond))) + || ((code == LE || code == GT) + && XEXP (cond, 1) == CONSTM1_RTX (GET_MODE (cond)))) + ; + else + return false; + + /* It would be good if this could be extended since constant synthesis + on some platforms will result in blocks which fail this test. */ + if (!noce_simple_bbs (if_info)) + return false; + + /* Only try this for constants in the true/false arms and a REG + destination. We could select between 0 and a REG pretty + easily with a logical AND. */ + if (!CONST_INT_P (if_info->a) + || !CONST_INT_P (if_info->b) + || !REG_P (if_info->x)) + return false; + + machine_mode mode = GET_MODE (if_info->x); + + /* If the mode of the destination does not match the mode of + the value we're testing, then this optimization is not valid. */ + if (mode != GET_MODE (XEXP (cond, 0))) + return false; + + HOST_WIDE_INT val_a = INTVAL (if_info->a); + HOST_WIDE_INT val_b = INTVAL (if_info->b); + + rtx_insn *seq; + start_sequence (); + + /* We're testing the sign bit of this operand. */ + rtx condop = XEXP (cond, 0); + + /* To splat the sign bit we arithmetically shift the + input value right by the size of the object - 1 bits. + + Note some targets do not have strong shifters, but do have + alternative ways to generate the sign bit splat. The + profitability test when we end the sequence should reject + cases when the branchy sequence is better. */ + int splat_count = GET_MODE_BITSIZE (GET_MODE (condop)).to_constant () - 1; + rtx splat = GEN_INT (splat_count); + + rtx temp; + temp = expand_simple_binop (GET_MODE (XEXP (cond, 0)), ASHIFTRT, + XEXP (cond, 0), splat, NULL_RTX, + false, OPTAB_WIDEN); + if (!temp) + goto fail; + + /* IOR of anything with -1 still results in -1. So we can + IOR the other operand to generate a select between -1 and + an arbitrary constant. */ + if (val_a == -1) + { + if (code == LT || code == LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_b), + if_info->x, false, OPTAB_WIDEN); + } + /* AND of anything with 0 is still zero. So we can AND + with the -1 operand with the a constant to select + between the constant and zero. */ + else if (val_b == 0) + { + if (code == LT || code == LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + /* Since we know the value is currenly -1 or 0, some constants may + be more easily handled by shifting the value again. A right + logical shift constructs 2^n-1 constants a left shift constructs + ~(2^n-1) constants. Given some targets don't have efficient + shifts, generate the obvious RTL for both forms and select the + one with smaller cost. */ + rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_a)); + rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_a))); + rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (ctz_hwi (~val_a))); + bool speed_p = optimize_insn_for_speed_p (); + if (exact_log2 (val_a + 1) >= 0 + && (rtx_cost (shift_right, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, LSHIFTRT, temp, + GEN_INT (ctz_hwi (~val_a)), + if_info->x, false, OPTAB_WIDEN); + else if (exact_log2 (~val_a + 1) >= 0 + && (rtx_cost (shift_left, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, ASHIFT, temp, + GEN_INT (ctz_hwi (val_a)), + if_info->x, false, OPTAB_WIDEN); + else + temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_a), + if_info->x, false, OPTAB_WIDEN); + } + /* Same cases, but with the test or arms swapped. These + can be realized as well, though it typically costs + an extra instruction. */ + else if (val_b == -1) + { + if (code != LT && code != LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_a), + if_info->x, false, OPTAB_WIDEN); + } + else if (val_a == 0) + { + if (code != LT && code != LE) + { + temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true); + if (!temp) + goto fail; + } + + /* Since we know the value is currenly -1 or 0, some constants may + be more easily handled by shifting the value again. A right + logical shift constructs 2^n-1 constants a left shift constructs + ~(2^n-1) constants. Given some targets don't have efficient + shifts, generate the obvious RTL for both forms and select the + one with smaller cost. */ + rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_b)); + rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_b))); + rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (ctz_hwi (~val_b))); + bool speed_p = optimize_insn_for_speed_p (); + if (exact_log2 (val_b + 1) >= 0 + && (rtx_cost (shift_right, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, LSHIFTRT, temp, + GEN_INT (ctz_hwi (~val_b)), + if_info->x, false, OPTAB_WIDEN); + else if (exact_log2 (~val_b + 1) >= 0 + && (rtx_cost (shift_left, mode, SET, 1, speed_p) + <= rtx_cost (and_form, mode, SET, 1, speed_p))) + temp = expand_simple_binop (mode, ASHIFT, temp, + GEN_INT (ctz_hwi (val_b)), + if_info->x, false, OPTAB_WIDEN); + else + temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_b), + if_info->x, false, OPTAB_WIDEN); + } + /* Nothing worked. */ + else + temp = NULL_RTX; + + if (!temp) + goto fail; + + /* Move into the final destination if the value wasn't + constructed there. */ + if (if_info->x != temp) + emit_move_insn (if_info->x, temp); + + /* This ends the sequence and tests the cost model. */ + seq = end_ifcvt_sequence (if_info); + if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info)) + return false; + + /* Everything looks good. Install the if-converted sequence. */ + emit_insn_before_setloc (seq, if_info->jump, + INSN_LOCATION (if_info->insn_a)); + if_info->transform_name = "splat_sign_bit_trivial"; + return true; + + fail: + end_ifcvt_sequence (if_info); + return false; +} + + /* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE. If that is the case, emit the result into x. */ @@ -4238,6 +4439,8 @@ noce_process_if_block (struct noce_if_info *if_info) if (!targetm.have_conditional_execution () && noce_try_store_flag_constants (if_info)) goto success; + if (noce_try_sign_bit_splat (if_info)) + goto success; if (HAVE_conditional_move && noce_try_cmove (if_info)) goto success; diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-1.c b/gcc/testsuite/gcc.target/riscv/pr120553-1.c new file mode 100644 index 00000000000..95ff1d9a70a --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-1.c @@ -0,0 +1,90 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? -ONE : (ONE << N); } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? (ONE << N) : -ONE; } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* { dg-final { scan-assembler-times "\\t(srai)" 128 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 128 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 64 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 64 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-2.c b/gcc/testsuite/gcc.target/riscv/pr120553-2.c new file mode 100644 index 00000000000..1501f8654d9 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-2.c @@ -0,0 +1,90 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c >= 0 ? -ONE : (ONE << N); } \ + TYPE test2_##N (TYPE c) { return c < 0 ? (ONE << N) : -ONE; } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* { dg-final { scan-assembler-times "\\t(srai)" 128 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(orn|ori|bset)" 128 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 64 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(orn|ori|bset)" 64 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-3.c b/gcc/testsuite/gcc.target/riscv/pr120553-3.c new file mode 100644 index 00000000000..09ec7140dc7 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-3.c @@ -0,0 +1,90 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? -ONE : 0xff; } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? 0xff : -ONE; } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* { dg-final { scan-assembler-times "\\t(srai)" 128 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 128 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 64 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 64 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-4.c b/gcc/testsuite/gcc.target/riscv/pr120553-4.c new file mode 100644 index 00000000000..bc8c1b26e7b --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-4.c @@ -0,0 +1,90 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? -ONE : 0x7ff; } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? 0x7ff : -ONE; } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* { dg-final { scan-assembler-times "\\t(srai)" 128 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 128 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 64 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(ori|bset)" 64 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-5.c b/gcc/testsuite/gcc.target/riscv/pr120553-5.c new file mode 100644 index 00000000000..1e4833091cc --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-5.c @@ -0,0 +1,91 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? ~(ONE << N) : 0; } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? 0 : ~(ONE << N); } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* The MSB case isn't handled the way we want. */ +/* { dg-final { scan-assembler-times "\\t(srai)" 126 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(slli|andi|bclr)" 126 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 62 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(slli|andi|bclr)" 62 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-6.c b/gcc/testsuite/gcc.target/riscv/pr120553-6.c new file mode 100644 index 00000000000..6c409afb3c8 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-6.c @@ -0,0 +1,91 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define TYPE int +#else +#define ONE 1ULL +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c >= 0 ? ~(ONE << N) : 0; } \ + TYPE test2_##N (TYPE c) { return c < 0 ? 0 : ~(ONE << N); } \ + +T1(0) +T1(1) +T1(2) +T1(3) +T1(4) +T1(5) +T1(6) +T1(7) +T1(8) +T1(9) +T1(10) +T1(11) +T1(12) +T1(13) +T1(14) +T1(15) +T1(16) +T1(17) +T1(18) +T1(19) +T1(20) +T1(21) +T1(22) +T1(23) +T1(24) +T1(25) +T1(26) +T1(27) +T1(28) +T1(29) +T1(30) +T1(31) +#if __riscv_xlen == 64 +T1(32) +T1(33) +T1(34) +T1(35) +T1(36) +T1(37) +T1(38) +T1(39) +T1(40) +T1(41) +T1(42) +T1(43) +T1(44) +T1(45) +T1(46) +T1(47) +T1(48) +T1(49) +T1(50) +T1(51) +T1(52) +T1(53) +T1(54) +T1(55) +T1(56) +T1(57) +T1(58) +T1(59) +T1(60) +T1(61) +T1(62) +T1(63) +#endif + +/* Not working for the high bit case yet. */ +/* { dg-final { scan-assembler-times "\\t(srai)" 126 { target rv64 } } } */ +/* { dg-final { scan-assembler-times "\\t(andn|andi|bclr)" 126 { target rv64 } } } */ + +/* { dg-final { scan-assembler-times "\\t(srai)" 62 { target rv32 } } } */ +/* { dg-final { scan-assembler-times "\\t(andn|andi|bclr)" 62 { target rv32 } } } */ + diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-7.c b/gcc/testsuite/gcc.target/riscv/pr120553-7.c new file mode 100644 index 00000000000..27953f59222 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-7.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define TYPE int +#else +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? ~0xff : 0; } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? 0 : ~0xff; } \ + +T1(0) + +/* { dg-final { scan-assembler-times "\\t(srai)" 2 } } */ +/* { dg-final { scan-assembler-times "\\t(slli|andi|bclr)" 2 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/pr120553-8.c b/gcc/testsuite/gcc.target/riscv/pr120553-8.c new file mode 100644 index 00000000000..90bec45579d --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr120553-8.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb_zicond -mbranch-cost=3 -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb_zicond -mbranch-cost=3 -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define TYPE int +#else +#define TYPE long +#endif + +#define T1(N) TYPE test1_##N (TYPE c) { return c < 0 ? ~0x7ff : 0; } \ + TYPE test2_##N (TYPE c) { return c >= 0 ? 0 : ~0x7ff; } \ + +T1(0) + +/* { dg-final { scan-assembler-times "\\t(srai)" 2 } } */ +/* { dg-final { scan-assembler-times "\\t(slli|andi|bclr)" 2 } } */ -- 2.47.2