[clang] [llvm] [WebAssembly] Add a new `wasm_multivalue` calling convention (PR #200076)
Alex Crichton via cfe-commits
cfe-commits at lists.llvm.org
Wed May 27 15:52:13 PDT 2026
https://github.com/alexcrichton created https://github.com/llvm/llvm-project/pull/200076
This is an implementation of WebAssembly/tool-conventions#268 here in LLVM. This adds a new calling convention to Clang, named `wasm_multivalue`, which is intended to be used on WebAssembly targets to configure multiple return values and slightly tweak the ABI. Changes here are:
* Parsing/validation of `__attribute__((wasm_multivalue))`. Note that validation here means that it's not only well-formed but on wasm targets the `multivalue` target feature is additionally enabled.
* Clang-level ABI adjustments for the `wasm_multivalue` calling convention. These are defined by WebAssembly/tool-conventions#268 and notably includes expanding structs with exactly 2 scalar fields in parameter-position and directly returning structs with any number of scalar fields in return-position.
* A new `wasm_multivaluecc` keyword/calling convention for LLVM IR. This is what Clang lowers to when using the `wasm_multivalue` calling convention.
* Adjustments at the LLVM ABI layer to support returning multiple values with the `wasm_multivaluecc` calling convention.
My goal after this would be to start integrating this into Rust next, under and unstable feature, and then further continue testing/vetting/etc for component model usage.
>From 4b34b67d14089677cc9e0dd1890f5ae0c0c0c7d5 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex at alexcrichton.com>
Date: Wed, 27 May 2026 13:25:52 -0700
Subject: [PATCH] [WebAssembly] Add a new `wasm_multivalue` calling convention
This is an implementation of WebAssembly/tool-conventions#268 here in
LLVM. This adds a new calling convention to Clang, named
`wasm_multivalue`, which is intended to be used on WebAssembly targets
to configure multiple return values and slightly tweak the ABI. Changes
here are:
* Parsing/validation of `__attribute__((wasm_multivalue))`. Note that
validation here means that it's not only well-formed but on wasm
targets the `multivalue` target feature is additionally enabled.
* Clang-level ABI adjustments for the `wasm_multivalue` calling
convention. These are defined by WebAssembly/tool-conventions#268 and
notably includes expanding structs with exactly 2 scalar fields in
parameter-position and directly returning structs with any number of
scalar fields in return-position.
* A new `wasm_multivaluecc` keyword/calling convention for LLVM IR. This
is what Clang lowers to when using the `wasm_multivalue` calling
convention.
* Adjustments at the LLVM ABI layer to support returning multiple values
with the `wasm_multivaluecc` calling convention.
My goal after this would be to start integrating this into Rust next,
under and unstable feature, and then further continue
testing/vetting/etc for component model usage.
---
clang/include/clang/Basic/Attr.td | 6 ++
clang/include/clang/Basic/AttrDocs.td | 15 ++++
.../clang/Basic/DiagnosticSemaKinds.td | 5 +-
clang/include/clang/Basic/Specifiers.h | 1 +
clang/lib/AST/ItaniumMangle.cpp | 1 +
clang/lib/AST/Type.cpp | 3 +
clang/lib/AST/TypePrinter.cpp | 6 ++
clang/lib/Basic/Targets/WebAssembly.h | 1 +
clang/lib/CodeGen/CGCall.cpp | 2 +
clang/lib/CodeGen/CGDebugInfo.cpp | 2 +
clang/lib/CodeGen/Targets/WebAssembly.cpp | 74 ++++++++++++++++--
clang/lib/Sema/SemaDeclAttr.cpp | 13 ++++
clang/lib/Sema/SemaType.cpp | 5 +-
.../CodeGen/WebAssembly/wasm-multivalue-abi.c | 77 +++++++++++++++++++
.../WebAssembly/wasm-multivalue-functype.c | 45 +++++++++++
clang/test/Sema/attr-wasm-multivalue.c | 33 ++++++++
llvm/include/llvm/AsmParser/LLToken.h | 1 +
llvm/include/llvm/BinaryFormat/Dwarf.def | 1 +
llvm/include/llvm/IR/CallingConv.h | 4 +
llvm/lib/AsmParser/LLLexer.cpp | 1 +
llvm/lib/AsmParser/LLParser.cpp | 4 +
llvm/lib/IR/AsmWriter.cpp | 3 +
llvm/lib/IR/Function.cpp | 1 +
.../GISel/WebAssemblyCallLowering.cpp | 5 +-
.../WebAssembly/WebAssemblyISelLowering.cpp | 8 +-
.../WebAssemblyMachineFunctionInfo.cpp | 2 +-
.../WebAssemblyRuntimeLibcallSignatures.cpp | 26 +++----
.../WebAssembly/WebAssemblyUtilities.cpp | 13 +++-
.../Target/WebAssembly/WebAssemblyUtilities.h | 12 ++-
.../CodeGen/WebAssembly/wasm-multivalue-cc.ll | 38 +++++++++
30 files changed, 373 insertions(+), 35 deletions(-)
create mode 100644 clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c
create mode 100644 clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c
create mode 100644 clang/test/Sema/attr-wasm-multivalue.c
create mode 100644 llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 70b5773f95b08..42a68ff02975d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3559,6 +3559,12 @@ def M68kRTD: DeclOrTypeAttr {
let Documentation = [M68kRTDDocs];
}
+def WasmMultivalue : DeclOrTypeAttr,
+ TargetSpecificAttr<TargetWebAssembly> {
+ let Spellings = [Clang<"wasm_multivalue">];
+ let Documentation = [WasmMultivalueDocs];
+}
+
def PreserveNone : DeclOrTypeAttr,
TargetSpecificAttr<TargetArch<!listconcat(TargetAArch64.Arches, TargetAnyX86.Arches)>> {
let Spellings = [Clang<"preserve_none">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 87b9053be7cb6..dafba0108506d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3732,6 +3732,21 @@ using the `rtd` instruction.
}];
}
+def WasmMultivalueDocs : Documentation {
+ let Category = DocCatCallingConvs;
+ let Content = [{
+On WebAssembly targets, this attribute selects the ``wasm-multivalue`` calling
+convention as defined in the WebAssembly/tool-conventions repository. Relative
+to the default calling convention this takes advantage of the multi-value
+proposal in its ABI definition.
+
+This calling convention requires the ``multivalue`` target feature. Using the
+attribute without this feature enabled is a compile-time error. Enabling
+``multivalue`` does not change the default calling convention; this attribute
+must be used to opt in to the new behavior on a per-function basis.
+ }];
+}
+
def DocCatConsumed : DocumentationCategory<"Consumed Annotation Checking"> {
let Content = [{
Clang supports additional attributes for checking basic resource management
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e330ea03d0544..71df166089342 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13828,7 +13828,7 @@ def err_builtin_pass_in_regs_non_class : Error<
"argument %0 is not an unqualified class type">;
-// WebAssembly reference type and table diagnostics.
+// WebAssembly-related diagnostics.
def err_wasm_reference_pr : Error<
"%select{pointer|reference}0 to WebAssembly reference type is not allowed">;
def err_wasm_ca_reference : Error<
@@ -13871,6 +13871,9 @@ def err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union
: Error<"not supported with the multivalue ABI for "
"function pointers with a struct/union as %select{return "
"value|parameter}0">;
+def err_wasm_multivalue_requires_feature : Error<
+ "the 'wasm_multivalue' calling convention requires the 'multivalue' target "
+ "feature to be enabled">;
// OpenACC diagnostics.
def warn_acc_routine_unimplemented
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 8da6fd4cf454a..b50deb91eef2c 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -313,6 +313,7 @@ namespace clang {
CC_RISCVVLSCall_16384, // __attribute__((riscv_vls_cc(16384)))
CC_RISCVVLSCall_32768, // __attribute__((riscv_vls_cc(32768)))
CC_RISCVVLSCall_65536, // __attribute__((riscv_vls_cc(65536)))
+ CC_WasmMultivalue, // __attribute__((wasm_multivalue))
};
/// Checks whether the given calling convention supports variadic
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 1cb6fa05f22ac..743dc390866d6 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3534,6 +3534,7 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) {
case CC_PreserveMost:
case CC_PreserveAll:
case CC_M68kRTD:
+ case CC_WasmMultivalue:
case CC_PreserveNone:
case CC_RISCVVectorCall:
#define CC_VLS_CASE(ABI_VLEN) case CC_RISCVVLSCall_##ABI_VLEN:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 96a398aa21dad..fa66c146b1d87 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3741,6 +3741,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
return "preserve_all";
case CC_M68kRTD:
return "m68k_rtd";
+ case CC_WasmMultivalue:
+ return "wasm_multivalue";
case CC_PreserveNone:
return "preserve_none";
// clang-format off
@@ -4552,6 +4554,7 @@ bool AttributedType::isCallingConv() const {
case attr::PreserveMost:
case attr::PreserveAll:
case attr::M68kRTD:
+ case attr::WasmMultivalue:
case attr::PreserveNone:
case attr::RISCVVectorCC:
case attr::RISCVVLSCC:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 80f5b90ba35c4..273e12168ac97 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1173,6 +1173,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
case CC_M68kRTD:
OS << " __attribute__((m68k_rtd))";
break;
+ case CC_WasmMultivalue:
+ OS << " __attribute__((wasm_multivalue))";
+ break;
case CC_PreserveNone:
OS << " __attribute__((preserve_none))";
break;
@@ -2081,6 +2084,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::M68kRTD:
OS << "m68k_rtd";
break;
+ case attr::WasmMultivalue:
+ OS << "wasm_multivalue";
+ break;
case attr::RISCVVectorCC:
OS << "riscv_vector_cc";
break;
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 6085197498163..165acaabe96df 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -184,6 +184,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
switch (CC) {
case CC_C:
case CC_Swift:
+ case CC_WasmMultivalue:
return CCCR_OK;
case CC_SwiftAsync:
return CCCR_Error;
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..2dad85ec71e30 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -103,6 +103,8 @@ unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) {
return llvm::CallingConv::SwiftTail;
case CC_M68kRTD:
return llvm::CallingConv::M68k_RTD;
+ case CC_WasmMultivalue:
+ return llvm::CallingConv::WASM_Multivalue;
case CC_PreserveNone:
return llvm::CallingConv::PreserveNone;
// clang-format off
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0d45df02a2a21..090741c1ff992 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1838,6 +1838,8 @@ static unsigned getDwarfCC(CallingConv CC) {
return llvm::dwarf::DW_CC_LLVM_X86RegCall;
case CC_M68kRTD:
return llvm::dwarf::DW_CC_LLVM_M68kRTD;
+ case CC_WasmMultivalue:
+ return llvm::dwarf::DW_CC_LLVM_WasmMultivalue;
case CC_PreserveNone:
return llvm::dwarf::DW_CC_LLVM_PreserveNone;
case CC_RISCVVectorCall:
diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index ebe996a4edd8d..8942e3e78b6de 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -28,17 +28,19 @@ class WebAssemblyABIInfo final : public ABIInfo {
: ABIInfo(CGT), defaultInfo(CGT), Kind(Kind) {}
private:
- ABIArgInfo classifyReturnType(QualType RetTy) const;
- ABIArgInfo classifyArgumentType(QualType Ty) const;
+ ABIArgInfo classifyReturnType(QualType RetTy, llvm::CallingConv::ID CC) const;
+ ABIArgInfo classifyArgumentType(QualType Ty, llvm::CallingConv::ID CC) const;
// DefaultABIInfo's classifyReturnType and classifyArgumentType are
// non-virtual, but computeInfo and EmitVAArg are virtual, so we
// overload them.
void computeInfo(CGFunctionInfo &FI) const override {
+ llvm::CallingConv::ID CC = FI.getCallingConvention();
if (!getCXXABI().classifyReturnType(FI))
- FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+ FI.getReturnInfo() =
+ classifyReturnType(FI.getReturnType(), CC);
for (auto &Arg : FI.arguments())
- Arg.info = classifyArgumentType(Arg.type);
+ Arg.info = classifyArgumentType(Arg.type, CC);
}
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
@@ -95,8 +97,54 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
}
};
+/// Count the number of "scalar fields" in the given record type, as defined by
+/// WebAssembly/tool-conventions for the "wasm-multivalue" calling convention
+/// primarily. A scalar field is a field that recursively, through nested
+/// structs, unions, and arrays, contains just a single scalar value.
+///
+/// Returns the number of scalar fields, or std::nullopt if the record contains
+/// a field that is not a scalar field (e.g., a sub-aggregate with multiple
+/// scalars, a bit-field, or a flexible array member).
+///
+/// Note that this is similar to `isSingleElementStruct` in structure.
+static std::optional<unsigned> countScalarFields(ASTContext &Context, QualType T) {
+ const auto *RD = T->getAsRecordDecl();
+ if (!RD)
+ return std::nullopt;
+ if (RD->hasFlexibleArrayMember())
+ return std::nullopt;
+
+ unsigned Count = 0;
+
+ // Check bases first for C++ records.
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const auto &Base : CXXRD->bases()) {
+ auto SubCount = countScalarFields(Context, Base.getType());
+ if (!SubCount)
+ return std::nullopt;
+ Count += *SubCount;
+ }
+ }
+
+ for (const auto *FD : RD->fields()) {
+ if (FD->isBitField())
+ return std::nullopt;
+ if (isEmptyField(Context, FD, true))
+ continue;
+
+ QualType T = FD->getType();
+ if (isAggregateTypeForABI(T) && !isSingleElementStruct(T, Context))
+ return std::nullopt;
+
+ ++Count;
+ }
+
+ return Count;
+}
+
/// Classify argument of given type \p Ty.
-ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
+ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty,
+ llvm::CallingConv::ID CC) const {
Ty = useFirstFieldIfTransparentUnion(Ty);
if (isAggregateTypeForABI(Ty)) {
@@ -113,6 +161,12 @@ ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
// though watch out for things like bitfields.
if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
+ // For the wasm-multivalue calling convention, structs with exactly two
+ // scalar fields are passed directly as two arguments.
+ if (CC == llvm::CallingConv::WASM_Multivalue) {
+ if (auto N = countScalarFields(getContext(), Ty); N && *N == 2)
+ return ABIArgInfo::getExpand();
+ }
// For the experimental multivalue ABI, fully expand all other aggregates
if (Kind == WebAssemblyABIKind::ExperimentalMV) {
const auto *RD = Ty->castAsRecordDecl();
@@ -132,7 +186,8 @@ ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
return defaultInfo.classifyArgumentType(Ty);
}
-ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy) const {
+ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy,
+ llvm::CallingConv::ID CC) const {
if (isAggregateTypeForABI(RetTy)) {
// Records with non-trivial destructors/copy-constructors should not be
// returned by value.
@@ -145,6 +200,13 @@ ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy) const {
// ABIArgInfo::getDirect().
if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
+ // For the wasm-multivalue calling convention, structs whose fields are
+ // (recursively) scalars are returned directly via the multivalue
+ // proposal.
+ if (CC == llvm::CallingConv::WASM_Multivalue) {
+ if (auto N = countScalarFields(getContext(), RetTy); N && *N > 0)
+ return ABIArgInfo::getDirect();
+ }
// For the experimental multivalue ABI, return all other aggregates
if (Kind == WebAssemblyABIKind::ExperimentalMV)
return ABIArgInfo::getDirect();
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ae04d3855f01c..ac21210bd7112 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5543,6 +5543,9 @@ static void handleCallConvAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
case ParsedAttr::AT_M68kRTD:
D->addAttr(::new (S.Context) M68kRTDAttr(S.Context, AL));
return;
+ case ParsedAttr::AT_WasmMultivalue:
+ D->addAttr(::new (S.Context) WasmMultivalueAttr(S.Context, AL));
+ return;
case ParsedAttr::AT_PreserveNone:
D->addAttr(::new (S.Context) PreserveNoneAttr(S.Context, AL));
return;
@@ -5814,6 +5817,15 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
case ParsedAttr::AT_M68kRTD:
CC = CC_M68kRTD;
break;
+ case ParsedAttr::AT_WasmMultivalue:
+ CC = CC_WasmMultivalue;
+ if (Context.getTargetInfo().getTriple().isWasm() &&
+ !Context.getTargetInfo().hasFeature("multivalue")) {
+ Attrs.setInvalid();
+ Diag(Attrs.getLoc(), diag::err_wasm_multivalue_requires_feature);
+ return true;
+ }
+ break;
case ParsedAttr::AT_PreserveNone:
CC = CC_PreserveNone;
break;
@@ -8068,6 +8080,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_PreserveNone:
case ParsedAttr::AT_RISCVVectorCC:
case ParsedAttr::AT_RISCVVLSCC:
+ case ParsedAttr::AT_WasmMultivalue:
handleCallConvAttr(S, D, AL);
break;
case ParsedAttr::AT_DeviceKernel:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 44ac4f6630690..2811cb0ee95d4 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -143,7 +143,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
case ParsedAttr::AT_M68kRTD: \
case ParsedAttr::AT_PreserveNone: \
case ParsedAttr::AT_RISCVVectorCC: \
- case ParsedAttr::AT_RISCVVLSCC
+ case ParsedAttr::AT_RISCVVLSCC: \
+ case ParsedAttr::AT_WasmMultivalue
// Function type attributes.
#define FUNCTION_TYPE_ATTRS_CASELIST \
@@ -7803,6 +7804,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
return createSimpleAttr<PreserveAllAttr>(Ctx, Attr);
case ParsedAttr::AT_M68kRTD:
return createSimpleAttr<M68kRTDAttr>(Ctx, Attr);
+ case ParsedAttr::AT_WasmMultivalue:
+ return createSimpleAttr<WasmMultivalueAttr>(Ctx, Attr);
case ParsedAttr::AT_PreserveNone:
return createSimpleAttr<PreserveNoneAttr>(Ctx, Attr);
case ParsedAttr::AT_RISCVVectorCC:
diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c
new file mode 100644
index 0000000000000..5194616a13733
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue \
+// RUN: %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +multivalue \
+// RUN: %s -emit-llvm -o - | FileCheck %s
+
+// Verify that the `wasm_multivalue` calling convention produces the function
+// signatures described in the WebAssembly tool conventions PR.
+
+// CHECK-LABEL: define wasm_multivaluecc void @f1()
+__attribute__((wasm_multivalue))
+void f1(void) {}
+
+// CHECK-LABEL: define wasm_multivaluecc i32 @f2(float {{.*}}, double {{.*}})
+__attribute__((wasm_multivalue))
+int f2(float a, double b) { return (int)(a + b); }
+
+// CHECK-LABEL: define wasm_multivaluecc i128 @f3(fp128
+__attribute__((wasm_multivalue))
+__int128 f3(long double x) { return (__int128)x; }
+
+struct Foo4 { };
+union Bar4 { };
+
+// CHECK-LABEL: define wasm_multivaluecc void @f4()
+__attribute__((wasm_multivalue))
+union Bar4 f4(struct Foo4 x) { union Bar4 r; return r; }
+
+struct Foo5 { int a; };
+union Bar5 { int a; };
+
+// CHECK-LABEL: define wasm_multivaluecc i32 @f5(i32
+__attribute__((wasm_multivalue))
+union Bar5 f5(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+
+struct Foo6 { int a; int b; };
+
+// CHECK-LABEL: define wasm_multivaluecc %struct.Foo6 @f6(i32 {{.*}}, i32 {{.*}})
+__attribute__((wasm_multivalue))
+struct Foo6 f6(struct Foo6 x) { return x; }
+
+// CHECK-LABEL: define wasm_multivaluecc i128 @f7()
+__attribute__((wasm_multivalue))
+__int128 f7(void) { return 1; }
+
+struct Foo8 { int a; int b; int c; };
+// CHECK-LABEL: define wasm_multivaluecc %struct.Foo8 @f8(ptr
+__attribute__((wasm_multivalue))
+struct Foo8 f8(struct Foo8 x) { return x; }
+
+struct Foo9 {
+ struct Foo6 inner;
+};
+// CHECK-LABEL: define wasm_multivaluecc void @f9(ptr {{.*}} sret
+__attribute__((wasm_multivalue))
+struct Foo9 f9(void) { struct Foo9 r = {{0, 0}}; return r; }
+
+// bitfields force pointers
+struct Foo10 {
+ int a : 4;
+ int b : 4;
+};
+// CHECK-LABEL: define wasm_multivaluecc void @f10(ptr
+__attribute__((wasm_multivalue))
+struct Foo10 f10(void) { struct Foo10 r = {0, 0}; return r; }
+
+// The default calling convention isn't changed from `+multivalue`
+// CHECK-LABEL: define void @f11(ptr{{.*}}sret(%struct.Foo6){{.*}}, ptr {{.*}}byval(%struct.Foo6)
+struct Foo6 f11(struct Foo6 x) { return x; }
+
+// Test cross-calling-convention indierct calls
+typedef __attribute__((wasm_multivalue)) struct Foo6 (*mv_ptr)(struct Foo6);
+
+// CHECK-LABEL: define void @f12(
+// CHECK: call wasm_multivaluecc {{(noundef )?}}%struct.Foo6 %0(i32{{.*}}, i32
+struct Foo6 f12(mv_ptr fn, struct Foo6 x) {
+ return fn(x);
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c b/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c
new file mode 100644
index 0000000000000..c522640901473
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue %s -S -O2 -o - | FileCheck %s
+
+// CHECK: .functype f1 () -> ()
+void f1(void) {}
+// CHECK: .functype f1mv () -> ()
+__attribute__((wasm_multivalue))
+void f1mv(void) {}
+
+// CHECK: .functype f2 (f32, f64) -> (i32)
+int f2(float a, double b) { return (int)(a + b); }
+// CHECK: .functype f2mv (f32, f64) -> (i32)
+__attribute__((wasm_multivalue))
+int f2mv(float a, double b) { return (int)(a + b); }
+
+// CHECK: .functype f3 (i32, i64, i64) -> ()
+__int128 f3(long double x) { return (__int128)x; }
+// CHECK: .functype f3mv (i64, i64) -> (i64, i64)
+__attribute__((wasm_multivalue))
+__int128 f3mv(long double x) { return (__int128)x; }
+
+struct Foo4 { };
+union Bar4 { };
+
+// CHECK: .functype f4 () -> ()
+union Bar4 f4(struct Foo4 x) { union Bar4 r; return r; }
+// CHECK: .functype f4mv () -> ()
+__attribute__((wasm_multivalue))
+union Bar4 f4mv(struct Foo4 x) { union Bar4 r; return r; }
+
+struct Foo5 { int a; };
+union Bar5 { int a; };
+
+// CHECK: .functype f5 (i32) -> (i32)
+union Bar5 f5(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+// CHECK: .functype f5mv (i32) -> (i32)
+__attribute__((wasm_multivalue))
+union Bar5 f5mv(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+
+struct Foo6 { int a; int b; };
+
+// CHECK: .functype f6 (i32, i32) -> ()
+struct Foo6 f6(struct Foo6 x) { return x; }
+// CHECK: .functype f6mv (i32, i32) -> (i32, i32)
+__attribute__((wasm_multivalue))
+struct Foo6 f6mv(struct Foo6 x) { return x; }
diff --git a/clang/test/Sema/attr-wasm-multivalue.c b/clang/test/Sema/attr-wasm-multivalue.c
new file mode 100644
index 0000000000000..51e9f494b657d
--- /dev/null
+++ b/clang/test/Sema/attr-wasm-multivalue.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue -fsyntax-only -verify=enabled %s
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature -multivalue -fsyntax-only -verify=disabled %s
+// RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -verify=nonwasm %s
+
+// disabled-error at +3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}}
+// nonwasm-warning at +2 {{'wasm_multivalue' calling convention is not supported for this target}}
+// nonwasm-warning at +1 2 {{unknown attribute 'wasm_multivalue' ignored}}
+__attribute__((wasm_multivalue))
+void f1(void);
+
+
+struct Pair { int a; int b; };
+
+// disabled-error at +3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}}
+// nonwasm-warning at +2 {{'wasm_multivalue' calling convention is not supported for this target}}
+// nonwasm-warning at +1 2 {{unknown attribute 'wasm_multivalue' ignored}}
+__attribute__((wasm_multivalue))
+struct Pair returns_pair(struct Pair x);
+
+// The attribute can be applied to function pointer types.
+// disabled-error at +3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}}
+// nonwasm-warning at +2 {{'wasm_multivalue' calling convention is not supported for this target}}
+// nonwasm-warning at +1 2 {{unknown attribute 'wasm_multivalue' ignored}}
+typedef __attribute__((wasm_multivalue)) struct Pair (*pair_fn_t)(struct Pair);
+
+#if defined(__wasm__)
+// Attribute should not take arguments. Only checked on wasm because on other
+// targets the attribute is unknown and the diagnostic flow differs.
+// enabled-error at +2 {{'wasm_multivalue' attribute takes no arguments}}
+// disabled-error at +1 {{'wasm_multivalue' attribute takes no arguments}}
+__attribute__((wasm_multivalue(1)))
+void takes_no_args(void);
+#endif
diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index 426ca35ededb6..ba0eba37f64d1 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -192,6 +192,7 @@ enum Kind {
kw_cheriot_compartmentcallcc,
kw_cheriot_compartmentcalleecc,
kw_cheriot_librarycallcc,
+ kw_wasm_multivaluecc,
// Attributes:
kw_attributes,
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index 8bb1766bcc259..c8bd9b3b5233a 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -1179,6 +1179,7 @@ HANDLE_DW_CC(0xcd, LLVM_PreserveNone)
HANDLE_DW_CC(0xce, LLVM_RISCVVectorCall)
HANDLE_DW_CC(0xcf, LLVM_SwiftTail)
HANDLE_DW_CC(0xd0, LLVM_RISCVVLSCall)
+HANDLE_DW_CC(0xd1, LLVM_WasmMultivalue)
// From GCC source code (include/dwarf2.h): This DW_CC_ value is not currently
// generated by any toolchain. It is used internally to GDB to indicate OpenCL
// C functions that have been compiled with the IBM XL C for OpenCL compiler and
diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h
index 249c512dda532..32f1e5bca2a03 100644
--- a/llvm/include/llvm/IR/CallingConv.h
+++ b/llvm/include/llvm/IR/CallingConv.h
@@ -297,6 +297,10 @@ namespace CallingConv {
/// stateless compartment.
CHERIoT_LibraryCall = 127,
+ /// Calling convention for WebAssembly that takes advantage of the
+ /// multivalue proposal.
+ WASM_Multivalue = 128,
+
/// The highest possible ID. Must be some 2^k - 1.
MaxID = 1023
};
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index b1a0a71fcc3ae..79a3e2298e525 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -705,6 +705,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(cheriot_compartmentcallcc);
KEYWORD(cheriot_compartmentcalleecc);
KEYWORD(cheriot_librarycallcc);
+ KEYWORD(wasm_multivaluecc);
KEYWORD(cc);
KEYWORD(c);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index b9c36c28d04cd..d797b06db506f 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -2257,6 +2257,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) {
/// ::= 'graalcc'
/// ::= 'riscv_vector_cc'
/// ::= 'riscv_vls_cc'
+/// ::= 'wasm_multivaluecc'
/// ::= 'cc' UINT
///
bool LLParser::parseOptionalCallingConv(unsigned &CC) {
@@ -2376,6 +2377,9 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) {
case lltok::kw_cheriot_librarycallcc:
CC = CallingConv::CHERIoT_LibraryCall;
break;
+ case lltok::kw_wasm_multivaluecc:
+ CC = CallingConv::WASM_Multivalue;
+ break;
case lltok::kw_cc: {
Lex.Lex();
return parseUInt32(CC);
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 057255caaea3b..d3f7191ba8404 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -448,6 +448,9 @@ static void printCallingConv(unsigned cc, raw_ostream &Out) {
case CallingConv::CHERIoT_LibraryCall:
Out << "cheriot_librarycallcc";
break;
+ case CallingConv::WASM_Multivalue:
+ Out << "wasm_multivaluecc";
+ break;
}
}
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index de5d3f62efdc9..b1c671bf0bab3 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -1241,6 +1241,7 @@ bool llvm::CallingConv::supportsNonVoidReturnType(CallingConv::ID CC) {
case CallingConv::RISCV_VLSCall_16384:
case CallingConv::RISCV_VLSCall_32768:
case CallingConv::RISCV_VLSCall_65536:
+ case CallingConv::WASM_Multivalue:
return true;
default:
return false;
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 9f3a1d1ba7fa2..ae7372d68f544 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -47,6 +47,7 @@ static bool callingConvSupported(CallingConv::ID CallConv) {
CallConv == CallingConv::PreserveAll ||
CallConv == CallingConv::CXX_FAST_TLS ||
CallConv == CallingConv::WASM_EmscriptenInvoke ||
+ CallConv == CallingConv::WASM_Multivalue ||
CallConv == CallingConv::Swift;
}
@@ -84,8 +85,8 @@ bool WebAssemblyCallLowering::canLowerReturn(MachineFunction &MF,
CallingConv::ID CallConv,
SmallVectorImpl<BaseArgInfo> &Outs,
bool IsVarArg) const {
- return WebAssembly::canLowerReturn(Outs.size(),
- &MF.getSubtarget<WebAssemblySubtarget>());
+ return WebAssembly::canLowerReturn(
+ Outs.size(), &MF.getSubtarget<WebAssemblySubtarget>(), &MF.getFunction());
}
bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 7f22dc0fed135..8201c4afb3a23 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -1281,6 +1281,7 @@ static bool callingConvSupported(CallingConv::ID CallConv) {
CallConv == CallingConv::PreserveAll ||
CallConv == CallingConv::CXX_FAST_TLS ||
CallConv == CallingConv::WASM_EmscriptenInvoke ||
+ CallConv == CallingConv::WASM_Multivalue ||
CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail;
}
@@ -1581,11 +1582,11 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
}
bool WebAssemblyTargetLowering::CanLowerReturn(
- CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/,
+ CallingConv::ID /*CallConv*/, MachineFunction &MF, bool /*IsVarArg*/,
const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext & /*Context*/,
const Type *RetTy) const {
// WebAssembly can only handle returning tuples with multivalue enabled
- return WebAssembly::canLowerReturn(Outs.size(), Subtarget);
+ return WebAssembly::canLowerReturn(Outs.size(), Subtarget, &MF.getFunction());
}
SDValue WebAssemblyTargetLowering::LowerReturn(
@@ -1593,7 +1594,8 @@ SDValue WebAssemblyTargetLowering::LowerReturn(
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
SelectionDAG &DAG) const {
- assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) &&
+ assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget,
+ &DAG.getMachineFunction().getFunction()) &&
"MVP WebAssembly can only return up to one value");
if (!callingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
index 696a9529e432d..962a664fe82e0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
@@ -69,7 +69,7 @@ void llvm::computeSignatureVTs(const FunctionType *Ty,
MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
if (!WebAssembly::canLowerReturn(
Results.size(),
- &TM.getSubtarget<WebAssemblySubtarget>(ContextFunc))) {
+ &TM.getSubtarget<WebAssemblySubtarget>(ContextFunc), TargetFunc)) {
// WebAssembly can't lower returns of multiple values without demoting to
// sret unless multivalue is enabled (see
// WebAssemblyTargetLowering::CanLowerReturn). So replace multiple return
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
index f3c236ca8c9ce..ea6276bb83de7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
@@ -732,7 +732,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(PtrTy);
break;
case i64_i64_func_f32:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -741,7 +741,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::F32);
break;
case i64_i64_func_f64:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -750,7 +750,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::F64);
break;
case i16_i16_func_i16_i16:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I32);
Rets.push_back(wasm::ValType::I32);
} else {
@@ -760,7 +760,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i32_i32_func_i32_i32:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I32);
Rets.push_back(wasm::ValType::I32);
} else {
@@ -770,7 +770,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i64_i64_func_i64_i64:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -780,7 +780,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_iPTR:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -791,7 +791,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(PtrTy);
break;
case i64_i64_func_i64_i64_i64_i64:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -803,7 +803,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i64_i64_iPTR:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -816,7 +816,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(PtrTy);
break;
case i64_i64_i64_i64_func_i64_i64_i64_i64:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
@@ -830,7 +830,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i64_i64_i32:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -900,7 +900,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 (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -914,7 +914,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I64);
break;
case i64_i64_func_i32:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
@@ -923,7 +923,7 @@ void WebAssembly::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
Params.push_back(wasm::ValType::I32);
break;
case i64_i64_func_i64:
- if (WebAssembly::canLowerMultivalueReturn(&Subtarget)) {
+ if (WebAssembly::canLowerMultivalueReturn(&Subtarget, nullptr)) {
Rets.push_back(wasm::ValType::I64);
Rets.push_back(wasm::ValType::I64);
} else {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index ac8df67fe7557..6ea3190c4bf55 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -184,15 +184,20 @@ unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
}
bool WebAssembly::canLowerMultivalueReturn(
- const WebAssemblySubtarget *Subtarget) {
+ const WebAssemblySubtarget *Subtarget, const Function *F) {
+ if (!Subtarget->hasMultivalue())
+ return false;
+ if (F && F->getCallingConv() == CallingConv::WASM_Multivalue)
+ return true;
const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
Subtarget->getTargetLowering()->getTargetMachine());
- return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
+ return TM.usesMultivalueABI();
}
bool WebAssembly::canLowerReturn(size_t ResultSize,
- const WebAssemblySubtarget *Subtarget) {
- return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
+ const WebAssemblySubtarget *Subtarget,
+ const Function *F) {
+ return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget, F);
}
MachineSDNode *WebAssembly::getTLSBase(SelectionDAG &DAG, const SDLoc &DL,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
index 0827791d93657..820a7624af7db 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -69,13 +69,17 @@ 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);
+/// memory. If F is provided then its calling convention is taken into account
+/// when making this determination.
+bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget,
+ const Function *F);
/// 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);
+/// memory. If F is provided then its calling convention is taken into account
+/// when making this determination.
+bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget,
+ const Function *F);
// Get the TLS base value for the current target
// If using libcall thread context, calls
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll b/llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll
new file mode 100644
index 0000000000000..1d2b8b1ee0ab2
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll
@@ -0,0 +1,38 @@
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+%pair = type { i32, i32 }
+
+; CHECK-LABEL: returns_pair_mv:
+; CHECK-NEXT: .functype returns_pair_mv () -> (i32, i32)
+; CHECK-NEXT: i32.const 1
+; CHECK-NEXT: i32.const 2
+; CHECK-NEXT: end_function
+define wasm_multivaluecc %pair @returns_pair_mv() {
+ ret %pair { i32 1, i32 2 }
+}
+
+; CHECK-LABEL: returns_pair_c:
+; CHECK-NEXT: .functype returns_pair_c (i32) -> ()
+define %pair @returns_pair_c() {
+ ret %pair { i32 1, i32 2 }
+}
+
+; CHECK-LABEL: returns_i128:
+; CHECK-NEXT: .functype returns_i128 () -> (i64, i64)
+; CHECK-NEXT: i64.const 42
+; CHECK-NEXT: i64.const 0
+; CHECK-NEXT: end_function
+define wasm_multivaluecc i128 @returns_i128() {
+ ret i128 42
+}
+
+; CHECK-LABEL: forward_pair:
+; CHECK-NEXT: .functype forward_pair () -> (i32, i32)
+; CHECK-NEXT: call returns_pair_mv
+; CHECK-NEXT: end_function
+define wasm_multivaluecc %pair @forward_pair() {
+ %p = call wasm_multivaluecc %pair @returns_pair_mv()
+ ret %pair %p
+}
More information about the cfe-commits
mailing list