Skip to content

Commit 710c8ad

Browse files
authored
Unrolled build for rust-lang#129630
Rollup merge of rust-lang#129630 - alexcrichton:document-broken-c-abi-on-wasm32-u-u, r=workingjubilee Document the broken C ABI of `wasm32-unknown-unknown` Inspired by discussion on rust-lang#129486 this is intended to at least document the current state of the world in a more public location than throughout a series of issues.
2 parents 4ac7bcb + 2d6d6a8 commit 710c8ad

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md

+117
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,120 @@ conditionally compile code instead. This is notably different to the way native
195195
platforms such as x86\_64 work, and this is due to the fact that WebAssembly
196196
binaries must only contain code the engine understands. Native binaries work so
197197
long as the CPU doesn't execute unknown code dynamically at runtime.
198+
199+
## Broken `extern "C"` ABI
200+
201+
This target has what is considered a broken `extern "C"` ABI implementation at
202+
this time. Notably the same signature in Rust and C will compile to different
203+
WebAssembly functions and be incompatible. This is considered a bug and it will
204+
be fixed in a future version of Rust.
205+
206+
For example this Rust code:
207+
208+
```rust,ignore (does-not-link)
209+
#[repr(C)]
210+
struct MyPair {
211+
a: u32,
212+
b: u32,
213+
}
214+
215+
extern "C" {
216+
fn take_my_pair(pair: MyPair) -> u32;
217+
}
218+
219+
#[no_mangle]
220+
pub unsafe extern "C" fn call_c() -> u32 {
221+
take_my_pair(MyPair { a: 1, b: 2 })
222+
}
223+
```
224+
225+
compiles to a WebAssembly module that looks like:
226+
227+
```wasm
228+
(module
229+
(import "env" "take_my_pair" (func $take_my_pair (param i32 i32) (result i32)))
230+
(func $call_c
231+
i32.const 1
232+
i32.const 2
233+
call $take_my_pair
234+
)
235+
)
236+
```
237+
238+
The function when defined in C, however, looks like
239+
240+
```c
241+
struct my_pair {
242+
unsigned a;
243+
unsigned b;
244+
};
245+
246+
unsigned take_my_pair(struct my_pair pair) {
247+
return pair.a + pair.b;
248+
}
249+
```
250+
251+
```wasm
252+
(module
253+
(import "env" "__linear_memory" (memory 0))
254+
(func $take_my_pair (param i32) (result i32)
255+
local.get 0
256+
i32.load offset=4
257+
local.get 0
258+
i32.load
259+
i32.add
260+
)
261+
)
262+
```
263+
264+
Notice how Rust thinks `take_my_pair` takes two `i32` parameters but C thinks it
265+
only takes one.
266+
267+
The correct definition of the `extern "C"` ABI for WebAssembly is located in the
268+
[WebAssembly/tool-conventions](https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md)
269+
repository. The `wasm32-unknown-unknown` target (and only this target, not other
270+
WebAssembly targets Rust support) does not correctly follow this document.
271+
272+
Example issues in the Rust repository about this bug are:
273+
274+
* [#115666](https://github.com/rust-lang/rust/issues/115666)
275+
* [#129486](https://github.com/rust-lang/rust/issues/129486)
276+
277+
This current state of the `wasm32-unknown-unknown` backend is due to an
278+
unfortunate accident which got relied on. The `wasm-bindgen` project prior to
279+
0.2.89 was incompatible with the "correct" definition of `extern "C"` and it was
280+
seen as not worth the tradeoff of breaking `wasm-bindgen` historically to fix
281+
this issue in the compiler.
282+
283+
Thanks to the heroic efforts of many involved in this, however, `wasm-bindgen`
284+
0.2.89 and later are compatible with the correct definition of `extern "C"` and
285+
the nightly compiler currently supports a `-Zwasm-c-abi` implemented in
286+
[#117919](https://github.com/rust-lang/rust/pull/117919). This nightly-only flag
287+
can be used to indicate whether the spec-defined version of `extern "C"` should
288+
be used instead of the "legacy" version of
289+
whatever-the-Rust-target-originally-implemented. For example using the above
290+
code you can see (lightly edited for clarity):
291+
292+
```shell
293+
$ rustc +nightly -Zwasm-c-abi=spec foo.rs --target wasm32-unknown-unknown --crate-type lib --emit obj -O
294+
$ wasm-tools print foo.o
295+
(module
296+
(import "env" "take_my_pair" (func $take_my_pair (param i32) (result i32)))
297+
(func $call_c (result i32)
298+
;; ...
299+
)
300+
;; ...
301+
)
302+
```
303+
304+
which shows that the C and Rust definitions of the same function now agree like
305+
they should.
306+
307+
The `-Zwasm-c-abi` compiler flag is tracked in
308+
[#122532](https://github.com/rust-lang/rust/issues/122532) and a lint was
309+
implemented in [#117918](https://github.com/rust-lang/rust/issues/117918) to
310+
help warn users about the transition if they're using `wasm-bindgen` 0.2.88 or
311+
prior. The current plan is to, in the future, switch `-Zwasm-c-api=spec` to
312+
being the default. Some time after that the `-Zwasm-c-abi` flag and the
313+
"legacy" implementation will all be removed. During this process users on a
314+
sufficiently updated version of `wasm-bindgen` should not experience breakage.

0 commit comments

Comments
 (0)