llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Alex Crichton (alexcrichton)

<details>
<summary>Changes</summary>

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.


---

Patch is 39.14 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/200076.diff


30 Files Affected:

- (modified) clang/include/clang/Basic/Attr.td (+6) 
- (modified) clang/include/clang/Basic/AttrDocs.td (+15) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4-1) 
- (modified) clang/include/clang/Basic/Specifiers.h (+1) 
- (modified) clang/lib/AST/ItaniumMangle.cpp (+1) 
- (modified) clang/lib/AST/Type.cpp (+3) 
- (modified) clang/lib/AST/TypePrinter.cpp (+6) 
- (modified) clang/lib/Basic/Targets/WebAssembly.h (+1) 
- (modified) clang/lib/CodeGen/CGCall.cpp (+2) 
- (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+2) 
- (modified) clang/lib/CodeGen/Targets/WebAssembly.cpp (+68-6) 
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+13) 
- (modified) clang/lib/Sema/SemaType.cpp (+4-1) 
- (added) clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c (+77) 
- (added) clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c (+45) 
- (added) clang/test/Sema/attr-wasm-multivalue.c (+33) 
- (modified) llvm/include/llvm/AsmParser/LLToken.h (+1) 
- (modified) llvm/include/llvm/BinaryFormat/Dwarf.def (+1) 
- (modified) llvm/include/llvm/IR/CallingConv.h (+4) 
- (modified) llvm/lib/AsmParser/LLLexer.cpp (+1) 
- (modified) llvm/lib/AsmParser/LLParser.cpp (+4) 
- (modified) llvm/lib/IR/AsmWriter.cpp (+3) 
- (modified) llvm/lib/IR/Function.cpp (+1) 
- (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp 
(+3-2) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+5-3) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp 
(+1-1) 
- (modified) 
llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp (+13-13) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp (+9-4) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h (+8-4) 
- (added) llvm/test/CodeGen/WebAssembly/wasm-multivalue-cc.ll (+38) 


``````````diff
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 70b5773f95b08..42a68ff02975d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3559,6 +3559,12 @@ def M68kRTD: DeclOrTypeAttr {
   let Documentation = [M68kRTDDocs];
 }
 
+def WasmMultivalue : DeclOrTypeAttr,
+                     TargetSpecificAttr<TargetWebAssembly> {
+  let Spellings = [Clang<"wasm_multivalue">];
+  let Documentation = [WasmMultivalueDocs];
+}
+
 def PreserveNone : DeclOrTypeAttr,
                    
TargetSpecificAttr<TargetArch<!listconcat(TargetAArch64.Arches, 
TargetAnyX86.Arches)>> {
   let Spellings = [Clang<"preserve_none">];
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 87b9053be7cb6..dafba0108506d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3732,6 +3732,21 @@ using the `rtd` instruction.
   }];
 }
 
+def WasmMultivalueDocs : Documentation {
+  let Category = DocCatCallingConvs;
+  let Content = [{
+On WebAssembly targets, this attribute selects the ``wasm-multivalue`` calling
+convention as defined in the WebAssembly/tool-conventions repository. Relative
+to the default calling convention this takes advantage of the multi-value
+proposal in its ABI definition.
+
+This calling convention requires the ``multivalue`` target feature. Using the
+attribute without this feature enabled is a compile-time error. Enabling
+``multivalue`` does not change the default calling convention; this attribute
+must be used to opt in to the new behavior on a per-function basis.
+  }];
+}
+
 def DocCatConsumed : DocumentationCategory<"Consumed Annotation Checking"> {
   let Content = [{
 Clang supports additional attributes for checking basic resource management
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e330ea03d0544..71df166089342 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13828,7 +13828,7 @@ def err_builtin_pass_in_regs_non_class : Error<
   "argument %0 is not an unqualified class type">;
 
 
-// WebAssembly reference type and table diagnostics.
+// WebAssembly-related diagnostics.
 def err_wasm_reference_pr : Error<
   "%select{pointer|reference}0 to WebAssembly reference type is not allowed">;
 def err_wasm_ca_reference : Error<
@@ -13871,6 +13871,9 @@ def 
err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union
     : Error<"not supported with the multivalue ABI for "
             "function pointers with a struct/union as %select{return "
             "value|parameter}0">;
+def err_wasm_multivalue_requires_feature : Error<
+  "the 'wasm_multivalue' calling convention requires the 'multivalue' target "
+  "feature to be enabled">;
 
 // OpenACC diagnostics.
 def warn_acc_routine_unimplemented
diff --git a/clang/include/clang/Basic/Specifiers.h 
b/clang/include/clang/Basic/Specifiers.h
index 8da6fd4cf454a..b50deb91eef2c 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -313,6 +313,7 @@ namespace clang {
     CC_RISCVVLSCall_16384, // __attribute__((riscv_vls_cc(16384)))
     CC_RISCVVLSCall_32768, // __attribute__((riscv_vls_cc(32768)))
     CC_RISCVVLSCall_65536, // __attribute__((riscv_vls_cc(65536)))
+    CC_WasmMultivalue,     // __attribute__((wasm_multivalue))
   };
 
   /// Checks whether the given calling convention supports variadic
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 1cb6fa05f22ac..743dc390866d6 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3534,6 +3534,7 @@ StringRef 
CXXNameMangler::getCallingConvQualifierName(CallingConv CC) {
   case CC_PreserveMost:
   case CC_PreserveAll:
   case CC_M68kRTD:
+  case CC_WasmMultivalue:
   case CC_PreserveNone:
   case CC_RISCVVectorCall:
 #define CC_VLS_CASE(ABI_VLEN) case CC_RISCVVLSCall_##ABI_VLEN:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 96a398aa21dad..fa66c146b1d87 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3741,6 +3741,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv 
CC) {
     return "preserve_all";
   case CC_M68kRTD:
     return "m68k_rtd";
+  case CC_WasmMultivalue:
+    return "wasm_multivalue";
   case CC_PreserveNone:
     return "preserve_none";
     // clang-format off
@@ -4552,6 +4554,7 @@ bool AttributedType::isCallingConv() const {
   case attr::PreserveMost:
   case attr::PreserveAll:
   case attr::M68kRTD:
+  case attr::WasmMultivalue:
   case attr::PreserveNone:
   case attr::RISCVVectorCC:
   case attr::RISCVVLSCC:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 80f5b90ba35c4..273e12168ac97 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1173,6 +1173,9 @@ void TypePrinter::printFunctionAfter(const 
FunctionType::ExtInfo &Info,
     case CC_M68kRTD:
       OS << " __attribute__((m68k_rtd))";
       break;
+    case CC_WasmMultivalue:
+      OS << " __attribute__((wasm_multivalue))";
+      break;
     case CC_PreserveNone:
       OS << " __attribute__((preserve_none))";
       break;
@@ -2081,6 +2084,9 @@ void TypePrinter::printAttributedAfter(const 
AttributedType *T,
   case attr::M68kRTD:
     OS << "m68k_rtd";
     break;
+  case attr::WasmMultivalue:
+    OS << "wasm_multivalue";
+    break;
   case attr::RISCVVectorCC:
     OS << "riscv_vector_cc";
     break;
diff --git a/clang/lib/Basic/Targets/WebAssembly.h 
b/clang/lib/Basic/Targets/WebAssembly.h
index 6085197498163..165acaabe96df 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -184,6 +184,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : 
public TargetInfo {
     switch (CC) {
     case CC_C:
     case CC_Swift:
+    case CC_WasmMultivalue:
       return CCCR_OK;
     case CC_SwiftAsync:
       return CCCR_Error;
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..2dad85ec71e30 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -103,6 +103,8 @@ unsigned 
CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) {
     return llvm::CallingConv::SwiftTail;
   case CC_M68kRTD:
     return llvm::CallingConv::M68k_RTD;
+  case CC_WasmMultivalue:
+    return llvm::CallingConv::WASM_Multivalue;
   case CC_PreserveNone:
     return llvm::CallingConv::PreserveNone;
     // clang-format off
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0d45df02a2a21..090741c1ff992 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1838,6 +1838,8 @@ static unsigned getDwarfCC(CallingConv CC) {
     return llvm::dwarf::DW_CC_LLVM_X86RegCall;
   case CC_M68kRTD:
     return llvm::dwarf::DW_CC_LLVM_M68kRTD;
+  case CC_WasmMultivalue:
+    return llvm::dwarf::DW_CC_LLVM_WasmMultivalue;
   case CC_PreserveNone:
     return llvm::dwarf::DW_CC_LLVM_PreserveNone;
   case CC_RISCVVectorCall:
diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index ebe996a4edd8d..8942e3e78b6de 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -28,17 +28,19 @@ class WebAssemblyABIInfo final : public ABIInfo {
       : ABIInfo(CGT), defaultInfo(CGT), Kind(Kind) {}
 
 private:
-  ABIArgInfo classifyReturnType(QualType RetTy) const;
-  ABIArgInfo classifyArgumentType(QualType Ty) const;
+  ABIArgInfo classifyReturnType(QualType RetTy, llvm::CallingConv::ID CC) 
const;
+  ABIArgInfo classifyArgumentType(QualType Ty, llvm::CallingConv::ID CC) const;
 
   // DefaultABIInfo's classifyReturnType and classifyArgumentType are
   // non-virtual, but computeInfo and EmitVAArg are virtual, so we
   // overload them.
   void computeInfo(CGFunctionInfo &FI) const override {
+    llvm::CallingConv::ID CC = FI.getCallingConvention();
     if (!getCXXABI().classifyReturnType(FI))
-      FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+      FI.getReturnInfo() =
+          classifyReturnType(FI.getReturnType(), CC);
     for (auto &Arg : FI.arguments())
-      Arg.info = classifyArgumentType(Arg.type);
+      Arg.info = classifyArgumentType(Arg.type, CC);
   }
 
   RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
@@ -95,8 +97,54 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
   }
 };
 
+/// Count the number of "scalar fields" in the given record type, as defined by
+/// WebAssembly/tool-conventions for the "wasm-multivalue" calling convention
+/// primarily. A scalar field is a field that recursively, through nested
+/// structs, unions, and arrays, contains just a single scalar value.
+///
+/// Returns the number of scalar fields, or std::nullopt if the record contains
+/// a field that is not a scalar field (e.g., a sub-aggregate with multiple
+/// scalars, a bit-field, or a flexible array member).
+///
+/// Note that this is similar to `isSingleElementStruct` in structure.
+static std::optional<unsigned> countScalarFields(ASTContext &Context, QualType 
T) {
+  const auto *RD = T->getAsRecordDecl();
+  if (!RD)
+    return std::nullopt;
+  if (RD->hasFlexibleArrayMember())
+    return std::nullopt;
+
+  unsigned Count = 0;
+
+  // Check bases first for C++ records.
+  if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+    for (const auto &Base : CXXRD->bases()) {
+      auto SubCount = countScalarFields(Context, Base.getType());
+      if (!SubCount)
+        return std::nullopt;
+      Count += *SubCount;
+    }
+  }
+
+  for (const auto *FD : RD->fields()) {
+    if (FD->isBitField())
+      return std::nullopt;
+    if (isEmptyField(Context, FD, true))
+      continue;
+
+    QualType T = FD->getType();
+    if (isAggregateTypeForABI(T) && !isSingleElementStruct(T, Context))
+      return std::nullopt;
+
+    ++Count;
+  }
+
+  return Count;
+}
+
 /// Classify argument of given type \p Ty.
-ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
+ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty,
+                                                    llvm::CallingConv::ID CC) 
const {
   Ty = useFirstFieldIfTransparentUnion(Ty);
 
   if (isAggregateTypeForABI(Ty)) {
@@ -113,6 +161,12 @@ ABIArgInfo 
WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
     // though watch out for things like bitfields.
     if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
       return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
+    // For the wasm-multivalue calling convention, structs with exactly two
+    // scalar fields are passed directly as two arguments.
+    if (CC == llvm::CallingConv::WASM_Multivalue) {
+      if (auto N = countScalarFields(getContext(), Ty); N && *N == 2)
+        return ABIArgInfo::getExpand();
+    }
     // For the experimental multivalue ABI, fully expand all other aggregates
     if (Kind == WebAssemblyABIKind::ExperimentalMV) {
       const auto *RD = Ty->castAsRecordDecl();
@@ -132,7 +186,8 @@ ABIArgInfo 
WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
   return defaultInfo.classifyArgumentType(Ty);
 }
 
-ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy) const {
+ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy,
+                                                  llvm::CallingConv::ID CC) 
const {
   if (isAggregateTypeForABI(RetTy)) {
     // Records with non-trivial destructors/copy-constructors should not be
     // returned by value.
@@ -145,6 +200,13 @@ ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType 
RetTy) const {
       // ABIArgInfo::getDirect().
       if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
         return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
+      // For the wasm-multivalue calling convention, structs whose fields are
+      // (recursively) scalars are returned directly via the multivalue
+      // proposal.
+      if (CC == llvm::CallingConv::WASM_Multivalue) {
+        if (auto N = countScalarFields(getContext(), RetTy); N && *N > 0)
+          return ABIArgInfo::getDirect();
+      }
       // For the experimental multivalue ABI, return all other aggregates
       if (Kind == WebAssemblyABIKind::ExperimentalMV)
         return ABIArgInfo::getDirect();
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ae04d3855f01c..ac21210bd7112 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5543,6 +5543,9 @@ static void handleCallConvAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
   case ParsedAttr::AT_M68kRTD:
     D->addAttr(::new (S.Context) M68kRTDAttr(S.Context, AL));
     return;
+  case ParsedAttr::AT_WasmMultivalue:
+    D->addAttr(::new (S.Context) WasmMultivalueAttr(S.Context, AL));
+    return;
   case ParsedAttr::AT_PreserveNone:
     D->addAttr(::new (S.Context) PreserveNoneAttr(S.Context, AL));
     return;
@@ -5814,6 +5817,15 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, 
CallingConv &CC,
   case ParsedAttr::AT_M68kRTD:
     CC = CC_M68kRTD;
     break;
+  case ParsedAttr::AT_WasmMultivalue:
+    CC = CC_WasmMultivalue;
+    if (Context.getTargetInfo().getTriple().isWasm() &&
+        !Context.getTargetInfo().hasFeature("multivalue")) {
+      Attrs.setInvalid();
+      Diag(Attrs.getLoc(), diag::err_wasm_multivalue_requires_feature);
+      return true;
+    }
+    break;
   case ParsedAttr::AT_PreserveNone:
     CC = CC_PreserveNone;
     break;
@@ -8068,6 +8080,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_PreserveNone:
   case ParsedAttr::AT_RISCVVectorCC:
   case ParsedAttr::AT_RISCVVLSCC:
+  case ParsedAttr::AT_WasmMultivalue:
     handleCallConvAttr(S, D, AL);
     break;
   case ParsedAttr::AT_DeviceKernel:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 44ac4f6630690..2811cb0ee95d4 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -143,7 +143,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const 
ParsedAttr &attr,
   case ParsedAttr::AT_M68kRTD:                                                 
\
   case ParsedAttr::AT_PreserveNone:                                            
\
   case ParsedAttr::AT_RISCVVectorCC:                                           
\
-  case ParsedAttr::AT_RISCVVLSCC
+  case ParsedAttr::AT_RISCVVLSCC:                                              
\
+  case ParsedAttr::AT_WasmMultivalue
 
 // Function type attributes.
 #define FUNCTION_TYPE_ATTRS_CASELIST                                           
\
@@ -7803,6 +7804,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr 
&Attr) {
     return createSimpleAttr<PreserveAllAttr>(Ctx, Attr);
   case ParsedAttr::AT_M68kRTD:
     return createSimpleAttr<M68kRTDAttr>(Ctx, Attr);
+  case ParsedAttr::AT_WasmMultivalue:
+    return createSimpleAttr<WasmMultivalueAttr>(Ctx, Attr);
   case ParsedAttr::AT_PreserveNone:
     return createSimpleAttr<PreserveNoneAttr>(Ctx, Attr);
   case ParsedAttr::AT_RISCVVectorCC:
diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c 
b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c
new file mode 100644
index 0000000000000..5194616a13733
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-abi.c
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue \
+// RUN:   %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +multivalue \
+// RUN:   %s -emit-llvm -o - | FileCheck %s
+
+// Verify that the `wasm_multivalue` calling convention produces the function
+// signatures described in the WebAssembly tool conventions PR.
+
+// CHECK-LABEL: define wasm_multivaluecc void @f1()
+__attribute__((wasm_multivalue))
+void f1(void) {}
+
+// CHECK-LABEL: define wasm_multivaluecc i32 @f2(float {{.*}}, double {{.*}})
+__attribute__((wasm_multivalue))
+int f2(float a, double b) { return (int)(a + b); }
+
+// CHECK-LABEL: define wasm_multivaluecc i128 @f3(fp128
+__attribute__((wasm_multivalue))
+__int128 f3(long double x) { return (__int128)x; }
+
+struct Foo4 { };
+union Bar4 { };
+
+// CHECK-LABEL: define wasm_multivaluecc void @f4()
+__attribute__((wasm_multivalue))
+union Bar4 f4(struct Foo4 x) { union Bar4 r; return r; }
+
+struct Foo5 { int a; };
+union Bar5 { int a; };
+
+// CHECK-LABEL: define wasm_multivaluecc i32 @f5(i32
+__attribute__((wasm_multivalue))
+union Bar5 f5(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+
+struct Foo6 { int a; int b; };
+
+// CHECK-LABEL: define wasm_multivaluecc %struct.Foo6 @f6(i32 {{.*}}, i32 
{{.*}})
+__attribute__((wasm_multivalue))
+struct Foo6 f6(struct Foo6 x) { return x; }
+
+// CHECK-LABEL: define wasm_multivaluecc i128 @f7()
+__attribute__((wasm_multivalue))
+__int128 f7(void) { return 1; }
+
+struct Foo8 { int a; int b; int c; };
+// CHECK-LABEL: define wasm_multivaluecc %struct.Foo8 @f8(ptr
+__attribute__((wasm_multivalue))
+struct Foo8 f8(struct Foo8 x) { return x; }
+
+struct Foo9 {
+  struct Foo6 inner;
+};
+// CHECK-LABEL: define wasm_multivaluecc void @f9(ptr {{.*}} sret
+__attribute__((wasm_multivalue))
+struct Foo9 f9(void) { struct Foo9 r = {{0, 0}}; return r; }
+
+// bitfields force pointers
+struct Foo10 {
+  int a : 4;
+  int b : 4;
+};
+// CHECK-LABEL: define wasm_multivaluecc void @f10(ptr
+__attribute__((wasm_multivalue))
+struct Foo10 f10(void) { struct Foo10 r = {0, 0}; return r; }
+
+// The default calling convention isn't changed from `+multivalue`
+// CHECK-LABEL: define void @f11(ptr{{.*}}sret(%struct.Foo6){{.*}}, ptr 
{{.*}}byval(%struct.Foo6)
+struct Foo6 f11(struct Foo6 x) { return x; }
+
+// Test cross-calling-convention indierct calls
+typedef __attribute__((wasm_multivalue)) struct Foo6 (*mv_ptr)(struct Foo6);
+
+// CHECK-LABEL: define void @f12(
+// CHECK: call wasm_multivaluecc {{(noundef )?}}%struct.Foo6 %0(i32{{.*}}, i32
+struct Foo6 f12(mv_ptr fn, struct Foo6 x) {
+  return fn(x);
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c 
b/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c
new file mode 100644
index 0000000000000..c522640901473
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-multivalue-functype.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue 
%s -S -O2 -o - | FileCheck %s
+
+// CHECK: .functype f1 () -> ()
+void f1(void) {}
+// CHECK: .functype f1mv () -> ()
+__attribute__((wasm_multivalue))
+void f1mv(void) {}
+
+// CHECK: .functype f2 (f32, f64) -> (i32)
+int f2(float a, double b) { return (int)(a + b); }
+// CHECK: .functype f2mv (f32, f64) -> (i32)
+__attribute__((wasm_multivalue))
+int f2mv(float a, double b) { return (int)(a + b); }
+
+// CHECK: .functype f3 (i32, i64, i64) -> ()
+__int128 f3(long double x) { return (__int128)x; }
+// CHECK: .functype f3mv (i64, i64) -> (i64, i64)
+__attribute__((wasm_multivalue))
+__int128 f3mv(long double x) { return (__int128)x; }
+
+struct Foo4 { };
+union Bar4 { };
+
+// CHECK: .functype f4 () -> ()
+union Bar4 f4(struct Foo4 x) { union Bar4 r; return r; }
+// CHECK: .functype f4mv () -> ()
+__attribute__((wasm_multivalue))
+union Bar4 f4mv(struct Foo4 x) { union Bar4 r; return r; }
+
+struct Foo5 { int a; };
+union Bar5 { int a; };
+
+// CHECK: .functype f5 (i32) -> (i32)
+union Bar5 f5(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+// CHECK: .functype f5mv (i32) -> (i32)
+__attribute__((wasm_multivalue))
+union Bar5 f5mv(struct Foo5 x) { union Bar5 r; r.a = x.a; return r; }
+
+struct Foo6 { int a; int b; };
+
+// CHECK: .functype f6 (i32, i32) -> ()
+struct Foo6 f6(struct Foo6 x) { return x; }
+// CHECK: .functype f6mv (i32, i32) -> (i32, i32)
+__attribute__((wasm_multivalue))
+struct Foo6 f6mv(struct Foo6 x) { return x; }
diff --git a/clang/test/Sema/attr-wasm-multivalue.c 
b/clang/test/Sema/attr-wasm-multivalue.c
new file mode 100644
index 0000000000000..51e9f494b657d
--- /dev/null
+++ b/clang/test/Sema/attr-wasm-multivalue.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +multivalue 
-fsyntax-only -verify=enabled %s...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/200076
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to