riscv64: 2 of 4: 16 RVV matmul kernels behind Zvfh - #2600
Open
czoli1976 wants to merge 4 commits into
Open
Conversation
added 4 commits
August 7, 2026 09:26
tract had no RISC-V backend, so rv64gc ran the generic Rust kernels for every matmul. Add an RVV 1.0 f32 mmm tier, detecting V from the AT_HWCAP bit that Linux never sets for the incompatible 0.7.1 draft and VLEN from the vlenb CSR. Because VLEN is a runtime property while MR must be a const generic, each kernel fixes (MR, NR, LMUL) and pins vl to MR, which is correct wherever VLMAX >= MR and short below it, so dispatch is gated on the hart's VLMAX reaching MR and the kernel re-checks the granted vl before running. An assembler probe keeps toolchains predating RVV 1.0 on the generic fallback.
hwbench's load_a_slice has hand-written versions for x86_64, aarch64 and arm only, so tract-linalg fails to compile with the hwbench feature on any other target -- which includes riscv64, and so blocks tract-cli entirely, since it always enables that feature. Add a portable version reading one volatile word per cache line, which moves the same traffic; with no wide loads there are fewer misses in flight, so an out-of-order core may not saturate and the figure reads low rather than wrong.
The RVV kernels are gated on the hart's vector length, so a single emulated width would leave half the kernel set untested. Add riscv64gc to the qemu cross-test platforms twice, at VLEN 256 and 128, which select disjoint halves. -cpu max rather than a profile model because the generic rv64 model cannot run Debian's riscv64 glibc at all.
The riscv64 tier covered f32 only, so f16 matmul fell back to the generic kernels even on parts with native half-precision vectors, which includes the SpacemiT X60 in the K1. Add an f16 mmm tier from the same template at SEW=16, where VLMAX doubles and so does every tile height for a given LMUL and VLEN. Zvfh is read from the /proc/cpuinfo isa line, since RVA23 mandates only Zvfhmin and that cannot hold an f16 accumulator, and a second assembler probe keeps toolchains predating Zvfh on the f32 tier alone.
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on the f32 tier. Adds four f16 mmm kernels — 16x8 and 32x8 GEMM, 64x1
and 128x1 GEMV — from the same template at SEW=16, where VLMAX doubles and so
does every tile height for a given LMUL and VLEN.
No template change was needed: the f32 PR already parameterised element size,
so this is a kernel table, a probe, and a predicate.
Why this is not speculative
Zvfh is optional in RVA23, so it has to be detected rather than assumed — but
it is not hypothetical hardware. The SpacemiT X60 in the K1 reports
zvfhandzvfhminin its/proc/cpuinfoisa string, which covers the Banana Pi BPI-F3,Orange Pi RV2 and Milk-V Jupiter. llama.cpp's RISC-V build enables
zvfhalongside
vfor exactly this hardware.The C920v2 in the SG2044 has scalar
zfhonly, so this is a VLEN=256-classfeature in practice, and the predicates fall back cleanly on parts without it.
Zvfhmin is not sufficient: it provides only f16<->f32 conversion and so cannot
carry an f16 accumulator, which is why the detection looks for Zvfh
specifically.
Testing
No CI change needed — the
-cpu maxmodels the f32 PR added already enableZvfh, so both existing riscv64 entries exercise this tier. Locally, tract-linalg
passes at VLEN 128 and 256 with Zvfh on and off, and
dispatch_matches_vlencovers the f16 predicates alongside the f32 ones.
Same caveat as the f32 PR: correctness under emulation, no hardware numbers.
🍍