
Multivalue feature of WebAssembly has been standardized for several years now. I think it makes sense to be able to enable it in the feature section by default for our clang/llvm-produced binaries so that the multivalue feature can be used as necessary when necessary within our toolchain and also when running other optimizers (e.g. wasm-opt) after the LLVM code generation. But some WebAssembly toolchains, such as Emscripten, do not provide both mulvalue-returning and not-multivalue-returning versions of libraries. Also allowing the uses of multivalue in the features section does not necessarily mean we generate them whenever we can to the fullest, which is a different code generation / optimization option. So this makes the lowering of multivalue returns conditional on the use of 'experimental-mv' target ABI. This ABI is turned off by default and turned on by passing `-Xclang -target-abi -Xclang experimental-mv` to `clang`, or `-target-abi experimental-mv` to `clang -cc1` or `llc`. But the purpose of this PR is not tying the multivalue lowering to this specific 'experimental-mv'. 'experimental-mv' is just one multivalue ABI we currently have, and it is still experimental, meaning it is not very well optimized or tuned for performance. (e.g. it does not have the limitation of the max number of multivalue-lowered values, which can be detrimental to performance.) We may change the name of this ABI, or improve it, or add a new multivalue ABI in the future. Also I heard that WASI is planning to add their multivalue ABI soon. So the plan is, whenever any one of multivalue ABIs is enabled, we enable the lowering of multivalue returns in the backend. We currently have only 'experimental-mv' in the repo so we only check for that in this PR. Related past discussions: #82714 https://github.com/WebAssembly/tool-conventions/pull/223#issuecomment-2008298652
144 lines
5.3 KiB
LLVM
144 lines
5.3 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
|
|
; RUN: llc < %s -verify-machineinstrs -mcpu=mvp -mattr=+multivalue -target-abi=experimental-mv | FileCheck %s --check-prefix=MULTIVALUE
|
|
; RUN: llc < %s -verify-machineinstrs -mcpu=mvp | FileCheck %s --check-prefix=NO_MULTIVALUE
|
|
|
|
; Test libcall signatures when multivalue is enabled and disabled
|
|
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
define i128 @multivalue_sdiv(i128 %a, i128 %b) {
|
|
; MULTIVALUE-LABEL: multivalue_sdiv:
|
|
; MULTIVALUE: .functype multivalue_sdiv (i64, i64, i64, i64) -> (i64, i64)
|
|
; MULTIVALUE-NEXT: # %bb.0:
|
|
; MULTIVALUE-NEXT: local.get 0
|
|
; MULTIVALUE-NEXT: local.get 1
|
|
; MULTIVALUE-NEXT: local.get 2
|
|
; MULTIVALUE-NEXT: local.get 3
|
|
; MULTIVALUE-NEXT: call __divti3
|
|
; MULTIVALUE-NEXT: # fallthrough-return
|
|
;
|
|
; NO_MULTIVALUE-LABEL: multivalue_sdiv:
|
|
; NO_MULTIVALUE: .functype multivalue_sdiv (i32, i64, i64, i64, i64) -> ()
|
|
; NO_MULTIVALUE-NEXT: .local i32
|
|
; NO_MULTIVALUE-NEXT: # %bb.0:
|
|
; NO_MULTIVALUE-NEXT: global.get __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.sub
|
|
; NO_MULTIVALUE-NEXT: local.tee 5
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: local.get 1
|
|
; NO_MULTIVALUE-NEXT: local.get 2
|
|
; NO_MULTIVALUE-NEXT: local.get 3
|
|
; NO_MULTIVALUE-NEXT: local.get 4
|
|
; NO_MULTIVALUE-NEXT: call __divti3
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 8
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 8
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: # fallthrough-return
|
|
%div = sdiv i128 %a, %b
|
|
ret i128 %div
|
|
}
|
|
|
|
|
|
define fp128 @multivalue_fsub(fp128 %a, fp128 %b) {
|
|
; MULTIVALUE-LABEL: multivalue_fsub:
|
|
; MULTIVALUE: .functype multivalue_fsub (i64, i64, i64, i64) -> (i64, i64)
|
|
; MULTIVALUE-NEXT: # %bb.0:
|
|
; MULTIVALUE-NEXT: local.get 0
|
|
; MULTIVALUE-NEXT: local.get 1
|
|
; MULTIVALUE-NEXT: local.get 2
|
|
; MULTIVALUE-NEXT: local.get 3
|
|
; MULTIVALUE-NEXT: call __subtf3
|
|
; MULTIVALUE-NEXT: # fallthrough-return
|
|
;
|
|
; NO_MULTIVALUE-LABEL: multivalue_fsub:
|
|
; NO_MULTIVALUE: .functype multivalue_fsub (i32, i64, i64, i64, i64) -> ()
|
|
; NO_MULTIVALUE-NEXT: .local i32
|
|
; NO_MULTIVALUE-NEXT: # %bb.0:
|
|
; NO_MULTIVALUE-NEXT: global.get __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.sub
|
|
; NO_MULTIVALUE-NEXT: local.tee 5
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: local.get 1
|
|
; NO_MULTIVALUE-NEXT: local.get 2
|
|
; NO_MULTIVALUE-NEXT: local.get 3
|
|
; NO_MULTIVALUE-NEXT: local.get 4
|
|
; NO_MULTIVALUE-NEXT: call __subtf3
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 8
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 8
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: # fallthrough-return
|
|
%sub = fsub fp128 %a, %b
|
|
ret fp128 %sub
|
|
}
|
|
|
|
define i128 @multivalue_lshr(i128 %a, i128 %b) {
|
|
; MULTIVALUE-LABEL: multivalue_lshr:
|
|
; MULTIVALUE: .functype multivalue_lshr (i64, i64, i64, i64) -> (i64, i64)
|
|
; MULTIVALUE-NEXT: # %bb.0:
|
|
; MULTIVALUE-NEXT: local.get 2
|
|
; MULTIVALUE-NEXT: local.get 3
|
|
; MULTIVALUE-NEXT: local.get 0
|
|
; MULTIVALUE-NEXT: i32.wrap_i64
|
|
; MULTIVALUE-NEXT: call __ashlti3
|
|
; MULTIVALUE-NEXT: # fallthrough-return
|
|
;
|
|
; NO_MULTIVALUE-LABEL: multivalue_lshr:
|
|
; NO_MULTIVALUE: .functype multivalue_lshr (i32, i64, i64, i64, i64) -> ()
|
|
; NO_MULTIVALUE-NEXT: .local i32
|
|
; NO_MULTIVALUE-NEXT: # %bb.0:
|
|
; NO_MULTIVALUE-NEXT: global.get __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.sub
|
|
; NO_MULTIVALUE-NEXT: local.tee 5
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: local.get 3
|
|
; NO_MULTIVALUE-NEXT: local.get 4
|
|
; NO_MULTIVALUE-NEXT: local.get 1
|
|
; NO_MULTIVALUE-NEXT: i32.wrap_i64
|
|
; NO_MULTIVALUE-NEXT: call __ashlti3
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 8
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 8
|
|
; NO_MULTIVALUE-NEXT: local.get 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i64.load 0
|
|
; NO_MULTIVALUE-NEXT: i64.store 0
|
|
; NO_MULTIVALUE-NEXT: local.get 5
|
|
; NO_MULTIVALUE-NEXT: i32.const 16
|
|
; NO_MULTIVALUE-NEXT: i32.add
|
|
; NO_MULTIVALUE-NEXT: global.set __stack_pointer
|
|
; NO_MULTIVALUE-NEXT: # fallthrough-return
|
|
%tmp = shl i128 %b, %a
|
|
ret i128 %tmp
|
|
}
|