-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Multi value Wasm compilation #73755
Comments
Multivalue support is a target feature. You need to pass |
@CryZe Thanks for info.
|
@AchalaSB Don't know if it helps in any way; but I can run the following with
|
@Muqito with rustc we can do. Since i have some library dependencies i need to run it with |
will |
I am performing some tests with multivalue compilation, and I am encountering a strange issue. Take the following code: #[no_mangle]
pub extern "C" fn magic(a: i32, b: i32) -> (i32, i32) {
(a + b, a - b)
} (Almost the same thing showed by @AchalaSB) This emits to the following asm (using .text
.file "test_wasm.5nl5ejrj-cgu.0"
.section .text.magic,"",@
.globl magic
.type magic,@function
magic:
.functype magic (i32, i32) -> (i32, i32)
local.get 1
local.get 0
i32.add
local.get 0
local.get 1
i32.sub
end_function
.Lfunc_end0:
.size magic, .Lfunc_end0-magic
.section .custom_section.target_features,"",@
.int8 1
.int8 43
.int8 10
.ascii "multivalue"
.section .text.magic,"",@ Fine! Let's compile it and try to convert it back to wat:
Let's try to see what
Just to be sure, let's try to write the corresponding (simplified) wat file, convert it to wasm and back to wat: (module
(func $magic
(export "magic")
(param i32 i32)
(result i32 i32)
local.get 0
local.get 1
i32.mul
local.get 0
local.get 1
i32.sub
)
)
All of this makes me think about linking issue. Let's try something different:
This looks fine! Let's try to link it.
Ok, this is the exact same issue we tried to use normal compilation using cargo. It looks like there are no options to make the linker aware we are using multivalue (maybe it is enabled by default). In any case,
And I am on Arch Linux, if it could help. |
I just realized that it is possible to use emscripten for the linking stage, after the generation of the object files. The generated wasm file is valid, therefore is definitely an issue with the linker. EDIT: I think this is not the real issue, see my comment below. |
@dodomorandi I faced the exactly the same issue when I compile rust code to wasm. The generated wasm file is invalid with pure LLVM. |
I started thinking I am very wrong about the issue: yesterday I just realized that maybe the std need to be compiled with However, I am still unable to create a working example using
#![feature(lang_items)]
#[lang = "eh_personality"] extern fn eh_personality() {}
#[no_mangle]
pub extern "C" fn magic(a: i32, b: i32) -> (i32, i32) {
(a + b, a - b)
} |
Any progress here? I’d be curious about how to create a rust module that imports and exports functions using Wasm multi-value features. |
Ouch, I just though "hey, why didn't I try xargo??"... but rustc did not like that:
Notice that |
So is there a possible working solution for this? |
I've been trying to investigate this further. No solution yet, but some pointers:
Based on the Tweet and LLVM commit code ( test.rs: #[no_mangle]
pub extern fn greet(x: i32, y: i32) -> (i32, i32) {
(x + y, x - y)
} rustc version:
Multivalue feature is visible
Compiling as .wasm
Produces following LLVM (test.ll):
(knowing nothing of LLVM syntax, the test.o contents (
Linking (
Which displays: (
Same also if trying with https://webassembly.github.io/wabt/demo/wasm2wat/ Contents (
Direct compilation to .wasm produces a large file (1.5MB)
Wasm2wat says similar as above. objdump:
I also tried with no success:
With clang, following can be producedC contents:
Emit LLVM (test.ll):
Compile to test.wasm
Produced wat:
|
With further investigation, the I tried the -C values with no help:
Some relevant tickets: |
Just wanted to add that using the
|
This very much seems like a bug in
See WASM specification: https://webassembly.github.io/spec/core/binary/types.html For comparison, this is the (valid) object file before linking:
The only way I found to produce valid multivalue WASM with LLVM 11 was using
(Note that the function signature is different because I played around with different functions but this shouldn't matter) Emitting LLVM-IR from |
Based on what has been discovered by @blaind and @Schuwi, I tried the same test with |
I have not verified it by manually patching LLVM 11 but I am very sure I have found the commit that fixed the incorrect behaviour that I described in my last comment: llvm/llvm-project@304264e (see also https://reviews.llvm.org/D85783) |
I cannot reproduce this with latest nightly (which has llvm12 backend upgrade). The produced wasm artifact built with |
I just tried to compile a project using |
Also see: #73916 |
@dodomorandi are you trying release or debug build? I found that you need to turn on lto in release build. [profile.release]
lto = true and then I once saw a related issue but cannot find it now... |
@zimond Unfortunately I was already trying to build the project in release with LTO (I was interested in changes in size of the resulting wasm) 😞. use std::collections::HashMap;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn test(indices: Vec<i32>, floats: Vec<f32>, k: i32) -> Option<f32> {
let mut data: HashMap<_, _> = indices.into_iter().zip(floats.into_iter()).collect();
data.remove(&k)
} I wanted to use an hashmap because I know that it comes from a precompiled crate, and indeed the linking error is exactly about a signature mismatch for |
This was working fine for me with the above tipps, but sadly seems to have regressed with a very recent nightly. |
Before the regression, were you able to compile with multivalue and link against |
Multi-values with rustc
For those functions: ...
#[no_mangle]
pub extern "C" fn allocate(size: usize) -> *mut c_void {
let mut buffer = Vec::with_capacity(size);
let pointer = buffer.as_mut_ptr();
mem::forget(buffer);
pointer as *mut c_void
}
#[no_mangle]
pub extern "C" fn deallocate(pointer: *mut c_void, capacity: usize) {
unsafe {
let _ = Vec::from_raw_parts(pointer, 0, capacity);
}
}
#[no_mangle]
pub fn load_model() -> (*mut c_char, usize) {
...
let pred: String = prediction[0].to_string();
unsafe {
return (CString::from_vec_unchecked(pred.as_bytes().to_vec()).into_raw(), pred.len());
}
} |
EDIT: Disregard! Multivalue return is supported! See the two following messages below: Edit on 8/9/23, some weeks later: Though multivalue is technically supported, there's a bug that prevents successful compilation of certain libraries, including the popular serde_json. For me, I'm not sure if it's worth the headache for people trying to use Rust for real WASM projects in the real world. There's a workaround (described here), if you're brave enough to try. Original post follows below: So. I'm hard struggling figuring out the current state of affairs on this issue. And to just put it in incredibly simple terms, all I want to do is the turn the following Rust code.
Into something like this in Web Assembly.
In my head, this seems very simple, but I'm neither an expert on Rust nor WASM! Here's a review of what I've found so far:
|
Thank you so much Ashton! Also, 🤦 , now that I see the terminal command, I'm looking above and seeing that there IS a clear "yes." To me, as someone who is admittedly quickly Googling/skimming looking for a quick answer, I found myself feeling it wasn't clear. (And I'm not the only one whose confused!) I'm gonna lay it out very clearly for the future googlers: Can Rust compile to WASM with multi-value returns enabled? You may hit strange compiler errors, like, for example, when using certain libraries, due to this current bug. However, if you want to proceed anyway, I've outlined a working example below. For me, for my project, I noticed that if I passed the --release flag into build, the project would build successfully. I've been told below that it's likely because the function that's ultimately causing this issue is likely optimized out. So, my examples below have you build with --release. Original post continues below. Imagine for example the following very simple Rust code that you want to turn into WASM:
Here's what you need to put in your terminal. Don't need WASI? The following will work:
This will create a WASM file here: Need WASI? You can first install the Bytecode Alliance WASI crate and then:
This will create a WASI-enabled WASM file here: Note: For reasons that I don't fully understand, I wasn't able to get compilation of certain crates (like serde_json) to work unless the --release flag is on. Maybe someone else can help explain what's going on there! Note 2: Okay. Again, for future Googlers just trying to make stuff work. There's a bug that makes it so you can't use certain Rust packages (like, for example, serde_json) when the "multivalue" compiler flag is turned on. See the next few comments for a long train that leads back to this comment. There are a couple suggested workarounds, but the only thing that worked for me was using the |
Just confirmed that this has been working since v1.62. |
Just FYI for future Googlers. I also started to run into weird linking errors when using certain crates like serde_json. For whatever reason, compiling with |
This is probably because the functions that cause this issue are optimized out. #[no_mangle]
pub extern "C" fn test(a: i128, b: i128) -> i128 {
a * b
} ... which uses the |
daxpedda, That's interesting. That makes me think that I'm likely to hit this error in the future. That's scary. I wonder if multi-value is just not ready yet, and we should be making our WASM packages with single returns only. If you or anyone else is interested in diving into the specific problem of serde_json not compiling unless the --release flag is turned on, when multi-value returns are also turned on, I made a minimum reproducible repo here. It's very simple. The readme also has a full report of the error message I've been getting. |
This comment was marked as outdated.
This comment was marked as outdated.
@oxalica, That makes a lot of sense. But...
I've tried this, and I'm getting a similar compiler error. Just for my own sanity, does this command look right?
You can find the repo that I'm using here. Full output:
|
We are going in like the 5th circle now. The current up to date comment regarding the bug is here: #73755 (comment) |
Works on my end as well now. |
Same for me. So I guess this can be closed now? |
@workingjubilee you indicated in #73916 that this still requires a test, but we don't need two open issues for that I think. |
Agreed. |
Using rustc 1.75.0-nightly (249624b 2023-10-20), I just came across this issue again in a wasm32-wasi crate:
which is built using |
wasi-libc is precompiled and even building std won't help with that. Long term LLVM will probably enable multivalue by default, resolving this entire issue. |
I feared as much … is this something that Rust could fix by rebuilding wasi-libc with the requested target features, or is it better to use a variant of wasm-bindgen’s multivalue wrapper for exports (I don’t use wasm-bindgen in my crate yet so I’m not quite sure here what to do about the shadow stack the wrapper uses)? |
…=wesleywiser Stabilize Wasm target features that are in phase 4 and 5 This stabilizes the Wasm target features that are known to be working and in [phase 4 and 5](https://github.com/WebAssembly/proposals/tree/04fa8c810e1dc99ab399e41052a6e427ee988180). Feature stabilized: - [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) - [Import/Export of Mutable Globals](https://github.com/WebAssembly/mutable-global) - [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops) - [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) - [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) Features not stabilized: - [Multi-value](https://github.com/WebAssembly/multi-value): requires rebuilding `std` rust-lang#73755. - [Reference Types](https://github.com/WebAssembly/reference-types): no point stabilizing without rust-lang#103516. - [Threads](https://github.com/webassembly/threads): requires rebuilding `std` rust-lang#77839. - [Relaxed SIMD](https://github.com/WebAssembly/relaxed-simd): separate PR rust-lang#117468. - [Multi Memory](https://github.com/WebAssembly/multi-memory): not implemented. See rust-lang#117457 (comment) for more context. Documentation: rust-lang/reference#1420 Tracking issue: rust-lang#44839
…=wesleywiser Stabilize Wasm target features that are in phase 4 and 5 This stabilizes the Wasm target features that are known to be working and in [phase 4 and 5](https://github.com/WebAssembly/proposals/tree/04fa8c810e1dc99ab399e41052a6e427ee988180). Feature stabilized: - [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) - [Import/Export of Mutable Globals](https://github.com/WebAssembly/mutable-global) - [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops) - [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) - [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) Features not stabilized: - [Multi-value](https://github.com/WebAssembly/multi-value): requires rebuilding `std` rust-lang#73755. - [Reference Types](https://github.com/WebAssembly/reference-types): no point stabilizing without rust-lang#103516. - [Threads](https://github.com/webassembly/threads): requires rebuilding `std` rust-lang#77839. - [Relaxed SIMD](https://github.com/WebAssembly/relaxed-simd): separate PR rust-lang#117468. - [Multi Memory](https://github.com/WebAssembly/multi-memory): not implemented. See rust-lang#117457 (comment) for more context. Documentation: rust-lang/reference#1420 Tracking issue: rust-lang#44839
I wanted to use tuple in my code for wasm application. When i tried to compile my code to wasm using
wasm32-unknown-unknown
code is compiled but its not returning any return. But I see multi-value is recently supported by webassembly community.How can I use multivalue ?
My sample example (Note: I'm not using external library like
wasm-bindgen
as i want to test with pure LLVM backend)My wasm code looks like
LLVM version: 9
Please suggest in this feature
Thanks
The text was updated successfully, but these errors were encountered: