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;

Reply via email to