From: Nicholas Piggin <npiggin@gmail.com>
To: qemu-riscv@nongnu.org
Cc: Nicholas Piggin <npiggin@gmail.com>,
Palmer Dabbelt <palmer@dabbelt.com>,
Alistair Francis <alistair.francis@wdc.com>,
Weiwei Li <liwei1518@gmail.com>,
Daniel Henrique Barboza <dbarboza@ventanamicro.com>,
Liu Zhiwei <zhiwei_liu@linux.alibaba.com>,
qemu-devel@nongnu.org, Chao Liu <chao.liu@zevorn.cn>
Subject: [PATCH 3/3] tests/tcg: Add riscv test for interrupted vector ops
Date: Wed, 3 Sep 2025 13:01:13 +1000 [thread overview]
Message-ID: <20250903030114.274535-4-npiggin@gmail.com> (raw)
In-Reply-To: <20250903030114.274535-1-npiggin@gmail.com>
riscv vector instructions can be interrupted with a trap, and partial
completion is recorded in the vstart register. Some causes are
implementation dependent, for example an asynchronous interrupt (which I
don't think TCG allows). Others are architectural, typically memory
access faults on vector load/store instructions.
Add some TCG tests for interrupting vector load instructions and
resuming partially completed ones.
This would have caught a recent (now reverted) regression in vector
stride load implementation, commit 28c12c1f2f50d ("Generate strided
vector loads/stores with tcg nodes.")
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
tests/tcg/riscv64/Makefile.target | 5 +
tests/tcg/riscv64/test-interrupted-v.c | 208 +++++++++++++++++++++++++
2 files changed, 213 insertions(+)
create mode 100644 tests/tcg/riscv64/test-interrupted-v.c
diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
index 19a49b6467..8f4690ac57 100644
--- a/tests/tcg/riscv64/Makefile.target
+++ b/tests/tcg/riscv64/Makefile.target
@@ -23,3 +23,8 @@ run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,zfa=true
TESTS += test-vstart-overflow
test-vstart-overflow: CFLAGS += -march=rv64gcv
run-test-vstart-overflow: QEMU_OPTS += -cpu rv64,v=on
+
+# Test for interrupted vector instructions
+TESTS += test-interrupted-v
+test-interrupted-v: CFLAGS += -march=rv64gcv
+run-test-interrupted-v: QEMU_OPTS += -cpu rv64,v=on
diff --git a/tests/tcg/riscv64/test-interrupted-v.c b/tests/tcg/riscv64/test-interrupted-v.c
new file mode 100644
index 0000000000..db4fb6092f
--- /dev/null
+++ b/tests/tcg/riscv64/test-interrupted-v.c
@@ -0,0 +1,208 @@
+/*
+ * Test for interrupted vector operations.
+ *
+ * Some vector instructions can be interrupted partially complete, vstart will
+ * be set to where the operation has progressed to, and the instruction can be
+ * re-executed with vstart != 0. It is implementation dependent as to what
+ * instructions can be interrupted and what vstart values are permitted when
+ * executing them. Vector memory operations can typically be interrupted
+ * (as they can take page faults), so these are easy to test.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+#include <riscv_vector.h>
+
+static unsigned long page_size;
+
+static volatile int nr_segv;
+static volatile unsigned long fault_start, fault_end;
+
+/*
+ * Careful: qemu-user does not save/restore vector state in
+ * signals yet, so any library or compiler autovec code will
+ * corrupt our test.
+ *
+ * Do only minimal work in the signal handler.
+ */
+static void SEGV_handler(int signo, siginfo_t *info, void *context)
+{
+ unsigned long page = (unsigned long)info->si_addr &
+ ~(unsigned long)(page_size - 1);
+
+ assert((unsigned long)info->si_addr >= fault_start);
+ assert((unsigned long)info->si_addr < fault_end);
+ mprotect((void *)page, page_size, PROT_READ);
+ nr_segv++;
+}
+
+/* Use noinline to make generated code easier to inspect */
+static __attribute__((noinline))
+uint8_t unit_load(uint8_t *mem, size_t nr, bool ff)
+{
+ size_t vl;
+ vuint8m1_t vec, redvec, sum;
+
+ vl = __riscv_vsetvl_e8m1(nr);
+ if (ff) {
+ vec = __riscv_vle8ff_v_u8m1(mem, &vl, vl);
+ } else {
+ vec = __riscv_vle8_v_u8m1(mem, vl);
+ }
+ redvec = __riscv_vmv_v_x_u8m1(0, vl);
+ sum = __riscv_vredsum_vs_u8m1_u8m1(vec, redvec, vl);
+ return __riscv_vmv_x_s_u8m1_u8(sum);
+}
+
+static __attribute__((noinline))
+uint8_t seg2_load(uint8_t *mem, size_t nr, bool ff)
+{
+ size_t vl;
+ vuint8m1x2_t segvec;
+ vuint8m1_t vec, redvec, sum;
+
+ vl = __riscv_vsetvl_e8m1(nr);
+ if (ff) {
+ segvec = __riscv_vlseg2e8ff_v_u8m1x2(mem, &vl, vl);
+ } else {
+ segvec = __riscv_vlseg2e8_v_u8m1x2(mem, vl);
+ }
+ vec = __riscv_vadd_vv_u8m1(__riscv_vget_v_u8m1x2_u8m1(segvec, 0),
+ __riscv_vget_v_u8m1x2_u8m1(segvec, 1), vl);
+ redvec = __riscv_vmv_v_x_u8m1(0, vl);
+ sum = __riscv_vredsum_vs_u8m1_u8m1(vec, redvec, vl);
+ return __riscv_vmv_x_s_u8m1_u8(sum);
+}
+
+static __attribute__((noinline))
+uint8_t strided_load(uint8_t *mem, size_t nr, size_t stride)
+{
+ size_t vl;
+ vuint8m1_t vec, redvec, sum;
+
+ vl = __riscv_vsetvl_e8m1(nr);
+ vec = __riscv_vlse8_v_u8m1(mem, stride, vl);
+ redvec = __riscv_vmv_v_x_u8m1(0, vl);
+ sum = __riscv_vredsum_vs_u8m1_u8m1(vec, redvec, vl);
+ return __riscv_vmv_x_s_u8m1_u8(sum);
+}
+
+static __attribute__((noinline))
+uint8_t indexed_load(uint8_t *mem, size_t nr, uint32_t *indices)
+{
+ size_t vl;
+ vuint32m4_t idx;
+ vuint8m1_t vec, redvec, sum;
+
+ vl = __riscv_vsetvl_e8m1(nr);
+ idx = __riscv_vle32_v_u32m4(indices, vl);
+ vec = __riscv_vloxei32_v_u8m1(mem, idx, vl);
+ redvec = __riscv_vmv_v_x_u8m1(0, vl);
+ sum = __riscv_vredsum_vs_u8m1_u8m1(vec, redvec, vl);
+ return __riscv_vmv_x_s_u8m1_u8(sum);
+}
+
+/* Use e8 elements, 128-bit vectors */
+#define NR_ELEMS 16
+
+static int run_interrupted_v_tests(void)
+{
+ struct sigaction act = { 0 };
+ uint8_t *mem;
+ uint32_t indices[NR_ELEMS];
+ int i;
+
+ page_size = sysconf(_SC_PAGESIZE);
+
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = &SEGV_handler;
+ if (sigaction(SIGSEGV, &act, NULL) == -1) {
+ perror("sigaction");
+ exit(EXIT_FAILURE);
+ }
+
+ mem = mmap(NULL, NR_ELEMS * page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(mem != MAP_FAILED);
+ madvise(mem, NR_ELEMS * page_size, MADV_NOHUGEPAGE);
+
+ /* Unit-stride tests load memory crossing a page boundary */
+ memset(mem, 0, NR_ELEMS * page_size);
+ for (i = 0; i < NR_ELEMS; i++) {
+ mem[page_size - NR_ELEMS + i] = 3;
+ }
+ for (i = 0; i < NR_ELEMS; i++) {
+ mem[page_size + i] = 5;
+ }
+
+ nr_segv = 0;
+ fault_start = (unsigned long)&mem[page_size - (NR_ELEMS / 2)];
+ fault_end = fault_start + NR_ELEMS;
+ mprotect(mem, page_size * 2, PROT_NONE);
+ assert(unit_load(&mem[page_size - (NR_ELEMS / 2)], NR_ELEMS, false)
+ == 8 * NR_ELEMS / 2);
+ assert(nr_segv == 2);
+
+ nr_segv = 0;
+ fault_start = (unsigned long)&mem[page_size - NR_ELEMS];
+ fault_end = fault_start + NR_ELEMS * 2;
+ mprotect(mem, page_size * 2, PROT_NONE);
+ assert(seg2_load(&mem[page_size - NR_ELEMS], NR_ELEMS, false)
+ == 8 * NR_ELEMS);
+ assert(nr_segv == 2);
+
+ nr_segv = 0;
+ fault_start = (unsigned long)&mem[page_size - (NR_ELEMS / 2)];
+ fault_end = fault_start + (NR_ELEMS / 2);
+ mprotect(mem, page_size * 2, PROT_NONE);
+ assert(unit_load(&mem[page_size - (NR_ELEMS / 2)], NR_ELEMS, true)
+ == 3 * NR_ELEMS / 2);
+ assert(nr_segv == 1); /* fault-first does not fault the second page */
+
+ nr_segv = 0;
+ fault_start = (unsigned long)&mem[page_size - NR_ELEMS];
+ fault_end = fault_start + NR_ELEMS;
+ mprotect(mem, page_size * 2, PROT_NONE);
+ assert(seg2_load(&mem[page_size - NR_ELEMS], NR_ELEMS * 2, true)
+ == 3 * NR_ELEMS);
+ assert(nr_segv == 1); /* fault-first does not fault the second page */
+
+ /* Following tests load one element from first byte of each page */
+ mprotect(mem, page_size * 2, PROT_READ | PROT_WRITE);
+ memset(mem, 0, NR_ELEMS * page_size);
+ for (i = 0; i < NR_ELEMS; i++) {
+ mem[i * page_size] = 3;
+ indices[i] = i * page_size;
+ }
+
+ nr_segv = 0;
+ fault_start = (unsigned long)mem;
+ fault_end = fault_start + NR_ELEMS * page_size;
+ mprotect(mem, NR_ELEMS * page_size, PROT_NONE);
+ assert(strided_load(mem, NR_ELEMS, page_size) == 3 * NR_ELEMS);
+ assert(nr_segv == NR_ELEMS);
+
+ nr_segv = 0;
+ fault_start = (unsigned long)mem;
+ fault_end = fault_start + NR_ELEMS * page_size;
+ mprotect(mem, NR_ELEMS * page_size, PROT_NONE);
+ assert(indexed_load(mem, NR_ELEMS, indices) == 3 * NR_ELEMS);
+ assert(nr_segv == NR_ELEMS);
+
+ munmap(mem, NR_ELEMS * page_size);
+
+ return 0;
+}
+
+int main(void)
+{
+ return run_interrupted_v_tests();
+}
--
2.51.0
prev parent reply other threads:[~2025-09-03 3:03 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-03 3:01 [PATCH 0/3] target/riscv: corner case fixes Nicholas Piggin
2025-09-03 3:01 ` [PATCH 1/3] target/riscv: Fix IALIGN check in misa write Nicholas Piggin
2025-09-03 17:19 ` Daniel Henrique Barboza
2025-09-03 3:01 ` [PATCH 2/3] target/risvc: Fix vector whole ldst vstart check Nicholas Piggin
2025-09-03 20:13 ` Daniel Henrique Barboza
2025-09-04 5:16 ` Nicholas Piggin
2025-09-04 11:06 ` Daniel Henrique Barboza
2025-09-05 7:18 ` Richard Henderson
2025-09-17 13:44 ` Joel Stanley
2025-09-17 13:53 ` Daniel P. Berrangé
2025-09-03 3:01 ` Nicholas Piggin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250903030114.274535-4-npiggin@gmail.com \
--to=npiggin@gmail.com \
--cc=alistair.francis@wdc.com \
--cc=chao.liu@zevorn.cn \
--cc=dbarboza@ventanamicro.com \
--cc=liwei1518@gmail.com \
--cc=palmer@dabbelt.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.