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

Reply via email to