While working on a fiber implementation for 32-bit RISC-V, we noticed that the implementation for 64-bit RISC-V uses instructions specific to the F extension, which includes additional registers for floating point values, regardless whether the CPU has the extension or not.
When using an analogous implementation for a 32-bit RISC-V processor without the F extension, execution stops at the first F-specific instruction without an error (we assume a trap handler is activated). We do not have a 64-bit RISC-V chip without the F-extension to test this with, but we assume it would show the same behaviour.
For our implementation for 32-bit RISC-V, we plan on solving this by checking for the target_feature in addition to the target_arch and only select the implementation if both match the implementation (see the version of stackswitch.rs in our fork).
We're wondering whether the 64-bit RISC-V implementation being used regardless of the F extension being present on the target CPU actually is a bug or is there something we're missing/misunderstanding?
The riscv64 port targets riscv64gc, which includes F (and D); these are required for the code that we generate on that platform
(and the code that rustc will compile since we build with riscv64gc-unknown-linux-gnu)
You'll need to either do some sort of dynamic feature detection if you want to work on riscv32imac (for example), or build fully for an FPU-free environment
Rustc only has a baremetal riscv64imac target (Linux requires full gc support and other OSes probably do too). For baremetal targets wasmtime doesn't use fibers at all.
It's perhaps not intended but fibers (as in wasmtime-fiber and the async feature of wasmtime) do work on baremetal ARM Thumbv7/Thumbv8 targets
indeed, fibers do work in a no_std build and that's intended -- I added that a bit ago for our no_std x86-64 embedding
Thanks for the clarifications!
Quick sanity check about your messages, @Chris Fallin:
So there does not need to be a check for the features in stackswitch.rs because wasmtime only supports riscv64gc.
For the riscv32 implementation, this means we also only need to check for the riscv32 architecture, because only riscv32imac is supported by wasmtime.
Do we understand your comments correctly?
Max D. said:
For the riscv32 implementation, this means we also only need to check for the riscv32 architecture, because only riscv32imac is supported by wasmtime.
Well, no, not quite; I never said that Wasmtime supports only riscv32imac. In fact (with Pulley) it should run fine (without async/fibers) on all sorts of riscv32 platforms, whatever is supported by rustc.
I would expect that we'd need some sort of feature detection to select between different fiber-switching assembly sequences for different variants...
Chris Fallin said:
I never said that Wasmtime supports only
riscv32imac.
Oh, sorry, we got that from the documentation, which we forgot to link. Our implementation only uses instructions from riscv32i, so it should work for any architecture without additional registers, which to our knowledge just means the f and v extensions cannot be present (as the d extension implies the f extension being present).
I would expect that we'd need some sort of feature detection to select between different fiber-switching assembly sequences for different variants...
If our assumptions above are correct, the implementation in our fork should do this by only using our implementation if target_arch is riscv32 and target_feature does not include f or v. Is this what you mean by feature detection?
Feature-detection-wise yeah you'll want to be using cfg(target_feature = "...") in Rust. In theory it would be best to do runtime feature detection but that's not really suitable given the low-level nature of this crate.
In theory what we'll want is something like #[cfg(target_feature = "f")] compile_error!("this isn't ready for the \"f\" feature yet");
Ah, that's just the list of builds that we check I think, but we don't want to bake in any assumptions that riscv32 == riscv32imac. I think your compile-time feature checks there make sense; let's call the arch-specific file riscv32imac.rs or something more specific like that
I'll note though that rustc hasn't stabilized all risc-v features, so for example even the riscv64gc target looks like it has "f" disabled
in the sense that cfg(target_feature = "f") isn't set
Alex Crichton said:
I'll note though that rustc hasn't stabilized all risc-v features, so for example even the
riscv64gctarget looks like it has "f" disabled
that's... extremely odd and sounds like a bug? egads
I don't think this is a situation we can be 100% sound right now -- with a combination of rustc not exposing features to us and there being no reasonable path to runtime feature detection all we can do is try our best to provide guard rails
It's not a bug per se, it's that the "f" target feature name in rust isn't stable
ah, gotcha
for example this is stable rust right now
and on current main it's still unstable
another way to put what I'm saying, @Max D. feel free to send a PR with what you have. I don't think it's currently possible to be 100% correct on riscv32 at this time. We'll just have to document that riscv32 for wasmtime is incompatible with floats and that's the best we can do
Thanks a lot again for your help! We've cleaned up our git history and created a pull request on GitHub: https://github.com/bytecodealliance/wasmtime/pull/12506
Looking forward to any feedback on this!
Last updated: Feb 24 2026 at 04:36 UTC