Hi,
This implements the (cond_len_)cbranch_all/_any optabs for riscv and
adds a few tests. The patch requires a small vectorizer fix that I sent to the
mailing-list already. Without it we'll ICE when trying to legitimize operands.
Regtested on rv64gcv_zvl512b.
Regards
Robin
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.
---
gcc/config/riscv/autovec.md | 77 +++++++++++
gcc/config/riscv/predicates.md | 7 +
gcc/config/riscv/vector-iterators.md | 18 +++
.../riscv/rvv/autovec/early-break-3.c | 70 ++++++++++
.../riscv/rvv/autovec/early-break-4.c | 70 ++++++++++
.../riscv/rvv/autovec/early-break-5.c | 122 ++++++++++++++++++
6 files changed, 364 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-3.c
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-4.c
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c
diff --git a/gcc/config/riscv/autovec.md b/gcc/config/riscv/autovec.md
index c694684328f..1f3ff16ed67 100644
--- a/gcc/config/riscv/autovec.md
+++ b/gcc/config/riscv/autovec.md
@@ -2986,6 +2986,83 @@ (define_expand "cbranch<mode>4"
}
)
+;; 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 f811a4e40ca..c9405b86427 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -614,6 +614,13 @@ (define_predicate "comparison_except_ge_operator"
(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 90865a3ec51..e4f3c449637 100644
--- a/gcc/config/riscv/vector-iterators.md
+++ b/gcc/config/riscv/vector-iterators.md
@@ -121,6 +121,10 @@ (define_c_enum "unspec" [
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 @@ (define_mode_attr NDS_QUAD_FIX [
(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 00000000000..4e170ff1034
--- /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[lg][te]\.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 00000000000..4e170ff1034
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-4.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[lg][te]\.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-5.c
b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c
new file mode 100644
index 00000000000..0926d2abb4d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/early-break-5.c
@@ -0,0 +1,122 @@
+/* { dg-do run { 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
+#ifndef FMT
+#define FMT "d"
+#endif
+
+TYPE a[N] = {0};
+TYPE b[N] = {0};
+
+char *curr_test;
+
+/* Macro to define a function with a specific comparison */
+#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; \
+ } \
+ }
+
+/* Generate the six comparison functions using the macro. */
+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));
+
+ /* Condition never met — full loop executes. */
+ 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;
+}
--
2.51.1