target/riscv/tcg/vector_helper.c | 23 +++++---- tests/tcg/riscv64/Makefile.softmmu-target | 5 ++ tests/tcg/riscv64/test-vle32ff.S | 57 +++++++++++++++++++++++ 3 files changed, 76 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
Reviewed-by: Chao Liu <chao.liu@processmission.com>
Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
---
Changes in v2:
- Fix commit message formatting.
- Enable vector state in the bare-metal test.
- Use an aligned unmapped address and compare against the vsetvli result.
- Express the mstatus.VS setting as a field shift.
target/riscv/tcg/vector_helper.c | 23 +++++----
tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
tests/tcg/riscv64/test-vle32ff.S | 57 +++++++++++++++++++++++
3 files changed, 76 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..f510960c05
--- /dev/null
+++ b/tests/tcg/riscv64/test-vle32ff.S
@@ -0,0 +1,57 @@
+/*
+ * 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 t0, (1 << 9) /* Set mstatus.VS to Initial. */
+ csrs mstatus, t0
+ li t1, 4
+ vsetvli t2, t1, e32, m1, ta, ma
+ li t0, 0x18000000 /* Unmapped gap in the virt memory map. */
+ 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
+ bne t0, t2, 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
On Mon, 2026-07-27 at 16:42 +0800, Zephyr Li wrote:
> 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
> Reviewed-by: Chao Liu <chao.liu@processmission.com>
Was this Reviewed?
Alistair
> Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
> ---
> Changes in v2:
> - Fix commit message formatting.
> - Enable vector state in the bare-metal test.
> - Use an aligned unmapped address and compare against the vsetvli
> result.
> - Express the mstatus.VS setting as a field shift.
>
> target/riscv/tcg/vector_helper.c | 23 +++++----
> tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
> tests/tcg/riscv64/test-vle32ff.S | 57
> +++++++++++++++++++++++
> 3 files changed, 76 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..f510960c05
> --- /dev/null
> +++ b/tests/tcg/riscv64/test-vle32ff.S
> @@ -0,0 +1,57 @@
> +/*
> + * 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 t0, (1 << 9) /* Set mstatus.VS to Initial. */
> + csrs mstatus, t0
> + li t1, 4
> + vsetvli t2, t1, e32, m1, ta, ma
> + li t0, 0x18000000 /* Unmapped gap in the virt memory
> map. */
> + 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
> + bne t0, t2, 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
On Wed, Aug 12, 2026 at 05:41:00AM +0800, Alistair wrote:
> On Mon, 2026-07-27 at 16:42 +0800, Zephyr Li wrote:
> > 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
> > Reviewed-by: Chao Liu <chao.liu@processmission.com>
>
> Was this Reviewed?
I’ve checked v2. The test improvements look good to me and the
fix is unchanged.
Thanks,
Chao
>
> Alistair
>
> > Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
> > ---
> > Changes in v2:
> > - Fix commit message formatting.
> > - Enable vector state in the bare-metal test.
> > - Use an aligned unmapped address and compare against the vsetvli
> > result.
> > - Express the mstatus.VS setting as a field shift.
> >
> > target/riscv/tcg/vector_helper.c | 23 +++++----
> > tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
> > tests/tcg/riscv64/test-vle32ff.S | 57
> > +++++++++++++++++++++++
> > 3 files changed, 76 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..f510960c05
> > --- /dev/null
> > +++ b/tests/tcg/riscv64/test-vle32ff.S
> > @@ -0,0 +1,57 @@
> > +/*
> > + * 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 t0, (1 << 9) /* Set mstatus.VS to Initial. */
> > + csrs mstatus, t0
> > + li t1, 4
> > + vsetvli t2, t1, e32, m1, ta, ma
> > + li t0, 0x18000000 /* Unmapped gap in the virt memory
> > map. */
> > + 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
> > + bne t0, t2, 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
Thanks Chao.
I have sent v3 to rebase onto current master and regenerate the malformed
v2 submission. There are no functional changes from v2, so Chao's
Reviewed-by still applies.
Thanks,
Zephyr
On Wed, Aug 12, 2026 at 10:52 AM Chao Liu <chao.liu@processmission.com>
wrote:
> On Wed, Aug 12, 2026 at 05:41:00AM +0800, Alistair wrote:
> > On Mon, 2026-07-27 at 16:42 +0800, Zephyr Li wrote:
> > > 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
> > > Reviewed-by: Chao Liu <chao.liu@processmission.com>
> >
> > Was this Reviewed?
> I’ve checked v2. The test improvements look good to me and the
> fix is unchanged.
>
> Thanks,
> Chao
> >
> > Alistair
> >
> > > Signed-off-by: Zephyr Li <fritchleybohrer@gmail.com>
> > > ---
> > > Changes in v2:
> > > - Fix commit message formatting.
> > > - Enable vector state in the bare-metal test.
> > > - Use an aligned unmapped address and compare against the vsetvli
> > > result.
> > > - Express the mstatus.VS setting as a field shift.
> > >
> > > target/riscv/tcg/vector_helper.c | 23 +++++----
> > > tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
> > > tests/tcg/riscv64/test-vle32ff.S | 57
> > > +++++++++++++++++++++++
> > > 3 files changed, 76 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..f510960c05
> > > --- /dev/null
> > > +++ b/tests/tcg/riscv64/test-vle32ff.S
> > > @@ -0,0 +1,57 @@
> > > +/*
> > > + * 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 t0, (1 << 9) /* Set mstatus.VS to Initial. */
> > > + csrs mstatus, t0
> > > + li t1, 4
> > > + vsetvli t2, t1, e32, m1, ta, ma
> > > + li t0, 0x18000000 /* Unmapped gap in the virt memory
> > > map. */
> > > + 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
> > > + bne t0, t2, 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.