Skip to content

Silent vector register loss when whole-register V state is restored under a different VLEN than it was saved #2321

Description

@om-mahesh

Problem

Spike models per-hart VLEN faithfully. But a byte-for-byte whole-register
vector save/restore — the exact pattern an OS uses to save/restore V state
across a context switch — silently drops most of the register file when the
saving hart and the restoring hart have different VLEN, with no trap and no
diagnostic. A vsetvli e8,m8; vse8.v save on a hart with vlenb=64 (VLEN=512)
writes a flat buffer using a 64-byte per-register stride; loading that same
buffer back with vsetvli e8,m8; vle8.v on a hart with vlenb=16 (VLEN=128)
reads it with a 16-byte stride, so the upper bytes are truncated and the
surviving bytes are reassembled across the wrong register boundaries. The
program continues with a corrupted vector register file and a success exit
status from the simulator.

Reproducer

Full source (~90 lines, no dependencies beyond the toolchain + Spike + pk):
reproducer/spike-corruption/xwidth_corruption.c and run.sh in the repo
linked below. The essence:

# compile (newlib bare-metal, rv64gcv)
riscv64-unknown-elf-gcc -march=rv64gcv -mabi=lp64d -O2 \
    xwidth_corruption.c -o xwidth_corruption.elf

# shared save buffer (pk's syscall proxy has no O_CREAT, so pre-create it)
head -c 2064 /dev/zero > state.bin

# VLEN is selected via the Zvl extension in the ISA string (--varch is gone):
#   save on a 512-bit hart, then restore the SAME buffer on a 128-bit hart
spike --isa=rv64gcv_zvl512b pk xwidth_corruption.elf save    state.bin
spike --isa=rv64gcv_zvl128b pk xwidth_corruption.elf restore state.bin

The program fills the 32 vector registers with a known pattern, does the
kernel-style whole-state save on the wide hart, restores on the narrow hart,
then reports how many registers survived intact and how many bytes were lost.

Observed vs. expected

Observed (verbatim stdout, restore step):

[save]    vlenb=64 (VLEN=512b) wrote 2048 bytes, golden=fd8f43f997b2ec83
[restore] vlenb=16 (VLEN=128b)  src_vlenb=64 (VLEN=512b)
[restore] registers_intact=2/32  first_corrupt_reg=1  bytes_lost=1536
RESULT src_vlen=512 dst_vlen=128 intact=2/32 bytes_lost=1536 verdict=CORRUPT

Only 2 of 32 registers survive; 1536 bytes are lost — and nothing flags it. The
equal-width control in the same harness (save and restore both at VLEN=512)
restores 32/32 with 0 bytes lost, confirming the harness itself is sound and the
loss is purely a function of the VLEN change.

Expected: a strict model would surface the lossy reinterpretation rather than
let it pass silently — e.g. a trap or a diagnostic when whole-register vector
state is restored under a vlenb different from the one that produced the
buffer. (Spike cannot generally know a buffer's provenance, which is exactly
what the question below is about.)

Environment

  • Spike (stock upstream riscv-software-src/riscv-isa-sim), commit
    de829033327fb5f34839b9946a213917186b6a4d — clean build, no local patches.
  • Toolchain: riscv64-unknown-elf-gcc 14.2.0 (newlib).
  • Runtime: riscv-pk (proxy kernel).
  • Host: Linux x86_64.

Question

Is a trap or diagnostic for lossy cross-width vector state restore in scope for
Spike, or is this considered a spec-level gap (i.e. the architecture leaves the
save/restore contract entirely to software, and a functional ISA simulator is
not expected to flag it)?


Full context (a vector-width-virtualization study that this corruption motivates)
is at https://github.com/om-mahesh/riscv-vector-width-virtualization — reading it
is not required to evaluate this bug; the reproducer above is self-contained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions