llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Zibi Sarbinowski (zibi2)

<details>
<summary>Changes</summary>

This patch introduces full support for the XPLINK calling convention on z/OS 
within LLVM. XPLINK is documented in the Language Environment Vendor Interface 
([Chapter 
22](https://www.ibm.com/docs/en/zos/3.2.0?topic=applications-call-linkage-convention-amode-64))
 and in the IBM Redbook [XPLink: OS/390 Extra Performance 
Linkage](http://www.redbooks.ibm.com/abstracts/sg245991.html?Open
). It defines a high‑performance linkage model used by the z/OS Language 
Environment (LE) and by the XL C/C++ compiler.
This work succeeds and replaces the earlier effort in [PR 
101024](https://github.com/llvm/llvm-project/pull/101024). The present 
implementation restructures the original approach, significantly expands test 
coverage, and aligns more closely with both the architectural requirements of 
XPLINK and the de‑facto ABI behavior of XL C/C++.

---

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


5 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+2) 
- (modified) clang/lib/CodeGen/TargetInfo.h (+4) 
- (modified) clang/lib/CodeGen/Targets/SystemZ.cpp (+410) 
- (added) clang/test/CodeGen/SystemZ/zos-abi.c (+414) 
- (added) clang/test/CodeGen/SystemZ/zos-abi.cpp (+96) 


``````````diff
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index daaa846bf42bc..bbc96b0b3916b 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -242,6 +242,8 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
   case llvm::Triple::systemz: {
     bool SoftFloat = CodeGenOpts.FloatABI == "soft";
     bool HasVector = !SoftFloat && Target.getABI() == "vector";
+    if (Triple.getOS() == llvm::Triple::ZOS)
+      return createSystemZ_ZOS_TargetCodeGenInfo(CGM, HasVector, SoftFloat);
     return createSystemZTargetCodeGenInfo(CGM, HasVector, SoftFloat);
   }
 
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index 98ee894fe557f..0be1cb225858c 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -572,6 +572,10 @@ std::unique_ptr<TargetCodeGenInfo>
 createSystemZTargetCodeGenInfo(CodeGenModule &CGM, bool HasVector,
                                bool SoftFloatABI);
 
+std::unique_ptr<TargetCodeGenInfo>
+createSystemZ_ZOS_TargetCodeGenInfo(CodeGenModule &CGM, bool HasVector,
+                                    bool SoftFloatABI);
+
 std::unique_ptr<TargetCodeGenInfo>
 createTCETargetCodeGenInfo(CodeGenModule &CGM);
 
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index e50f06c2f548c..879e11ea4108d 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -544,9 +544,419 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const 
Type *Ty,
   return false;
 }
 
+//===----------------------------------------------------------------------===//
+// z/OS XPLINK ABI Implementation
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class ZOSXPLinkABIInfo : public ABIInfo {
+  const unsigned GPRBits = 64;
+  bool HasVector;
+
+public:
+  ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV)
+      : ABIInfo(CGT), HasVector(HV) {}
+
+  bool isPromotableIntegerType(QualType Ty) const;
+  bool isCompoundType(QualType Ty) const;
+  bool isVectorArgumentType(QualType Ty) const;
+  bool isFPArgumentType(QualType Ty) const;
+  QualType getSingleElementType(QualType Ty) const;
+  unsigned getMaxAlignFromTypeDefs(QualType Ty) const;
+  std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const;
+
+  ABIArgInfo classifyReturnType(QualType RetTy,
+                                unsigned functionCallConv) const;
+  ABIArgInfo classifyArgumentType(QualType ArgTy, bool IsNamedArg,
+                                  unsigned functionCallConv) const;
+
+  void computeInfo(CGFunctionInfo &FI) const override {
+    if (!getCXXABI().classifyReturnType(FI))
+      FI.getReturnInfo() =
+          classifyReturnType(FI.getReturnType(), FI.getCallingConvention());
+
+    unsigned NumRequiredArgs = FI.getNumRequiredArgs();
+    unsigned ArgNo = 0;
+
+    for (auto &I : FI.arguments()) {
+      bool IsNamedArg = ArgNo < NumRequiredArgs;
+      I.info =
+          classifyArgumentType(I.type, IsNamedArg, FI.getCallingConvention());
+      ++ArgNo;
+    }
+  }
+
+  RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
+                   AggValueSlot Slot) const override;
+};
+
+class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo {
+public:
+  ZOSXPLinkTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector)
+      : TargetCodeGenInfo(std::make_unique<ZOSXPLinkABIInfo>(CGT, HasVector)) {
+    SwiftInfo =
+        std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
+  }
+};
+
+} // namespace
+
+// Return true if the ABI requires Ty to be passed sign- or zero-
+// extended to 64 bits.
+bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const {
+  // Treat an enum type as its underlying type.
+  if (const EnumType *EnumTy = Ty->getAs<EnumType>())
+    Ty = EnumTy->getDecl()->getIntegerType();
+
+  // Promotable integer types are required to be promoted by the ABI.
+  if (getContext().isPromotableIntegerType(Ty))
+    return true;
+
+  // In addition to the usual promotable integer types, we also need to
+  // extend all 32-bit types, since the ABI requires promotion to 64 bits.
+  if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
+    switch (BT->getKind()) {
+    case BuiltinType::Int:
+    case BuiltinType::UInt:
+      return true;
+    default:
+      break;
+    }
+
+  return false;
+}
+
+bool ZOSXPLinkABIInfo::isCompoundType(QualType Ty) const {
+  return (Ty->isAnyComplexType() || Ty->isVectorType() ||
+          isAggregateTypeForABI(Ty));
+}
+
+bool ZOSXPLinkABIInfo::isVectorArgumentType(QualType Ty) const {
+  return (HasVector && Ty->isVectorType() &&
+          getContext().getTypeSize(Ty) <= 128);
+}
+
+bool ZOSXPLinkABIInfo::isFPArgumentType(QualType Ty) const {
+  if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
+    switch (BT->getKind()) {
+    case BuiltinType::Float:
+    case BuiltinType::Double:
+    case BuiltinType::LongDouble:
+      return true;
+    default:
+      return false;
+    }
+
+  return false;
+}
+
+QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const {
+  // Unions just containing a floating point type, e.g. union { float f1, f2; 
};
+  // are treated as a single floating point number. Check if the union only
+  // consists of a single type (handling embedded unions recursively), and
+  // return that type.
+  if (const RecordType *RT = Ty->getAsUnionType()) {
+    QualType Found;
+    // Check the fields.
+    const RecordDecl *RD = RT->getDecl();
+    for (const auto *FD : RD->fields()) {
+      if (Found.isNull())
+        Found = getSingleElementType(FD->getType());
+      else if (Found != getSingleElementType(FD->getType()))
+        return Ty;
+    }
+    return Found.isNull() ? Ty : Found;
+  }
+
+  if (const RecordType *RT = Ty->getAsStructureType()) {
+    const RecordDecl *RD = RT->getDecl();
+    QualType Found;
+
+    // If this is a C++ class/struct, inspect its base classes first.
+    if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+      for (const auto &I : CXXRD->bases()) {
+        QualType Base = I.getType();
+
+        // Skip empty base classes, they cannot contribute a data member.
+        if (isEmptyRecord(getContext(), Base, true))
+          continue;
+
+        // A candidate base type was already found; encountering another 
non‑empty
+        // base means the choice is no longer unique. Return the type from the
+        // first candidate.
+        if (!Found.isNull())
+          return Ty;
+        Found = getSingleElementType(Base);
+      }
+
+    // Now inspect the record's own fields. We allow at most one field to
+    // contribute a single element type. If we've already recorded one
+    // candidate, encountering another field immediately disqualifies the
+    // record from being a single element aggregate.
+    for (const auto *FD : RD->fields()) {
+      if (!Found.isNull())
+        return Ty; // more than one field,  not a single-element
+      Found = getSingleElementType(FD->getType());
+    }
+    return Found.isNull() ? Ty : Found;
+  }
+  return Ty; // not record/union, unchanged
+}
+
+unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType Ty) const {
+  unsigned MaxAlign = 0;
+  clang::QualType Cur = Ty;
+
+  while (true) {
+    if (auto *TypedefTy = dyn_cast<TypedefType>(Cur.getTypePtr())) {
+      auto *TyDecl = TypedefTy->getDecl();
+      unsigned CurrAlign = TyDecl->getMaxAlignment();
+      MaxAlign = std::max(CurrAlign, MaxAlign);
+    }
+    // Peel exactly one sugar layer (Typedef, Attributed, Paren, Elaborated, 
etc.).
+    clang::QualType Next = Cur.getSingleStepDesugaredType(getContext());
+    if (Next == Cur) // no more sugar to peel
+      break;
+    Cur = Next;
+  }
+  return MaxAlign;
+}
+
+std::optional<QualType>
+ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
+  if (const RecordType *RT = Ty->getAsStructureType()) {
+    const RecordDecl *RD = RT->getDecl();
+
+    // Check for non-empty base classes.
+    if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+      if (CXXRD->hasDefinition())
+        for (const auto &I : CXXRD->bases()) {
+          QualType Base = I.getType();
+          if (!isEmptyRecord(getContext(), Base, true))
+            return std::nullopt;
+        }
+
+    // Check for exactly two elements with exactly the same floating point 
type.
+    // A single-element struct containing only a float, double, or long double
+    // counts as a field of that type. If the struct has one field consisting
+    // of a complex type, it does not count. This design may be somewhat
+    // inconsistent but it matches the behavior of the legacy C compiler.
+    int Count = 0;
+    clang::BuiltinType::Kind elemKind;
+    QualType RetTy;
+    for (const auto *FD : RD->fields()) {
+      if (Count >= 2)
+        return std::nullopt;
+
+      unsigned MaxAlignOnDecl = FD->getMaxAlignment();
+      QualType FT = FD->getType();
+      QualType FTSingleTy = getSingleElementType(FT);
+      unsigned MaxAlign =
+          std::max(getMaxAlignFromTypeDefs(FTSingleTy), MaxAlignOnDecl);
+
+      // The first element of a complex type may have an alignment enforced
+      // that is less strict than twice its size, since that would be naturally
+      // enforced by any complex type anyways. The second element may have an
+      // alignment enforced that is less strict than its size.
+      if (Count == 0) {
+        if (MaxAlign > 2 * getContext().getTypeSize(FTSingleTy))
+          return std::nullopt;
+      }
+      else if (Count == 1) {
+        if (MaxAlign > getContext().getTypeSize(FTSingleTy))
+          return std::nullopt;
+      }
+
+      if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
+        switch (BT->getKind()) {
+        case BuiltinType::Float:
+        case BuiltinType::Double:
+        case BuiltinType::LongDouble:
+          if (Count == 0) {
+            elemKind = BT->getKind();
+            RetTy = FTSingleTy;
+            break;
+          } else if (elemKind == BT->getKind())
+            break;
+          else
+            return std::nullopt;
+        default:
+          return std::nullopt;
+        }
+      } else
+        return std::nullopt;
+
+      Count++;
+      fprintf(stderr, "Count increased to %d\n", Count);
+    }
+
+    if (Count == 2) {
+      // The last thing that needs to be checked is the alignment of the 
struct.
+      // If we have to emit any padding (eg. because of attribute aligned), 
this
+      // disqualifies the type from being complex.
+      unsigned MaxAlign = RT->getDecl()->getMaxAlignment();
+      unsigned ElemSize = getContext().getTypeSize(RetTy);
+      if (MaxAlign > 2 * ElemSize)
+        return std::nullopt;
+      return RetTy;
+    }
+  }
+  return std::nullopt;
+}
+
+ABIArgInfo
+ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
+                                     unsigned CallConv) const {
+
+  // Ignore void types.
+  if (RetTy->isVoidType())
+    return ABIArgInfo::getIgnore();
+
+  // For non-C calling convention, indirect by value for structs and complex.
+  if ((CallConv != llvm::CallingConv::C) &&
+      (isAggregateTypeForABI(RetTy) || RetTy->isAnyComplexType())) {
+    return getNaturalAlignIndirect(RetTy, 
getDataLayout().getAllocaAddrSpace());
+  }
+
+  // Vectors are returned directly.
+  if (isVectorArgumentType(RetTy))
+    return ABIArgInfo::getDirect();
+
+  // Complex types are returned by value as per the XPLINK docs.
+  // Their members will be placed in FPRs.
+  if (RetTy->isAnyComplexType())
+    return ABIArgInfo::getDirect();
+
+  // Complex LIKE structures are returned by value as per the XPLINK docs.
+  // Their members will be placed in FPRs.
+  if (RetTy->getAs<RecordType>()) {
+    if (getFPTypeOfComplexLikeType(RetTy))
+      return ABIArgInfo::getDirect();
+  }
+
+  // Aggregates with a size of less than 3 GPRs are returned in GRPs 1, 2 and 
3.
+  // Other aggregates are passed in memory as an implicit first parameter.
+  if (isAggregateTypeForABI(RetTy)) {
+    uint64_t AggregateTypeSize = getContext().getTypeSize(RetTy);
+
+    if (AggregateTypeSize <= 3 * GPRBits) {
+      uint64_t NumElements =
+          AggregateTypeSize / GPRBits + (AggregateTypeSize % GPRBits != 0);
+
+      // Types up to 8 bytes are passed as an integer type in GPR1.
+      // Types between 8 and 16 bytes are passed as integer types in GPR1, 2.
+      // Types between 16 and 24 bytes are passed as integer types in GPR1, 2
+      // and 3.
+      llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits);
+      CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
+      return ABIArgInfo::getDirectInReg(CoerceTy);
+    } else
+      return getNaturalAlignIndirect(RetTy, 
getDataLayout().getAllocaAddrSpace());
+  }
+
+  // Treat an enum type as its underlying type.
+  if (const EnumType *EnumTy = RetTy->getAs<EnumType>())
+    RetTy = EnumTy->getDecl()->getIntegerType();
+
+  return (isPromotableIntegerType(RetTy) ? ABIArgInfo::getExtend(RetTy)
+                                         : ABIArgInfo::getDirect());
+}
+
+ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
+                                                  unsigned CallConv) const {
+
+  // Handle the generic C++ ABI.
+  if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), 
RAA == CGCXXABI::RAA_DirectInMemory);
+
+  // Integers and enums are extended to full register width.
+  if (isPromotableIntegerType(Ty))
+    return ABIArgInfo::getExtend(Ty);
+
+  // For non-C calling conventions, compound types passed by address copy.
+  if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty))
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
+                                   /*ByVal=*/false);
+
+  // Complex types are passed by value as per the XPLINK docs.
+  // If place available, their members will be placed in FPRs.
+  auto CompTy = getFPTypeOfComplexLikeType(Ty);
+  if (IsNamedArg) {
+    if (Ty->isComplexType()) {
+      auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty));
+      AI.setCanBeFlattened(false);
+      return AI;
+    }
+
+    if (CompTy.has_value()) {
+      llvm::Type *FPTy = CGT.ConvertType(*CompTy);
+      llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
+      auto AI = ABIArgInfo::getDirectInReg(CoerceTy);
+      AI.setCanBeFlattened(false);
+      return AI;
+    }
+  }
+
+  // Vectors are passed directly.
+  if (isVectorArgumentType(Ty))
+    return ABIArgInfo::getDirect();
+
+  // Handle structures. They are returned by value.
+  // If not complex like types, they are passed in GPRs, if possible.
+  // If place available, complex like types will have their members
+  // placed in FPRs.
+  if (Ty->getAs<RecordType>() || Ty->isAnyComplexType() || CompTy.has_value()) 
{
+    if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType() || 
CompTy.has_value()) {
+      // Since an aggregate may end up in registers, pass the aggregate as
+      // array. This is usually beneficial since we avoid forcing the back-end
+      // to store the argument to memory.
+      uint64_t Bits = getContext().getTypeSize(Ty);
+      llvm::Type *CoerceTy;
+
+      // Struct types up to 8 bytes are passed as integer type (which  will be
+      // properly aligned in the argument save area doubleword).
+      if (Bits <= GPRBits)
+        CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits);
+      // Larger types are passed as arrays, with the base type selected
+      // according to the required alignment in the save area.
+      else {
+        uint64_t NumRegs = llvm::alignTo(Bits, GPRBits) / GPRBits;
+        llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), GPRBits);
+        CoerceTy = llvm::ArrayType::get(RegTy, NumRegs);
+      }
+
+      return ABIArgInfo::getDirectInReg(CoerceTy);
+    }
+
+    return ABIArgInfo::getDirectInReg();
+  }
+
+  // Non-structure compounds are passed indirectly, i.e. arrays.
+  if (isCompoundType(Ty))
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), 
/*ByVal=*/false);
+
+  return ABIArgInfo::getDirect();
+}
+
+RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
+                                    QualType Ty, AggValueSlot Slot) const {
+  return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false,
+                          CGF.getContext().getTypeInfoInChars(Ty),
+                          CGF.getPointerSize(),
+                          /*allowHigherAlign*/ false, Slot);
+}
+
 std::unique_ptr<TargetCodeGenInfo>
 CodeGen::createSystemZTargetCodeGenInfo(CodeGenModule &CGM, bool HasVector,
                                         bool SoftFloatABI) {
   return std::make_unique<SystemZTargetCodeGenInfo>(CGM.getTypes(), HasVector,
                                                     SoftFloatABI);
 }
+
+std::unique_ptr<TargetCodeGenInfo>
+CodeGen::createSystemZ_ZOS_TargetCodeGenInfo(CodeGenModule &CGM, bool 
HasVector,
+                                             bool SoftFloatABI) {
+  return std::make_unique<ZOSXPLinkTargetCodeGenInfo>(CGM.getTypes(),
+                                                      HasVector);
+}
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c 
b/clang/test/CodeGen/SystemZ/zos-abi.c
new file mode 100644
index 0000000000000..999b8f07814c1
--- /dev/null
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -0,0 +1,414 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKNOVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \
+// RUN:   -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECKVEC
+
+// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \
+// RUN:   -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \
+// RUN:   -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC,CHECK-ZVEC
+
+// Scalar types
+
+signed char pass_schar(signed char arg) { return arg; }
+// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}})
+
+unsigned char pass_uchar(unsigned char arg) { return arg; }
+// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}})
+
+short pass_short(short arg) { return arg; }
+// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}})
+
+int pass_int(int arg) { return arg; }
+// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}})
+
+long pass_long(long arg) { return arg; }
+// CHECK-LABEL: define i64 @pass_long(i64 %{{.*}})
+
+long long pass_longlong(long long arg) { return arg; }
+// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}})
+
+float pass_float(float arg) { return arg; }
+// CHECK-LABEL: define float @pass_float(float %{{.*}})
+
+double pass_double(double arg) { return arg; }
+// CHECK-LABEL: define double @pass_double(double %{{.*}})
+
+long double pass_longdouble(long double arg) { return arg; }
+// CHECK-LABEL: define fp128 @pass_longdouble(fp128 %{{.*}})
+
+enum Color { Red, Blue };
+enum Color pass_enum(enum Color arg) { return arg; }
+// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}})
+
+#ifdef TEST_VEC
+vector unsigned int pass_vector(vector unsigned int arg) { return arg; };
+// C...
[truncated]

``````````

</details>


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

Reply via email to