https://gcc.gnu.org/g:427c695b66e218970417ea5f6f038604b83b12a7
commit r16-6290-g427c695b66e218970417ea5f6f038604b83b12a7 Author: Robin Dapp <[email protected]> Date: Mon Dec 8 12:18:55 2025 +0100 RISC-V: Implement cbranch_all/any. This implements the (cond_len_)cbranch_all/_any optabs for riscv and adds a few tests. The patch requires a small vectorizer fix before optabs take any effect. gcc/ChangeLog: * config/riscv/autovec.md (<cbranch_optab><mode>): Implement. * config/riscv/predicates.md (riscv_cbranch_comparison_operator): Define. * config/riscv/vector-iterators.md: New iterators. gcc/testsuite/ChangeLog: * gcc.target/riscv/rvv/autovec/early-break-3.c: New test. * gcc.target/riscv/rvv/autovec/early-break-4.c: New test. * gcc.target/riscv/rvv/autovec/early-break-5.c: New test. Diff: --- gcc/config/riscv/autovec.md | 77 ++++++++++++++ gcc/config/riscv/predicates.md | 7 ++ gcc/config/riscv/vector-iterators.md | 18 ++++ .../gcc.target/riscv/rvv/autovec/early-break-3.c | 70 ++++++++++++ .../gcc.target/riscv/rvv/autovec/early-break-4.c | 68 ++++++++++++ .../gcc.target/riscv/rvv/autovec/early-break-5.c | 117 +++++++++++++++++++++ 6 files changed, 357 insertions(+) diff --git a/gcc/config/riscv/autovec.md b/gcc/config/riscv/autovec.md index c694684328f4..1f3ff16ed678 100644 --- a/gcc/config/riscv/autovec.md +++ b/gcc/config/riscv/autovec.md @@ -2986,6 +2986,83 @@ } ) +;; Implement cond_len_vec_cbranch_any and cond_len_vec_cbranch_all +;; Vector comparison with length and mask, then branch for integer types. +(define_expand "<cbranch_optab><mode>" + [(set (pc) + (unspec:V_VLSI + [(if_then_else + (match_operator 0 "riscv_cbranch_comparison_operator" + [(match_operand:<VM> 1 "register_operand") + (match_operand:V_VLSI 2 "register_operand") + (match_operand:V_VLSI 3 "nonmemory_operand") + (match_operand 4 "autovec_length_operand") + (match_operand 5 "const_0_operand")]) + (label_ref (match_operand 6 "")) + (pc))] + COND_LEN_CBRANCH_CMP))] + "TARGET_VECTOR" +{ + rtx_code code = GET_CODE (operands[0]); + rtx mask = gen_reg_rtx (<VM>mode); + + /* Generate the masked comparison. */ + rtx maskoff = CONST0_RTX (<VM>mode); + riscv_vector::expand_vec_cmp (mask, code, operands[2], operands[3], + operands[1], maskoff); + + /* Use vcpop to count the number of active elements. */ + rtx count = gen_reg_rtx (Pmode); + rtx cpop_ops[] = {count, mask}; + riscv_vector::emit_vlmax_insn (code_for_pred_popcount (<VM>mode, Pmode), + riscv_vector::CPOP_OP, cpop_ops); + + /* Branch based on whether count is zero or non-zero. */ + riscv_expand_conditional_branch (operands[6], <cbranch_op>, count, + const0_rtx); + DONE; +}) + +;; Floating-point version with length and mask +(define_expand "<cbranch_optab><mode>" + [(set (pc) + (unspec:V_VLSF + [(if_then_else + (match_operator 0 "riscv_cbranch_comparison_operator" + [(match_operand:<VM> 1 "register_operand") + (match_operand:V_VLSF 2 "register_operand") + (match_operand:V_VLSF 3 "register_operand") + (match_operand 4 "autovec_length_operand") + (match_operand 5 "const_0_operand")]) + (label_ref (match_operand 6 "")) + (pc))] + COND_LEN_CBRANCH_CMP))] + "TARGET_VECTOR" +{ + rtx_code code = GET_CODE (operands[0]); + rtx mask = gen_reg_rtx (<VM>mode); + + rtx tmp = gen_reg_rtx (<VM>mode); + riscv_vector::expand_vec_cmp_float (tmp, code, operands[2], operands[3], + false); + + /* Combine with the incoming mask using AND. */ + rtx ops[] = {mask, operands[1], tmp}; + riscv_vector::emit_vlmax_insn (code_for_pred (AND, <VM>mode), + riscv_vector::BINARY_MASK_OP, ops); + + /* Use vcpop to count the number of active elements. */ + rtx count = gen_reg_rtx (Pmode); + rtx cpop_ops[] = {count, mask}; + riscv_vector::emit_vlmax_insn (code_for_pred_popcount (<VM>mode, Pmode), + riscv_vector::CPOP_OP, cpop_ops); + + /* Branch based on whether count is zero or non-zero. */ + riscv_expand_conditional_branch (operands[6], <cbranch_op>, count, + const0_rtx); + DONE; +}) + ;; ------------------------------------------------------------------------- ;; - vrol.vv vror.vv ;; ------------------------------------------------------------------------- diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index 5b44165ec993..3b3ae4cdcf42 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -617,6 +617,13 @@ (define_predicate "ge_operator" (match_code "ge,geu")) +(define_special_predicate "riscv_cbranch_comparison_operator" + (match_code "eq,ne,le,lt,ge,gt,geu,gtu,leu,ltu,unordered, + ordered,unlt,unle,unge,ungt") +{ + return TARGET_VECTOR; +}) + ;; pmode_reg_or_uimm5_operand can be used by vsll.vx/vsrl.vx/vsra.vx instructions ;; It is *not* equivalent to vector_length_operand due to the vector_length_operand ;; needing to conditionalize some behavior on XTHEADVECTOR. diff --git a/gcc/config/riscv/vector-iterators.md b/gcc/config/riscv/vector-iterators.md index 90865a3ec510..e4f3c4496370 100644 --- a/gcc/config/riscv/vector-iterators.md +++ b/gcc/config/riscv/vector-iterators.md @@ -121,6 +121,10 @@ UNSPEC_SF_VFNRCLIP UNSPEC_SF_VFNRCLIPU UNSPEC_SF_CV + + ;; Vector conditional branch optabs + UNSPEC_COND_LEN_CMP_ALL + UNSPEC_COND_LEN_CMP_ANY ]) (define_c_enum "unspecv" [ @@ -5154,3 +5158,17 @@ (V32DI "V32HI") (V64DI "V64HI") (V128DI "V128HI") (V256DI "V256HI") (V512DI "V512HI") ]) + +;; Vector conditional branch iterators +(define_int_iterator COND_LEN_CBRANCH_CMP + [UNSPEC_COND_LEN_CMP_ALL UNSPEC_COND_LEN_CMP_ANY]) + +(define_int_attr cbranch_op [ + (UNSPEC_COND_LEN_CMP_ALL "EQ") + (UNSPEC_COND_LEN_CMP_ANY "NE") +]) + +(define_int_attr cbranch_optab [ + (UNSPEC_COND_LEN_CMP_ALL "cond_len_vec_cbranch_all") + (UNSPEC_COND_LEN_CMP_ANY "cond_len_vec_cbranch_any") +]) diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-3.c new file mode 100644 index 000000000000..8fff924c28e0 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-3.c @@ -0,0 +1,70 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O3 -march=rv64gcv -mabi=lp64d -ftree-vectorize -std=gnu99" } */ +/* { dg-final { scan-assembler-times {vms[lgen][teq]\.v[vxi]} 6 } } */ +/* { dg-final { scan-assembler-times {vcpop\.m} 6 } } */ + +#define N 640 +int a[N] = {0}; +int b[N] = {0}; + +/* Should all generate compare + vcpop + branch. */ + +void f1 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] > 0) + break; + } +} + +void f2 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] >= 0) + break; + } +} + +void f3 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] == 0) + break; + } +} + +void f4 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] != 0) + break; + } +} + +void f5 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] < 0) + break; + } +} + +void f6 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] <= 0) + break; + } +} diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-4.c new file mode 100644 index 000000000000..2549778713c5 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-4.c @@ -0,0 +1,68 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O3 -march=rv64gcv -mabi=lp64d -ftree-vectorize -std=gnu99" } */ +/* { dg-final { scan-assembler-times {vms[lgen][teq]\.v[vxi]} 6 } } */ +/* { dg-final { scan-assembler-times {vcpop\.m} 6 } } */ + +#define N 640 +int a[N] = {0}; +int b[N] = {0}; + +void f1 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] > 0) + break; + } +} + +void f2 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] >= 0) + break; + } +} + +void f3 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] == 0) + break; + } +} + +void f4 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] != 0) + break; + } +} + +void f5 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] < 0) + break; + } +} + +void f6 () +{ + for (int i = 0; i < N; i++) + { + b[i] += a[i]; + if (a[i] <= 0) + break; + } +} diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c new file mode 100644 index 000000000000..1547914d8b91 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c @@ -0,0 +1,117 @@ +/* { dg-do run } */ +/* { dg-require-effective-target riscv_v_ok } */ +/* { dg-additional-options "-O3 -march=rv64gcv -mabi=lp64d -std=gnu99" } */ + +#include <stdio.h> + +#define N 640 +#ifndef TYPE +#define TYPE int +#endif + +TYPE a[N] = {0}; +TYPE b[N] = {0}; + +char *curr_test; + +#define DEFINE_TEST_FUNC(NAME, OP) \ + __attribute__((noipa)) \ + void NAME(void) { \ + for (int i = 0; i < N; i++) { \ + b[i] += a[i]; \ + if (a[i] OP 0) \ + break; \ + } \ + } + +DEFINE_TEST_FUNC(f1, >) +DEFINE_TEST_FUNC(f2, >=) +DEFINE_TEST_FUNC(f3, ==) +DEFINE_TEST_FUNC(f4, !=) +DEFINE_TEST_FUNC(f5, <) +DEFINE_TEST_FUNC(f6, <=) + +/* Array setup macro. */ +#define RESET_ARRAYS(_aval, _idx, _force, _bval) \ + do { \ + _Pragma("GCC novector") \ + for (int i = 0; i < N; ++i) { \ + a[i] = _aval; \ + b[i] = _bval; \ + } \ + if (_idx >= 0 && _idx < N) \ + a[_idx] = _force; \ + } while (0) + +/* Value check macros. */ +#define CHECK_EQ(_i, _val) \ + do { \ + if (b[_i] != _val) \ + __builtin_abort (); \ + } while (0) + +#define CHECK_RANGE_EQ(_start, _end, _val) \ + do { \ + _Pragma("GCC novector") \ + for (int i = _start; i < _end; ++i) \ + if (b[i] != _val) \ + __builtin_abort (); \ + } while (0) + +#define str(s) #s +#define TEST_FUNC(_func, _aval, _idx, _force, _bval, _check_stmt) \ + do { \ + curr_test = str (_func); \ + RESET_ARRAYS((_aval), (_idx), (_force), (_bval)); \ + _func(); \ + _check_stmt; \ + } while (0) + +int main(void) { + /* Break on random intervals. */ + TEST_FUNC (f1, 1, 0, 1, 10, CHECK_EQ (0, 11); CHECK_EQ (1, 10)); + TEST_FUNC (f2, -1, 5, 0, 10, CHECK_EQ (0, 9); CHECK_EQ (5, 10)); + TEST_FUNC (f3, 3, 3, 0, 0, CHECK_EQ (0, 3); CHECK_EQ (3, 0)); + TEST_FUNC (f4, 0, 4, 1, 1, CHECK_EQ (4, 2); CHECK_EQ (5, 1)); + TEST_FUNC (f5, 1, 6, -1, 5, CHECK_EQ (6, 4); CHECK_EQ (7, 5)); + TEST_FUNC (f6, 2, 10, 0, 7, CHECK_EQ (10, 7); CHECK_EQ (11, 7)); + + /* Break on last iteration. */ + TEST_FUNC (f1, 0, N-1, 1, 1, + CHECK_RANGE_EQ (0, N-1, 1); CHECK_EQ (N-1, 2)); + + TEST_FUNC (f2, -5, N-1, 0, 9, + CHECK_RANGE_EQ (0, N-1, 4); CHECK_EQ (N-1, 9)); + + TEST_FUNC (f3, 2, N-1, 0, 0, + CHECK_RANGE_EQ(0, N-1, 2); CHECK_EQ (N-1, 0)); + + TEST_FUNC (f4, 0, N-1, 2, 1, + CHECK_RANGE_EQ (0, N-1, 1); CHECK_EQ (N-1, 3)); + + TEST_FUNC (f5, 2, N-1, -3, 6, + CHECK_RANGE_EQ (0, N-1, 8); CHECK_EQ (N-1, 3)); + + TEST_FUNC (f6, 5, N-1, 0, 7, + CHECK_RANGE_EQ (0, N-1, 12); CHECK_EQ (N-1, 7)); + + TEST_FUNC (f1, 0, -1, 0, 2, + CHECK_RANGE_EQ (0, N, 2)); + + TEST_FUNC (f2, -2, -1, 0, 5, + CHECK_RANGE_EQ (0, N, 3)); + + TEST_FUNC (f3, 1, -1, 0, 0, + CHECK_RANGE_EQ (0, N, 1)); + + TEST_FUNC (f4, 0, -1, 0, 7, + CHECK_RANGE_EQ (0, N, 7)); + + TEST_FUNC (f5, 1, -1, 0, 4, + CHECK_RANGE_EQ (0, N, 5)); + + TEST_FUNC (f6, 5, -1, 0, 3, + CHECK_RANGE_EQ (0, N, 8)); + + return 0; +}
