Stabilize Wasm target features that are in phase 4 and 5 by daxpedda · Pull Request #117457 · rust-lang/rust (original) (raw)

Sure yeah I can try to document and clarify a few things here. I'll reiterate
some bits I mentioned
above
as well.

For background here the WebAssembly Community Group (CG) is the main technical
governing body for WebAssembly at this time. There is a process in place for
adding new features to WebAssembly and it is documented in this repository. As proposals go through
their stages it requires adding support to toolchains and languages which often
means implementing this in LLVM. For Rust that means that there's a question of
when to stabilize and how to support new features of WebAssembly.

Proposals which have reached at least stage 4 are generally considered done. At
that point browsers start shipping features ungated for example. Historically
this at least been my personal loose threshold of when to stabilize features for
Rust. Any feature in phase 3 or lower would, in my opinion, not be a candidate
for stabilization in Rust.

All of the features stabilized here are phase 4 and beyond (many at phase 5).
This means that the threshold for stability in WebAssembly itself is achieved
and the question now turns to Rust to how to expose these features. Features in
WebAssembly are often new instructions but sometimes manifest as new syntactic
forms in a WebAssembly binary. Features also do not work like native platforms
because for a WebAssembly binary to be considered valid to execute it must be
valid in its entirety. In contrast a native binary can contain code that the
current processor doesn't support so long as it doesn't execute it. In that
sense native runtime feature detection is not possible in WebAssembly unlike how
it is for native platforms.

Ok so how does any of this affect Rust. Rust supports compilation to WebAssembly
through LLVM, and in general needs to express the ability to customize the
output binary to handle the presence or non-presence of these features. For
example for performance-related features users may wish to have builds both with
and without the feature. This is achieved right now via a few means:

  1. Via -Ctarget-feature=+foo. AFAIK this isn't gated and is passed through
    straight to LLVM, so this works today and does not require stabilization.
    This works well for WebAssembly since if a feature is enabled for one
    function it may as well be enabled for the whole binary since if one function
    gets it all others will be able to have access to it as well.
  2. Via #[target_feature(enable = "foo")]. This is not stable today and is what
    would be stabilized here. This is where the story is a little murky for
    WebAssembly since unlike native platforms not much is gained from enabling a
    feature per-function rather than at the whole module level. That being said
    this is required for correctness in the case of simd128 for example (e.g.
    LLVM can't codegen intrinsics unless that feature is enabled) and can still
    be useful in some niche situations.
  3. Testing via #[cfg(target_feature = "foo")] currently doesn't work today
    since only when these attributes are stable are the cfg directives defined.
    This can be useful in general but isn't too useful for many of the features
    stabilized here.

So given all that, this PR largely boils down to enabling #[cfg(target_feature = "foo")] and #[target_feature(enable = "foo")] on stable. These aren't the
most useful things in the world but can be useful in some niche situations. What
follows is a more detailed description of what all these features are.

"bulk-memory"

The bulk memory proposal
for WebAssembly can more-or-less be thought of as adding a memcpy instruction
to WebAssembly (or memmove, I forget which allows overlap). There's other bits
and bobs though for "data segments" and constructs in WebAssembly which are not
relevant to Rust-the-language directly. Why users might want this proposal:

"extended-const"

The extended const proposal
enables new forms of constant expressions in WebAssembly. This has no effect on
Rust itself and has nothing to do with Rust const. This is not useful nor
exposed to Rust code at all through LLVM and is only used, as far as I know, for
dynamic linking. The dynamic linking story in Rust for WebAssembly is not
well-defined and has not been fleshed out. Nevertheless though it's stable in
WebAssembly and implemented in LLVM and will be integral for any future
experimentation with dynamic linking for example. This may also be useful for
other more niche situations of users trying to craft particular shapes of wasm
modules.

"mutable-globals"

The mutable global proposal is
largely centered around JS integration of WebAssembly. Honestly I'm not really
sure why this is exposed through LLVM. This has no effect on generated code as
far as I know and is basically only required if you want to experiment with
threads. Any threads-using target for WebAssembly is required to enable this
proposal (e.g. it's a hard requirement check in the LLD linker).

"nontrapping-fptoint"

The non-trapping float-to-int conversions proposal
affects how f32 as i32 is translated in Rust for example. A comparison can be
seen on godbolt.org of how the generated code
differs. For any application bottlenecked on performance of this operation this
would be critical in getting the best performance.

"sign-ext"

The sign extension operations proposal
added a few instructions related to sign extending 8/16-bit values to 32/64-bit
values to the base instruction set. A comparison here can also be seen on godbolt.org. Note that this feature is enabled
by default in the WebAssembly targets today so it's actually somewhat difficult
to disable.