Skip to content
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 wasm32v1-none target (compiler-team/#791) #131487

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,7 @@ supported_targets! {

("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
("wasm32-unknown-unknown", wasm32_unknown_unknown),
("wasm32v1-none", wasm32v1_none),
Copy link

@leighmcculloch leighmcculloch Oct 22, 2024

Choose a reason for hiding this comment

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

Will the way to cfg on this specific target be to specify:

#[cfg(all(target_family = "wasm", target_os = "none"))]

or

#[cfg(all(target_arch = "wasm32", target_os = "none"))]

If in the future a wasm32v2-none is released, how will it be distinguished in a cfg?

Or will the target_arch be wasm32v1 and in the future wasm32v2?

Copy link
Contributor

Choose a reason for hiding this comment

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

You can just differentiate them through the target features.

Copy link

@leighmcculloch leighmcculloch Oct 22, 2024

Choose a reason for hiding this comment

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

Could you provide an example?

At the moment differentiating using target_feature is unreliable, because it's common for target features to become released but their cfg to be still marked as unstable and is therefore not usable in stable rust, as has been the case with:

Copy link
Member

Choose a reason for hiding this comment

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

FWIW the documentation in this PR has an answer to your original question:

It's recommended to conditionally compile code for this target with:

#[cfg(all(target_family = "wasm", target_os = "none"))]

Otherwise I would also agree with @CryZe. All other native platforms have a constant target_arch string with a variable target_feature set corresponding to levels of support for various features.

The situation you're referring to where multivalue and reference-types aren't stable right now isn't an issue for this target. This'll be a new target when it lands and users should also be able to see multivalue and reference-types when they get to use it since that PR should land relatively soon as well.

Stabilization of wasm features has lagged behind for awhile and this is basically good motivation to ensure we keep up in the future. For example another option is blocking a hypothetical wasm32v2-none target until all of the features it enables are landed and stable in Rust. (or, for example, blocking this PR on #131080 but to be clear I'm not advocating we do that)

Copy link

@leighmcculloch leighmcculloch Oct 22, 2024

Choose a reason for hiding this comment

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

Will the target_env be set to v1 or v2?

For example, the following cfg to unambiguously distinguish the target:

#[cfg(all(target_arch = "wasm32", target_env = "v1", target_os = "none"))]

Copy link
Member

Choose a reason for hiding this comment

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

As Alex said: The incentive structure that encourages people to push for stabilizing a target feature name that is well-defined, so that we can e.g. evaluate it for its ABI impacts, is intentional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think what @leighmcculloch is looking for is a way to make a cfg check that specifically identifies the condition of "being limited to the features in wasm32v1-none". Like he wants to not detect any specific new features, but detect the absence of all new features, including features not-yet-invented. Because that's the only case we want to compile for. We want to emit an error if the user tries to build for a target that enables any further wasm features. Which -- even if the current set of feature names all stabilize -- he can't write code to detect because they don't exist yet.

If there were -- speaking hypothetically -- some way to say cfg(target_triple="wasm32v1-none") or even cfg(target_subarch="wasm32v1") that would do the trick. Or anything else that pins to this subarch version and not future versions. I'm not sure if there's a good way to make that happen with the tools currently available though.

Copy link
Member

@workingjubilee workingjubilee Oct 23, 2024

Choose a reason for hiding this comment

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

I believe there is nonesuch in "ways to conveniently do that", yes.

The answer, however, is cfg(target_feature = "nontrapping-fptoint"), or the cfg(not()) version.

The reason that will work is that feature was specifically added for Rust, and it is part of the v2 definition, so "post v1" Rust targets simply will not ever exclude it.

Copy link
Member

Choose a reason for hiding this comment

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

Unless you're asking for "and no 'manual' use of any target_feature anywhere, either", which is:

  • not about the target tuple
  • much closer to "just plain impossible" due to source-level target_feature(enable = "feature") being possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mhm. Thanks. I'm sure we'll figure out some not-so-bad guardrails that handle the majority of cases.

("wasm32-wasi", wasm32_wasi),
("wasm32-wasip1", wasm32_wasip1),
("wasm32-wasip2", wasm32_wasip2),
Expand Down
51 changes: 51 additions & 0 deletions compiler/rustc_target/src/spec/targets/wasm32v1_none.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! A "bare wasm" target representing a WebAssembly output that does not import
//! anything from its environment and also specifies an _upper_ bound on the set
//! of WebAssembly proposals that are supported.
//!
//! It's equivalent to the `wasm32-unknown-unknown` target with the additional
//! flags `-Ctarget-cpu=mvp` and `-Ctarget-feature=+mutable-globals`. This
//! enables just the features specified in <https://www.w3.org/TR/wasm-core-1/>
//!
//! This is a _separate target_ because using `wasm32-unknown-unknown` with
//! those target flags doesn't automatically rebuild libcore / liballoc with
//! them, and in order to get those libraries rebuilt you need to use the
//! nightly Rust feature `-Zbuild-std`. This target is for people who want to
//! use stable Rust, and target a stable set pf WebAssembly features.

use crate::spec::{Cc, LinkerFlavor, Target, base};

pub(crate) fn target() -> Target {
let mut options = base::wasm::options();
options.os = "none".into();

// WebAssembly 1.0 shipped in 2019 and included exactly one proposal
// after the initial "MVP" feature set: "mutable-globals".
options.cpu = "mvp".into();
options.features = "+mutable-globals".into();
alexcrichton marked this conversation as resolved.
Show resolved Hide resolved

options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[
// For now this target just never has an entry symbol no matter the output
// type, so unconditionally pass this.
"--no-entry",
]);
options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[
// Make sure clang uses LLD as its linker and is configured appropriately
// otherwise
"--target=wasm32-unknown-unknown",
"-Wl,--no-entry",
]);

Target {
llvm_target: "wasm32-unknown-unknown".into(),
metadata: crate::spec::TargetMetadata {
description: Some("WebAssembly".into()),
tier: Some(2),
host_tools: Some(false),
std: Some(false),
},
pointer_width: 32,
data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
arch: "wasm32".into(),
options,
}
}
8 changes: 7 additions & 1 deletion compiler/rustc_target/src/spec/tests/tests_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@ impl Target {
// Check dynamic linking stuff
// BPF: when targeting user space vms (like rbpf), those can load dynamic libraries.
// hexagon: when targeting QuRT, that OS can load dynamic libraries.
if self.os == "none" && (self.arch != "bpf" && self.arch != "hexagon") {
// wasm{32,64}: dynamic linking is inherent in the definition of the VM.
if self.os == "none"
&& (self.arch != "bpf"
&& self.arch != "hexagon"
&& self.arch != "wasm32"
&& self.arch != "wasm64")
{
assert!(!self.dynamic_linking);
}
if self.only_cdylib
Expand Down
1 change: 1 addition & 0 deletions src/ci/docker/host-x86_64/dist-various-2/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ ENV TARGETS=$TARGETS,wasm32-wasi
ENV TARGETS=$TARGETS,wasm32-wasip1
ENV TARGETS=$TARGETS,wasm32-wasip1-threads
ENV TARGETS=$TARGETS,wasm32-wasip2
ENV TARGETS=$TARGETS,wasm32v1-none
ENV TARGETS=$TARGETS,sparcv9-sun-solaris
ENV TARGETS=$TARGETS,x86_64-pc-solaris
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
Expand Down
1 change: 1 addition & 0 deletions src/doc/rustc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
- [wasm32-wasip2](platform-support/wasm32-wasip2.md)
- [wasm32-unknown-emscripten](platform-support/wasm32-unknown-emscripten.md)
- [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md)
- [wasm32v1-none](platform-support/wasm32v1-none.md)
- [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md)
- [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md)
- [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md)
Expand Down
1 change: 1 addition & 0 deletions src/doc/rustc/src/platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ target | std | notes
`wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename])
[`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASI
[`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads
[`wasm32v1-none`](platform-support/wasm32v1-none.md) | * | WebAssembly limited to 1.0 features and no imports
[`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS
[`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64
[`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX
Expand Down
24 changes: 17 additions & 7 deletions src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,20 @@ As of the time of this writing the proposals that are enabled by default (the

If you're compiling WebAssembly code for an engine that does not support a
feature in LLVM's default feature set then the feature must be disabled at
compile time. Note, though, that enabled features may be used in the standard
library or precompiled libraries shipped via rustup. This means that not only
does your own code need to be compiled with the correct set of flags but the
Rust standard library additionally must be recompiled.
compile time. There are two approaches to choose from:

- If you are targeting a feature set no smaller than the W3C WebAssembly Core
1.0 recommendation -- which is equivalent to the WebAssembly MVP plus the
`mutable-globals` feature -- and you are building `no_std`, then you can
simply use the [`wasm32v1-none` target](./wasm32v1-none.md) instead of
`wasm32-unknown-unknown`, which uses only those minimal features and
includes a core and alloc library built with only those minimal features.

- Otherwise -- if you need std, or if you need to target the ultra-minimal
"MVP" feature set, excluding `mutable-globals` -- you will need to manually
specify `-Ctarget-cpu=mvp` and also rebuild the stdlib using that target to
ensure no features are used in the stdlib. This in turn requires use of a
nightly compiler.

Compiling all code for the initial release of WebAssembly looks like:

Expand All @@ -150,9 +160,9 @@ then used to recompile the standard library in addition to your own code. This
will produce a binary that uses only the original WebAssembly features by
default and no proposals since its inception.

To enable individual features it can be done with `-Ctarget-feature=+foo`.
Available features for Rust code itself are documented in the [reference] and
can also be found through:
To enable individual features on either this target or `wasm32v1-none`, pass
arguments of the form `-Ctarget-feature=+foo`. Available features for Rust code
itself are documented in the [reference] and can also be found through:

```sh
$ rustc -Ctarget-feature=help --target wasm32-unknown-unknown
Expand Down
109 changes: 109 additions & 0 deletions src/doc/rustc/src/platform-support/wasm32v1-none.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# `wasm32v1-none`

**Tier: 2**

The `wasm32v1-none` target is a WebAssembly compilation target that:

- Imports nothing from its host environment
- Enables no proposals / features past the [W3C WebAssembly Core 1.0 spec]

[W3C WebAssembly Core 1.0 spec]: https://www.w3.org/TR/wasm-core-1/

The target is very similar to [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md) and similarly uses LLVM's `wasm32-unknown-unknown` backend target. It contains only three minor differences:

* Setting the `target-cpu` to `mvp` rather than the default `generic`. Requesting `mvp` disables _all_ WebAssembly proposals / LLVM target feature flags.
* Enabling the [Import/Export of Mutable Globals] proposal (i.e. the `+mutable-globals` LLVM target feature flag)
* Not compiling the `std` library at all, rather than compiling it with stubs.

[Import/Export of Mutable Globals]: https://github.com/WebAssembly/mutable-global

## Target maintainers

- Alex Crichton, https://github.com/alexcrichton
- Graydon Hoare, https://github.com/graydon

## Requirements

This target is cross-compiled. It does not support `std`, only `core` and `alloc`. Since it imports nothing from its environment, any `std` parts that use OS facilities would be stubbed out with functions-that-fail anyways, and the experience of working with the stub `std` in the `wasm32-unknown-unknown` target was deemed not something worth repeating here.

Everything else about this target's requirements, building, usage and testing is the same as what's described in the [`wasm32-unknown-unknown` document](./wasm32-unknown-unknown.md), just using the target string `wasm32v1-none` in place of `wasm32-unknown-unknown`.

## Conditionally compiling code

It's recommended to conditionally compile code for this target with:

```text
#[cfg(all(target_family = "wasm", target_os = "none"))]
```

Note that there is no way to tell via `#[cfg]` whether code will be running on
the web or not.

## Enabled WebAssembly features

As noted above, _no WebAssembly proposals past 1.0_ are enabled on this target by default. Indeed, the entire point of this target is to have a way to compile for a stable "no post-1.0 proposals" subset of WebAssembly _on stable Rust_.

The [W3C WebAssembly Core 1.0 spec] was adopted as a W3C recommendation in December 2019, and includes exactly one "post-MVP" proposal: the [Import/Export of Mutable Globals] proposal.

All subsequent proposals are _disabled_ on this target by default, though they can be individually enabled by passing LLVM target-feature flags.

For reference sake, the set of proposals that LLVM supports at the time of writing, that this target _does not enable by default_, are listed here along with their LLVM target-feature flags:

* Post-1.0 proposals (integrated into the WebAssembly core 2.0 spec):
* [Bulk memory] - `+bulk-memory`
* [Sign-extending operations] - `+sign-ext`
* [Non-trapping fp-to-int operations] - `+nontrapping-fptoint`
* [Multi-value] - `+multivalue`
* [Reference Types] - `+reference-types`
* [Fixed-width SIMD] - `+simd128`
* Post-2.0 proposals:
* [Threads] (supported by atomics) - `+atomics`
* [Exception handling] - `+exception-handling`
* [Extended Constant Expressions] - `+extended-const`
* [Half Precision] - `+half-precision`
* [Multiple memories]- `+multimemory`
* [Relaxed SIMD] - `+relaxed-simd`
* [Tail call] - `+tail-call`

[Bulk memory]: https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.md
[Sign-extending operations]: https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md
[Non-trapping fp-to-int operations]: https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md
[Multi-value]: https://github.com/WebAssembly/spec/blob/main/proposals/multi-value/Overview.md
[Reference Types]: https://github.com/WebAssembly/spec/blob/main/proposals/reference-types/Overview.md
[Fixed-width SIMD]: https://github.com/WebAssembly/spec/blob/main/proposals/simd/SIMD.md
[Threads]: https://github.com/webassembly/threads
[Exception handling]: https://github.com/WebAssembly/exception-handling
[Extended Constant Expressions]: https://github.com/WebAssembly/extended-const
[Half Precision]: https://github.com/WebAssembly/half-precision
[Multiple memories]: https://github.com/WebAssembly/multi-memory
[Relaxed SIMD]: https://github.com/WebAssembly/relaxed-simd
[Tail call]: https://github.com/WebAssembly/tail-call

Additional proposals in the future are, of course, also not enabled by default.

## Rationale relative to wasm32-unknown-unknown

As noted in the [`wasm32-unknown-unknown` document](./wasm32-unknown-unknown.md), it is possible to compile with `--target wasm32-unknown-unknown` and disable all WebAssembly proposals "by hand", by passing `-Ctarget-cpu=mvp`. Furthermore one can enable proposals one by one by passing LLVM target feature flags, such as `-Ctarget-feature=+mutable-globals`.

Is it therefore reasonable to wonder what the difference is between building with this:

```sh
$ rustc --target wasm32-unknown-unknown -Ctarget-cpu=mvp -Ctarget-feature=+mutable-globals
```

and building with this:

```sh
$ rustc --target wasm32v1-none
```

The difference is in how the `core` and `alloc` crates are compiled for distribution with the toolchain, and whether it works on _stable_ Rust toolchains or requires _nightly_ ones. Again referring back to the [`wasm32-unknown-unknown` document](./wasm32-unknown-unknown.md), note that to disable all post-MVP proposals on that target one _actually_ has to compile with this:
Copy link
Member

Choose a reason for hiding this comment

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

If you're feeling up for it (optional as I'm happy to do this later too), mind updating the docs in wasm32-unknown-unknown.md with respect to this new target? For example the documentation can indicate how to disable default features both with -Zbuild-std but also referring to this target primarily. The -Zbuild-std bits aren't really need any more unless you really want the standard library of wasm32-unknown-unknown which is probably unlikely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a note, but I think the build-std part needs to stay if you're targeting MVP and not MVP+mutable-globals as the wasm32v1 target supplies. Or if you need std.


```sh
$ export RUSTFLAGS="-Ctarget-cpu=mvp -Ctarget-feature=+mutable-globals"
$ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unknown
```

Which not only rebuilds `std`, `core` and `alloc` (which is somewhat costly and annoying) but more importantly requires the use of nightly Rust toolchains (for the `-Zbuild-std` flag). This is very undesirable for the target audience, which consists of people targeting WebAssembly implementations that prioritize stability, simplicity and/or security over feature support.

This `wasm32v1-none` target exists as an alternative option that works on stable Rust toolchains, without rebuilding the stdlib.
1 change: 1 addition & 0 deletions src/tools/build-manifest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ static TARGETS: &[&str] = &[
"wasm32-wasip1",
"wasm32-wasip1-threads",
"wasm32-wasip2",
"wasm32v1-none",
"x86_64-apple-darwin",
"x86_64-apple-ios",
"x86_64-apple-ios-macabi",
Expand Down
3 changes: 3 additions & 0 deletions tests/assembly/targets/targets-elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@
//@ revisions: wasm32_unknown_unknown
//@ [wasm32_unknown_unknown] compile-flags: --target wasm32-unknown-unknown
//@ [wasm32_unknown_unknown] needs-llvm-components: webassembly
//@ revisions: wasm32v1_none
//@ [wasm32v1_none] compile-flags: --target wasm32v1-none
//@ [wasm32v1_none] needs-llvm-components: webassembly
//@ revisions: wasm32_wasi
//@ [wasm32_wasi] compile-flags: --target wasm32-wasi
//@ [wasm32_wasi] needs-llvm-components: webassembly
Expand Down
Loading