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

Multi value Wasm compilation #73755

Closed
AchalaSB opened this issue Jun 26, 2020 · 62 comments
Closed

Multi value Wasm compilation #73755

AchalaSB opened this issue Jun 26, 2020 · 62 comments
Labels
A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. O-wasm Target: WASM (WebAssembly), http://webassembly.org/

Comments

@AchalaSB
Copy link

AchalaSB commented Jun 26, 2020

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)

#[no_mangle]
pub extern fn test(x:u64, y:u64) -> (u64, u64) {
  (x + y, x - y)
}

My wasm code looks like
Screenshot from 2020-06-26 12-23-07

LLVM version: 9
Please suggest in this feature
Thanks

@CryZe
Copy link
Contributor

CryZe commented Jun 26, 2020

Multivalue support is a target feature. You need to pass -C target-feature=+multivalue to rustc, for it to use it. It then emits the code as you would expect: https://rust.godbolt.org/z/KxUavA

@AchalaSB
Copy link
Author

@CryZe Thanks for info.
I wanted to run it with cargo I tried cargo build --release -Zmultitarget --target=wasm32-unknown-unknown --target-feature=+multivalue
But ended up with errors

error: Found argument '--target-feature' which wasn't expected, or isn't valid in this context
        Did you mean --target-dir?

USAGE:
    cargo build --release --target <Build for the target triple>... --target-dir <DIRECTORY>

For more information try --help

@Muqito
Copy link

Muqito commented Jun 30, 2020

@AchalaSB Don't know if it helps in any way; but I can run the following with rustc

rustc -C target-feature=+multivalue --target=wasm32-unknown-unknown {input}

@AchalaSB
Copy link
Author

AchalaSB commented Jul 1, 2020

@Muqito with rustc we can do. Since i have some library dependencies i need to run it with cargo build

@yodaldevoid
Copy link
Contributor

will cargo rustc --release -Zmultitarget --target=wasm32-unknown-unknown -- -C target-feature=+multivalue work?

@dodomorandi
Copy link

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 --emit asm, kinda misleading when talking about wasm 😅)

        .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:

❯ cargo +nightly rustc --release --target wasm32-unknown-unknown -- -C target-feature=+multivalue
   Compiling test_wasm v0.1.0 (/tmp/test_wasm)
warning: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
 --> src/lib.rs:2:44
  |
2 | pub extern "C" fn magic(a: i32, b: i32) -> (i32, i32) {
  |                                            ^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = help: consider using a struct instead
  = note: tuples have unspecified layout

warning: 1 warning emitted

    Finished release [optimized] target(s) in 0.09s

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm2wat target/wasm32-unknown-unknown/release/test_wasm.wasm
0000010: error: invalid function result count 2, only 1 bytes left in section

Let's try to see what wasm-objdump sees:

❯ wasm-objdump -d target/wasm32-unknown-unknown/release/test_wasm.wasm

test_wasm.wasm: file format wasm 0x1
0000010: error: invalid function result count 2, only 1 bytes left in section

Code Disassembly:

00006f func[0] <magic>:
 000070: 20 01                      | local.get 1
 000072: 20 00                      | local.get 0
 000074: 6a                         | i32.add
 000075: 20 00                      | local.get 0
 000077: 20 01                      | local.get 1
 000079: 6b                         | i32.sub
 00007a: 0b                         | end

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm-objdump -x target/wasm32-unknown-unknown/release/test_wasm.wasm

test_wasm.wasm: file format wasm 0x1
0000010: error: invalid function result count 2, only 1 bytes left in section

Section Details:

Type[1]:
Function[1]:
 - func[0] sig=0 <magic>
Table[1]:
 - table[0] type=funcref initial=1 max=1
Memory[1]:
 - memory[0] pages: initial=16
Global[3]:
 - global[0] i32 mutable=1 - init i32=1048576
 - global[1] i32 mutable=0 <__data_end> - init i32=1048576
 - global[2] i32 mutable=0 <__heap_base> - init i32=1048576
Export[4]:
 - memory[0] -> "memory"
 - func[0] <magic> -> "magic"
 - global[1] -> "__data_end"
 - global[2] -> "__heap_base"
Code[1]:
 - func[0] size=12 <magic>
Custom:
 - name: ".debug_info"
Custom:
 - name: ".debug_pubtypes"
Custom:
 - name: ".debug_ranges"
Custom:
 - name: ".debug_aranges"
Custom:
 - name: ".debug_abbrev"
Custom:
 - name: ".debug_line"
Custom:
 - name: ".debug_str"
Custom:
 - name: ".debug_pubnames"
Custom:
 - name: "name"
 - func[0] <magic>
Custom:
 - name: "producers"
Custom:
 - name: "target_features"

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
  )
)
test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wat2wasm test.wat

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm2wat test.wasm
(module
  (type (;0;) (func (param i32 i32) (result i32 i32)))
  (func (;0;) (type 0) (param i32 i32) (result i32 i32)
    local.get 0
    local.get 1
    i32.mul
    local.get 0
    local.get 1
    i32.sub)
  (export "magic" (func 0)))

All of this makes me think about linking issue. Let's try something different:

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ rustc --crate-type cdylib --target wasm32-unknown-unknown src/lib.rs -C opt-level=3 --emit obj -C target-feature=+multivalue
warning: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
 --> src/lib.rs:2:44
  |
2 | pub extern "C" fn magic(a: i32, b: i32) -> (i32, i32) {
  |                                            ^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = help: consider using a struct instead
  = note: tuples have unspecified layout

warning: 1 warning emitted


test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm-objdump -d lib.o

lib.o:  file format wasm 0x1

Code Disassembly:

000066 func[0] <magic>:
 000067: 20 01                      | local.get 1
 000069: 20 00                      | local.get 0
 00006b: 6a                         | i32.add
 00006c: 20 00                      | local.get 0
 00006e: 20 01                      | local.get 1
 000070: 6b                         | i32.sub
 000071: 0b                         | end

This looks fine! Let's try to link it.

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm-ld lib.o -o libtest.wasm --shared

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0 took 5s
❯ wasm-objdump -d libtest.wasm

libtest.wasm:   file format wasm 0x1
0000021: error: invalid function result count 2, only 1 bytes left in section

Code Disassembly:

0000a7 func[0] <__wasm_call_ctors>:
 0000a8: 10 01                      | call 1 <__wasm_apply_relocs>
 0000aa: 0b                         | end
0000ac func[1] <__wasm_apply_relocs>:
 0000ad: 0b                         | end
0000af func[2] <magic>:
 0000b0: 20 01                      | local.get 1
 0000b2: 20 00                      | local.get 0
 0000b4: 6a                         | i32.add
 0000b5: 20 00                      | local.get 0
 0000b7: 20 01                      | local.get 1
 0000b9: 6b                         | i32.sub
 0000ba: 0b                         | end

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,

test_wasm on  master [?] is 📦 v0.1.0 via 🦀 v1.46.0
❯ wasm-ld --version
LLD 10.0.1

And I am on Arch Linux, if it could help.

@dodomorandi
Copy link

dodomorandi commented Sep 12, 2020

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.

@AchalaSB
Copy link
Author

@dodomorandi I faced the exactly the same issue when I compile rust code to wasm. The generated wasm file is invalid with pure LLVM.
(In my case, I wanted to use LLVM only not emscripten )

@dodomorandi
Copy link

I started thinking I am very wrong about the issue: yesterday I just realized that maybe the std need to be compiled with multivalue enabled, otherwise there is some sort of incompatibility between object files. This makes sense with the kind of errors I am encountering, and the only issue with the linker is not notifying that you are trying to link together files that should not.

However, I am still unable to create a working example using -Z build-std in nightly. I am trying the following, without success -- maybe some of you know how to tackle down the issue.

RUSTFLAGS="-C target-feature=+multivalue,+reference-types,+bulk-memory -C panic=unwind" cargo build --target wasm32-unknown-unknown -Z build-std
#![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)
}

@camelid camelid added the O-wasm Target: WASM (WebAssembly), http://webassembly.org/ label Oct 20, 2020
@nomeata
Copy link

nomeata commented Nov 2, 2020

Any progress here? I’d be curious about how to create a rust module that imports and exports functions using Wasm multi-value features.

@dodomorandi
Copy link

Ouch, I just though "hey, why didn't I try xargo??"... but rustc did not like that:

> cat Xargo.toml 
[target.wasm32-unknown-unknown.dependencies.std]
features = []

> RUSTFLAGS="-C target-feature=+multivalue" xargo +nightly build --target wasm32-unknown-unknown
warning: Patch `rustc-std-workspace-std v1.99.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/rustc-std-workspace-std)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
   Compiling compiler_builtins v0.1.36
   Compiling core v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core)
   Compiling libc v0.2.79
   Compiling cc v1.0.60
   Compiling dlmalloc v0.1.4
   Compiling std v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std)
   Compiling unwind v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/unwind)
   Compiling rustc-std-workspace-core v1.99.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/rustc-std-workspace-core)
   Compiling alloc v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc)
   Compiling cfg-if v0.1.10
   Compiling rustc-demangle v0.1.18
   Compiling panic_abort v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/panic_abort)
error: could not compile `compiler_builtins`

Caused by:
  process didn't exit successfully: `rustc --crate-name compiler_builtins /home/user/.cargo/registry/src/git.luolix.top-1ecc6299db9ec823/compiler_builtins-0.1.36/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts --crate-type lib --emit=dep-info,metadata,link -C opt-level=3 -C embed-bitcode=no --cfg 'feature="compiler-builtins"' --cfg 'feature="core"' --cfg 'feature="default"' --cfg 'feature="rustc-dep-of-std"' -C metadata=635019da5e105c8b -C extra-filename=-635019da5e105c8b --out-dir /tmp/xargo.mX5YtAnFkwJX/target/wasm32-unknown-unknown/release/deps --target wasm32-unknown-unknown -L dependency=/tmp/xargo.mX5YtAnFkwJX/target/wasm32-unknown-unknown/release/deps -L dependency=/tmp/xargo.mX5YtAnFkwJX/target/release/deps --extern core=/tmp/xargo.mX5YtAnFkwJX/target/wasm32-unknown-unknown/release/deps/librustc_std_workspace_core-4718ce578524c9f5.rmeta --cap-lints allow -C target-feature=+multivalue --sysroot /home/user/.xargo -Z force-unstable-if-unmarked --cfg 'feature="unstable"' --cfg 'feature="mem"'` (signal: 7, SIGBUS: access to undefined memory)
warning: build failed, waiting for other jobs to finish...
error: build failed
error: `"cargo" "build" "--release" "--manifest-path" "/tmp/xargo.mX5YtAnFkwJX/Cargo.toml" "--target" "wasm32-unknown-unknown" "-p" "std"` failed with exit code: Some(101)
note: run with `RUST_BACKTRACE=1` for a backtrace

Notice that SIGBUS error at the end of the cause...

@zimond
Copy link

zimond commented Dec 24, 2020

So is there a possible working solution for this?

@blaind
Copy link

blaind commented Jan 17, 2021

I've been trying to investigate this further. No solution yet, but some pointers:

Based on the Tweet and LLVM commit code (getTarget().getABI() == "experimental-mv"), the experimental-mv ABI should be set, but how?

test.rs:

#[no_mangle]
pub extern fn greet(x: i32, y: i32) -> (i32, i32) {
  (x + y, x - y)
}

rustc version:

$ rustc --version
rustc 1.51.0-nightly (bc39d4d9c 2021-01-15)

Multivalue feature is visible

$ rustc --print target-features --target wasm32-unknown-unknown|grep multivalue
    multivalue            - Enable multivalue blocks, instructions, and functions.

Compiling as .wasm

rustc -v --crate-type=cdylib -C opt-level=1 --target wasm32-unknown-unknown -C target-feature=+multivalue --emit obj,llvm-ir test.rs
warning: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
 --> test.rs:2:40
  |
2 | pub extern fn greet(x: i32, y: i32) -> (i32, i32) {
  |                                        ^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = help: consider using a struct instead
  = note: tuples have unspecified layout

warning: 1 warning emitted

Produces following LLVM (test.ll):

; ModuleID = 'test.3a1fbbbh-cgu.0'
source_filename = "test.3a1fbbbh-cgu.0"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

; Function Attrs: norecurse nounwind readnone
define { i32, i32 } @greet(i32 %x, i32 %y) unnamed_addr #0 {
start:
  %_3 = add i32 %y, %x
  %_6 = sub i32 %x, %y
  %0 = insertvalue { i32, i32 } undef, i32 %_3, 0
  %1 = insertvalue { i32, i32 } %0, i32 %_6, 1
  ret { i32, i32 } %1
}

attributes #0 = { norecurse nounwind readnone "target-cpu"="generic" "target-features"="+multivalue" }

(knowing nothing of LLVM syntax, the unnamed_addr and undefpoint out, what are those?

test.o contents (xxd test.o)

00000000: 0061 736d 0100 0000 0188 8080 8000 0160  .asm...........`
00000010: 027f 7f02 7f7f 02ba 8080 8000 0203 656e  ..............en
00000020: 760f 5f5f 6c69 6e65 6172 5f6d 656d 6f72  v.__linear_memor
00000030: 7902 0000 0365 6e76 195f 5f69 6e64 6972  y....env.__indir
00000040: 6563 745f 6675 6e63 7469 6f6e 5f74 6162  ect_function_tab
00000050: 6c65 0170 0000 0382 8080 8000 0100 0a8e  le.p............
00000060: 8080 8000 010c 0020 0120 006a 2000 2001  ....... . .j . .
00000070: 6b0b 0099 8080 8000 076c 696e 6b69 6e67  k........linking
00000080: 0208 8a80 8080 0001 0000 0005 6772 6565  ............gree
00000090: 7400 9d80 8080 000f 7461 7267 6574 5f66  t.......target_f
000000a0: 6561 7475 7265 7301 2b0a 6d75 6c74 6976  eatures.+.multiv
000000b0: 616c 7565                                alue

Linking ($ wasm-ld-11 --verbose test.o -o test.wasm --shared)

wasm-ld-11: Loading: test.o
wasm-ld-11: warning: creating shared libraries, with -shared, is not yet stable
wasm-ld-11: Processing: test.o
wasm-ld-11: -- createOutputSegments
wasm-ld-11: -- createSyntheticSections
wasm-ld-11: -- populateProducers
wasm-ld-11: -- populateTargetFeatures
wasm-ld-11: -- calculateImports
wasm-ld-11: -- layoutMemory
wasm-ld-11: mem: global base = 0
wasm-ld-11: mem: static data = 0
wasm-ld-11: -- scanRelocations
wasm-ld-11: -- assignIndexes
wasm-ld-11: -- calculateInitFunctions
wasm-ld-11: -- calculateTypes
wasm-ld-11: -- calculateExports
wasm-ld-11: -- calculateCustomSections
wasm-ld-11: calculateCustomSections
wasm-ld-11: -- populateSymtab
wasm-ld-11: -- addSections
wasm-ld-11: addSection: CUSTOM(dylink)
wasm-ld-11: addSection: TYPE
wasm-ld-11: addSection: IMPORT
wasm-ld-11: addSection: FUNCTION
wasm-ld-11: addSection: EXPORT
wasm-ld-11: addSection: CODE
wasm-ld-11: createCustomSections
wasm-ld-11: addSection: CUSTOM(name)
wasm-ld-11: addSection: CUSTOM(target_features)
wasm-ld-11: Defined Functions: 3
wasm-ld-11: Defined Globals  : 0
wasm-ld-11: Defined Events   : 0
wasm-ld-11: Function Imports : 0
wasm-ld-11: Global Imports   : 2
wasm-ld-11: Event Imports    : 0
wasm-ld-11: info for: test.o
              Symbols : 1
     Function Imports : 0
       Global Imports : 0
        Event Imports : 0
wasm-ld-11: -- finalizeSections
wasm-ld-11: setOffset: CUSTOM(dylink): 8
wasm-ld-11: createHeader: CUSTOM(dylink) body=12 total=14
wasm-ld-11: setOffset: TYPE: 22
wasm-ld-11: createHeader: TYPE body=10 total=12
wasm-ld-11: setOffset: IMPORT: 34
wasm-ld-11: createHeader: IMPORT body=90 total=92
wasm-ld-11: setOffset: FUNCTION: 126
wasm-ld-11: createHeader: FUNCTION body=4 total=6
wasm-ld-11: setOffset: EXPORT: 132
wasm-ld-11: createHeader: EXPORT body=29 total=31
wasm-ld-11: setOffset: CODE: 163
wasm-ld-11: createHeader: CODE body=22 total=24
wasm-ld-11: setOffset: CUSTOM(name): 187
wasm-ld-11: createHeader: CUSTOM(name) body=55 total=57
wasm-ld-11: setOffset: CUSTOM(target_features): 244
wasm-ld-11: createHeader: CUSTOM(target_features) body=29 total=31
wasm-ld-11: -- openFile
wasm-ld-11: writing: test.wasm
wasm-ld-11: -- writeSections
wasm-ld-11: writing CUSTOM(target_features)
wasm-ld-11: writing CUSTOM(name)
wasm-ld-11: writing CODE
wasm-ld-11:  size=24
wasm-ld-11:  headersize=2
wasm-ld-11: writing FUNCTION
wasm-ld-11: writing IMPORT
wasm-ld-11:  codeheadersize=1
wasm-ld-11: writing EXPORT
wasm-ld-11: writing TYPE
wasm-ld-11: writing CUSTOM(dylink

Which displays: ($ wasm2wat --enable-multi-value test.wasm)

0000021: error: invalid function result count 2, only 1 bytes left in section

Same also if trying with https://webassembly.github.io/wabt/demo/wasm2wat/

Contents ($ xxd test.wasm)

xxd test.wasm 
00000000: 0061 736d 0100 0000 000c 0664 796c 696e  .asm.......dylin
00000010: 6b00 0000 0000 010a 0260 0000 6002 7f7f  k........`..`...
00000020: 027f 025a 0403 656e 7606 6d65 6d6f 7279  ...Z..env.memory
00000030: 0200 0003 656e 7619 5f5f 696e 6469 7265  ....env.__indire
00000040: 6374 5f66 756e 6374 696f 6e5f 7461 626c  ct_function_tabl
00000050: 6501 7000 0003 656e 760d 5f5f 6d65 6d6f  e.p...env.__memo
00000060: 7279 5f62 6173 6503 7f00 0365 6e76 0c5f  ry_base....env._
00000070: 5f74 6162 6c65 5f62 6173 6503 7f00 0304  _table_base.....
00000080: 0300 0001 071d 0211 5f5f 7761 736d 5f63  ........__wasm_c
00000090: 616c 6c5f 6374 6f72 7300 0005 6772 6565  all_ctors...gree
000000a0: 7400 020a 1603 0400 1001 0b02 000b 0c00  t...............
000000b0: 2001 2000 6a20 0020 016b 0b00 3704 6e61   . .j . .k..7.na
000000c0: 6d65 0130 0300 115f 5f77 6173 6d5f 6361  me.0...__wasm_ca
000000d0: 6c6c 5f63 746f 7273 0113 5f5f 7761 736d  ll_ctors..__wasm
000000e0: 5f61 7070 6c79 5f72 656c 6f63 7302 0567  _apply_relocs..g
000000f0: 7265 6574 001d 0f74 6172 6765 745f 6665  reet...target_fe
00000100: 6174 7572 6573 012b 0a6d 756c 7469 7661  atures.+.multiva
00000110: 6c75 65                                  lue

Direct compilation to .wasm produces a large file (1.5MB)

$ rustc -v --crate-type=cdylib -C opt-level=1 --target wasm32-unknown-unknown -C target-feature=+multivalue test.rs
warning: `extern` fn uses type `(i32, i32)`, which is not FFI-safe
 --> test.rs:2:40
  |
2 | pub extern fn greet(x: i32, y: i32) -> (i32, i32) {
  |                                        ^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = help: consider using a struct instead
  = note: tuples have unspecified layout

warning: 1 warning emitted

Wasm2wat says similar as above.

objdump:

$ wasm-objdump -x test.wasm 

test.wasm:	file format wasm 0x1
0000010: error: invalid function result count 2, only 1 bytes left in section

Section Details:

Type[1]:
Function[1]:
 - func[0] sig=0 <greet>
(...)

I also tried with no success:

  • disabling the cargo lint warning (#[allow(improper_ctypes_definitions)])
  • trying to return array [i32; 2] instead of a tuple (i32, i32)
  • wasm-ld-10 (earlier version)
  • building with wasm32-wasi and wasm32-unknown-emscripten targets (did not fail, but produces no multi-value wasm)
  • returning struct(i32, i32) and struct { a: i32, b: i32 }

With clang, following can be produced

C contents: $ cat test.cpp

typedef struct {
  int aa;
  int bb;
} s1;

s1 struct_ret(int a) {
  s1 foo;
  return foo;
}

Emit LLVM (test.ll):

$ clang++-11 -mmultivalue -Xclang -target-abi -Xclang experimental-mv test.cpp -ObjC++ --compile --target=wasm32-unknown-unknown-wasm --optimize=3 --output test.ll -S -emit-llvm
$ cat test.ll 
; ModuleID = 'test.cpp'
source_filename = "test.cpp"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown-wasm"

%struct.s1 = type { i32, i32 }

; Function Attrs: norecurse nounwind readnone
define hidden %struct.s1 @_Z10struct_reti(i32 %0) local_unnamed_addr #0 {
  ret %struct.s1 undef
}

attributes #0 = { norecurse nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+multivalue" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Ubuntu clang version 11.0.0-2~ubuntu20.04.1"}

Compile to test.wasm

$ clang++-11 -mmultivalue -Xclang -target-abi -Xclang experimental-mv test.cpp -ObjC++ --compile --target=wasm32-unknown-unknown-wasm --optimize=3 --output test.wasm

Produced wat:

$ wasm2wat --enable-multi-value test.wasm 
(module
  (type (;0;) (func (param i32) (result i32 i32)))
  (import "env" "__linear_memory" (memory (;0;) 0))
  (import "env" "__indirect_function_table" (table (;0;) 0 funcref))
  (func (;0;) (type 0) (param i32) (result i32 i32)
    (local i32)
    local.get 1
    local.get 1))

@blaind
Copy link

blaind commented Jan 17, 2021

With further investigation, the -target-abi experimental-mv seems to be a crucial part. In addition, rust should return the value as a struct (maybe the target-abi will effect that?).

I tried the -C values with no help:

$ rustc --help --target wasm32-unknown-unknown|grep target
        --print [crate-name|file-names|sysroot|target-libdir|cfg|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|native-static-libs]

Some relevant tickets:
#80970
#60617 (comment)

@Schuwi
Copy link

Schuwi commented Feb 4, 2021

Just wanted to add that using the wasm-ld linker version 12 or later makes it work.
For example by compiling it and adding it to the ~/.cargo/config.toml:

[target.wasm32-wasi]
linker = "/path/to/llvm-12-binaries/lld"

@Schuwi
Copy link

Schuwi commented Feb 5, 2021

This very much seems like a bug in wasm-ld v11 because the linker actually produces syntactially invalid WASM bytecode:

$ wasm-objdump -sj type wasm-ld_linked_rust.wasm 

test.wasm:	file format wasm 0x1
0000012: error: invalid function result count 2, only 1 bytes left in section

Contents of section Type:
000000a: 0260 0000 6001 7f02 7f                   .`..`....
         ^ ^       ^      ^  ^ ^
         | +-------+      |  | +-- MISSING
    vector         |      |  |
   length 2     type      |  type
            function      |  i32
                          |
              vector (return types)
                    length 2

See WASM specification: https://webassembly.github.io/spec/core/binary/types.html

For comparison, this is the (valid) object file before linking:

$ wasm-objdump -sj type rustc_and_llc_compiled_rust.o

test.o:	file format wasm 0x1

Contents of section Type:
000000e: 0160 017f 027f 7f                        .`.....

The only way I found to produce valid multivalue WASM with LLVM 11 was using clang to compile directly to .wasm, the way @blaind described:

$ wasm-objdump -sj type clang_compiled_c.wasm

whatevernew.wasm:	file format wasm 0x1

Contents of section Type:
000000a: 0260 0000 6002 7e7e 027e 7e              .`..`.~~.~~

(Note that the function signature is different because I played around with different functions but this shouldn't matter)

Emitting LLVM-IR from clang, compiling it with llc to .o and linking it with wasm-ld yields the same result as with the Rust source code. The object file seems good but the linked WASM file is broken.

@dodomorandi
Copy link

Based on what has been discovered by @blaind and @Schuwi, I tried the same test with wasm-objdump using nightly -- some days ago the migration to LLVM 12 has been merged. It looks like things are working fine 🎉. If anyone wants to perform further checks, it could be even better, in order to understand if there are still some issue around compiling with multi-value, or if we just need to wait 1.52 to have this really nice feature in stable 😀.

@Schuwi
Copy link

Schuwi commented Mar 16, 2021

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)

@zimond
Copy link

zimond commented Mar 17, 2021

I cannot reproduce this with latest nightly (which has llvm12 backend upgrade). The produced wasm artifact built with RUSTFLAGS="-C target-feature=+multivalue" has the correct signatures and works well.

@dodomorandi
Copy link

I just tried to compile a project using multivalue, but unfortunately the resulting wasm is invalid, according to wasm2wat. I think that the reason is just that std, core and alloc are compiled without multivalue. I tried to use -Z build-std, but I get duplicated symbols at linking time.

@adrian17
Copy link

Also see: #73916

@zimond
Copy link

zimond commented Mar 18, 2021

@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 RUSTFLAGS="-C target-feature=+multivalue" cargo build --release --target wasm32-unknown-unkown

I once saw a related issue but cannot find it now...

@dodomorandi
Copy link

@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) 😞.
Moreover, I am not able to create a small, not working, test. The only thing I am able to reproduce is a linking error if I remove LTO (like you just said). The code triggering the issue is the following:

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 hashbrown::raw::Fallibility::alloc_err::h9aec3be52e926342. It looks like when LTO is able to inline a function, it can use the multivalue signature, otherwise an error occurs. What I don't understand is why, in my project, the linking process succeed but the wasm is invalid -- I would expect the opposite...

@theduke
Copy link
Contributor

theduke commented May 19, 2021

This was working fine for me with the above tipps, but sadly seems to have regressed with a very recent nightly.

@dodomorandi
Copy link

Before the regression, were you able to compile with multivalue and link against std, core and alloc? It just worked out of the box or you were able to use build-std nightly feature?

@proohit
Copy link

proohit commented May 20, 2021

Multi-values with rustc 1.52.1 and nightly 1.54.0 are failing for me too. I am getting function signature mismatch errors from rust-lld

$ cargo rustc --release --target wasm32-wasi -- -C target-feature=+multivalue
...
= note: rust-lld: error: function signature mismatch: _ZN3std3ffi5c_str7CString18from_vec_unchecked17heb55af31731c143eE
          >>> defined as (i32) -> i32 in /home/direnc/workspace/nodejs-rust/smartcore-wasi/target/wasm32-wasi/release/deps/smartcore_wasi_lib.smartcore_wasi_lib.bwxwt9he-cgu.7.rcgu.o
          >>> defined as (i32, i32) -> void in /home/direnc/.rustup/toolchains/1.52.1-x86_64-unknown-linux-gnu/lib/rustlib/wasm32-wasi/lib/libstd-3ee14805eea1e96d.rlib(std-3ee14805eea1e96d.std.4z7x2hk1-cgu.0.rcgu.o)
          
          rust-lld: error: function signature mismatch: _ZN3std3ffi5c_str7CString10into_inner17h5643fc78239ca534E
          >>> defined as (i32, i32) -> i32 in /home/direnc/workspace/nodejs-rust/smartcore-wasi/target/wasm32-wasi/release/deps/smartcore_wasi_lib.smartcore_wasi_lib.bwxwt9he-cgu.7.rcgu.o
          >>> defined as (i32, i32, i32) -> void in /home/direnc/.rustup/toolchains/1.52.1-x86_64-unknown-linux-gnu/lib/rustlib/wasm32-wasi/lib/libstd-3ee14805eea1e96d.rlib(std-3ee14805eea1e96d.std.4z7x2hk1-cgu.0.rcgu.o)
          
          rust-lld: error: function signature mismatch: _ZN7bincode2de4read11SliceReader3new17h0c9b273d280dc95dE
          >>> defined as (i32, i32) -> i32 in /home/direnc/workspace/nodejs-rust/smartcore-wasi/target/wasm32-wasi/release/deps/smartcore_wasi_lib.smartcore_wasi_lib.bwxwt9he-cgu.15.rcgu.o
          >>> defined as (i32, i32, i32) -> void in /home/direnc/workspace/nodejs-rust/smartcore-wasi/target/wasm32-wasi/release/deps/libbincode-f7c047cbbfd01ab7.rlib(bincode-f7c047cbbfd01ab7.bincode.4dc53osx-cgu.14.rcgu.o)

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());
        }
}

@eastside
Copy link

eastside commented Jun 2, 2023

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.

pub extern "C" fn flip(a: i32, b: i32) -> (i32, i32) {
    (b, a)
}

Into something like this in Web Assembly.

(func (param i32 i32) (result i32 i32)))

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:

  • There's this long article by Mozilla which seems to suggest it's possible. But it ends with returning a String, with WASI turned on; not the simple multi-return that the article begins with (and what I want to do).
  • There's a suspiciously-named multi-value-xform crate in wasm-bindgen. I see some Github comments saying that it's possible to use it successfully, but the documentation on the crate is (to my eyes) insufficient to explain how to use it.
  • There's a Stack Overflow article post that categorically says there's no way.

So, my own conclusion for my company is, multi-return in Rust isn't supported, shouldn't be relied upon, and other methods should be used instead (pointers/shared memory). But I'd love to be proven wrong. Edit: It's supported! See below!

@ashtonmeuser
Copy link

@eastside What you're describing is entirely possible. You'll need to build with RUSTFLAGS="-C target-feature=+multivalue" cargo build. See Rust example here.

@eastside
Copy link

eastside commented Jun 5, 2023

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? YES! Edit on 8/9/23: I've crossed out my all caps "YES" and instead am writing "Yes, with caveats."

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:

#[no_mangle]
pub fn flip(a: u32, b: u32) -> (u32, u32) {
    (b, a)
}

Here's what you need to put in your terminal.

Don't need WASI? The following will work:

    RUSTFLAGS="-C target-feature=+multivalue" cargo build --target=wasm32-unknown-unknown --release

This will create a WASM file here: ./target/wasm32-unknown-unknown/debug/<project_name>.wasm".

Need WASI? You can first install the Bytecode Alliance WASI crate and then:

    RUSTFLAGS="-C target-feature=+multivalue" cargo wasi build --release

This will create a WASI-enabled WASM file here: ./target/wasm32-wasi/debug/<project_name>.wasi.wasm

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 --release flag on build. (Which is in the examples.)

@daxpedda
Copy link
Contributor

daxpedda commented Jun 6, 2023

Just confirmed that this has been working since v1.62.
Though it doesn't work with -Zbuild-std, which might or might not deserve it's own separate issue.

@eastside
Copy link

eastside commented Aug 7, 2023

In my case, it's serde_json::from_str(&data).unwrap(); I used in my function which causes the signature mismatch;

= note: rust-lld: error: function signature mismatch: _ZN4core5slice6memchr19memchr_general_case17hcccfc3a6608065d9E
          >>> defined as (i32, i32, i32) -> i32 in /root/test/target/wasm32-unknown-unknown/debug/deps/libserde_json-5d93b3d735b5d5db.rlib(serde_json-5d93b3d735b5d5db.serde_json.068d47d2-cgu.11.rcgu.o)
          >>> defined as (i32, i32, i32, i32) -> void in /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/wasm32-unknown-unknown/lib/libcore-3365b6c50e94718e.rlib(core-3365b6c50e94718e.core.6784a36f-cgu.0.rcgu.o)

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 --release makes compilation work. I don't know why!

@daxpedda
Copy link
Contributor

daxpedda commented Aug 8, 2023

This is probably because the functions that cause this issue are optimized out.
You can try the following (this is just an example, i128 can't actually be used in FFI like this):

#[no_mangle]
pub extern "C" fn test(a: i128, b: i128) -> i128 {
    a * b
}

... which uses the __multi3() function, which then triggers the usual error.

@eastside
Copy link

eastside commented Aug 8, 2023

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.

@oxalica

This comment was marked as outdated.

@eastside
Copy link

eastside commented Aug 9, 2023

@oxalica, That makes a lot of sense. But...

The workaround is to rebuild std with +multivalue, via -Zbuild-std=panic_abort,std. It works for my wasm32 project.

I've tried this, and I'm getting a similar compiler error. Just for my own sanity, does this command look right?

RUSTFLAGS="-C target-feature=+multivalue" cargo build --target=wasm32-unknown-unknown -Zbuild-std=panic_abort,std

You can find the repo that I'm using here.

Full output:

MacBook-Pro:minirepro-libserde-multivalue-return adam$ RUSTFLAGS="-C target-feature=+multivalue" cargo build --target=wasm32-unknown-unknown -Zbuild-std=panic_abort,std
   Compiling minirepro-libserde-multivalue-return v0.1.0 (/Users/adam/minirepro-libserde-multivalue-return)
error: linking with `rust-lld` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/Users/adam/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/bin:/usr/local/opt/binutils/bin:/Library/Frameworks/Python.framework/Versions/3.11/bin:/Users/adam/.cargo/bin:/Users/adam/google-cloud-sdk/bin:/Library/Frameworks/Python.framework/Versions/3.10/bin:/Users/adam/wabt/build:/usr/local/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/share/dotnet:/opt/X11/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/usr/local/bin" VSLANG="1033" "rust-lld" "-flavor" "wasm" "--rsp-quoting=posix" "--export" "flip" "--export=__heap_base" "--export=__data_end" "-z" "stack-size=1048576" "--stack-first" "--allow-undefined" "--fatal-warnings" "--no-demangle" "--no-entry" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/minirepro_libserde_multivalue_return.4y90sm3p1fejt64q.rcgu.o" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/minirepro_libserde_multivalue_return.zal0zzip8bz4m58.rcgu.o" "-L" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps" "-L" "/Users/adam/minirepro-libserde-multivalue-return/target/debug/deps" "-L" "/Users/adam/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/wasm32-unknown-unknown/lib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libserde_json-e33cba2ad6a76f4a.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libryu-88801755591df16f.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libitoa-8761e3f6cc21fb20.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libserde-0b0f9b61aef32b7b.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libstd-2032a15c123357aa.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libpanic_abort-60b1814859309e95.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libdlmalloc-229a97408b34b9eb.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/librustc_demangle-de952055b73b7865.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libstd_detect-8f4c1825027796fb.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libhashbrown-c9e6965dd7c929a1.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libminiz_oxide-982767ea9f46eb7b.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libadler-197b17a0b3e79988.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/librustc_std_workspace_alloc-3a9b4c04bd114af0.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libunwind-f3a0528864c34232.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcfg_if-87a85cd5561f2c8a.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/liblibc-49d9f47b0288b47b.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/liballoc-95a2c3bfb3c2627b.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/librustc_std_workspace_core-11bf4e7280bba621.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcore-00aecf5c9d208fd5.rlib" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib" "-L" "/Users/adam/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/wasm32-unknown-unknown/lib" "-o" "/Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/minirepro_libserde_multivalue_return.wasm" "--gc-sections" "--no-entry" "-O0"
  = note: rust-lld: error: function signature mismatch: __udivti3
          >>> defined as (i32, i64, i64, i64, i64) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcore-00aecf5c9d208fd5.rlib(core-00aecf5c9d208fd5.core.39255c92-cgu.0.rcgu.o)
          >>> defined as (i64, i64, i64, i64) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.7.rcgu.o)
          
          rust-lld: error: function signature mismatch: __ashlti3
          >>> defined as (i32, i64, i64, i32) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.1.rcgu.o)
          >>> defined as (i64, i64, i32) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          
          rust-lld: error: function signature mismatch: __multi3
          >>> defined as (i32, i64, i64, i64, i64) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libserde_json-e33cba2ad6a76f4a.rlib(serde_json-e33cba2ad6a76f4a.serde_json.bb90434a-cgu.1.rcgu.o)
          >>> defined as (i64, i64, i64, i64) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          
          rust-lld: error: function signature mismatch: __ashrti3
          >>> defined as (i32, i64, i64, i32) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.8.rcgu.o)
          >>> defined as (i64, i64, i32) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          
          rust-lld: error: function signature mismatch: __umodti3
          >>> defined as (i32, i64, i64, i64, i64) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcore-00aecf5c9d208fd5.rlib(core-00aecf5c9d208fd5.core.39255c92-cgu.0.rcgu.o)
          >>> defined as (i64, i64, i64, i64) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          
          rust-lld: error: function signature mismatch: __lshrti3
          >>> defined as (i32, i64, i64, i32) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libryu-88801755591df16f.rlib(ryu-88801755591df16f.ryu.e140e1de-cgu.5.rcgu.o)
          >>> defined as (i64, i64, i32) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          
          rust-lld: error: function signature mismatch: __muloti4
          >>> defined as (i32, i64, i64, i64, i64, i32) -> void in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcore-00aecf5c9d208fd5.rlib(core-00aecf5c9d208fd5.core.39255c92-cgu.1.rcgu.o)
          >>> defined as (i64, i64, i64, i64, i32) -> i64 in /Users/adam/minirepro-libserde-multivalue-return/target/wasm32-unknown-unknown/debug/deps/libcompiler_builtins-f3506048a557a9c3.rlib(compiler_builtins-f3506048a557a9c3.compiler_builtins.f5caec29-cgu.6.rcgu.o)
          

error: could not compile `minirepro-libserde-multivalue-return` (lib) due to previous error

@CryZe
Copy link
Contributor

CryZe commented Aug 9, 2023

We are going in like the 5th circle now. The current up to date comment regarding the bug is here: #73755 (comment)

@a1phyr
Copy link
Contributor

a1phyr commented Aug 10, 2023

LLVM 17 contains a patch to fix libcalls signatures with multivalue.
With it being merged in #114048, multivalue compilation is now working on latest nightly !

Due to the ABI mismatch thing, it still requires -Zbuild-std though.

@daxpedda
Copy link
Contributor

daxpedda commented Aug 10, 2023

With it being merged in #114048, multivalue compilation is now working on latest nightly !

Works on my end as well now.

@CryZe
Copy link
Contributor

CryZe commented Aug 10, 2023

Same for me. So I guess this can be closed now?

@daxpedda
Copy link
Contributor

@workingjubilee you indicated in #73916 that this still requires a test, but we don't need two open issues for that I think.

@workingjubilee
Copy link
Member

Agreed.

@juntyr
Copy link
Contributor

juntyr commented Oct 21, 2023

Using rustc 1.75.0-nightly (249624b 2023-10-20), I just came across this issue again in a wasm32-wasi crate:

error: linking with `rust-lld` failed: exit status: 1
    |
    = note: rust-lld: error: function signature mismatch: __muloti4
            >>> defined as (i32, i64, i64, i64, i64, i32) -> void in /home/gitpod/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/wasm32-wasi/lib/self-contained/libc.a(utimensat.o)
            >>> defined as (i64, i64, i64, i64, i32) -> i64 in /workspace/field-compression-benchmark/target/wasm32-wasi/release/deps/libcompiler_builtins-561993f29369fecc.rlib(compiler_builtins-561993f29369fecc.compiler_builtins.10ff36cf6ec863e6-cgu.2.rcgu.o)

which is built using RUSTFLAGS="-C target-feature=+multivalue" cargo +nightly build --target wasm32-wasi -Zbuild-std=panic_abort,std

@CryZe
Copy link
Contributor

CryZe commented Oct 21, 2023

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.

@juntyr
Copy link
Contributor

juntyr commented Oct 21, 2023

wasi-libc is precompiled and even building std won't help with that.

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)?

bors added a commit to rust-lang-ci/rust that referenced this issue Apr 9, 2024
…=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
bors added a commit to rust-lang-ci/rust that referenced this issue Apr 21, 2024
…=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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. O-wasm Target: WASM (WebAssembly), http://webassembly.org/
Projects
None yet
Development

No branches or pull requests