https://github.com/alexcrichton updated https://github.com/llvm/llvm-project/pull/200076
>From 5fb29c53d4bf66a8036e3c3e35bbc1c05f007bbb Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 13:25:52 -0700 Subject: [PATCH 01/10] [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 077aace321264..db24923e40b8d 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 2468394929360..c45492674acdc 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@+3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}} +// nonwasm-warning@+2 {{'wasm_multivalue' calling convention is not supported for this target}} +// nonwasm-warning@+1 2 {{unknown attribute 'wasm_multivalue' ignored}} +__attribute__((wasm_multivalue)) +void f1(void); + + +struct Pair { int a; int b; }; + +// disabled-error@+3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}} +// nonwasm-warning@+2 {{'wasm_multivalue' calling convention is not supported for this target}} +// nonwasm-warning@+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@+3 {{'wasm_multivalue' calling convention requires the 'multivalue' target feature to be enabled}} +// nonwasm-warning@+2 {{'wasm_multivalue' calling convention is not supported for this target}} +// nonwasm-warning@+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@+2 {{'wasm_multivalue' attribute takes no arguments}} +// disabled-error@+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 +} >From 99253281c56ad681bbc7adacccc00ddf18a9efdc Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 16:11:00 -0700 Subject: [PATCH 02/10] Add cc to the C API --- llvm/include/llvm-c/Core.h | 83 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 86f636c636783..5582c097d3616 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -216,47 +216,48 @@ typedef enum { } LLVMDLLStorageClass; typedef enum { - LLVMCCallConv = 0, - LLVMFastCallConv = 8, - LLVMColdCallConv = 9, - LLVMGHCCallConv = 10, - LLVMHiPECallConv = 11, - LLVMAnyRegCallConv = 13, - LLVMPreserveMostCallConv = 14, - LLVMPreserveAllCallConv = 15, - LLVMSwiftCallConv = 16, - LLVMCXXFASTTLSCallConv = 17, - LLVMX86StdcallCallConv = 64, - LLVMX86FastcallCallConv = 65, - LLVMARMAPCSCallConv = 66, - LLVMARMAAPCSCallConv = 67, - LLVMARMAAPCSVFPCallConv = 68, - LLVMMSP430INTRCallConv = 69, - LLVMX86ThisCallCallConv = 70, - LLVMPTXKernelCallConv = 71, - LLVMPTXDeviceCallConv = 72, - LLVMSPIRFUNCCallConv = 75, - LLVMSPIRKERNELCallConv = 76, - LLVMIntelOCLBICallConv = 77, - LLVMX8664SysVCallConv = 78, - LLVMWin64CallConv = 79, - LLVMX86VectorCallCallConv = 80, - LLVMHHVMCallConv = 81, - LLVMHHVMCCallConv = 82, - LLVMX86INTRCallConv = 83, - LLVMAVRINTRCallConv = 84, - LLVMAVRSIGNALCallConv = 85, - LLVMAVRBUILTINCallConv = 86, - LLVMAMDGPUVSCallConv = 87, - LLVMAMDGPUGSCallConv = 88, - LLVMAMDGPUPSCallConv = 89, - LLVMAMDGPUCSCallConv = 90, - LLVMAMDGPUKERNELCallConv = 91, - LLVMX86RegCallCallConv = 92, - LLVMAMDGPUHSCallConv = 93, - LLVMMSP430BUILTINCallConv = 94, - LLVMAMDGPULSCallConv = 95, - LLVMAMDGPUESCallConv = 96 + LLVMCCallConv = 0, + LLVMFastCallConv = 8, + LLVMColdCallConv = 9, + LLVMGHCCallConv = 10, + LLVMHiPECallConv = 11, + LLVMAnyRegCallConv = 13, + LLVMPreserveMostCallConv = 14, + LLVMPreserveAllCallConv = 15, + LLVMSwiftCallConv = 16, + LLVMCXXFASTTLSCallConv = 17, + LLVMX86StdcallCallConv = 64, + LLVMX86FastcallCallConv = 65, + LLVMARMAPCSCallConv = 66, + LLVMARMAAPCSCallConv = 67, + LLVMARMAAPCSVFPCallConv = 68, + LLVMMSP430INTRCallConv = 69, + LLVMX86ThisCallCallConv = 70, + LLVMPTXKernelCallConv = 71, + LLVMPTXDeviceCallConv = 72, + LLVMSPIRFUNCCallConv = 75, + LLVMSPIRKERNELCallConv = 76, + LLVMIntelOCLBICallConv = 77, + LLVMX8664SysVCallConv = 78, + LLVMWin64CallConv = 79, + LLVMX86VectorCallCallConv = 80, + LLVMHHVMCallConv = 81, + LLVMHHVMCCallConv = 82, + LLVMX86INTRCallConv = 83, + LLVMAVRINTRCallConv = 84, + LLVMAVRSIGNALCallConv = 85, + LLVMAVRBUILTINCallConv = 86, + LLVMAMDGPUVSCallConv = 87, + LLVMAMDGPUGSCallConv = 88, + LLVMAMDGPUPSCallConv = 89, + LLVMAMDGPUCSCallConv = 90, + LLVMAMDGPUKERNELCallConv = 91, + LLVMX86RegCallCallConv = 92, + LLVMAMDGPUHSCallConv = 93, + LLVMMSP430BUILTINCallConv = 94, + LLVMAMDGPULSCallConv = 95, + LLVMAMDGPUESCallConv = 96, + LLVMWasmMultivalueCallConv = 128 } LLVMCallConv; typedef enum { >From ffb15415e9f03a18a6d4a72205856bb5c420281c Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 16:11:26 -0700 Subject: [PATCH 03/10] Run clang-format --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 16 +++++++++------- .../WebAssemblyMachineFunctionInfo.cpp | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 8942e3e78b6de..6cc55c10114be 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -37,8 +37,7 @@ class WebAssemblyABIInfo final : public ABIInfo { void computeInfo(CGFunctionInfo &FI) const override { llvm::CallingConv::ID CC = FI.getCallingConvention(); if (!getCXXABI().classifyReturnType(FI)) - FI.getReturnInfo() = - classifyReturnType(FI.getReturnType(), CC); + FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), CC); for (auto &Arg : FI.arguments()) Arg.info = classifyArgumentType(Arg.type, CC); } @@ -107,7 +106,8 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { /// 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) { +static std::optional<unsigned> countScalarFields(ASTContext &Context, + QualType T) { const auto *RD = T->getAsRecordDecl(); if (!RD) return std::nullopt; @@ -143,8 +143,9 @@ static std::optional<unsigned> countScalarFields(ASTContext &Context, QualType T } /// Classify argument of given type \p Ty. -ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty, - llvm::CallingConv::ID CC) const { +ABIArgInfo +WebAssemblyABIInfo::classifyArgumentType(QualType Ty, + llvm::CallingConv::ID CC) const { Ty = useFirstFieldIfTransparentUnion(Ty); if (isAggregateTypeForABI(Ty)) { @@ -186,8 +187,9 @@ ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty, return defaultInfo.classifyArgumentType(Ty); } -ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy, - llvm::CallingConv::ID CC) 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. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp index 962a664fe82e0..9efeea7346c25 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp @@ -68,8 +68,8 @@ void llvm::computeSignatureVTs(const FunctionType *Ty, MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits()); if (!WebAssembly::canLowerReturn( - Results.size(), - &TM.getSubtarget<WebAssemblySubtarget>(ContextFunc), TargetFunc)) { + Results.size(), &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 >From 36606fd695becdbc94fafc2e572a2e84bfa26165 Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 16:16:20 -0700 Subject: [PATCH 04/10] clang-format --- llvm/include/llvm-c/Core.h | 82 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 5582c097d3616..3befdc94d688f 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -216,47 +216,47 @@ typedef enum { } LLVMDLLStorageClass; typedef enum { - LLVMCCallConv = 0, - LLVMFastCallConv = 8, - LLVMColdCallConv = 9, - LLVMGHCCallConv = 10, - LLVMHiPECallConv = 11, - LLVMAnyRegCallConv = 13, - LLVMPreserveMostCallConv = 14, - LLVMPreserveAllCallConv = 15, - LLVMSwiftCallConv = 16, - LLVMCXXFASTTLSCallConv = 17, - LLVMX86StdcallCallConv = 64, - LLVMX86FastcallCallConv = 65, - LLVMARMAPCSCallConv = 66, - LLVMARMAAPCSCallConv = 67, - LLVMARMAAPCSVFPCallConv = 68, - LLVMMSP430INTRCallConv = 69, - LLVMX86ThisCallCallConv = 70, - LLVMPTXKernelCallConv = 71, - LLVMPTXDeviceCallConv = 72, - LLVMSPIRFUNCCallConv = 75, - LLVMSPIRKERNELCallConv = 76, - LLVMIntelOCLBICallConv = 77, - LLVMX8664SysVCallConv = 78, - LLVMWin64CallConv = 79, - LLVMX86VectorCallCallConv = 80, - LLVMHHVMCallConv = 81, - LLVMHHVMCCallConv = 82, - LLVMX86INTRCallConv = 83, - LLVMAVRINTRCallConv = 84, - LLVMAVRSIGNALCallConv = 85, - LLVMAVRBUILTINCallConv = 86, - LLVMAMDGPUVSCallConv = 87, - LLVMAMDGPUGSCallConv = 88, - LLVMAMDGPUPSCallConv = 89, - LLVMAMDGPUCSCallConv = 90, - LLVMAMDGPUKERNELCallConv = 91, - LLVMX86RegCallCallConv = 92, - LLVMAMDGPUHSCallConv = 93, - LLVMMSP430BUILTINCallConv = 94, - LLVMAMDGPULSCallConv = 95, - LLVMAMDGPUESCallConv = 96, + LLVMCCallConv = 0, + LLVMFastCallConv = 8, + LLVMColdCallConv = 9, + LLVMGHCCallConv = 10, + LLVMHiPECallConv = 11, + LLVMAnyRegCallConv = 13, + LLVMPreserveMostCallConv = 14, + LLVMPreserveAllCallConv = 15, + LLVMSwiftCallConv = 16, + LLVMCXXFASTTLSCallConv = 17, + LLVMX86StdcallCallConv = 64, + LLVMX86FastcallCallConv = 65, + LLVMARMAPCSCallConv = 66, + LLVMARMAAPCSCallConv = 67, + LLVMARMAAPCSVFPCallConv = 68, + LLVMMSP430INTRCallConv = 69, + LLVMX86ThisCallCallConv = 70, + LLVMPTXKernelCallConv = 71, + LLVMPTXDeviceCallConv = 72, + LLVMSPIRFUNCCallConv = 75, + LLVMSPIRKERNELCallConv = 76, + LLVMIntelOCLBICallConv = 77, + LLVMX8664SysVCallConv = 78, + LLVMWin64CallConv = 79, + LLVMX86VectorCallCallConv = 80, + LLVMHHVMCallConv = 81, + LLVMHHVMCCallConv = 82, + LLVMX86INTRCallConv = 83, + LLVMAVRINTRCallConv = 84, + LLVMAVRSIGNALCallConv = 85, + LLVMAVRBUILTINCallConv = 86, + LLVMAMDGPUVSCallConv = 87, + LLVMAMDGPUGSCallConv = 88, + LLVMAMDGPUPSCallConv = 89, + LLVMAMDGPUCSCallConv = 90, + LLVMAMDGPUKERNELCallConv = 91, + LLVMX86RegCallCallConv = 92, + LLVMAMDGPUHSCallConv = 93, + LLVMMSP430BUILTINCallConv = 94, + LLVMAMDGPULSCallConv = 95, + LLVMAMDGPUESCallConv = 96, LLVMWasmMultivalueCallConv = 128 } LLVMCallConv; >From b56eb7a0e54ea74e5aaa69343d4112e78ba4ba94 Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 17:57:24 -0700 Subject: [PATCH 05/10] Handle `WasmMultivalue` in another location --- clang/tools/libclang/CXType.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp index 3feb56334d79c..e355f17099618 100644 --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -728,6 +728,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(RISCVVLSCall_16384); TCALLINGCONV(RISCVVLSCall_32768); TCALLINGCONV(RISCVVLSCall_65536); + TCALLINGCONV(WasmMultivalue); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_DeviceKernel: return CXCallingConv_Unexposed; >From c653634b54b2bbc4f455bf92a1beed59949a949b Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Wed, 27 May 2026 18:17:13 -0700 Subject: [PATCH 06/10] Try to fix CI error --- clang/include/clang-c/Index.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 119bd68ff9814..818a2d29d2979 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3086,6 +3086,7 @@ enum CXCallingConv { CXCallingConv_RISCVVLSCall_16384 = 31, CXCallingConv_RISCVVLSCall_32768 = 32, CXCallingConv_RISCVVLSCall_65536 = 33, + CXCallingConv_WasmMultivalue = 34, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 >From c563023274699f29d16800da9bc3fab0af53da95 Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Thu, 28 May 2026 15:47:35 -0700 Subject: [PATCH 07/10] Rename `wasm_multivaluecc` to `wasm_multivalue` --- .../CodeGen/WebAssembly/wasm-multivalue-abi.c | 22 +++++++++---------- llvm/include/llvm/AsmParser/LLToken.h | 2 +- llvm/lib/AsmParser/LLLexer.cpp | 2 +- llvm/lib/AsmParser/LLParser.cpp | 4 ++-- llvm/lib/IR/AsmWriter.cpp | 2 +- ...sm-multivalue-cc.ll => wasm-multivalue.ll} | 8 +++---- 6 files changed, 20 insertions(+), 20 deletions(-) rename llvm/test/CodeGen/WebAssembly/{wasm-multivalue-cc.ll => wasm-multivalue.ll} (80%) diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c index 5194616a13733..cdac18534e780 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c +++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c @@ -6,51 +6,51 @@ // 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() +// CHECK-LABEL: define wasm_multivalue void @f1() __attribute__((wasm_multivalue)) void f1(void) {} -// CHECK-LABEL: define wasm_multivaluecc i32 @f2(float {{.*}}, double {{.*}}) +// CHECK-LABEL: define wasm_multivalue 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 +// CHECK-LABEL: define wasm_multivalue 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() +// CHECK-LABEL: define wasm_multivalue 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 +// CHECK-LABEL: define wasm_multivalue 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 {{.*}}) +// CHECK-LABEL: define wasm_multivalue %struct.Foo6 @f6(i32 {{.*}}, i32 {{.*}}) __attribute__((wasm_multivalue)) struct Foo6 f6(struct Foo6 x) { return x; } -// CHECK-LABEL: define wasm_multivaluecc i128 @f7() +// CHECK-LABEL: define wasm_multivalue 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 +// CHECK-LABEL: define wasm_multivalue %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 +// CHECK-LABEL: define wasm_multivalue void @f9(ptr {{.*}} sret __attribute__((wasm_multivalue)) struct Foo9 f9(void) { struct Foo9 r = {{0, 0}}; return r; } @@ -59,7 +59,7 @@ struct Foo10 { int a : 4; int b : 4; }; -// CHECK-LABEL: define wasm_multivaluecc void @f10(ptr +// CHECK-LABEL: define wasm_multivalue void @f10(ptr __attribute__((wasm_multivalue)) struct Foo10 f10(void) { struct Foo10 r = {0, 0}; return r; } @@ -71,7 +71,7 @@ struct Foo6 f11(struct Foo6 x) { return x; } 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 +// CHECK: call wasm_multivalue {{(noundef )?}}%struct.Foo6 %0(i32{{.*}}, i32 struct Foo6 f12(mv_ptr fn, struct Foo6 x) { return fn(x); } diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index ba0eba37f64d1..66cacbeacf24f 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -192,7 +192,7 @@ enum Kind { kw_cheriot_compartmentcallcc, kw_cheriot_compartmentcalleecc, kw_cheriot_librarycallcc, - kw_wasm_multivaluecc, + kw_wasm_multivalue, // Attributes: kw_attributes, diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 79a3e2298e525..c3a99d069e412 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -705,7 +705,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(cheriot_compartmentcallcc); KEYWORD(cheriot_compartmentcalleecc); KEYWORD(cheriot_librarycallcc); - KEYWORD(wasm_multivaluecc); + KEYWORD(wasm_multivalue); KEYWORD(cc); KEYWORD(c); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index d797b06db506f..cff3138e0123f 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2257,7 +2257,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'graalcc' /// ::= 'riscv_vector_cc' /// ::= 'riscv_vls_cc' -/// ::= 'wasm_multivaluecc' +/// ::= 'wasm_multivalue' /// ::= 'cc' UINT /// bool LLParser::parseOptionalCallingConv(unsigned &CC) { @@ -2377,7 +2377,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) { case lltok::kw_cheriot_librarycallcc: CC = CallingConv::CHERIoT_LibraryCall; break; - case lltok::kw_wasm_multivaluecc: + case lltok::kw_wasm_multivalue: CC = CallingConv::WASM_Multivalue; break; case lltok::kw_cc: { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index d3f7191ba8404..23972af1bb192 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -449,7 +449,7 @@ static void printCallingConv(unsigned cc, raw_ostream &Out) { Out << "cheriot_librarycallcc"; break; case CallingConv::WASM_Multivalue: - Out << "wasm_multivaluecc"; + Out << "wasm_multivalue"; break; } } diff --git a/llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll similarity index 80% rename from llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll rename to llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll index 1d2b8b1ee0ab2..e5743de841beb 100644 --- a/llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll +++ b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-unknown" ; CHECK-NEXT: i32.const 1 ; CHECK-NEXT: i32.const 2 ; CHECK-NEXT: end_function -define wasm_multivaluecc %pair @returns_pair_mv() { +define wasm_multivalue %pair @returns_pair_mv() { ret %pair { i32 1, i32 2 } } @@ -24,7 +24,7 @@ define %pair @returns_pair_c() { ; CHECK-NEXT: i64.const 42 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: end_function -define wasm_multivaluecc i128 @returns_i128() { +define wasm_multivalue i128 @returns_i128() { ret i128 42 } @@ -32,7 +32,7 @@ define wasm_multivaluecc i128 @returns_i128() { ; 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() +define wasm_multivalue %pair @forward_pair() { + %p = call wasm_multivalue %pair @returns_pair_mv() ret %pair %p } >From 3c770923fc8e75cbe581d27d2b4bb1e6f2ab358f Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Thu, 28 May 2026 12:52:01 -0700 Subject: [PATCH 08/10] Use the `CallingConv` passed in, don't use the target function Fixes a case where a function calls another function with a different calling convention. --- .../GISel/WebAssemblyCallLowering.cpp | 2 +- .../WebAssembly/WebAssemblyISelLowering.cpp | 7 +- .../WebAssemblyMachineFunctionInfo.cpp | 3 +- .../WebAssembly/WebAssemblyRegStackify.cpp | 2 + .../WebAssemblyRuntimeLibcallSignatures.cpp | 26 ++++---- .../WebAssembly/WebAssemblyUtilities.cpp | 8 +-- .../Target/WebAssembly/WebAssemblyUtilities.h | 13 ++-- .../CodeGen/WebAssembly/wasm-multivalue.ll | 65 ++++++++++++++----- 8 files changed, 80 insertions(+), 46 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp index ae7372d68f544..93b42456c36f0 100644 --- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp +++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp @@ -86,7 +86,7 @@ bool WebAssemblyCallLowering::canLowerReturn(MachineFunction &MF, SmallVectorImpl<BaseArgInfo> &Outs, bool IsVarArg) const { return WebAssembly::canLowerReturn( - Outs.size(), &MF.getSubtarget<WebAssemblySubtarget>(), &MF.getFunction()); + Outs.size(), &MF.getSubtarget<WebAssemblySubtarget>(), CallConv); } bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 8201c4afb3a23..b9f9376afb5ad 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -1582,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, &MF.getFunction()); + return WebAssembly::canLowerReturn(Outs.size(), Subtarget, CallConv); } SDValue WebAssemblyTargetLowering::LowerReturn( @@ -1594,8 +1594,7 @@ SDValue WebAssemblyTargetLowering::LowerReturn( const SmallVectorImpl<ISD::OutputArg> &Outs, const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { - assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget, - &DAG.getMachineFunction().getFunction()) && + assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget, CallConv) && "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 9efeea7346c25..dd971ff31768d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp @@ -69,7 +69,8 @@ void llvm::computeSignatureVTs(const FunctionType *Ty, MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits()); if (!WebAssembly::canLowerReturn( Results.size(), &TM.getSubtarget<WebAssemblySubtarget>(ContextFunc), - TargetFunc)) { + TargetFunc ? TargetFunc->getCallingConv() + : CallingConv::ID(CallingConv::C))) { // 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/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 9015ceab87fb7..6ab81c5fa152d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -375,6 +375,8 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, // current def in the stack, which cannot be achieved, even with locals. // Also ensure we don't sink the def past any other prior uses. for (const auto &SubsequentDef : drop_begin(DefI->defs())) { + if (true) + return false; auto I = std::next(MachineBasicBlock::const_iterator(DefI)); auto E = std::next(MachineBasicBlock::const_iterator(UseI)); for (; I != E; ++I) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp index ea6276bb83de7..a48e90529b7eb 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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, nullptr)) { + if (WebAssembly::canLowerMultivalueReturn(&Subtarget, CallingConv::C)) { 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 6ea3190c4bf55..010ed5abc0e1f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -184,10 +184,10 @@ unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) { } bool WebAssembly::canLowerMultivalueReturn( - const WebAssemblySubtarget *Subtarget, const Function *F) { + const WebAssemblySubtarget *Subtarget, CallingConv::ID CC) { if (!Subtarget->hasMultivalue()) return false; - if (F && F->getCallingConv() == CallingConv::WASM_Multivalue) + if (CC == CallingConv::WASM_Multivalue) return true; const auto &TM = static_cast<const WebAssemblyTargetMachine &>( Subtarget->getTargetLowering()->getTargetMachine()); @@ -196,8 +196,8 @@ bool WebAssembly::canLowerMultivalueReturn( bool WebAssembly::canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget, - const Function *F) { - return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget, F); + CallingConv::ID CC) { + return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget, CC); } 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 820a7624af7db..4fb8df9eee3e9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -16,6 +16,7 @@ #define LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYUTILITIES_H #include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/CallingConv.h" #include "llvm/Support/CommandLine.h" namespace llvm { @@ -69,17 +70,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. If F is provided then its calling convention is taken into account -/// when making this determination. +/// memory. The calling convention of the call or function whose return is +/// being lowered is taken into account when making this determination. bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget, - const Function *F); + CallingConv::ID CC); /// 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. If F is provided then its calling convention is taken into account -/// when making this determination. +/// memory. The calling convention of the call or function whose return is +/// being lowered is taken into account when making this determination. bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget, - const Function *F); + CallingConv::ID CC); // Get the TLS base value for the current target // If using libcall thread context, calls diff --git a/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll index e5743de841beb..6d8b662974969 100644 --- a/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll +++ b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll @@ -1,38 +1,69 @@ -; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | 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_multivalue %pair @returns_pair_mv() { +; CHECK-LABEL: returns_pair_mv: +; CHECK: .functype returns_pair_mv () -> (i32, i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: i32.const 1 +; CHECK-NEXT: i32.const 2 +; CHECK-NEXT: # fallthrough-return ret %pair { i32 1, i32 2 } } -; CHECK-LABEL: returns_pair_c: -; CHECK-NEXT: .functype returns_pair_c (i32) -> () define %pair @returns_pair_c() { +; CHECK-LABEL: returns_pair_c: +; CHECK: .functype returns_pair_c (i32) -> () +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i64.const 8589934593 +; CHECK-NEXT: i64.store 0 +; CHECK-NEXT: # fallthrough-return 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_multivalue i128 @returns_i128() { +; CHECK-LABEL: returns_i128: +; CHECK: .functype returns_i128 () -> (i64, i64) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: i64.const 42 +; CHECK-NEXT: i64.const 0 +; CHECK-NEXT: # fallthrough-return 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_multivalue %pair @forward_pair() { +; CHECK-LABEL: forward_pair: +; CHECK: .functype forward_pair () -> (i32, i32) +; CHECK-NEXT: .local i32, i32 +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call returns_pair_mv +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: # fallthrough-return %p = call wasm_multivalue %pair @returns_pair_mv() ret %pair %p } + +; A multi-value caller calling a non-multi-value ABI should do the appropriate +; handling for the ABI-at-hand. +define wasm_multivalue %pair @caller_mv_callee_c() { +; CHECK-LABEL: caller_mv_callee_c: +; CHECK: .functype caller_mv_callee_c () -> (i32, i32) +; CHECK-NEXT: .local i32, i32 +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call returns_pair_c +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: # fallthrough-return + %p = call %pair @returns_pair_c() + ret %pair %p +} >From 976cda022f5b296811174638960133b211eeb6ea Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Thu, 28 May 2026 16:32:16 -0700 Subject: [PATCH 09/10] Remove some debugging --- .../WebAssembly/WebAssemblyRegStackify.cpp | 2 -- .../CodeGen/WebAssembly/wasm-multivalue.ll | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 6ab81c5fa152d..9015ceab87fb7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -375,8 +375,6 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, // current def in the stack, which cannot be achieved, even with locals. // Also ensure we don't sink the def past any other prior uses. for (const auto &SubsequentDef : drop_begin(DefI->defs())) { - if (true) - return false; auto I = std::next(MachineBasicBlock::const_iterator(DefI)); auto E = std::next(MachineBasicBlock::const_iterator(UseI)); for (; I != E; ++I) { diff --git a/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll index 6d8b662974969..ebcaae43cbb55 100644 --- a/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll +++ b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll @@ -39,13 +39,8 @@ define wasm_multivalue i128 @returns_i128() { define wasm_multivalue %pair @forward_pair() { ; CHECK-LABEL: forward_pair: ; CHECK: .functype forward_pair () -> (i32, i32) -; CHECK-NEXT: .local i32, i32 ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: call returns_pair_mv -; CHECK-NEXT: local.set 1 -; CHECK-NEXT: local.set 0 -; CHECK-NEXT: local.get 0 -; CHECK-NEXT: local.get 1 ; CHECK-NEXT: # fallthrough-return %p = call wasm_multivalue %pair @returns_pair_mv() ret %pair %p @@ -56,12 +51,28 @@ define wasm_multivalue %pair @forward_pair() { define wasm_multivalue %pair @caller_mv_callee_c() { ; CHECK-LABEL: caller_mv_callee_c: ; CHECK: .functype caller_mv_callee_c () -> (i32, i32) -; CHECK-NEXT: .local i32, i32 +; CHECK-NEXT: .local i32, i32, i32 ; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: global.get __stack_pointer +; CHECK-NEXT: i32.const 16 +; CHECK-NEXT: i32.sub +; CHECK-NEXT: local.tee 0 +; CHECK-NEXT: global.set __stack_pointer +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.const 8 +; CHECK-NEXT: i32.add ; CHECK-NEXT: call returns_pair_c +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.load 12 ; CHECK-NEXT: local.set 1 -; CHECK-NEXT: local.set 0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.load 8 +; CHECK-NEXT: local.set 2 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.const 16 +; CHECK-NEXT: i32.add +; CHECK-NEXT: global.set __stack_pointer +; CHECK-NEXT: local.get 2 ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: # fallthrough-return %p = call %pair @returns_pair_c() >From c8296e385b8242710e465038271b8b76498d4cd2 Mon Sep 17 00:00:00 2001 From: Alex Crichton <[email protected]> Date: Thu, 28 May 2026 17:05:32 -0700 Subject: [PATCH 10/10] Add test showcasing what happens to arrays --- .../CodeGen/WebAssembly/wasm-multivalue-abi.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c index cdac18534e780..9319ba4d16647 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c +++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c @@ -75,3 +75,43 @@ typedef __attribute__((wasm_multivalue)) struct Foo6 (*mv_ptr)(struct Foo6); struct Foo6 f12(mv_ptr fn, struct Foo6 x) { return fn(x); } + +struct Foo13 { + int empty_array[0]; +}; + +// CHECK-LABEL: define wasm_multivalue void @f13() +__attribute__((wasm_multivalue)) +struct Foo13 f13(struct Foo13 x) { + return x; +} + +struct Foo14 { + int one_element_array[1]; +}; + +// CHECK-LABEL: define wasm_multivalue i32 @f14(i32 +__attribute__((wasm_multivalue)) +struct Foo14 f14(struct Foo14 x) { + return x; +} + +struct Foo15 { + int two_element_array[2]; +}; + +// CHECK-LABEL: define wasm_multivalue void @f15(ptr {{.*}}, ptr {{.*}}) +__attribute__((wasm_multivalue)) +struct Foo15 f15(struct Foo15 x) { + return x; +} + +struct Foo16 { + int three_element_array[3]; +}; + +// CHECK-LABEL: define wasm_multivalue void @f16(ptr {{.*}}, ptr {{.*}}) +__attribute__((wasm_multivalue)) +struct Foo16 f16(struct Foo16 x) { + return x; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
