[gcc r17-3247] RISC-V: Add test cases for vwmacc.vv reg overlap

Pan Li via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:55afdf36b7a2acd881c93485ce2af6d1cf006e18

commit r17-3247-g55afdf36b7a2acd881c93485ce2af6d1cf006e18
Author: Pan Li <[email protected]>
Date:   Wed Aug 12 10:42:17 2026 +0800

    RISC-V: Add test cases for vwmacc.vv reg overlap
    
    Add test cases for register group overlap, please
    note it is not overlap as much as possible.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/rvv/autovec/group_overlap/group_overlap.h:
            Add test helper macros.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m1.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m4.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf4.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m1.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m4.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-mf2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m1.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m4.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf2.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf4.c: New test.
            * gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf8.c: New test.
    
    Signed-off-by: Pan Li <[email protected]>

Diff:
---
 .../rvv/autovec/group_overlap/group_overlap.h      | 230 +++++++++++++++++++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i16-m1.c   |  57 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i16-m2.c   |  49 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i16-m4.c   |  45 ++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i16-mf2.c  |  21 ++
 .../rvv/autovec/group_overlap/vwmacc_vv-i16-mf4.c  |  21 ++
 .../rvv/autovec/group_overlap/vwmacc_vv-i32-m1.c   |  57 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i32-m2.c   |  49 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i32-m4.c   |  45 ++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i32-mf2.c  |  21 ++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-m1.c    |  57 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-m2.c    |  49 +++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-m4.c    |  45 ++++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-mf2.c   |  21 ++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-mf4.c   |  21 ++
 .../rvv/autovec/group_overlap/vwmacc_vv-i8-mf8.c   |  21 ++
 16 files changed, 809 insertions(+)

diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/group_overlap.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/group_overlap.h
index 9d6e5f2b8049..ac2abed02ba8 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/group_overlap.h
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/group_overlap.h
@@ -533,6 +533,205 @@
     ST_F ((void *)out, vd14, VL); OUT += VL;                                \
     ST_F ((void *)out, vd15, VL); OUT += VL;                                \
 
+#define LOOP_DUAL_WIDEN_TERNARY_BODY_X4(NT, WT, LD_NF, LD_WF, OUT_F, ST_F,  \
+					 OUT, START, VL)                    \
+    NT vs0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs3 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt3 = LD_NF ((void *)START, VL); START += VL;                        \
+    WT vw0 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw1 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw2 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw3 = LD_WF ((void *)START, VL); START += VL;                        \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    WT vd0 = OUT_F (vw0, vs0, vt0, VL);                                     \
+    WT vd1 = OUT_F (vw1, vs1, vt1, VL);                                     \
+    WT vd2 = OUT_F (vw2, vs2, vt2, VL);                                     \
+    WT vd3 = OUT_F (vw3, vs3, vt3, VL);                                     \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    ST_F ((void *)out, vd0, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd1, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd2, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd3, VL); OUT += VL;                                 \
+
+#define LOOP_DUAL_WIDEN_TERNARY_BODY_X8(NT, WT, LD_NF, LD_WF, OUT_F, ST_F,  \
+					 OUT, START, VL)                    \
+    NT vs0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs3 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs4 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs5 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs6 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs7 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt3 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt4 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt5 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt6 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt7 = LD_NF ((void *)START, VL); START += VL;                        \
+    WT vw0 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw1 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw2 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw3 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw4 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw5 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw6 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw7 = LD_WF ((void *)START, VL); START += VL;                        \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    WT vd0 = OUT_F (vw0, vs0, vt0, VL);                                     \
+    WT vd1 = OUT_F (vw1, vs1, vt1, VL);                                     \
+    WT vd2 = OUT_F (vw2, vs2, vt2, VL);                                     \
+    WT vd3 = OUT_F (vw3, vs3, vt3, VL);                                     \
+    WT vd4 = OUT_F (vw4, vs4, vt4, VL);                                     \
+    WT vd5 = OUT_F (vw5, vs5, vt5, VL);                                     \
+    WT vd6 = OUT_F (vw6, vs6, vt6, VL);                                     \
+    WT vd7 = OUT_F (vw7, vs7, vt7, VL);                                     \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    ST_F ((void *)out, vd0, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd1, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd2, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd3, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd4, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd5, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd6, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd7, VL); OUT += VL;                                 \
+
+#define LOOP_DUAL_WIDEN_TERNARY_BODY_X16(NT, WT, LD_NF, LD_WF, OUT_F, ST_F, \
+					 OUT, START, VL)                    \
+    NT vs0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs3 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs4 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs5 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs6 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs7 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs8 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs9 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vs10 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vs11 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vs12 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vs13 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vs14 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vs15 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt1 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt2 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt3 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt4 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt5 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt6 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt7 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt8 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt9 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt10 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt11 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt12 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt13 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt14 = LD_NF ((void *)START, VL); START += VL;                       \
+    NT vt15 = LD_NF ((void *)START, VL); START += VL;                       \
+    WT vw0 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw1 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw2 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw3 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw4 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw5 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw6 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw7 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw8 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw9 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw10 = LD_WF ((void *)START, VL); START += VL;                       \
+    WT vw11 = LD_WF ((void *)START, VL); START += VL;                       \
+    WT vw12 = LD_WF ((void *)START, VL); START += VL;                       \
+    WT vw13 = LD_WF ((void *)START, VL); START += VL;                       \
+    WT vw14 = LD_WF ((void *)START, VL); START += VL;                       \
+    WT vw15 = LD_WF ((void *)START, VL); START += VL;                       \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    WT vd0 = OUT_F (vw0, vs0, vt0, VL);                                     \
+    WT vd1 = OUT_F (vw1, vs1, vt1, VL);                                     \
+    WT vd2 = OUT_F (vw2, vs2, vt2, VL);                                     \
+    WT vd3 = OUT_F (vw3, vs3, vt3, VL);                                     \
+    WT vd4 = OUT_F (vw4, vs4, vt4, VL);                                     \
+    WT vd5 = OUT_F (vw5, vs5, vt5, VL);                                     \
+    WT vd6 = OUT_F (vw6, vs6, vt6, VL);                                     \
+    WT vd7 = OUT_F (vw7, vs7, vt7, VL);                                     \
+    WT vd8 = OUT_F (vw8, vs8, vt8, VL);                                     \
+    WT vd9 = OUT_F (vw9, vs9, vt9, VL);                                     \
+    WT vd10 = OUT_F (vw10, vs10, vt10, VL);                                 \
+    WT vd11 = OUT_F (vw11, vs11, vt11, VL);                                 \
+    WT vd12 = OUT_F (vw12, vs12, vt12, VL);                                 \
+    WT vd13 = OUT_F (vw13, vs13, vt13, VL);                                 \
+    WT vd14 = OUT_F (vw14, vs14, vt14, VL);                                 \
+    WT vd15 = OUT_F (vw15, vs15, vt15, VL);                                 \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    ST_F ((void *)out, vd0, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd1, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd2, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd3, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd4, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd5, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd6, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd7, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd8, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd9, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd10, VL); OUT += VL;                                \
+    ST_F ((void *)out, vd11, VL); OUT += VL;                                \
+    ST_F ((void *)out, vd12, VL); OUT += VL;                                \
+    ST_F ((void *)out, vd13, VL); OUT += VL;                                \
+    ST_F ((void *)out, vd14, VL); OUT += VL;                                \
+    ST_F ((void *)out, vd15, VL); OUT += VL;                                \
+
+/* The widened destination register group of a dual widen ternary insn is tied
+   to the accumulator, which is still live when the narrowed sources are read.
+   Feeding one narrowed source from the highest-numbered half of the
+   accumulator's own register group is thus the only way the sources can
+   legally overlap the destination register group: the shared registers hold
+   one single value, seen as the accumulator's high part and as the narrowed
+   source at the same time.  RI_F reinterprets the widened type as the
+   narrowed element type, GET_F extracts the highest-numbered half of it.  */
+#define LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2(NT, WT, WNT, LD_NF, LD_WF,   \
+						 RI_F, GET_F, OUT_F, ST_F,  \
+						 OUT, START, VL)            \
+    NT vt0 = LD_NF ((void *)START, VL); START += VL;                        \
+    NT vt1 = LD_NF ((void *)START, VL); START += VL;                        \
+    WT vw0 = LD_WF ((void *)START, VL); START += VL;                        \
+    WT vw1 = LD_WF ((void *)START, VL); START += VL;                        \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    WNT vr0 = RI_F (vw0);                                                   \
+    WNT vr1 = RI_F (vw1);                                                   \
+                                                                            \
+    NT vs0 = GET_F (vr0, 1);                                                \
+    NT vs1 = GET_F (vr1, 1);                                                \
+                                                                            \
+    WT vd0 = OUT_F (vw0, vs0, vt0, VL);                                     \
+    WT vd1 = OUT_F (vw1, vs1, vt1, VL);                                     \
+                                                                            \
+    asm volatile("nop" ::: "memory");                                       \
+                                                                            \
+    ST_F ((void *)out, vd0, VL); OUT += VL;                                 \
+    ST_F ((void *)out, vd1, VL); OUT += VL;                                 \
+
 #define DEF_GROUP_OVERLAP_UNARY_0(VL_F, NT, WT, LD_F, OUT_F, ST_F, NAME, \
 				  LOOP_BODY)                             \
   void test_group_overlap_##NAME##_##NT##_unary_0(uint8_t *data,         \
@@ -594,4 +793,35 @@
     }                                                                       \
   }
 
+#define DEF_GROUP_OVERLAP_TERNARY_0(VL_F, NT, WT, LD_NF, LD_WF, OUT_F,      \
+				    ST_F, NAME, LOOP_BODY)                  \
+  void test_group_overlap_##NAME##_##NT##_ternary_0(uint8_t *data,          \
+						    uint8_t *out,           \
+						    size_t limit)           \
+  {                                                                         \
+    uint8_t *start = data;                                                  \
+    uint8_t *end = data + limit;                                            \
+    size_t vl = VL_F ();                                                    \
+                                                                            \
+    while (start < end) {                                                   \
+      LOOP_BODY (NT, WT, LD_NF, LD_WF, OUT_F, ST_F, out, start, vl);        \
+    }                                                                       \
+  }
+
+#define DEF_GROUP_OVERLAP_TERNARY_1(VL_F, NT, WT, WNT, LD_NF, LD_WF, RI_F,  \
+				    GET_F, OUT_F, ST_F, NAME, LOOP_BODY)    \
+  void test_group_overlap_##NAME##_##NT##_ternary_1(uint8_t *data,          \
+						    uint8_t *out,           \
+						    size_t limit)           \
+  {                                                                         \
+    uint8_t *start = data;                                                  \
+    uint8_t *end = data + limit;                                            \
+    size_t vl = VL_F ();                                                    \
+                                                                            \
+    while (start < end) {                                                   \
+      LOOP_BODY (NT, WT, WNT, LD_NF, LD_WF, RI_F, GET_F, OUT_F, ST_F, out,  \
+		 start, vl);                                                \
+    }                                                                       \
+  }
+
 #endif
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m1.c
new file mode 100644
index 000000000000..79f34b2aa0bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m1.c
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e16m1,
+  vint16m1_t,
+  vint32m2_t,
+  __riscv_vle16_v_i16m1,
+  __riscv_vle32_v_i32m2,
+  __riscv_vwmacc_vv_i32m2,
+  __riscv_vse32_v_i32m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e16m1,
+  vint16m1_t,
+  vint32m2_t,
+  vint16m2_t,
+  __riscv_vle16_v_i16m1,
+  __riscv_vle32_v_i32m2,
+  __riscv_vreinterpret_v_i32m2_i16m2,
+  __riscv_vget_v_i16m2_i16m1,
+  __riscv_vwmacc_vv_i32m2,
+  __riscv_vse32_v_i32m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v30,v29([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v16,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v0,v31([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v14,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v30,v28,v27([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v26,v25([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v6,v20,v19([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v26,v24,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v18,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v10,v1,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v22,v21([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v14,v19,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v21,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v18,v20,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v0,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v22,v0,v1([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v5,v8([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v3,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m2.c
new file mode 100644
index 000000000000..0d6660b5cb6e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m2.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e16m2,
+  vint16m2_t,
+  vint32m4_t,
+  __riscv_vle16_v_i16m2,
+  __riscv_vle32_v_i32m4,
+  __riscv_vwmacc_vv_i32m4,
+  __riscv_vse32_v_i32m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X8)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e16m2,
+  vint16m2_t,
+  vint32m4_t,
+  vint16m4_t,
+  __riscv_vle16_v_i16m2,
+  __riscv_vle32_v_i32m4,
+  __riscv_vreinterpret_v_i32m4_i16m4,
+  __riscv_vget_v_i16m4_i16m2,
+  __riscv_vwmacc_vv_i32m4,
+  __riscv_vse32_v_i32m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v0,v30([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v26([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v24,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v20,v18([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v16,v14([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v12,v10([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v8,v6([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v4,v2([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v10,v16([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v6,v2([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m4.c
new file mode 100644
index 000000000000..7568c6a871ab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-m4.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e16m4,
+  vint16m4_t,
+  vint32m8_t,
+  __riscv_vle16_v_i16m4,
+  __riscv_vle32_v_i32m8,
+  __riscv_vwmacc_vv_i32m8,
+  __riscv_vse32_v_i32m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X4)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e16m4,
+  vint16m4_t,
+  vint32m8_t,
+  vint16m8_t,
+  __riscv_vle16_v_i16m4,
+  __riscv_vle32_v_i32m8,
+  __riscv_vreinterpret_v_i32m8_i16m8,
+  __riscv_vget_v_i16m8_i16m4,
+  __riscv_vwmacc_vv_i32m8,
+  __riscv_vse32_v_i32m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v0,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v20([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v16,v12([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v8,v12([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v20,v0([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v12,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf2.c
new file mode 100644
index 000000000000..f90ed36ffa6d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e16m1,
+  vint16mf2_t,
+  vint32m1_t,
+  __riscv_vle16_v_i16mf2,
+  __riscv_vle32_v_i32m1,
+  __riscv_vwmacc_vv_i32m1,
+  __riscv_vse32_v_i32m1,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf4.c
new file mode 100644
index 000000000000..6f7544fd19d9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i16-mf4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e16m1,
+  vint16mf4_t,
+  vint32mf2_t,
+  __riscv_vle16_v_i16mf4,
+  __riscv_vle32_v_i32mf2,
+  __riscv_vwmacc_vv_i32mf2,
+  __riscv_vse32_v_i32mf2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m1.c
new file mode 100644
index 000000000000..bfd25e177d8a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m1.c
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e32m1,
+  vint32m1_t,
+  vint64m2_t,
+  __riscv_vle32_v_i32m1,
+  __riscv_vle64_v_i64m2,
+  __riscv_vwmacc_vv_i64m2,
+  __riscv_vse64_v_i64m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e32m1,
+  vint32m1_t,
+  vint64m2_t,
+  vint32m2_t,
+  __riscv_vle32_v_i32m1,
+  __riscv_vle64_v_i64m2,
+  __riscv_vreinterpret_v_i64m2_i32m2,
+  __riscv_vget_v_i32m2_i32m1,
+  __riscv_vwmacc_vv_i64m2,
+  __riscv_vse64_v_i64m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v30,v29([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v16,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v0,v31([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v14,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v30,v28,v27([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v26,v25([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v6,v20,v19([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v26,v24,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v18,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v10,v1,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v22,v21([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v14,v19,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v21,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v18,v20,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v0,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v22,v0,v1([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v5,v8([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v3,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m2.c
new file mode 100644
index 000000000000..a24c7410bc02
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m2.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e32m2,
+  vint32m2_t,
+  vint64m4_t,
+  __riscv_vle32_v_i32m2,
+  __riscv_vle64_v_i64m4,
+  __riscv_vwmacc_vv_i64m4,
+  __riscv_vse64_v_i64m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X8)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e32m2,
+  vint32m2_t,
+  vint64m4_t,
+  vint32m4_t,
+  __riscv_vle32_v_i32m2,
+  __riscv_vle64_v_i64m4,
+  __riscv_vreinterpret_v_i64m4_i32m4,
+  __riscv_vget_v_i32m4_i32m2,
+  __riscv_vwmacc_vv_i64m4,
+  __riscv_vse64_v_i64m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v0,v30([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v26([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v24,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v20,v18([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v16,v14([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v12,v10([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v8,v6([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v4,v2([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v10,v16([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v6,v2([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m4.c
new file mode 100644
index 000000000000..17490d331e0c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-m4.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e32m4,
+  vint32m4_t,
+  vint64m8_t,
+  __riscv_vle32_v_i32m4,
+  __riscv_vle64_v_i64m8,
+  __riscv_vwmacc_vv_i64m8,
+  __riscv_vse64_v_i64m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X4)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e32m4,
+  vint32m4_t,
+  vint64m8_t,
+  vint32m8_t,
+  __riscv_vle32_v_i32m4,
+  __riscv_vle64_v_i64m8,
+  __riscv_vreinterpret_v_i64m8_i32m8,
+  __riscv_vget_v_i32m8_i32m4,
+  __riscv_vwmacc_vv_i64m8,
+  __riscv_vse64_v_i64m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v0,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v20([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v16,v12([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v8,v12([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v20,v0([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v12,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-mf2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-mf2.c
new file mode 100644
index 000000000000..2b7c19cd5372
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i32-mf2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e32m1,
+  vint32mf2_t,
+  vint64m1_t,
+  __riscv_vle32_v_i32mf2,
+  __riscv_vle64_v_i64m1,
+  __riscv_vwmacc_vv_i64m1,
+  __riscv_vse64_v_i64m1,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m1.c
new file mode 100644
index 000000000000..a6399818a70d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m1.c
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m1,
+  vint8m1_t,
+  vint16m2_t,
+  __riscv_vle8_v_i8m1,
+  __riscv_vle16_v_i16m2,
+  __riscv_vwmacc_vv_i16m2,
+  __riscv_vse16_v_i16m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e8m1,
+  vint8m1_t,
+  vint16m2_t,
+  vint8m2_t,
+  __riscv_vle8_v_i8m1,
+  __riscv_vle16_v_i16m2,
+  __riscv_vreinterpret_v_i16m2_i8m2,
+  __riscv_vget_v_i8m2_i8m1,
+  __riscv_vwmacc_vv_i16m2,
+  __riscv_vse16_v_i16m2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v30,v29([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v16,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v0,v31([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v14,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v30,v28,v27([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v26,v25([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v6,v20,v19([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v26,v24,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v18,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v10,v1,v15([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v22,v21([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v14,v19,v17([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v21,v23([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v18,v20,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v0,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v22,v0,v1([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v5,v8([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v2,v3,v1([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m2.c
new file mode 100644
index 000000000000..a8e4628a8c5e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m2.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m2,
+  vint8m2_t,
+  vint16m4_t,
+  __riscv_vle8_v_i8m2,
+  __riscv_vle16_v_i16m4,
+  __riscv_vwmacc_vv_i16m4,
+  __riscv_vse16_v_i16m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X8)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e8m2,
+  vint8m2_t,
+  vint16m4_t,
+  vint8m4_t,
+  __riscv_vle8_v_i8m2,
+  __riscv_vle16_v_i16m4,
+  __riscv_vreinterpret_v_i16m4_i8m4,
+  __riscv_vget_v_i8m4_i8m2,
+  __riscv_vwmacc_vv_i16m4,
+  __riscv_vse16_v_i16m4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v0,v30([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v26([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v28,v24,v22([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v20,v18([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v20,v16,v14([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v12,v10([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v12,v8,v6([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v4,v2([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v10,v16([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v4,v6,v2([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m4.c
new file mode 100644
index 000000000000..67c647f4ca80
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-m4.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m4,
+  vint8m4_t,
+  vint16m8_t,
+  __riscv_vle8_v_i8m4,
+  __riscv_vle16_v_i16m8,
+  __riscv_vwmacc_vv_i16m8,
+  __riscv_vse16_v_i16m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X4)
+
+DEF_GROUP_OVERLAP_TERNARY_1(
+  __riscv_vsetvlmax_e8m4,
+  vint8m4_t,
+  vint16m8_t,
+  vint8m8_t,
+  __riscv_vle8_v_i8m4,
+  __riscv_vle16_v_i16m8,
+  __riscv_vreinterpret_v_i16m8_i8m8,
+  __riscv_vget_v_i8m8_i8m4,
+  __riscv_vwmacc_vv_i16m8,
+  __riscv_vse16_v_i16m8,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_OVERLAP_X2)
+
+/* ternary_0: the accumulator occupies the whole destination register group and
+   is live when the narrowed sources are read, so no source can be allocated
+   inside the destination register group.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v0,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v0,v28,v20([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v24,v16,v12([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v8,v12([^0-9]|$)} 1 } } */
+
+/* ternary_1: each narrowed source is the highest-numbered half of its own
+   accumulator, thus it overlaps the highest-numbered part of the destination
+   register group.  Without the group overlap the sources would have to be
+   copied out to a disjoint register group first.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v16,v20,v0([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-times {vwmacc\.vv\s+v8,v12,v4([^0-9]|$)} 1 } } */
+/* { dg-final { scan-assembler-not {vmv[0-9]+r\.v} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf2.c
new file mode 100644
index 000000000000..c0404d74b4aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m1,
+  vint8mf2_t,
+  vint16m1_t,
+  __riscv_vle8_v_i8mf2,
+  __riscv_vle16_v_i16m1,
+  __riscv_vwmacc_vv_i16m1,
+  __riscv_vse16_v_i16m1,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf4.c
new file mode 100644
index 000000000000..11bce6b79a96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m1,
+  vint8mf4_t,
+  vint16mf2_t,
+  __riscv_vle8_v_i8mf4,
+  __riscv_vle16_v_i16mf2,
+  __riscv_vwmacc_vv_i16mf2,
+  __riscv_vse16_v_i16mf2,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf8.c
new file mode 100644
index 000000000000..f0704ada8050
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/group_overlap/vwmacc_vv-i8-mf8.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d" } */
+
+#include "group_overlap.h"
+
+DEF_GROUP_OVERLAP_TERNARY_0(
+  __riscv_vsetvlmax_e8m1,
+  vint8mf8_t,
+  vint16mf4_t,
+  __riscv_vle8_v_i8mf8,
+  __riscv_vle16_v_i16mf4,
+  __riscv_vwmacc_vv_i16mf4,
+  __riscv_vse16_v_i16mf4,
+  vwmacc_vv,
+  LOOP_DUAL_WIDEN_TERNARY_BODY_X16)
+
+/* The fractional LMUL source has EMUL < 1, thus the widened destination
+   register group must not overlap either source at all.  */
+/* { dg-final { scan-assembler-times {vwmacc\.vv} 16 } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),\1,} } } */
+/* { dg-final { scan-assembler-not {vwmacc\.vv\s+(v[0-9]+),v[0-9]+,\1([^0-9]|$)} } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.