target/riscv/tcg/vector_helper.c | 23 ++++++---- tests/tcg/riscv64/Makefile.softmmu-target | 5 ++ tests/tcg/riscv64/test-vle32ff.S | 56 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 tests/tcg/riscv64/test-vle32ff.S
A unit-stride fault-only-first load must leave vl unchanged when element zero raises a synchronous exception. Keep the shortened value in a local bound until all loads complete, and only then update the architectural vl.
Add a bare-metal RVV regression test which faults vle32ff.v on element zero and checks that vl remains unchanged in the trap handler.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3544
Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
---
target/riscv/tcg/vector_helper.c | 23 ++++++----
tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
tests/tcg/riscv64/test-vle32ff.S | 56 +++++++++++++++++++++++
3 files changed, 75 insertions(+), 9 deletions(-)
create mode 100644 tests/tcg/riscv64/test-vle32ff.S
diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c
index e321ca2616..5a310822b7 100644
--- a/target/riscv/tcg/vector_helper.c
+++ b/target/riscv/tcg/vector_helper.c
@@ -686,7 +686,7 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env,
uint32_t desc, vext_ldst_elem_fn_tlb *ldst_tlb,
vext_ldst_elem_fn_host *ldst_host, uint32_t log2_esz, uintptr_t ra)
{
- uint32_t i, k, vl = 0;
+ uint32_t i, k, vl = 0, load_vl;
uint32_t nf = vext_nf(desc);
uint32_t vm = vext_vm(desc);
uint32_t max_elems = vext_max_elems(desc, log2_esz);
@@ -752,22 +752,24 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env,
}
}
ProbeSuccess:
- /* load bytes from guest memory */
- if (vl != 0) {
- env->vl = vl;
- }
+ /*
+ * Keep a shortened vl local until all loads complete. In particular,
+ * an exception from element zero must leave the architectural vl alone.
+ */
+ load_vl = vl ? vl : env->vl;
- if (env->vstart < env->vl) {
+ if (env->vstart < load_vl) {
if (vm) {
/* Load/store elements in the first page */
if (likely(elems)) {
+ elems = MIN(elems, load_vl - env->vstart);
vext_page_ldst_us(env, vd, addr, elems, nf, max_elems,
log2_esz, true, mmu_index, ldst_tlb,
ldst_host, ra);
}
/* Load/store elements in the second page */
- if (unlikely(env->vstart < env->vl)) {
+ if (unlikely(env->vstart < load_vl)) {
/* Cross page element */
if (unlikely(page_split % msize)) {
for (k = 0; k < nf; k++) {
@@ -780,7 +782,7 @@ ProbeSuccess:
addr = base + ((env->vstart * nf) << log2_esz);
/* Get number of elements of second page */
- elems = env->vl - env->vstart;
+ elems = load_vl - env->vstart;
/* Load/store elements in the second page */
vext_page_ldst_us(env, vd, addr, elems, nf, max_elems,
@@ -788,7 +790,7 @@ ProbeSuccess:
ldst_host, ra);
}
} else {
- for (i = env->vstart; i < env->vl; i++) {
+ for (i = env->vstart; i < load_vl; i++) {
k = 0;
while (k < nf) {
if (!vext_elem_mask(v0, i)) {
@@ -806,6 +808,9 @@ ProbeSuccess:
}
}
}
+ if (vl != 0) {
+ env->vl = vl;
+ }
env->vstart = 0;
vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems);
diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index 82be8a2c91..7ae225bd73 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -41,5 +41,10 @@ comma:= ,
run-test-crc32: test-crc32
$(call run-test, $<, $(QEMU) -cpu rv64$(comma)xlrbr=true $(QEMU_OPTS)$<)
+EXTRA_RUNS += run-test-vle32ff
+run-test-vle32ff: test-vle32ff
+ $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true $(QEMU_OPTS)$<)
+test-vle32ff: CFLAGS += -march=rv64gcv
+
# We don't currently support the multiarch system tests
undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-vle32ff.S b/tests/tcg/riscv64/test-vle32ff.S
new file mode 100644
index 0000000000..4c687bf1e4
--- /dev/null
+++ b/tests/tcg/riscv64/test-vle32ff.S
@@ -0,0 +1,56 @@
+/*
+ * Verify that a fault-only-first load keeps vl unchanged when element zero
+ * raises a synchronous exception.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ .option norvc
+
+ .text
+ .globl _start
+_start:
+ lla t0, trap
+ csrw mtvec, t0
+
+ li t1, 4
+ vsetvli t2, t1, e32, m1, ta, ma
+ li t0, 1
+ vle32ff.v v1, (t0)
+
+ /* Element zero did not trap. */
+ li a0, 1
+ j _exit
+
+trap:
+ csrr t0, mcause
+ li t1, 5 /* Load access fault. */
+ bne t0, t1, trap_fail
+
+ csrr t0, vl
+ li t1, 4
+ bne t0, t1, trap_fail
+
+ li a0, 0
+ j _exit
+
+trap_fail:
+ li a0, 1
+
+_exit:
+ lla a1, semiargs
+ li t0, 0x20026 /* ADP_Stopped_ApplicationExit */
+ sd t0, 0(a1)
+ sd a0, 8(a1)
+ li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */
+
+ .balign 16
+ slli zero, zero, 0x1f
+ ebreak
+ srai zero, zero, 0x7
+ j .
+
+ .data
+ .balign 8
+semiargs:
+ .space 16
© 2016 - 2026 Red Hat, Inc.