On Fri, 2026-07-17 at 09:40 +0000, Joel Bueno wrote:
> The RISC-V V(ector) extension specification allows VLEN up to 65536
> bits
> (vlenb = 8192 bytes).
>
> 3 changes are needed to support this:
>
> 1. Widen simd_desc SIMD_MAXSZ_BITS from 8 to 10, raising the maxsz
> encoding limit from 2048 to 8200 bytes. This costs 2 data bits
> (22 to 20).
>
> 2. Raise RV_VLEN_MAX from 1024 to 65536. This increases the static
> per-CPU vector register file from 4 KB to 256 KB.
>
> 3. Widen the vlen CPU property from uint16 to uint32 so that
> vlen=65536 can be specified on the command line.
>
> To ensure TCG emits instructions correctly, we, clamp the vl_eq_vlmax
> TB flag
> so it is never set when the total register group size (vlmax << vsew)
> exceeds the simd_desc maxsz limit. This forces the helper-based path
> for
> very large register groups (e.g., VLEN=32768 with LMUL=4), preventing
> the
> GVEC fast path from silently truncating the operation and zeroing
> tail
> registers.
>
> We also need to fix probe_pages() so that vector ld/st works due to
> larger
> vector register sizes in regards of how many memory pages they span.
> This
> enables larger memory operations to succeed since address
> calculations can
> now properly span more than 2 pages.
>
> To finish off, we add an additional check on trans_rvv.c.inc to
> properly
> decide when to use the helper function or not; this ensures that we
> never
> hit:
>
> ./build/qemu-riscv64: ../accel/tcg/translate-all.c:356: tb_gen_code:
> Assertion `max_insns > 1' failed;
>
> On high VLEN configurations due to the amount of ld/st it generates
> (Where TCG
> tries to halve the amount of emitted instructions so the generated
> machine code
> fits on the buffer but fails to do so when a single vector load/store
> opcode fills
> the entire buffer (Or overruns it...!).
>
> Signed-off-by: joel.bueno <[email protected]>
> ---
> * Fix vext_ldst_us emitter. Wasn't able to trigger the wrong
> functionality from
> within qemu-user but qemu-system with 16k VLEN crashed when context
> switching.
>
> include/tcg/tcg-gvec-desc.h | 7 ++++--
> target/riscv/cpu.c | 12 +++++-----
> target/riscv/cpu.h | 2 +-
> target/riscv/insn_trans/trans_rvv.c.inc | 3 ++-
> target/riscv/tcg/tcg-cpu.c | 4 +++-
> target/riscv/vector_helper.c | 30
Can you add a test case to test some operations with a very large vlen?
I suspect no one is actually using a 2^16 VLEN and worry this will just
bit rot
Alistair
> ++++++++++++++++---------
> 6 files changed, 36 insertions(+), 22 deletions(-)
>
> diff --git a/include/tcg/tcg-gvec-desc.h b/include/tcg/tcg-gvec-
> desc.h
> index 704bd86454..6c277fedad 100644
> --- a/include/tcg/tcg-gvec-desc.h
> +++ b/include/tcg/tcg-gvec-desc.h
> @@ -21,7 +21,7 @@
> #define TCG_TCG_GVEC_DESC_H
>
> /*
> - * This configuration allows MAXSZ to represent 2048 bytes, and
> + * This configuration allows MAXSZ to represent up to 8200 bytes,
> and
> * OPRSZ to match MAXSZ, or represent the smaller values 8, 16, or
> 32.
> *
> * Encode this with:
> @@ -29,9 +29,12 @@
> * 2 -> maxsz
> *
> * This steals the input that would otherwise map to 24 to match
> maxsz.
> + *
> + * MAXSZ uses 10 bits to support RISC-V VLEN up to 65536 bits
> + * (vlenb = 8192 bytes). This leaves 20 DATA bits (was 22).
> */
> #define SIMD_MAXSZ_SHIFT 0
> -#define SIMD_MAXSZ_BITS 8
> +#define SIMD_MAXSZ_BITS 10
>
> #define SIMD_OPRSZ_SHIFT (SIMD_MAXSZ_SHIFT + SIMD_MAXSZ_BITS)
> #define SIMD_OPRSZ_BITS 2
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index fa497e5e8a..b5c2139810 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -1637,10 +1637,10 @@ static void prop_vlen_set(Object *obj,
> Visitor *v, const char *name,
> void *opaque, Error **errp)
> {
> RISCVCPU *cpu = RISCV_CPU(obj);
> - uint16_t cpu_vlen = cpu->cfg.vlenb << 3;
> - uint16_t value;
> + uint32_t cpu_vlen = cpu->cfg.vlenb << 3;
> + uint32_t value;
>
> - if (!visit_type_uint16(v, name, &value, errp)) {
> + if (!visit_type_uint32(v, name, &value, errp)) {
> return;
> }
>
> @@ -1663,13 +1663,13 @@ static void prop_vlen_set(Object *obj,
> Visitor *v, const char *name,
> static void prop_vlen_get(Object *obj, Visitor *v, const char *name,
> void *opaque, Error **errp)
> {
> - uint16_t value = RISCV_CPU(obj)->cfg.vlenb << 3;
> + uint32_t value = RISCV_CPU(obj)->cfg.vlenb << 3;
>
> - visit_type_uint16(v, name, &value, errp);
> + visit_type_uint32(v, name, &value, errp);
> }
>
> static const PropertyInfo prop_vlen = {
> - .type = "uint16",
> + .type = "uint32",
> .description = "vlen",
> .get = prop_vlen_get,
> .set = prop_vlen_set,
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 7582874c35..5796aaf04b 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -186,7 +186,7 @@ extern RISCVCPUImpliedExtsRule
> *riscv_multi_ext_implied_rules[];
> #include "pmp.h"
> #endif
>
> -#define RV_VLEN_MAX 1024
> +#define RV_VLEN_MAX 65536
> #define RV_MAX_MHPMEVENTS 32
> #define RV_MAX_MHPMCOUNTERS 32
> #define RV_MAX_TRIGGERS 2
> diff --git a/target/riscv/insn_trans/trans_rvv.c.inc
> b/target/riscv/insn_trans/trans_rvv.c.inc
> index 23262b1d03..f34fea3407 100644
> --- a/target/riscv/insn_trans/trans_rvv.c.inc
> +++ b/target/riscv/insn_trans/trans_rvv.c.inc
> @@ -1190,9 +1190,10 @@ static bool ldst_whole_trans(uint32_t vd,
> uint32_t rs1, uint32_t nf,
> * Update vstart with the number of processed elements.
> * Use the helper function if either:
> * - vstart is not 0.
> + * - total transfer size potentially exceeding emittable buffer
> length.
> */
>
> - bool use_helper_fn = !s->vstart_eq_zero;
> + bool use_helper_fn = !s->vstart_eq_zero || ((s->cfg_ptr->vlenb *
> nf) > 512);
>
> if (!use_helper_fn) {
> uint32_t size = s->cfg_ptr->vlenb * nf;
> diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
> index b73e3e9dd4..021f9c60bd 100644
> --- a/target/riscv/tcg/tcg-cpu.c
> +++ b/target/riscv/tcg/tcg-cpu.c
> @@ -33,6 +33,7 @@
> #include "accel/accel-cpu-target.h"
> #include "accel/tcg/cpu-ops.h"
> #include "tcg/tcg.h"
> +#include "tcg/tcg-gvec-desc.h"
> #ifndef CONFIG_USER_ONLY
> #include "hw/core/boards.h"
> #include "system/tcg.h"
> @@ -125,7 +126,8 @@ static TCGTBCPUState
> riscv_get_tb_cpu_state(CPUState *cs)
> uint32_t vlmax = vext_get_vlmax(cpu->cfg.vlenb, vsew, lmul);
> uint32_t maxsz = vlmax << vsew;
> bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl)
> &&
> - (maxsz >= 8);
> + (maxsz >= 8) &&
> + (maxsz <= (8 << SIMD_MAXSZ_BITS));
> flags = FIELD_DP32(flags, TB_FLAGS, VILL, env->vill);
> flags = FIELD_DP32(flags, TB_FLAGS, SEW, vsew);
> flags = FIELD_DP32(flags, TB_FLAGS, LMUL,
> diff --git a/target/riscv/vector_helper.c
> b/target/riscv/vector_helper.c
> index e321ca2616..1254a7b9c4 100644
> --- a/target/riscv/vector_helper.c
> +++ b/target/riscv/vector_helper.c
> @@ -176,13 +176,15 @@ static void probe_pages(CPURISCVState *env,
> target_ulong addr, target_ulong len,
> mmu_index, ra);
> }
>
> - if (len > curlen) {
> + while (len > curlen) {
> addr += curlen;
> - curlen = len - curlen;
> + len -= curlen;
> + curlen = MIN(-(addr | TARGET_PAGE_MASK), len);
> if (flags != NULL) {
> + void *page_host;
> *flags |= probe_access_flags(env, adjust_addr(env,
> addr), curlen,
> access_type, mmu_index,
> nonfault,
> - host, ra);
> + &page_host, ra);
> } else {
> probe_access(env, adjust_addr(env, addr), curlen,
> access_type,
> mmu_index, ra);
> @@ -480,8 +482,8 @@ vext_ldst_us(void *vd, target_ulong base,
> CPURISCVState *env, uint32_t desc,
> }
>
> /* Load/store elements in the second page */
> - if (unlikely(env->vstart < evl)) {
> - /* Cross page element */
> + while (unlikely(env->vstart < evl)) {
> + /* Element crossing the page boundary */
> if (unlikely(page_split % msize)) {
> for (k = 0; k < nf; k++) {
> addr = base + ((env->vstart * nf + k) << log2_esz);
> @@ -489,15 +491,21 @@ vext_ldst_us(void *vd, target_ulong base,
> CPURISCVState *env, uint32_t desc,
> env->vstart + k * max_elems, vd, ra);
> }
> env->vstart++;
> + if (env->vstart >= evl) {
> + break;
> + }
> }
>
> addr = base + ((env->vstart * nf) << log2_esz);
> - /* Get number of elements of second page */
> - elems = evl - env->vstart;
> -
> - /* Load/store elements in the second page */
> - vext_page_ldst_us(env, vd, addr, elems, nf, max_elems,
> log2_esz,
> - is_load, mmu_index, ldst_tlb, ldst_host,
> ra);
> + page_split = -(addr | TARGET_PAGE_MASK);
> + elems = page_split / msize;
> + if (env->vstart + elems >= evl) {
> + elems = evl - env->vstart;
> + }
> + if (likely(elems)) {
> + vext_page_ldst_us(env, vd, addr, elems, nf, max_elems,
> log2_esz,
> + is_load, mmu_index, ldst_tlb,
> ldst_host, ra);
> + }
> }
>
> env->vstart = 0;