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 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 { + let Spellings = [Clang<"wasm_multivalue">]; + let Documentation = [WasmMultivalueDocs]; +} + def PreserveNone : DeclOrTypeAttr, TargetSpecificAttr> { 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..6cc55c10114be 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -28,17 +28,18 @@ 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 +96,56 @@ 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 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(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 +162,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 +187,9 @@ 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 +202,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(Ctx, Attr); case ParsedAttr::AT_M68kRTD: return createSimpleAttr(Ctx, Attr); + case ParsedAttr::AT_WasmMultivalue: + return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_PreserveNone: return createSimpleAttr(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..9319ba4d16647 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c @@ -0,0 +1,117 @@ +// 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_multivalue void @f1() +__attribute__((wasm_multivalue)) +void f1(void) {} + +// 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_multivalue i128 @f3(fp128 +__attribute__((wasm_multivalue)) +__int128 f3(long double x) { return (__int128)x; } + +struct Foo4 { }; +union Bar4 { }; + +// 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_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_multivalue %struct.Foo6 @f6(i32 {{.*}}, i32 {{.*}}) +__attribute__((wasm_multivalue)) +struct Foo6 f6(struct Foo6 x) { return x; } + +// 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_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_multivalue 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_multivalue 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_multivalue {{(noundef )?}}%struct.Foo6 %0(i32{{.*}}, i32 +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; +} 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/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; diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 86f636c636783..3befdc94d688f 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, + 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, + 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 + LLVMAMDGPULSCallConv = 95, + LLVMAMDGPUESCallConv = 96, + LLVMWasmMultivalueCallConv = 128 } LLVMCallConv; typedef enum { diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 426ca35ededb6..66cacbeacf24f 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_multivalue, // 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..c3a99d069e412 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_multivalue); KEYWORD(cc); KEYWORD(c); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index b9c36c28d04cd..cff3138e0123f 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_multivalue' /// ::= '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_multivalue: + 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..23972af1bb192 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_multivalue"; + 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..93b42456c36f0 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 &Outs, bool IsVarArg) const { - return WebAssembly::canLowerReturn(Outs.size(), - &MF.getSubtarget()); + return WebAssembly::canLowerReturn( + Outs.size(), &MF.getSubtarget(), CallConv); } bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 7f22dc0fed135..b9f9376afb5ad 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 &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, CallConv); } SDValue WebAssemblyTargetLowering::LowerReturn( @@ -1593,7 +1594,7 @@ SDValue WebAssemblyTargetLowering::LowerReturn( const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { - assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) && + 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 696a9529e432d..dd971ff31768d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp @@ -68,8 +68,9 @@ void llvm::computeSignatureVTs(const FunctionType *Ty, MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits()); if (!WebAssembly::canLowerReturn( - Results.size(), - &TM.getSubtarget(ContextFunc))) { + Results.size(), &TM.getSubtarget(ContextFunc), + 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/WebAssemblyRuntimeLibcallSignatures.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp index f3c236ca8c9ce..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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + 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 ac8df67fe7557..010ed5abc0e1f 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, CallingConv::ID CC) { + if (!Subtarget->hasMultivalue()) + return false; + if (CC == CallingConv::WASM_Multivalue) + return true; const auto &TM = static_cast( 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, + 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 0827791d93657..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,13 +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. -bool canLowerMultivalueReturn(const WebAssemblySubtarget *Subtarget); +/// 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, + 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. -bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget); +/// 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, + 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 new file mode 100644 index 0000000000000..ebcaae43cbb55 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/wasm-multivalue.ll @@ -0,0 +1,80 @@ +; 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 } + +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 } +} + +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 } +} + +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 +} + +define wasm_multivalue %pair @forward_pair() { +; CHECK-LABEL: forward_pair: +; CHECK: .functype forward_pair () -> (i32, i32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: call returns_pair_mv +; 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, 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.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() + ret %pair %p +}