Describe the feature request
ONNX Runtime already has everything needed to build and test riscv64 under emulation, and as far as I can tell from the repository nothing exercises it.
What's already there:
cmake/riscv64.toolchain.cmake (SiFive, 2024) sets CMAKE_CROSSCOMPILING_EMULATOR to ${RISCV_QEMU_PATH}, so ctest runs
cross-compiled tests under QEMU automatically.
build.py supports --rv64, --riscv_toolchain_root and --riscv_qemu_path, and explicitly requires the QEMU path when tests are not
skipped: "The --riscv_qemu_path option is required for testing riscv64."
tools/scripts/build_riscv64.sh is a working sample of the whole flow.
- There is real code to exercise: 13 RVV kernel files under
onnxruntime/core/mlas/lib/riscv64 added between April and July, plus
test_activation_rvv.cpp, test_qnbitgemm_rvv_fp16.cpp, and six benchmarks under test/mlas/bench/riscv64.
What I can't find is anything that runs it. Searching the tree for riscv/rv64 turns up no pipeline definitions, and grepping .github/, tools/ci_build/ and tools/scripts/ finds only the build plumbing above. I also looked at the check runs on a recent main commit -- 192 of them, across both GitHub Actions and Azure Pipelines -- and none is riscv64.
Scheduled pipelines wouldn't appear in per-commit check runs, so I can't rule those out from outside the project. If riscv64 is already covered somewhere I can't see, please just say so and I'll drop this.
Suggested scope, kept deliberately small:
build.py --rv64 with the existing toolchain and QEMU paths
- run the MLAS unit tests only, not the full suite, to bound runtime
- trigger nightly, or on changes under
onnxruntime/core/mlas/**
- build with
-march=rv64gcv so the RVV paths are actually taken, and run at more than one vlen -- see below
On emulation, which I think is the right tool here rather than a compromise. RVV is vector-length agnostic: the same binary runs at whatever VLEN the hardware provides, and a kernel can be correct at one vector length and wrong at another. A physical board has exactly one. QEMU takes VLEN as a CPU option -- -cpu rv64,v=true,vlen=256 -- and validates it (an invalid value is rejected with "Vector extension VLEN must be power of 2"), so an emulated job can sweep vector lengths that no single piece of hardware can cover. For that property emulation isn't a weaker stand-in for silicon; it tests something a single board can't.
On where it should run. I notice most workflows here use self-hosted runners, so adding a job is a request to spend your capacity. I'd suggest avoiding that: a GitHub-hosted ubuntu-latest job in the style of lint.yml should be able to apt install the riscv64 cross toolchain and qemu-user-static, build MLAS and its tests, and run them under emulation. That costs none of the self-hosted pool and means any contributor can reproduce and debug it.
I haven't verified an MLAS-only build fits comfortably in a hosted runner's limits -- that is the first thing I'd measure before proposing anything concrete.
Questions:
- Is riscv64 already covered by a pipeline I can't see from the repo?
- If not, is a QEMU-based job welcome in principle, scoped to the MLAS tests?
- Any objection to it being a GitHub-hosted Actions workflow rather than an Azure pipeline?
Happy to put it together and report the timings if there's interest.
Describe scenario use case
The scenario is catching architecture-specific correctness regressions in MLAS before they reach users on riscv64.
#20030 is a case study rather than a complaint. A packing width mismatch in the non-SIMD SGEMM path produced silently wrong results on riscv64 -- about 15% accuracy on a small CNN, and garbage output from whisper-tiny. It was reported in March 2024, and the reply was entirely reasonable:
Sorry our team doesn't have access to the kind of hardware. We cannot debug the issue.
It was fixed in March 2026 by #27819, a PR titled "[wasm] fix non-SIMD sgemm implementation", which repaired the shared non-WASM pack path that riscv64 also uses -- so RISC-V was fixed as a side effect rather than deliberately. The fix was first confirmed on riscv64 in August 2026, when a contributor cross-compiled and ran it under QEMU by hand. #20030 is still open.
That is roughly two years during which a silent numerical error was present on an architecture the project supports, and the eventual verification was one person doing manually what a CI job could do on every change.
Meanwhile the surface has grown considerably: 13 RVV kernel files landed between April and July this year, covering convolution, activation, LLM operators, INT8 GEMM/GEMV and QNBitGemm. These are hand-written vector intrinsics, which is the code most likely to be subtly wrong and least likely to be caught by review.
A job running the existing tests under the existing emulator would have caught the 2024 bug in 2024, and would give the contributors adding RVV kernels today a signal they currently don't get.
Describe the feature request
ONNX Runtime already has everything needed to build and test riscv64 under emulation, and as far as I can tell from the repository nothing exercises it.
What's already there:
cmake/riscv64.toolchain.cmake(SiFive, 2024) setsCMAKE_CROSSCOMPILING_EMULATORto${RISCV_QEMU_PATH}, soctestrunscross-compiled tests under QEMU automatically.
build.pysupports--rv64,--riscv_toolchain_rootand--riscv_qemu_path, and explicitly requires the QEMU path when tests are notskipped: "The --riscv_qemu_path option is required for testing riscv64."
tools/scripts/build_riscv64.shis a working sample of the whole flow.onnxruntime/core/mlas/lib/riscv64added between April and July, plustest_activation_rvv.cpp,test_qnbitgemm_rvv_fp16.cpp, and six benchmarks undertest/mlas/bench/riscv64.What I can't find is anything that runs it. Searching the tree for
riscv/rv64turns up no pipeline definitions, and grepping.github/,tools/ci_build/andtools/scripts/finds only the build plumbing above. I also looked at the check runs on a recentmaincommit -- 192 of them, across both GitHub Actions and Azure Pipelines -- and none is riscv64.Scheduled pipelines wouldn't appear in per-commit check runs, so I can't rule those out from outside the project. If riscv64 is already covered somewhere I can't see, please just say so and I'll drop this.
Suggested scope, kept deliberately small:
build.py --rv64with the existing toolchain and QEMU pathsonnxruntime/core/mlas/**-march=rv64gcvso the RVV paths are actually taken, and run at more than onevlen-- see belowOn emulation, which I think is the right tool here rather than a compromise. RVV is vector-length agnostic: the same binary runs at whatever
VLENthe hardware provides, and a kernel can be correct at one vector length and wrong at another. A physical board has exactly one. QEMU takesVLENas a CPU option ---cpu rv64,v=true,vlen=256-- and validates it (an invalid value is rejected with "Vector extension VLEN must be power of 2"), so an emulated job can sweep vector lengths that no single piece of hardware can cover. For that property emulation isn't a weaker stand-in for silicon; it tests something a single board can't.On where it should run. I notice most workflows here use
self-hostedrunners, so adding a job is a request to spend your capacity. I'd suggest avoiding that: a GitHub-hostedubuntu-latestjob in the style oflint.ymlshould be able toapt installthe riscv64 cross toolchain andqemu-user-static, build MLAS and its tests, and run them under emulation. That costs none of the self-hosted pool and means any contributor can reproduce and debug it.I haven't verified an MLAS-only build fits comfortably in a hosted runner's limits -- that is the first thing I'd measure before proposing anything concrete.
Questions:
Happy to put it together and report the timings if there's interest.
Describe scenario use case
The scenario is catching architecture-specific correctness regressions in MLAS before they reach users on riscv64.
#20030 is a case study rather than a complaint. A packing width mismatch in the non-SIMD SGEMM path produced silently wrong results on riscv64 -- about 15% accuracy on a small CNN, and garbage output from whisper-tiny. It was reported in March 2024, and the reply was entirely reasonable:
It was fixed in March 2026 by #27819, a PR titled "[wasm] fix non-SIMD sgemm implementation", which repaired the shared non-WASM pack path that riscv64 also uses -- so RISC-V was fixed as a side effect rather than deliberately. The fix was first confirmed on riscv64 in August 2026, when a contributor cross-compiled and ran it under QEMU by hand. #20030 is still open.
That is roughly two years during which a silent numerical error was present on an architecture the project supports, and the eventual verification was one person doing manually what a CI job could do on every change.
Meanwhile the surface has grown considerably: 13 RVV kernel files landed between April and July this year, covering convolution, activation, LLM operators, INT8 GEMM/GEMV and QNBitGemm. These are hand-written vector intrinsics, which is the code most likely to be subtly wrong and least likely to be caught by review.
A job running the existing tests under the existing emulator would have caught the 2024 bug in 2024, and would give the contributors adding RVV kernels today a signal they currently don't get.