Skip to content

Expand doc section about "what about #![no_std]?" #2024

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

Merged
merged 3 commits into from
Jul 29, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/stability-platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,98 @@ cases for for what `#[no_std]` might entail, so if you're interested in this
we'd love to hear about your use case! Feel free to [open an
issue](https://github.com/bytecodealliance/wasmtime/issues/new) on the
`wasmtime` repository to discuss this.

This is a common question we are asked, however, so to provide some more context
on why Wasmtime is the way it is, here's some responses to frequent points
raised about `#![no_std]`:

* **What if my platform doesn't have `std`?** - For platforms without support
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument doesn't entirely cover other crates than Wasmtime in this repository, or in other repositories, such as wasm-tools. Would it make sense to include another question below, along the lines of "The crate I want to use without std works with small changes, why not accept those"?

After that one, yet another question could be "But I can't use std; what are my options?", where the answer could link to build-std, pointing out that it's Nightly-only for now, but progressing.

for the Rust standard library the JIT compiler of Wasmtime often won't run on
the platform as well. The JIT compiler requires `mmap` (or an equivalent), and
presence of `mmap` often implies presence of a libc which means Rust's `std`
library works.

Cargo's [`-Z build-std` feature][zbuild-std] feature is also intended to help
easily build the standard library for all platforms. With this feature you can
recompile the standard library (using Nightly Rust for now) with a [custom
target specification][custom-target] if necessary. Additionally the intention
at this time is to get `std` building for all platforms, regardless of what
the platform actually supports. This change is taking time to implement, but
[rust-lang/rust#74033] is an example of this support growing over time.

We're also interested in running Wasmtime without a JIT compiler in the
future, but that is not implemented at this time. Implementing this will
require a lot more work than tagging crates `#![no_std]`. The Wasmtime
developers are also very interested in supporting as many targets as possible,
so if Wasmtime doesn't work on your platform yet we'd love to learn why and
what we can do to support that platform, but the conversation here is
typically more nuanced than simply making `wasmtime` compile without `std`.

* **Doesn't `#![no_std]` have smaller binary sizes?** - There's a lot of factors
that affect binary size in Rust. Compilation options are a huge one but beyond
that idioms and libraries linked matter quite a lot as well. Code is not
inherently large when using `std` instead of `core`, it's just that often code
using `std` has more dependencies (like `std::thread`) which requires code to
bind. Code size improvements can be made to code using `std` and `core`
equally, and switching to `#![no_std]` is not a silver bullet for compile
sizes.

* **The patch to switch to `#![no_std]` is small, why not accept it?** - PRs to
switch to `#![no_std]` are often relatively small or don't impact too many
parts of the system. There's a lot more to developing a `#![no_std]`
WebAssembly runtime than switching a few crates, however. Maintaining a
`#![no_std]` library over time has a number of costs associated with it:

* Rust has no stable way to diagnose `no_std` errors in an otherwise `std`
build, which means that to supoprt this feature it must be tested on CI with
a `no_std` target. This is costly in terms of CI time, CI maintenance, and
developers having to do extra builds to avoid CI errors. Note that this
isn't *more* costly than any other platform supported by Wasmtime, but it's
a cost nonetheless.

* Idioms in `#![no_std]` are quite different than normal Rust code. You'll
import from different crates (`core` instead of `std`) and data structures
have to all be manually imported from `alloc`. These idioms are difficult to
learn for newcomers to the project and are not well documented in the
ecosystem. This cost of development and maintenance is not unique to
Wasmtime but in general affects the `#![no_std]` ecosystem at large,
unfortunately.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the no-std-compat crate is considered, this paragraph feels overstated. no-std-compat eliminates the need to manually import from alloc, core, and other crates like sync. The only per-source-file clutter it adds is use std::prelude::v1::*;, which is still a burden, but it is a comparatively small one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally feel like no-std-compat is a good example of "if it compiles it works, right?" with a big emphasis on the question mark. It's trying to make the ecosystem better but in a way that fundamentally is not possible. The best example of this is the dependency on the spin crate to implement std::sync::Mutex. A spin lock is basically never what you want unless you're a kernel and can disable interrupts. Otherwise it's attempting to sidestep fundamental questions of "how do I do multithreading or interact with the system scheduler" where you can't really sidestep these questions.

Crates like that also seem to be taking the fundamental stance of "std will never attempt to fix any of these problems, right?" when in fact PRs like rust-lang/rust#74033 will basically fix the issue for us. If all you want is code to compile using std as-is the way it's written today, then we should be putting energy behind support in the standard library itself upstream. There really is no reason that std can't do what something like no-std-compat already does in a way that supports the already existing ecosystem idioms.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I'm just considering the perspective of a user of Wasmtime asking about no_std support: no-std-compat exists and works today, and doesn't do anything that would get in the way of better solutions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth explicitly calling this out in the documentation here? Basically adding my comment as a new "what about no-std-compat" FAQ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If rust-lang/rust#74033 means that our options will change in the not too distant future, one option here is to replace the above paragraph with one that links to that, and points out that we'd prefer to avoid the thrash of updating all the files to a temporary solution when a better one is one the way. Then I don't think we'd even need to mention no-std-compat.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should at least link to build-std. However, IIUC it'll be quite some time until that reaches stable, so it'll probably not be a "just do this" answer anytime soon. Given that, perhaps we could also link to no-std-compat and point out that people can use it if it works for their use case, but that we don't include it for the reasons @alexcrichton stated above?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-std-compat by itself isn't sufficient to make Wasmtime run in no_std, because at least mmap. We may make that avoidable at some point, but that'll take time too. So I suggest just linking to build-std, and not no-std-compat, and just say that we'll wait until the better solutions arrive.

Also, I think it's reasonable to ask people who want to do no_std things and don't want to wait for build-std features to stabilize to use nightly Rust for a Rust release cycle or two.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I don't really understand though is in what context we're writing this information down. This section is under the question "the patch is small, why not?" and this sub-point is "the idioms are different enough that it's nontrivial to do so". I don't get the impression that people are frequently sending patches with no-std-compat being used. Apart from that I'm not really sure how to tweak the words already there.

If y'all want I can write an explicit bullet saying "no-std-compat doesn't work", or I can add more words under "my target doesn't have std" pointing to -Zbuild-std and the PR I mentioned, but neither of those really changes the point that the idioms of #![no_std] are different than that of std in nontrivial ways.

Also, unrelated to this PR itself, but do y'all think that we should be moving to #![no_std] today? I can't quite get a feeling for whether y'all are playing devil's advocate or are instead along the lines of "I think we should do this and these words don't convince me we shouldn't"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we shouldn't do #![no_std] today. As you mention, until we have a way to avoid calling mmap, it wouldn't matter.

The -Zbuild-std thing sounds cool. I think we should mention/link to that, and say that it doesn't make sense to take on no_std changes with the current tools when better tools are on the horizon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just left another comment on the first question with a suggestion for how this might be structured. Also agreed that mentioning no-std-compat might not be needed.


* Currently Wasmtime does not have a target use case which requires
`#![no_std]` support, so it's hard to justify these costs of development.
We're very interested in supporting as many use cases and targets as
possible, but the decision to support a target needs to take into account
the costs associated so we can plan accordingly. Effectively we need to have
a goal in mind instead of taking on the costs of `#![no_std]` blindly.

* At this time it's not clear whether `#![no_std]` will be needed long-term,
so eating short-term costs may not pay off in the long run. Features like
Cargo's [`-Z build-std`][zbuild-std] may mean that `#![no_std]` is less and
less necessary over time.

* **How can Wasmtime support `#![no_std]` if it uses X?** - Wasmtime as-is today
is not suitable for many `#![no_std]` contexts. For example it might use
`mmap` for allocating JIT code memory, leverage threads for caching, or use
thread locals when calling into JIT code. These features are difficult to
support in their full fidelity on all platforms, but the Wasmtime developers
are very much aware of this! Wasmtime is intended to be configurable where
many of these features are compile-time or runtime options. For example caches
can be disabled, JITs can be removed and replaced with interpreters, or users
could provide a callback to allocate memory instead of using the OS.
This is sort of a long-winded way of saying that Wasmtime on the surface may
today look like it won't support `#![no_std]`, but this is almost always
simply a matter of time and development priorities rather than a fundamental
reason why Wasmtime *couldn't* support `#![no_std]`.

Note that at this time these guidelines apply not only to Wasmtime but also to
some of its dependencies developed by the Bytecode Alliance such as the
[wasm-tools repository](https://github.com/bytecodealliance/wasm-tools). These
projects don't have the same runtime requirements as Wasmtime (e.g. `wasmparser`
doesn't need `mmap`), but we're following the same guidelines above at this
time. Patches to add `#![no_std]`, while possibly small, incur many of the same
costs and also have an unclear longevity as features like [`-Z
build-std`][zbuild-std] evolve.

[zbuild-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
[custom-target]: https://doc.rust-lang.org/rustc/targets/custom.html
[rust-lang/rust#74033]: https://github.com/rust-lang/rust/pull/74033