-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add no_std support to Wasmtime #8341
Comments
I support this general direction, although I do have some bikes to shed on a few particular details. I'll leave those nitpicks for a little bit later on in the discussion. |
I support this direction as well. As the smallest of bikesheds, I'm curious if there's a reason for using #![no_std]
#[cfg(feature = "std")]
extern crate std; instead of #![cfg_attr(not(feature = "std"), no_std)] |
The former consistently imports libcore and uses the libcore prelude, thus reducing the need for |
Carring over some thoughts from this comment I've looked more closely now at the hash map and seeding situation. As background the Rust standard library uses a DoS-resistant hashing algorithm which randomizes the seed for hashes for each thread. GIven What I would propose as a solution for now is:
The consequence of this is that when There's two drawbacks to this. First is that the |
I appreciate the overall direction. I am the maintainer of the Wasmi crate and am using (*) I am aware that generally a well-tuned |
BTreeMap has a non-trivial impact on compile time performance and it too can have worse than average performance under adversarial circumstances (inssrting elements in ascending order is supposedly slower than inserting in descending order according to a recent topic on users.rust-lang.org.) |
Do you mean that the output of compilation and the execution of the resulting compiled wasm module needs to be deterministic? That should already be the case even with our current HashMap usage. Or do you mean that the execution environment in which wasmtime would run must be deterministic itself too? |
Are you referring to this forum post from 2 days ago?
That's what I meant but just for Wasmi and not Wasmtime. Wasmi is a |
Yes
I see. That only seems to be relevant to the wasmparser changes, and not the wasmtime changes as Wasmtime can't run in wasm anyway. It can compile wasm modules to machine code ( |
Yes that is correct. I should have said that. |
I don't really have benchmarks one way or another, but at the same time I would be hesitant to replace all usage of all hash maps in Wasmtime with BTree maps because randomness/seeding may be hard to come by in some niche situations. Hash maps feel tailor made for most of what Wasmtime needs in various purposes, so if they're problematic I would imagine that a refactoring to remove the need for a map completely would be necessary. In that sense I would prefer to not use no_std support as a reason to change how data structures are done in Wasmtime, but instead consider it as motivation to refactor and rewrite preexisting algorithms to avoid types that are less suitable in a no_std context. |
I was talking with @tschneidereit yesterday about this issue and one point he brought up is that it would be reasonable to have a compile-time Cargo feature on the |
That would be amazing for me! ❤️ Though what is the intended solution for If we plan to actually use this |
My thinking was that the type aliases in wasmparser would become type definitions which internally #[cfg] for hash maps or tree-based maps. For the tree-based-indexmap-version I was thinking we'd implement it directly in the wasmparser crate itself as opposed to using a fork of indexmap (under the assumption it's not too too big) |
Okay that could work. We could use the |
As someone who has maintained their own
If I can help out with this in any way I would love to do so. I see |
@Robbepop sounds good! And also to make sure I clarify I don't plan on personally pursuing the BTreeMap approach of wasmparser, but I wanted to outline how I think it'd be reasonable to add so if you're up for making a PR once the no_std work starts landing I'd be happy to review. @JonasKruckenberg oh nice! I think that PRs to add no_std to Cranelift after this work starts landing would indeed be welcome. I asked some folks at the Cranelift meeting yesterday and there were no objections and I suspect most won't object to this. One point brought up was that if Wasmtime is "doing the no_std thing" then it's pretty easy for Cranelift to do it as well as it'll just be an extension of what Wasmtime is already doing. Also, to clarify, I'm not sure precisely how best to have a "go no go" decision on this issue. My rough plan is to bring it up at the next Wasmtime meeting (a week from today) and assuming there are no objections start landing work at that point. My plan in landing the work is to do it piecemeal crate-by-crate so reviewers have an easier time seeing the impact as things progress. |
@alexcrichton Yes, I'd be willing to file that PR once I am already looking forward to the day I can finally put the |
This commit is the first in what will be multiple PRs to migrate Wasmtime to being compatible with `#![no_std]`. This work is outlined in bytecodealliance#8341 and the rough plan I have in mind is to go on a crate-by-crate basis and use CI as a "ratchet" to ensure that `no_std` compat is preserved. In that sense this PR is a bit of a template for future PRs. This PR migrates a few small crates to `no_std`, basically those that need no changes beyond simply adding the attribute. The nontrivial parts introduced in this PR are: * CI is introduced to verify that a subset of crates can indeed be built on a `no_std` target. The target selected is `x86_64-unknown-none` which is known to not have `std` and will result in a build error if it's attempted to be used. * The `anyhow` crate, which `wasmtime-jit-icache-coherence` now depends on, has its `std` feature disabled by default in Wasmtime's workspace. This means that some crates which require `std` now need to explicitly enable the feature, but it means that by-default its usage is appropriate for `no_std`. The first point should provide CI checks that compatibility with `no_std` indeed works, at least from an "it compiles" perspective. Note that it's not sufficient to test with a target like `x86_64-unknown-linux-gnu` because `extern crate std` will work on that target, even when `#![no_std]` is active. The second point however is likely to increase maintenance burden in Wasmtime unfortunately. Namely we'll inevitably, either here or in the future, forget to turn on some feature for some crate that's not covered in CI checks. While I've tried to do my best here in covering it there's no guarantee that everything will work and the combinatorial explosion of what could be checked in CI can't all be added to CI. Instead we'll have to rely on bug fixes, users, and perhaps point releases to add more use cases to CI over time as we see fit.
Wasmtime and Cranelift have a few miscellaenous use cases for "just take this Rust type and make it bytes", for example Wasmtime's serialization of internal metadata into a compiled module. Previously Wasmtime used the `bincode` crate for performing these tasks as the format was generally optimized to be small and fast, not general purpose (e.g. JSON). The `bincode` crate on crates.io doesn't work on `no_std`, however, and with the work in bytecodealliance#8341 that's an issue now for Wasmtime. This crate switches instead to the `postcard` crate. This crate is listed in Serde's documentation as: > Postcard, a no_std and embedded-systems friendly compact binary > format. While I've not personally used it before it checks all the boxes we relied on `bincode` for and additionally works with `no_std`. After auditing the crate this commit then switches out Wasmtime's usage of `bincode` for `postcard` throughout the repository.
Wasmtime and Cranelift have a few miscellaenous use cases for "just take this Rust type and make it bytes", for example Wasmtime's serialization of internal metadata into a compiled module. Previously Wasmtime used the `bincode` crate for performing these tasks as the format was generally optimized to be small and fast, not general purpose (e.g. JSON). The `bincode` crate on crates.io doesn't work on `no_std`, however, and with the work in #8341 that's an issue now for Wasmtime. This crate switches instead to the `postcard` crate. This crate is listed in Serde's documentation as: > Postcard, a no_std and embedded-systems friendly compact binary > format. While I've not personally used it before it checks all the boxes we relied on `bincode` for and additionally works with `no_std`. After auditing the crate this commit then switches out Wasmtime's usage of `bincode` for `postcard` throughout the repository.
This commit is the first in what will be multiple PRs to migrate Wasmtime to being compatible with `#![no_std]`. This work is outlined in bytecodealliance#8341 and the rough plan I have in mind is to go on a crate-by-crate basis and use CI as a "ratchet" to ensure that `no_std` compat is preserved. In that sense this PR is a bit of a template for future PRs. This PR migrates a few small crates to `no_std`, basically those that need no changes beyond simply adding the attribute. The nontrivial parts introduced in this PR are: * CI is introduced to verify that a subset of crates can indeed be built on a `no_std` target. The target selected is `x86_64-unknown-none` which is known to not have `std` and will result in a build error if it's attempted to be used. * The `anyhow` crate, which `wasmtime-jit-icache-coherence` now depends on, has its `std` feature disabled by default in Wasmtime's workspace. This means that some crates which require `std` now need to explicitly enable the feature, but it means that by-default its usage is appropriate for `no_std`. The first point should provide CI checks that compatibility with `no_std` indeed works, at least from an "it compiles" perspective. Note that it's not sufficient to test with a target like `x86_64-unknown-linux-gnu` because `extern crate std` will work on that target, even when `#![no_std]` is active. The second point however is likely to increase maintenance burden in Wasmtime unfortunately. Namely we'll inevitably, either here or in the future, forget to turn on some feature for some crate that's not covered in CI checks. While I've tried to do my best here in covering it there's no guarantee that everything will work and the combinatorial explosion of what could be checked in CI can't all be added to CI. Instead we'll have to rely on bug fixes, users, and perhaps point releases to add more use cases to CI over time as we see fit.
* Start migrating some Wasmtime crates to no_std This commit is the first in what will be multiple PRs to migrate Wasmtime to being compatible with `#![no_std]`. This work is outlined in #8341 and the rough plan I have in mind is to go on a crate-by-crate basis and use CI as a "ratchet" to ensure that `no_std` compat is preserved. In that sense this PR is a bit of a template for future PRs. This PR migrates a few small crates to `no_std`, basically those that need no changes beyond simply adding the attribute. The nontrivial parts introduced in this PR are: * CI is introduced to verify that a subset of crates can indeed be built on a `no_std` target. The target selected is `x86_64-unknown-none` which is known to not have `std` and will result in a build error if it's attempted to be used. * The `anyhow` crate, which `wasmtime-jit-icache-coherence` now depends on, has its `std` feature disabled by default in Wasmtime's workspace. This means that some crates which require `std` now need to explicitly enable the feature, but it means that by-default its usage is appropriate for `no_std`. The first point should provide CI checks that compatibility with `no_std` indeed works, at least from an "it compiles" perspective. Note that it's not sufficient to test with a target like `x86_64-unknown-linux-gnu` because `extern crate std` will work on that target, even when `#![no_std]` is active. The second point however is likely to increase maintenance burden in Wasmtime unfortunately. Namely we'll inevitably, either here or in the future, forget to turn on some feature for some crate that's not covered in CI checks. While I've tried to do my best here in covering it there's no guarantee that everything will work and the combinatorial explosion of what could be checked in CI can't all be added to CI. Instead we'll have to rely on bug fixes, users, and perhaps point releases to add more use cases to CI over time as we see fit. * Add another std feature * Another std feature * Enable anyhow/std for another crate * Activate `std` in more crates * Fix miri build * Fix compile on riscv64 prtest:full * Fix min-platform example build * Fix icache-coherence again
For reference I pulled my |
I'm a bit worried about the use of |
Agreed, the addition of maybe on from looking at the regalloc2 docs it seems |
That could theoretically be replaced by a |
Ah yeah, that might be worthwhile on its own actually. In the meantime I updated my branch with this JonasKruckenberg@eab337c (and cherry picked into it's PR #8489) |
With #8555 I believe that all the necessary pieces are now in place, so I'm going to close this. |
In this issue I'd like to propose officially adding support for Rust's
#![no_std]
mode and crate attribute to thewasmtime
crate and runtime. Notably, I'm proposing that we refute current documentation about "why notno_std
". This would additionally revert earlier work in #554 and #2024.Before I go into more depth, this is not a new issue to Wasmtime. This has been discussed in a number of places such as #1158, #3451, #3495, #6424, #7700, and I'm sure I'm missing others as well. I'll also point out that I was personally one of the ones previously advocating for specifically not supporting
no_std
, and my opinion has changed in the intervening years.Why support
no_std
?As far as I know the benefits of
no_std
haven't really ever been in question. After all, who doesn't want a project to be able to run in as many places as possible? To me I would personally rephrase this question as why to useno_std
to support platform as opposed to alternative strategies. I'd answer this with the fact thatno_std
is the most idiomatic and well-supported solution in the Rust ecosystem. Rust developers in the embedded space are already used tono_std
and what it entails. Additionally there are community idioms/expectations around theno_std
feature to follow which set precedent.Why now?
For a number of years now Wasmtime has had a page in its documentation for "What about
#[no_std]
?", so I think a fair question is why would we revert this and reconsider this previous decision at this point in time. The embedded space has become more interested in WebAssembly over time and there are a fair number of users today. The general feedback is that Wasmtime is not suitable in these environments, and one primary reason is that it's difficult to get Wasmtime working in these environments. Work such as #7995 is to obscure for others to productively use as-is without being able to slot more idiomatically into theno_std
ecosystem. More-or-less there's interest to use WebAssembly on platforms Wasmtime cannot easily target, andno_std
support is intended to be a step towards making Wasmtime support on these platforms easier.Was the previous rationale wrong?
In my opinion, the current documentation Wasmtime has for not supporting
no_std
is both right and wrong. Supportingno_std
is not "free", it's something we'll have to maintain over time as a project and explicitly test in CI and consider for all new features. This cost wasn't necessarily justifiable earlier on in Wasmtime's development. Nowadays, though, Wasmtime has many more compile-time features and it's more clear what ano_std
build of Wasmtime might look like. Supportingno_std
is, however, idiomatic and the best way to support non-standard platforms in Rust. While it's theoretically possible to change how the standard library looks in upstream rust-lang/rust that's basically not happening any time soon.What does
no_std
for Wasmtime mean?With some of the more rationale-facing questions out of the way, I want to dive into some more details as to what
no_std
means for Wasmtime. If you read the title of this issue and say "wow finally it's about time Wasmtime had support" you're probably not going to be too interested in this section. This section is intended to outline what I learned from my experience of porting Wasmtime tono_std
. I have two branches which ports thewasmparser
crate tono_std
as well as theruntime
feature of thewasmtime
crate:Features of Wasmtime and
no_std
Primarily the first part to mention is that I don't think we'll want to support every single feature of Wasmtime in the
no_std
configuration. Instead we'll want a few features explicitly listed as "this supportsno_std
" but everything else will implicitly enable a dependency on thestd
feature and the standard library. An example of this is that theruntime
feature, the ability to execute WebAssembly modules, won't depend on std but thecache
feature, which writes to a filesystem, would depend on the standard library. Notably, for the initial implementation, I'd also expect thatcranelift
itself would require the standard library. There's no inherent reason to do so, but that's how I wrote things originally.Usage of the
alloc
crateI also personally think it's worth clarifying that
no_std
for Wasmtime meanscore
andalloc
will be used. Notably this means that Wasmtime will still assume that all allocations succeed at all times and a failing allocation will cause a process abort and/or unrecoverable error. Wasmtime can't really feasibly be ported to analloc
-less build at this time, although if that's of interest to folks then it's something that might be worth investigating later on. I'm not sure how we'd support this in Wasmtime, though, so I'd prefer to defer such a question to a future issue. In the meantime the rest here will assume thatno_std
means thatalloc
can be used.Technical details of what support looks like to users
To end users and consumers of Wasmtime what I'd propose is that the
wasmtime
crate will grow a compile-time Cargo feature calledstd
. This feature is enabled by default and is additionally enabled by any other feature which depends on standard library support, such ascache
. If all of Wasmtime's features are disabled, however, then a small set of features, such asruntime
andcomponent-model
, can be enabled without enabling thestd
feature of Wasmtime. This will prevent use of thestd
crate on some platforms.The
wasmtime-runtime
crate will, by default, use the standard library on platforms that are known to have the standard library. For example on Linux thewasmtime-runtime
crate will continue to usestd
as necessary. Ifwasmtime-runtime
is compiled for an unknown platform, however, then the custom platform support added in #7995 will be enabled by default. This means that Wasmtime will not work out-of-the-box since this header file will need to be implemented. This will be mentioned as part of Wasmtime's documentation and will be the escape hatch to enable embedders to implement custom logic to implement Wasmtime's runtime needs.Put together, the general workflow for embedders using Wasmtime in
no_std
mode would look like:wasmtime
is audited to confirm that onlyno_std
-compatible features of Wasmtime are used (e.g. notcache
)GlobalAlloc
trait, etc, must be provided by the embedder. For now this'll be a header file for Wasmtime's runtime needs and otherwise it's up to the embedder to fix compile errors aboutGlobalAlloc
for example.My intent would be that we'd have at least one or two examples in the repository doing all this, like the current
min-platform
example.Technical details of what support looks like to Wasmtime developers
To those who develop Wasmtime this issue is effectively asking more development cost to be taken on by the project. Despite having community norms and expectations the
no_std
development mode in Rust is not the standard development mode and may serve as a road bump to contributions and/or developers. Here I'd like to outline the specific issues I've found when porting Wasmtime tono_std
:core
andalloc
, notstd
. This is mostly a muscle memory thing to get over, but it's a downside in that only some parts of the project will do this. For example inwasmtime-cache
we'll probably still haveuse std::mem;
whereas inwasmtime-runtime
that would be spelleduse core::mem;
.#![no_std]
mode. Notably collections likeVec
andString
are not present. To work around this I've created amod prelude { ... }
in thewasmtime-environ
crate which is reexported at all other crate roots. Most modules in crate then haveuse crate::prelude::*;
instead of individually importing common types likeString
andVec
.HashMap
in thealloc
crate due to seeding and DoS issues. The proof-of-concept branch I have switches everything needed to thehashbrown
crate'sHashMap
with theahash
hasher. I have not investigated how random keys are generated inno_std
mode yet. Note that these changes also apply to the ubiquitous usage ofIndexMap
. TheIndexMap
type inno-std
mode does not have a defaulted third type parameter and we will need to manually configure this parameter ourselvesstd::error::Error
trait is not present incore
. While there are efforts to do this they have not come to fruition yet. This crucially means thatanyhow::Error
no longer has a blanketFrom
impl for types that implementError
(that requires thestd
feature ofanyhow
which won't be enabled by default). For internal development I added extension traits to enable.err2anyhow()
onResult<T, E>
. This means that while?
will work alone instd
mode it'll require an explicit.err2anyhow()
whenstd
is not enabled. It's hoped that one day this will be easier to work with ascore::error::Error
eventually gets stabilized.wasmtime-runtime
are implemented with thelibm
crate instead of methods that require the standard library. Whenstd
is enabled, though, the standard library methods are used.Crates with no-std support will have a prelude that looks like:
and each module will have
Why open an issue on this?
This is a big enough topic that I don't want to "just" open a PR for this. Instead I'd like to make sure everyone's on board with the general direction. First I'd like to establish a baseline of "yes we'd like to support this" followed by bikeshedding as necessary over the technical details. My two branches above are a working-enough version of this proposal for what I was working on and my goal in linking them is to having a starting point for design conversations about how best to support no-std.
If accepted my plan would be to incrementally land the linked branch. Notably I'd like to transition a crate-at-a-time as opposed to landing everything all at once. This would need to start in
wasm-tools
with thewasmparser
crate for example.Put another way, I'd like to get others' thoughts on this. I plan on linking this in various locations to raise visibility for interested parties.
The text was updated successfully, but these errors were encountered: