Skip to content

Multi value Wasm compilation #73755

Closed
Closed

Description

@AchalaSB

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

Activity

  1. CryZe commented on Jun 26, 2020

    @CryZe
    Contributor

    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

  2. AchalaSB commented on Jun 29, 2020

    @AchalaSB
    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
    
  3. Muqito commented on Jun 30, 2020

    @Muqito

    @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}

  4. AchalaSB commented on Jul 1, 2020

    @AchalaSB
    Author

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

  5. yodaldevoid commented on Jul 2, 2020

    @yodaldevoid
    Contributor

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

  6. dodomorandi commented on Sep 12, 2020

    @dodomorandi

    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.

  7. dodomorandi commented on Sep 12, 2020

    @dodomorandi

    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.

  8. AchalaSB commented on Sep 15, 2020

    @AchalaSB
    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 )

  9. dodomorandi commented on Sep 15, 2020

    @dodomorandi

    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)
    }
  10. added
    O-wasmTarget: WASM (WebAssembly), http://webassembly.org/
    on Oct 20, 2020
  11. nomeata commented on Nov 2, 2020

    @nomeata

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

  12. dodomorandi commented on Nov 16, 2020

    @dodomorandi

    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/github.com-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...

  13. zimond commented on Dec 24, 2020

    @zimond

    So is there a possible working solution for this?

  14. blaind commented on Jan 17, 2021

    @blaind

    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))
    
  15. blaind commented on Jan 17, 2021

    @blaind

    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)

  16. 39 remaining items

  17. eastside commented on Aug 9, 2023

    @eastside

    @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
    
  18. CryZe commented on Aug 9, 2023

    @CryZe
    Contributor

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

  19. a1phyr commented on Aug 10, 2023

    @a1phyr
    Contributor

    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.

  20. daxpedda commented on Aug 10, 2023

    @daxpedda
    Contributor

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

    Works on my end as well now.

  21. CryZe commented on Aug 10, 2023

    @CryZe
    Contributor

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

  22. daxpedda commented on Aug 10, 2023

    @daxpedda
    Contributor

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

  23. workingjubilee commented on Aug 31, 2023

    @workingjubilee
    Member

    Agreed.

  24. juntyr commented on Oct 21, 2023

    @juntyr
    Contributor

    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

  25. CryZe commented on Oct 21, 2023

    @CryZe
    Contributor

    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.

  26. juntyr commented on Oct 21, 2023

    @juntyr
    Contributor

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

  27. added a commit that references this issue on Apr 9, 2024
  28. added a commit that references this issue on Apr 21, 2024
  29. upsuper commented on Oct 29, 2025

    @upsuper
    Contributor

    Now that multivalue is enabled by default, yet Rust still doesn't produce multi-value return for WebAssembly targets. None of the demo I found in this issue nowadays yields multi-value return with the latest rustc, and as described in the blog post last year:

    there is no longer any possible method of writing a function in Rust that returns multiple values at the WebAssembly function type level.

    Given this situation, should this issue be re-opened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-target-featureArea: Enabling/disabling target features like AVX, Neon, etc.O-wasmTarget: WASM (WebAssembly), http://webassembly.org/

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions