Skip to content

Commit

Permalink
[WebAssembly] Enable multivalue return when multivalue ABI is used
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
aheejin committed Apr 12, 2024
1 parent 32b95a3 commit 6ecbb86
Show file tree
Hide file tree
Showing 12 changed files with 58 additions and 27 deletions.
4 changes: 2 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1288,15 +1288,15 @@ bool WebAssemblyTargetLowering::CanLowerReturn(
const SmallVectorImpl<ISD::OutputArg> &Outs,
LLVMContext & /*Context*/) const {
// WebAssembly can only handle returning tuples with multivalue enabled
return Subtarget->hasMultivalue() || Outs.size() <= 1;
return WebAssembly::canLowerReturn(Outs.size(), Subtarget);
}

SDValue WebAssemblyTargetLowering::LowerReturn(
SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
SelectionDAG &DAG) const {
assert((Subtarget->hasMultivalue() || Outs.size() <= 1) &&
assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) &&
"MVP WebAssembly can only return up to one value");
if (!callingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "Utils/WebAssemblyTypeUtilities.h"
#include "WebAssemblyISelLowering.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/WasmEHFuncInfo.h"
#include "llvm/Target/TargetMachine.h"
Expand Down Expand Up @@ -70,8 +71,9 @@ void llvm::computeSignatureVTs(const FunctionType *Ty,
computeLegalValueVTs(ContextFunc, TM, Ty->getReturnType(), Results);

MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
if (Results.size() > 1 &&
!TM.getSubtarget<WebAssemblySubtarget>(ContextFunc).hasMultivalue()) {
if (!WebAssembly::canLowerReturn(
Results.size(),
&TM.getSubtarget<WebAssemblySubtarget>(ContextFunc))) {
// WebAssembly can't lower returns of multiple values without demoting to
// sret unless multivalue is enabled (see
// WebAssemblyTargetLowering::CanLowerReturn). So replace multiple return
Expand Down
25 changes: 13 additions & 12 deletions llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "WebAssemblyRuntimeLibcallSignatures.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/CodeGen/RuntimeLibcalls.h"

using namespace llvm;
Expand Down Expand Up @@ -694,7 +695,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(PtrTy);
break;
case i64_i64_func_f32:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -703,7 +704,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::F32);
break;
case i64_i64_func_f64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -712,7 +713,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::F64);
break;
case i16_i16_func_i16_i16:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I32);
Rets.push_back(wasm::ValType::I32);
} else {
Expand All @@ -722,7 +723,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i32_i32_func_i32_i32:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I32);
Rets.push_back(wasm::ValType::I32);
} else {
Expand All @@ -732,7 +733,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i64_i64_func_i64_i64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -742,7 +743,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i64_i64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -754,7 +755,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i64_i64_iPTR:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -767,7 +768,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(PtrTy);
break;
case i64_i64_i64_i64_func_i64_i64_i64_i64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
Expand All @@ -781,7 +782,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i32:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand Down Expand Up @@ -851,7 +852,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i64_i64_i64_i64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -865,7 +866,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i32:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand All @@ -874,7 +875,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i64_i64_func_i64:
if (Subtarget.hasMultivalue()) {
if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
"n32:64-S128-ni:1:10:20"),
TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
getEffectiveCodeModel(CM, CodeModel::Large), OL),
TLOF(new WebAssemblyTargetObjectFile()) {
TLOF(new WebAssemblyTargetObjectFile()),
UsesMultivalueABI(Options.MCOptions.getABIName() == "experimental-mv") {
// WebAssembly type-checks instructions, but a noreturn function with a return
// type that doesn't match the context will cause a check failure. So we lower
// LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace llvm {
class WebAssemblyTargetMachine final : public LLVMTargetMachine {
std::unique_ptr<TargetLoweringObjectFile> TLOF;
mutable StringMap<std::unique_ptr<WebAssemblySubtarget>> SubtargetMap;
bool UsesMultivalueABI = false;

public:
WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU,
Expand Down Expand Up @@ -62,6 +63,8 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine {
PerFunctionMIParsingState &PFS,
SMDiagnostic &Error,
SMRange &SourceRange) const override;

bool usesMultivalueABI() const { return UsesMultivalueABI; }
};

} // end namespace llvm
Expand Down
14 changes: 13 additions & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#include "WebAssemblyUtilities.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/IR/Function.h"
Expand Down Expand Up @@ -179,3 +179,15 @@ unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
llvm_unreachable("Unexpected register class");
}
}

bool WebAssembly::canLowerMultivalueReturn(
const WebAssemblySubtarget *Subtarget) {
const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
Subtarget->getTargetLowering()->getTargetMachine());
return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
}

bool WebAssembly::canLowerReturn(size_t ResultSize,
const WebAssemblySubtarget *Subtarget) {
return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
}
10 changes: 10 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ MachineInstr *findCatch(MachineBasicBlock *EHPad);
/// Returns the appropriate copy opcode for the given register class.
unsigned getCopyOpcodeForRegClass(const TargetRegisterClass *RC);

/// Returns true if multivalue returns of a function can be lowered directly,
/// i.e., not indirectly via a pointer parameter that points to the value in
/// memory.
bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget);

/// Returns true if the function's return value(s) can be lowered directly,
/// i.e., not indirectly via a pointer parameter that points to the value in
/// memory.
bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget);

} // end namespace WebAssembly

} // end namespace llvm
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-multi-return.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=EH
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue 2>&1 | FileCheck %s --check-prefix=SJLJ
; RUN: not --crash llc < %s -enable-emscripten-cxx-exceptions -mattr=+multivalue -target-abi=experimental-mv 2>&1 | FileCheck %s --check-prefix=EH
; RUN: not --crash llc < %s -enable-emscripten-sjlj -mattr=+multivalue -target-abi=experimental-mv 2>&1 | FileCheck %s --check-prefix=SJLJ

; Currently multivalue returning functions are not supported in Emscripten EH /
; SjLj. Make sure they error out.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -mtriple=wasm32-unknown-unknown -mattr=+multivalue -run-pass=wasm-reg-stackify -verify-machineinstrs %s -o - | FileCheck %s
# RUN: llc -mtriple=wasm32-unknown-unknown -mattr=+multivalue -target-abi=experimental-mv -run-pass=wasm-reg-stackify -verify-machineinstrs %s -o - | FileCheck %s

--- |
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; NOTE: Test functions have been generated by multivalue-stackify.py.

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

; Test that the multivalue stackification works

Expand Down
10 changes: 6 additions & 4 deletions llvm/test/CodeGen/WebAssembly/multivalue.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call | FileCheck %s
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+reference-types,+multivalue,+tail-call | FileCheck --check-prefix REF %s
; 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
; RUN: llc < %s --filetype=obj -mcpu=mvp -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call -target-abi=experimental-mv | FileCheck %s
; 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
; 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
; RUN: llc < %s --filetype=obj -mcpu=mvp -mattr=+multivalue,+tail-call -target-abi=experimental-mv | obj2yaml | FileCheck %s --check-prefix OBJ
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mcpu=mvp -mattr=+multivalue,+tail-call | FileCheck %s --check-prefix NO-MULTIVALUE

; Test that the multivalue calls, returns, function types, and block
; types work as expected.
Expand All @@ -19,6 +20,7 @@ declare void @use_i64(i64)
; CHECK-NEXT: i32.const 42{{$}}
; CHECK-NEXT: i64.const 42{{$}}
; CHECK-NEXT: end_function{{$}}
; NO-MULTIVALUE-NOT: .functype pair_const () -> (i32, i64)
define %pair @pair_const() {
ret %pair { i32 42, i64 42 }
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; 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 | FileCheck %s --check-prefix=MULTIVALUE
; 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
Expand Down

0 comments on commit 6ecbb86

Please sign in to comment.