Skip to content

Commit 6ecbb86

Browse files
committed
[WebAssembly] Enable multivalue return when multivalue ABI is used
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: llvm#82714 WebAssembly/tool-conventions#223 (comment)
1 parent 32b95a3 commit 6ecbb86

12 files changed

+58
-27
lines changed

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1288,15 +1288,15 @@ bool WebAssemblyTargetLowering::CanLowerReturn(
12881288
const SmallVectorImpl<ISD::OutputArg> &Outs,
12891289
LLVMContext & /*Context*/) const {
12901290
// WebAssembly can only handle returning tuples with multivalue enabled
1291-
return Subtarget->hasMultivalue() || Outs.size() <= 1;
1291+
return WebAssembly::canLowerReturn(Outs.size(), Subtarget);
12921292
}
12931293

12941294
SDValue WebAssemblyTargetLowering::LowerReturn(
12951295
SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
12961296
const SmallVectorImpl<ISD::OutputArg> &Outs,
12971297
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
12981298
SelectionDAG &DAG) const {
1299-
assert((Subtarget->hasMultivalue() || Outs.size() <= 1) &&
1299+
assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) &&
13001300
"MVP WebAssembly can only return up to one value");
13011301
if (!callingConvSupported(CallConv))
13021302
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "Utils/WebAssemblyTypeUtilities.h"
1818
#include "WebAssemblyISelLowering.h"
1919
#include "WebAssemblySubtarget.h"
20+
#include "WebAssemblyUtilities.h"
2021
#include "llvm/CodeGen/Analysis.h"
2122
#include "llvm/CodeGen/WasmEHFuncInfo.h"
2223
#include "llvm/Target/TargetMachine.h"
@@ -70,8 +71,9 @@ void llvm::computeSignatureVTs(const FunctionType *Ty,
7071
computeLegalValueVTs(ContextFunc, TM, Ty->getReturnType(), Results);
7172

7273
MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
73-
if (Results.size() > 1 &&
74-
!TM.getSubtarget<WebAssemblySubtarget>(ContextFunc).hasMultivalue()) {
74+
if (!WebAssembly::canLowerReturn(
75+
Results.size(),
76+
&TM.getSubtarget<WebAssemblySubtarget>(ContextFunc))) {
7577
// WebAssembly can't lower returns of multiple values without demoting to
7678
// sret unless multivalue is enabled (see
7779
// WebAssemblyTargetLowering::CanLowerReturn). So replace multiple return

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp

+13-12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "WebAssemblyRuntimeLibcallSignatures.h"
2222
#include "WebAssemblySubtarget.h"
23+
#include "WebAssemblyUtilities.h"
2324
#include "llvm/CodeGen/RuntimeLibcalls.h"
2425

2526
using namespace llvm;
@@ -694,7 +695,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
694695
Params.push_back(PtrTy);
695696
break;
696697
case i64_i64_func_f32:
697-
if (Subtarget.hasMultivalue()) {
698+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
698699
Rets.push_back(wasm::ValType::I64);
699700
Rets.push_back(wasm::ValType::I64);
700701
} else {
@@ -703,7 +704,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
703704
Params.push_back(wasm::ValType::F32);
704705
break;
705706
case i64_i64_func_f64:
706-
if (Subtarget.hasMultivalue()) {
707+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
707708
Rets.push_back(wasm::ValType::I64);
708709
Rets.push_back(wasm::ValType::I64);
709710
} else {
@@ -712,7 +713,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
712713
Params.push_back(wasm::ValType::F64);
713714
break;
714715
case i16_i16_func_i16_i16:
715-
if (Subtarget.hasMultivalue()) {
716+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
716717
Rets.push_back(wasm::ValType::I32);
717718
Rets.push_back(wasm::ValType::I32);
718719
} else {
@@ -722,7 +723,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
722723
Params.push_back(wasm::ValType::I32);
723724
break;
724725
case i32_i32_func_i32_i32:
725-
if (Subtarget.hasMultivalue()) {
726+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
726727
Rets.push_back(wasm::ValType::I32);
727728
Rets.push_back(wasm::ValType::I32);
728729
} else {
@@ -732,7 +733,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
732733
Params.push_back(wasm::ValType::I32);
733734
break;
734735
case i64_i64_func_i64_i64:
735-
if (Subtarget.hasMultivalue()) {
736+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
736737
Rets.push_back(wasm::ValType::I64);
737738
Rets.push_back(wasm::ValType::I64);
738739
} else {
@@ -742,7 +743,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
742743
Params.push_back(wasm::ValType::I64);
743744
break;
744745
case i64_i64_func_i64_i64_i64_i64:
745-
if (Subtarget.hasMultivalue()) {
746+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
746747
Rets.push_back(wasm::ValType::I64);
747748
Rets.push_back(wasm::ValType::I64);
748749
} else {
@@ -754,7 +755,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
754755
Params.push_back(wasm::ValType::I64);
755756
break;
756757
case i64_i64_func_i64_i64_i64_i64_iPTR:
757-
if (Subtarget.hasMultivalue()) {
758+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
758759
Rets.push_back(wasm::ValType::I64);
759760
Rets.push_back(wasm::ValType::I64);
760761
} else {
@@ -767,7 +768,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
767768
Params.push_back(PtrTy);
768769
break;
769770
case i64_i64_i64_i64_func_i64_i64_i64_i64:
770-
if (Subtarget.hasMultivalue()) {
771+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
771772
Rets.push_back(wasm::ValType::I64);
772773
Rets.push_back(wasm::ValType::I64);
773774
Rets.push_back(wasm::ValType::I64);
@@ -781,7 +782,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
781782
Params.push_back(wasm::ValType::I64);
782783
break;
783784
case i64_i64_func_i64_i64_i32:
784-
if (Subtarget.hasMultivalue()) {
785+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
785786
Rets.push_back(wasm::ValType::I64);
786787
Rets.push_back(wasm::ValType::I64);
787788
} else {
@@ -851,7 +852,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
851852
Params.push_back(wasm::ValType::I64);
852853
break;
853854
case i64_i64_func_i64_i64_i64_i64_i64_i64:
854-
if (Subtarget.hasMultivalue()) {
855+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
855856
Rets.push_back(wasm::ValType::I64);
856857
Rets.push_back(wasm::ValType::I64);
857858
} else {
@@ -865,7 +866,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
865866
Params.push_back(wasm::ValType::I64);
866867
break;
867868
case i64_i64_func_i32:
868-
if (Subtarget.hasMultivalue()) {
869+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
869870
Rets.push_back(wasm::ValType::I64);
870871
Rets.push_back(wasm::ValType::I64);
871872
} else {
@@ -874,7 +875,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
874875
Params.push_back(wasm::ValType::I32);
875876
break;
876877
case i64_i64_func_i64:
877-
if (Subtarget.hasMultivalue()) {
878+
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
878879
Rets.push_back(wasm::ValType::I64);
879880
Rets.push_back(wasm::ValType::I64);
880881
} else {

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
128128
"n32:64-S128-ni:1:10:20"),
129129
TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
130130
getEffectiveCodeModel(CM, CodeModel::Large), OL),
131-
TLOF(new WebAssemblyTargetObjectFile()) {
131+
TLOF(new WebAssemblyTargetObjectFile()),
132+
UsesMultivalueABI(Options.MCOptions.getABIName() == "experimental-mv") {
132133
// WebAssembly type-checks instructions, but a noreturn function with a return
133134
// type that doesn't match the context will cause a check failure. So we lower
134135
// LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace llvm {
2424
class WebAssemblyTargetMachine final : public LLVMTargetMachine {
2525
std::unique_ptr<TargetLoweringObjectFile> TLOF;
2626
mutable StringMap<std::unique_ptr<WebAssemblySubtarget>> SubtargetMap;
27+
bool UsesMultivalueABI = false;
2728

2829
public:
2930
WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU,
@@ -62,6 +63,8 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine {
6263
PerFunctionMIParsingState &PFS,
6364
SMDiagnostic &Error,
6465
SMRange &SourceRange) const override;
66+
67+
bool usesMultivalueABI() const { return UsesMultivalueABI; }
6568
};
6669

6770
} // end namespace llvm

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
#include "WebAssemblyUtilities.h"
1515
#include "WebAssemblyMachineFunctionInfo.h"
16-
#include "WebAssemblySubtarget.h"
16+
#include "WebAssemblyTargetMachine.h"
1717
#include "llvm/CodeGen/MachineInstr.h"
1818
#include "llvm/CodeGen/MachineLoopInfo.h"
1919
#include "llvm/IR/Function.h"
@@ -179,3 +179,15 @@ unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
179179
llvm_unreachable("Unexpected register class");
180180
}
181181
}
182+
183+
bool WebAssembly::canLowerMultivalueReturn(
184+
const WebAssemblySubtarget *Subtarget) {
185+
const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
186+
Subtarget->getTargetLowering()->getTargetMachine());
187+
return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
188+
}
189+
190+
bool WebAssembly::canLowerReturn(size_t ResultSize,
191+
const WebAssemblySubtarget *Subtarget) {
192+
return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
193+
}

Diff for: llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h

+10
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ MachineInstr *findCatch(MachineBasicBlock *EHPad);
6363
/// Returns the appropriate copy opcode for the given register class.
6464
unsigned getCopyOpcodeForRegClass(const TargetRegisterClass *RC);
6565

66+
/// Returns true if multivalue returns of a function can be lowered directly,
67+
/// i.e., not indirectly via a pointer parameter that points to the value in
68+
/// memory.
69+
bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget);
70+
71+
/// Returns true if the function's return value(s) can be lowered directly,
72+
/// i.e., not indirectly via a pointer parameter that points to the value in
73+
/// memory.
74+
bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget);
75+
6676
} // end namespace WebAssembly
6777

6878
} // end namespace llvm

Diff for: llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=EH
2-
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=SJLJ
1+
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue -target-abi=experimental-mv 2>&1 | FileCheck %s --check-prefix=EH
2+
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue -target-abi=experimental-mv 2>&1 | FileCheck %s --check-prefix=SJLJ
33

44
; Currently multivalue returning functions are not supported in Emscripten EH /
55
; SjLj. Make sure they error out.

Diff for: llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2-
# RUN: llc -mtriple=wasm32-unknown-unknown -mattr=+multivalue -run-pass=wasm-reg-stackify -verify-machineinstrs %s -o - | FileCheck %s
2+
# RUN: llc -mtriple=wasm32-unknown-unknown -mattr=+multivalue -target-abi=experimental-mv -run-pass=wasm-reg-stackify -verify-machineinstrs %s -o - | FileCheck %s
33

44
--- |
55
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"

Diff for: llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
22
; NOTE: Test functions have been generated by multivalue-stackify.py.
33

4-
; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue | FileCheck %s
4+
; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue -target-abi=experimental-mv | FileCheck %s
55

66
; Test that the multivalue stackification works
77

Diff for: llvm/test/CodeGen/WebAssembly/multivalue.ll

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call | FileCheck %s
2-
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+reference-types,+multivalue,+tail-call | FileCheck --check-prefix REF %s
3-
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mcpu=mvp -mattr=+multivalue,+tail-call | FileCheck %s --check-prefix REGS
4-
; RUN: llc < %s --filetype=obj -mcpu=mvp -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ
1+
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call -target-abi=experimental-mv | FileCheck %s
2+
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+reference-types,+multivalue,+tail-call -target-abi=experimental-mv | FileCheck --check-prefix REF %s
3+
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mcpu=mvp -mattr=+multivalue,+tail-call -target-abi=experimental-mv | FileCheck %s --check-prefix REGS
4+
; RUN: llc < %s --filetype=obj -mcpu=mvp -mattr=+multivalue,+tail-call -target-abi=experimental-mv | obj2yaml | FileCheck %s --check-prefix OBJ
5+
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call | FileCheck %s --check-prefix NO-MULTIVALUE
56

67
; Test that the multivalue calls, returns, function types, and block
78
; types work as expected.
@@ -19,6 +20,7 @@ declare void @use_i64(i64)
1920
; CHECK-NEXT: i32.const 42{{$}}
2021
; CHECK-NEXT: i64.const 42{{$}}
2122
; CHECK-NEXT: end_function{{$}}
23+
; NO-MULTIVALUE-NOT: .functype pair_const () -> (i32, i64)
2224
define %pair @pair_const() {
2325
ret %pair { i32 42, i64 42 }
2426
}

Diff for: llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
2-
; RUN: llc < %s -verify-machineinstrs -mcpu=mvp -mattr=+multivalue | FileCheck %s --check-prefix=MULTIVALUE
2+
; RUN: llc < %s -verify-machineinstrs -mcpu=mvp -mattr=+multivalue -target-abi=experimental-mv | FileCheck %s --check-prefix=MULTIVALUE
33
; RUN: llc < %s -verify-machineinstrs -mcpu=mvp | FileCheck %s --check-prefix=NO_MULTIVALUE
44

55
; Test libcall signatures when multivalue is enabled and disabled

0 commit comments

Comments
 (0)