https://github.com/zibi2 updated 
https://github.com/llvm/llvm-project/pull/188501

>From 5b53674ff92729eea5b3479764ecef9c670ba4c9 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Mon, 9 Mar 2026 22:17:03 +0000
Subject: [PATCH 1/5] Add new class, ZOSXPLinkABIInfo, for XPLink ABI on z/OS

---
 clang/lib/CodeGen/CodeGenModule.cpp    |   2 +
 clang/lib/CodeGen/TargetInfo.h         |   4 +
 clang/lib/CodeGen/Targets/SystemZ.cpp  | 410 ++++++++++++++++++++++++
 clang/test/CodeGen/SystemZ/zos-abi.c   | 414 +++++++++++++++++++++++++
 clang/test/CodeGen/SystemZ/zos-abi.cpp |  96 ++++++
 5 files changed, 926 insertions(+)
 create mode 100644 clang/test/CodeGen/SystemZ/zos-abi.c
 create mode 100644 clang/test/CodeGen/SystemZ/zos-abi.cpp

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; };
+// CHECK-ZVEC-LABEL: define <4 x i32> @pass_vector(<4 x i32> %{{.*}})
+
+struct SingleVec { vector unsigned int v; };
+struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; };
+// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] 
inreg %{{.*}})
+#endif
+
+// Complex types
+
+_Complex float pass_complex_float(_Complex float arg) { return arg; }
+// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } 
inreg %{{.*}})
+
+_Complex double pass_complex_double(_Complex double arg) { return arg; }
+// CHECK-LABEL: define { double, double } @pass_complex_double({ double, 
double } inreg %{{.*}})
+
+_Complex long double pass_complex_longdouble(_Complex long double arg) { 
return arg; }
+// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, 
fp128 } inreg %{{.*}})
+
+// Verify that the following are complex-like types
+struct complexlike_float { float re, im; };
+struct complexlike_float pass_complexlike_float2(struct complexlike_float arg) 
{ return arg; }
+// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ 
float, float } inreg %{{.*}})
+
+struct complexlike_double { double re, im; };
+struct complexlike_double pass_complexlike_double(struct complexlike_double 
arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ 
double, double } inreg %{{.*}})
+
+struct complexlike_longdouble { long double re, im; };
+struct complexlike_longdouble pass_complexlike_longdouble(struct 
complexlike_longdouble arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_longdouble 
@pass_complexlike_longdouble({ fp128, fp128 } inreg %{{.*}})
+
+struct single_element_float { float f; };
+struct complexlike_struct {
+  struct single_element_float x;
+  struct single_element_float y;
+};
+struct complexlike_struct pass_complexlike_struct(struct complexlike_struct 
arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ 
float, float } inreg %{{.*}})
+
+struct single_element_float_arr {
+  unsigned int :0;
+  float f[1];
+};
+struct complexlike_struct2 {
+  struct single_element_float_arr x;
+  struct single_element_float_arr y;
+};
+struct complexlike_struct2 pass_complexlike_struct2(struct complexlike_struct2 
arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct2(i64 inreg 
%{{.*}})
+
+struct float_and_empties {
+  struct S {} s;
+  int a[0];
+  float f;
+};
+struct complexlike_struct3 {
+  struct float_and_empties x;
+  struct float_and_empties y;
+};
+struct complexlike_struct3 pass_complexlike_struct3(struct complexlike_struct3 
arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 inreg 
%{{.*}})
+
+union two_float_union { float a; float b; };
+struct complexlike_struct_with_union {
+  float a;
+  union two_float_union b;
+};
+struct complexlike_struct_with_union pass_complexlike_struct_with_union(struct 
complexlike_struct_with_union arg) { return arg; }
+// CHECK-LABEL: struct.complexlike_struct_with_union 
@pass_complexlike_struct_with_union({ float, float } inreg %{{.*}})
+
+// structures with one field as complex type are not considered complex types.
+struct single_complex_struct {
+  _Complex float f;
+};
+struct single_complex_struct pass_single_complex_struct(struct 
single_complex_struct arg) {return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_single_complex_struct(i64 inreg 
%{{.*}})
+
+// Structures with extra padding are not considered complex types.
+struct complexlike_float_padded1 {
+  float x __attribute__((aligned(8)));
+  float y __attribute__((aligned(8)));
+};
+struct complexlike_float_padded1 pass_complexlike_float_padded1(struct 
complexlike_float_padded1 arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded1([2 x 
i64] inreg %{{.*}})
+
+struct complexlike_float_padded2 {
+  float x;
+  float y;
+} __attribute__((aligned(16)));
+struct complexlike_float_padded2 pass_complexlike_float_padded2(struct 
complexlike_float_padded2 arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded2([2 x 
i64] inreg %{{.*}})
+
+
+struct single_padded_struct {
+  float f;
+  unsigned int :2;
+};
+struct complexlike_float_padded3 {
+  struct single_padded_struct x;
+  struct single_padded_struct y;
+};
+struct complexlike_float_padded3 pass_complexlike_float_padded3(struct 
complexlike_float_padded3 arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded3([2 x 
i64] inreg %{{.*}})
+
+struct multi_element_float_arr { float f[2]; };
+struct complexlike_struct4 {
+  struct multi_element_float_arr x;
+  struct multi_element_float_arr y;
+};
+struct complexlike_struct4 pass_complexlike_struct4(struct complexlike_struct4 
arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] 
inreg %{{.*}})
+
+typedef double align32_double __attribute__((aligned(32)));
+struct complexlike_double_padded {
+  align32_double x;
+  double y;
+};
+struct complexlike_double_padded pass_complexlike_double_padded(struct 
complexlike_double_padded arg) { return arg; }
+// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} 
sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] inreg 
%{{.*}})
+
+struct complexlike_double_padded2  {
+  __attribute__((aligned(256))) align32_double x; // attribute on both type 
and typedef
+  double y;
+};
+
+struct complexlike_double_padded2 pass_complexlike_double_padded2(struct 
complexlike_double_padded2 arg) { return arg; }
+// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} 
sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] inreg 
%{{.*}})
+
+struct complexlike_double_padded3 {
+  __attribute__((aligned(32))) double x; // attribute on type only
+  double y;
+};
+struct complexlike_double_padded3 pass_complexlike_double_padded3(struct 
complexlike_double_padded3 arg) { return arg; }
+// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} 
sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] inreg 
%{{.*}})
+
+
+// Aggregate types
+
+struct agg_1byte { char a[1]; };
+struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 inreg %{{.*}})
+
+struct agg_2byte { char a[2]; };
+struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 inreg %{{.*}})
+
+struct agg_3byte { char a[3]; };
+struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 inreg %{{.*}})
+
+struct agg_4byte { char a[4]; };
+struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 inreg %{{.*}})
+
+struct agg_5byte { char a[5]; };
+struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 inreg %{{.*}})
+
+struct agg_6byte { char a[6]; };
+struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 inreg %{{.*}})
+
+struct agg_7byte { char a[7]; };
+struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 inreg %{{.*}})
+
+struct agg_8byte { char a[8]; };
+struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 inreg %{{.*}})
+
+struct agg_9byte { char a[9]; };
+struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] inreg %{{.*}})
+
+struct agg_16byte { char a[16]; };
+struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] inreg 
%{{.*}})
+
+struct agg_24byte { char a[24]; };
+struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; }
+// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] inreg 
%{{.*}})
+
+struct agg_25byte { char a[25]; };
+struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; }
+// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias 
writable sret{{.*}} align 1 %{{.*}}, [4 x i64] inreg %{{.*}})
+
+// Check that a float-like aggregate type is really passed as aggregate
+struct agg_float { float a; };
+struct agg_float pass_agg_float(struct agg_float arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 inreg %{{.*}})
+
+// Verify that the following are *not* float-like aggregate types
+
+struct agg_nofloat2 { float a; int b; };
+struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 inreg %{{.*}})
+
+struct agg_nofloat3 { float a; int : 0; };
+struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 inreg %{{.*}})
+
+char * pass_pointer(char * arg) { return arg; }
+// CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}})
+
+typedef int vecint __attribute__ ((vector_size(16)));
+vecint pass_vector_type(vecint arg) { return arg; }
+// CHECKNOVEC-LABEL: define <4 x i32> @pass_vector_type(ptr dead_on_return 
%{{.*}})
+// CHECKVEC-LABEL: define <4 x i32> @pass_vector_type(<4 x i32>  %{{.*}})
+
+// Union with just a single float element are treated as float inside a struct.
+union u1 {
+  float m1, m2;
+};
+
+union u2 {
+  float m1;
+  union u1 m2;
+};
+
+union u3 {
+  float m1;
+  int m2;
+};
+
+struct complexlike_union1 {
+  float m1;
+  union u1 m2;
+};
+
+struct complexlike_union2 {
+  float m1;
+  union u2 m2;
+};
+
+struct complexlike_union3 {
+  union u1 m1;
+  union u2 m2;
+};
+
+struct normal_struct {
+  float m1;
+  union u3 m2;
+};
+
+struct complexlike_union1 pass_complexlike_union1(struct complexlike_union1 
arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ 
float, float } inreg %{{.*}})
+
+struct complexlike_union2 pass_complexlike_union2(struct complexlike_union2 
arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ 
float, float } inreg %{{.*}})
+
+struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 
arg) { return arg; }
+// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ 
float, float } inreg %{{.*}})
+
+union u1 pass_union1(union u1 arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 inreg %{{.*}})
+
+union u2 pass_union2(union u2 arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 inreg %{{.*}})
+
+struct normal_struct pass_normal_struct(struct normal_struct arg) { return 
arg; }
+// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 inreg %{{.*}})
+
+// ============================================================================
+// Complex-like struct using nested typedefs with alignment attributes
+// applied at different typedef layers.
+// ============================================================================
+typedef double raw_double;
+
+// ============================================================================
+// Alignment at first typedef layer
+// ============================================================================
+typedef align32_double nested_A;
+
+struct S_A {
+  nested_A re;
+  double im;
+};
+
+struct S_A pass_S_A(struct S_A arg) { return arg; }
+// CHECK-LABEL: define void @pass_S_A(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32
+// CHECK-SAME: [4 x i64] inreg
+// CHECK: ret void
+
+// ============================================================================
+// Alignment applied at middle typedef layer
+// ============================================================================
+typedef double raw_B;
+typedef raw_B middle_B __attribute__((aligned(32)));
+typedef middle_B nested_B;
+
+struct S_B {
+  double im;
+  nested_B re;
+};
+
+struct S_B pass_S_B(struct S_B arg) { return arg; }
+// CHECK-LABEL: define void @pass_S_B(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32
+// CHECK-SAME: [8 x i64] inreg
+// CHECK: ret void
+
+// ============================================================================
+// Alignment applied only at deepest typedef
+// ============================================================================
+typedef float raw_C0;
+typedef raw_C0 raw_C1;
+typedef raw_C1 raw_C2;
+typedef raw_C2 nested_C_final __attribute__((aligned(32)));
+
+struct S_C {
+  nested_C_final re;
+  float im;
+};
+
+struct S_C pass_S_C(struct S_C arg) { return arg; }
+// CHECK-LABEL: define void @pass_S_C(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_C) align 32
+// CHECK-SAME: [4 x i64] inreg
+// CHECK: ret void
+
+// ============================================================================
+// Alignment on an intermediate typedef in a deeper chain
+// ============================================================================
+typedef float raw_D0;
+typedef raw_D0 nested_D1;
+typedef nested_D1 nested_D2 __attribute__((aligned(32)));
+typedef nested_D2 nested_D3;
+typedef nested_D3 nested_D;
+
+struct S_D {
+  float im;
+  nested_D re;
+};
+
+struct S_D pass_S_D(struct S_D arg) { return arg; }
+// CHECK-LABEL: define void @pass_S_D(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_D) align 32
+// CHECK-SAME: [8 x i64] inreg
+// CHECK: ret void
+
+// ============================================================================
+// NO alignment anywhere in the typedef chain
+// Should NOT get 32-byte ABI alignment.
+// ============================================================================
+typedef double nested_E1;
+typedef nested_E1 nested_E2;
+typedef nested_E2 nested_E;
+
+struct S_E {
+  nested_E re;
+  double im;
+};
+
+struct S_E pass_S_E(struct S_E arg) { return arg; }
+// CHECK-LABEL: define %struct.S_E @pass_S_E(
+// CHECK-SAME:{ double, double } inreg %{{.*}})
+// CHECK: ret %struct.S_E
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.cpp 
b/clang/test/CodeGen/SystemZ/zos-abi.cpp
new file mode 100644
index 0000000000000..0578ddfcb17cd
--- /dev/null
+++ b/clang/test/CodeGen/SystemZ/zos-abi.cpp
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos -emit-llvm 
-no-enable-noundef-analysis -x c++ -o - %s | FileCheck %s
+
+// Verify that class types are recognized as float-like aggregate types and 
passed in GPR.
+
+class agg_float_class { float a; };
+class agg_float_class pass_agg_float_class(class agg_float_class arg) { return 
arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z20pass_agg_float_class15agg_float_class(i64 inreg %arg.coerce)
+
+class agg_double_class { double a; };
+class agg_double_class pass_agg_double_class(class agg_double_class arg) { 
return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z21pass_agg_double_class16agg_double_class(i64 inreg %arg.coerce)
+
+
+// This structure is passed in also in GPR.
+struct agg_float_cpp { float a; int : 0; };
+struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return 
arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z18pass_agg_float_cpp13agg_float_cpp(i64 inreg %arg.coerce)
+
+// In C++ a  data member of empty class type makes the record nonhomogeneous,
+// regardless if it's marked as [[no_unique_address]] or not.
+struct empty { };
+struct agg_nofloat_empty { float a; empty dummy; };
+struct agg_nofloat_empty pass_agg_nofloat_empty(struct agg_nofloat_empty arg) 
{ return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 inreg %arg.coerce)
+struct complex_like_agg_nofloat_empty { struct agg_nofloat_empty a; struct 
agg_nofloat_empty b; };
+struct complex_like_agg_nofloat_empty 
pass_complex_like_agg_nofloat_empty(struct complex_like_agg_nofloat_empty arg) 
{ return arg; }
+// CHECK-LABEL: define inreg [2 x i64] 
@_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x 
i64] inreg %{{.*}})
+
+struct agg_float_empty { float a; [[no_unique_address]] empty dummy; };
+struct agg_float_empty pass_agg_float_empty(struct agg_float_empty arg) { 
return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z20pass_agg_float_empty15agg_float_empty(i64 inreg %arg.coerce)
+struct complex_like_agg_float_empty { struct agg_float_empty a; struct 
agg_float_empty b; };
+struct complex_like_agg_float_empty pass_complex_like_agg_float_empty(struct 
complex_like_agg_float_empty arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 inreg 
%{{.*}})
+
+struct agg_nofloat_emptyarray { float a; [[no_unique_address]] empty dummy[3]; 
};
+struct agg_nofloat_emptyarray pass_agg_nofloat_emptyarray(struct 
agg_nofloat_emptyarray arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 inreg %arg.coerce)
+
+
+// And likewise for members of base classes.
+struct noemptybase { empty dummy; };
+struct agg_nofloat_emptybase : noemptybase { float a; };
+struct agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct 
agg_nofloat_emptybase arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 inreg %arg.coerce)
+struct complex_like_agg_nofloat_emptybase { struct agg_nofloat_emptybase a; 
struct agg_nofloat_emptybase b; };
+struct complex_like_agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct 
complex_like_agg_nofloat_emptybase arg) { return arg; }
+// CHECK-LABEL: define inreg [2 x i64] 
@_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] 
inreg %{{.*}})
+
+struct emptybase { [[no_unique_address]] empty dummy; };
+struct agg_float_emptybase : emptybase { float a; };
+struct agg_float_emptybase pass_agg_float_emptybase(struct agg_float_emptybase 
arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 inreg %arg.coerce)
+struct complex_like_agg_float_emptybase { struct agg_float_emptybase a; struct 
agg_float_emptybase b; };
+struct complex_like_agg_float_emptybase pass_agg_float_emptybase(struct 
complex_like_agg_float_emptybase arg) { return arg; }
+// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase 
@_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float 
} inreg %{{.*}})
+
+struct noemptybasearray { [[no_unique_address]] empty dummy[3]; };
+struct agg_nofloat_emptybasearray : noemptybasearray { float a; };
+struct agg_nofloat_emptybasearray pass_agg_nofloat_emptybasearray(struct 
agg_nofloat_emptybasearray arg) { return arg; }
+// CHECK-LABEL: define inreg [1 x i64] 
@_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 inreg 
%{{.*}})
+
+using D = double;
+using E = __attribute__((aligned(32))) D; // attribute inside the alias
+struct complexlike_alias {
+  E x; // Using alias with attributed underlying type
+  double y;
+};
+struct complexlike_alias pass_complexlike_alias(struct complexlike_alias arg) 
{ return arg; }
+// CHECK-LABEL: define %struct.complexlike_alias 
@_Z22pass_complexlike_alias17complexlike_alias({ double, double } inreg %{{.*}})
+
+// ============================================================================
+// Complex-like struct using alignas specifier
+// ============================================================================
+
+struct S_A {
+  double re alignas(32);;
+  double im;
+};
+
+struct S_A pass_S_A(struct S_A arg) { return arg; }
+// CHECK-LABEL: define void @_Z8pass_S_A3S_A(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32
+// CHECK-SAME: [4 x i64] inreg
+// CHECK: ret void
+
+struct alignas(32) S_B {
+  double re;
+  double im;
+};
+
+struct S_B pass_S_B(struct S_B arg) { return arg; }
+// CHECK-LABEL: define void @_Z8pass_S_B3S_B(
+// CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32
+// CHECK-SAME: [4 x i64] inreg
+// CHECK: ret void

>From 908b45a9abef33837147dd8ea5cbaaee36a929f4 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Wed, 25 Mar 2026 16:05:06 +0000
Subject: [PATCH 2/5] fix formatting

---
 clang/lib/CodeGen/Targets/SystemZ.cpp | 34 ++++++++++++++-------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 879e11ea4108d..309380862bc9f 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -555,8 +555,7 @@ class ZOSXPLinkABIInfo : public ABIInfo {
   bool HasVector;
 
 public:
-  ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV)
-      : ABIInfo(CGT), HasVector(HV) {}
+  ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {}
 
   bool isPromotableIntegerType(QualType Ty) const;
   bool isCompoundType(QualType Ty) const;
@@ -682,9 +681,9 @@ QualType ZOSXPLinkABIInfo::getSingleElementType(QualType 
Ty) const {
         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.
+        // 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);
@@ -714,7 +713,8 @@ unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType 
Ty) const {
       unsigned CurrAlign = TyDecl->getMaxAlignment();
       MaxAlign = std::max(CurrAlign, MaxAlign);
     }
-    // Peel exactly one sugar layer (Typedef, Attributed, Paren, Elaborated, 
etc.).
+    // 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;
@@ -762,8 +762,7 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) 
const {
       if (Count == 0) {
         if (MaxAlign > 2 * getContext().getTypeSize(FTSingleTy))
           return std::nullopt;
-      }
-      else if (Count == 1) {
+      } else if (Count == 1) {
         if (MaxAlign > getContext().getTypeSize(FTSingleTy))
           return std::nullopt;
       }
@@ -805,9 +804,8 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) 
const {
   return std::nullopt;
 }
 
-ABIArgInfo
-ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
-                                     unsigned CallConv) const {
+ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
+                                                unsigned CallConv) const {
 
   // Ignore void types.
   if (RetTy->isVoidType())
@@ -852,7 +850,8 @@ ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
       CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
       return ABIArgInfo::getDirectInReg(CoerceTy);
     } else
-      return getNaturalAlignIndirect(RetTy, 
getDataLayout().getAllocaAddrSpace());
+      return getNaturalAlignIndirect(RetTy,
+                                     getDataLayout().getAllocaAddrSpace());
   }
 
   // Treat an enum type as its underlying type.
@@ -868,7 +867,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
 
   // Handle the generic C++ ABI.
   if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
-    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), 
RAA == CGCXXABI::RAA_DirectInMemory);
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
+                                   RAA == CGCXXABI::RAA_DirectInMemory);
 
   // Integers and enums are extended to full register width.
   if (isPromotableIntegerType(Ty))
@@ -907,7 +907,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
   // 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()) {
+    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.
@@ -934,13 +935,14 @@ ABIArgInfo 
ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
 
   // Non-structure compounds are passed indirectly, i.e. arrays.
   if (isCompoundType(Ty))
-    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), 
/*ByVal=*/false);
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
+                                   /*ByVal=*/false);
 
   return ABIArgInfo::getDirect();
 }
 
 RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
-                                    QualType Ty, AggValueSlot Slot) const {
+                                   QualType Ty, AggValueSlot Slot) const {
   return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false,
                           CGF.getContext().getTypeInfoInChars(Ty),
                           CGF.getPointerSize(),

>From 7d6d9b50084fd5bee96784f860686b9ba0a3fb14 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Mon, 6 Apr 2026 18:37:39 +0000
Subject: [PATCH 3/5] Remove inreg remker and address other comments

---
 clang/lib/CodeGen/Targets/SystemZ.cpp  | 40 ++++--------
 clang/test/CodeGen/SystemZ/zos-abi.c   | 90 +++++++++++++-------------
 clang/test/CodeGen/SystemZ/zos-abi.cpp | 32 ++++-----
 3 files changed, 75 insertions(+), 87 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 309380862bc9f..7f1c0faa7f606 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -557,10 +557,9 @@ class ZOSXPLinkABIInfo : public ABIInfo {
 public:
   ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {}
 
-  bool isPromotableIntegerType(QualType Ty) const;
+  bool isPromotableIntegerTypeForABI(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;
@@ -603,15 +602,19 @@ class ZOSXPLinkTargetCodeGenInfo : public 
TargetCodeGenInfo {
 
 // Return true if the ABI requires Ty to be passed sign- or zero-
 // extended to 64 bits.
-bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const {
+bool ZOSXPLinkABIInfo::isPromotableIntegerTypeForABI(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))
+  if (ABIInfo::isPromotableIntegerTypeForABI(Ty))
     return true;
 
+  if (const auto *EIT = Ty->getAs<BitIntType>())
+    if (EIT->getNumBits() < 64)
+      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>())
@@ -636,20 +639,6 @@ bool ZOSXPLinkABIInfo::isVectorArgumentType(QualType Ty) 
const {
           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
@@ -787,7 +776,6 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) 
const {
         return std::nullopt;
 
       Count++;
-      fprintf(stderr, "Count increased to %d\n", Count);
     }
 
     if (Count == 2) {
@@ -848,7 +836,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType 
RetTy,
       // and 3.
       llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits);
       CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
-      return ABIArgInfo::getDirectInReg(CoerceTy);
+      return ABIArgInfo::getDirect(CoerceTy);
     } else
       return getNaturalAlignIndirect(RetTy,
                                      getDataLayout().getAllocaAddrSpace());
@@ -858,7 +846,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType 
RetTy,
   if (const EnumType *EnumTy = RetTy->getAs<EnumType>())
     RetTy = EnumTy->getDecl()->getIntegerType();
 
-  return (isPromotableIntegerType(RetTy) ? ABIArgInfo::getExtend(RetTy)
+  return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
                                          : ABIArgInfo::getDirect());
 }
 
@@ -871,7 +859,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
                                    RAA == CGCXXABI::RAA_DirectInMemory);
 
   // Integers and enums are extended to full register width.
-  if (isPromotableIntegerType(Ty))
+  if (isPromotableIntegerTypeForABI(Ty))
     return ABIArgInfo::getExtend(Ty);
 
   // For non-C calling conventions, compound types passed by address copy.
@@ -884,7 +872,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
   auto CompTy = getFPTypeOfComplexLikeType(Ty);
   if (IsNamedArg) {
     if (Ty->isComplexType()) {
-      auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty));
+      auto AI = ABIArgInfo::getDirect(CGT.ConvertType(Ty));
       AI.setCanBeFlattened(false);
       return AI;
     }
@@ -892,7 +880,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
     if (CompTy.has_value()) {
       llvm::Type *FPTy = CGT.ConvertType(*CompTy);
       llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
-      auto AI = ABIArgInfo::getDirectInReg(CoerceTy);
+      auto AI = ABIArgInfo::getDirect(CoerceTy);
       AI.setCanBeFlattened(false);
       return AI;
     }
@@ -927,10 +915,10 @@ ABIArgInfo 
ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
         CoerceTy = llvm::ArrayType::get(RegTy, NumRegs);
       }
 
-      return ABIArgInfo::getDirectInReg(CoerceTy);
+      return ABIArgInfo::getDirect(CoerceTy);
     }
 
-    return ABIArgInfo::getDirectInReg();
+    return ABIArgInfo::getDirect();
   }
 
   // Non-structure compounds are passed indirectly, i.e. arrays.
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c 
b/clang/test/CodeGen/SystemZ/zos-abi.c
index 999b8f07814c1..4e7bd8171bbff 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.c
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -58,32 +58,32 @@ vector unsigned int pass_vector(vector unsigned int arg) { 
return arg; };
 
 struct SingleVec { vector unsigned int v; };
 struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; };
-// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] 
inreg %{{.*}})
+// CHECK-ZVEC-LABEL: define [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}})
 #endif
 
 // Complex types
 
 _Complex float pass_complex_float(_Complex float arg) { return arg; }
-// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } 
inreg %{{.*}})
+// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } 
%{{.*}})
 
 _Complex double pass_complex_double(_Complex double arg) { return arg; }
-// CHECK-LABEL: define { double, double } @pass_complex_double({ double, 
double } inreg %{{.*}})
+// CHECK-LABEL: define { double, double } @pass_complex_double({ double, 
double } %{{.*}})
 
 _Complex long double pass_complex_longdouble(_Complex long double arg) { 
return arg; }
-// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, 
fp128 } inreg %{{.*}})
+// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, 
fp128 } %{{.*}})
 
 // Verify that the following are complex-like types
 struct complexlike_float { float re, im; };
 struct complexlike_float pass_complexlike_float2(struct complexlike_float arg) 
{ return arg; }
-// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ 
float, float } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ 
float, float } %{{.*}})
 
 struct complexlike_double { double re, im; };
 struct complexlike_double pass_complexlike_double(struct complexlike_double 
arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ 
double, double } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ 
double, double } %{{.*}})
 
 struct complexlike_longdouble { long double re, im; };
 struct complexlike_longdouble pass_complexlike_longdouble(struct 
complexlike_longdouble arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_longdouble 
@pass_complexlike_longdouble({ fp128, fp128 } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_longdouble 
@pass_complexlike_longdouble({ fp128, fp128 } %{{.*}})
 
 struct single_element_float { float f; };
 struct complexlike_struct {
@@ -91,7 +91,7 @@ struct complexlike_struct {
   struct single_element_float y;
 };
 struct complexlike_struct pass_complexlike_struct(struct complexlike_struct 
arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ 
float, float } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ 
float, float } %{{.*}})
 
 struct single_element_float_arr {
   unsigned int :0;
@@ -102,7 +102,7 @@ struct complexlike_struct2 {
   struct single_element_float_arr y;
 };
 struct complexlike_struct2 pass_complexlike_struct2(struct complexlike_struct2 
arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct2(i64 inreg 
%{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_complexlike_struct2(i64 %{{.*}})
 
 struct float_and_empties {
   struct S {} s;
@@ -114,7 +114,7 @@ struct complexlike_struct3 {
   struct float_and_empties y;
 };
 struct complexlike_struct3 pass_complexlike_struct3(struct complexlike_struct3 
arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 inreg 
%{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_complexlike_struct3(i64 %{{.*}})
 
 union two_float_union { float a; float b; };
 struct complexlike_struct_with_union {
@@ -122,14 +122,14 @@ struct complexlike_struct_with_union {
   union two_float_union b;
 };
 struct complexlike_struct_with_union pass_complexlike_struct_with_union(struct 
complexlike_struct_with_union arg) { return arg; }
-// CHECK-LABEL: struct.complexlike_struct_with_union 
@pass_complexlike_struct_with_union({ float, float } inreg %{{.*}})
+// CHECK-LABEL: struct.complexlike_struct_with_union 
@pass_complexlike_struct_with_union({ float, float } %{{.*}})
 
 // structures with one field as complex type are not considered complex types.
 struct single_complex_struct {
   _Complex float f;
 };
 struct single_complex_struct pass_single_complex_struct(struct 
single_complex_struct arg) {return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_single_complex_struct(i64 inreg 
%{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_single_complex_struct(i64 %{{.*}})
 
 // Structures with extra padding are not considered complex types.
 struct complexlike_float_padded1 {
@@ -137,14 +137,14 @@ struct complexlike_float_padded1 {
   float y __attribute__((aligned(8)));
 };
 struct complexlike_float_padded1 pass_complexlike_float_padded1(struct 
complexlike_float_padded1 arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded1([2 x 
i64] inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded1([2 x i64] 
%{{.*}})
 
 struct complexlike_float_padded2 {
   float x;
   float y;
 } __attribute__((aligned(16)));
 struct complexlike_float_padded2 pass_complexlike_float_padded2(struct 
complexlike_float_padded2 arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded2([2 x 
i64] inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded2([2 x i64] 
%{{.*}})
 
 
 struct single_padded_struct {
@@ -156,7 +156,7 @@ struct complexlike_float_padded3 {
   struct single_padded_struct y;
 };
 struct complexlike_float_padded3 pass_complexlike_float_padded3(struct 
complexlike_float_padded3 arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded3([2 x 
i64] inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded3([2 x i64] 
%{{.*}})
 
 struct multi_element_float_arr { float f[2]; };
 struct complexlike_struct4 {
@@ -164,7 +164,7 @@ struct complexlike_struct4 {
   struct multi_element_float_arr y;
 };
 struct complexlike_struct4 pass_complexlike_struct4(struct complexlike_struct4 
arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] 
inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_complexlike_struct4([2 x i64] %{{.*}})
 
 typedef double align32_double __attribute__((aligned(32)));
 struct complexlike_double_padded {
@@ -172,7 +172,7 @@ struct complexlike_double_padded {
   double y;
 };
 struct complexlike_double_padded pass_complexlike_double_padded(struct 
complexlike_double_padded arg) { return arg; }
-// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} 
sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] inreg 
%{{.*}})
+// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} 
sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] %{{.*}})
 
 struct complexlike_double_padded2  {
   __attribute__((aligned(256))) align32_double x; // attribute on both type 
and typedef
@@ -180,80 +180,80 @@ struct complexlike_double_padded2  {
 };
 
 struct complexlike_double_padded2 pass_complexlike_double_padded2(struct 
complexlike_double_padded2 arg) { return arg; }
-// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} 
sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] inreg 
%{{.*}})
+// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} 
sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] %{{.*}})
 
 struct complexlike_double_padded3 {
   __attribute__((aligned(32))) double x; // attribute on type only
   double y;
 };
 struct complexlike_double_padded3 pass_complexlike_double_padded3(struct 
complexlike_double_padded3 arg) { return arg; }
-// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} 
sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] inreg 
%{{.*}})
+// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} 
sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] %{{.*}})
 
 
 // Aggregate types
 
 struct agg_1byte { char a[1]; };
 struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_1byte(i64 %{{.*}})
 
 struct agg_2byte { char a[2]; };
 struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_2byte(i64 %{{.*}})
 
 struct agg_3byte { char a[3]; };
 struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_3byte(i64 %{{.*}})
 
 struct agg_4byte { char a[4]; };
 struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_4byte(i64 %{{.*}})
 
 struct agg_5byte { char a[5]; };
 struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_5byte(i64 %{{.*}})
 
 struct agg_6byte { char a[6]; };
 struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_6byte(i64 %{{.*}})
 
 struct agg_7byte { char a[7]; };
 struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_7byte(i64 %{{.*}})
 
 struct agg_8byte { char a[8]; };
 struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_8byte(i64 %{{.*}})
 
 struct agg_9byte { char a[9]; };
 struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}})
 
 struct agg_16byte { char a[16]; };
 struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] inreg 
%{{.*}})
+// CHECK-LABEL: define [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}})
 
 struct agg_24byte { char a[24]; };
 struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; }
-// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] inreg 
%{{.*}})
+// CHECK-LABEL: define [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}})
 
 struct agg_25byte { char a[25]; };
 struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; }
-// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias 
writable sret{{.*}} align 1 %{{.*}}, [4 x i64] inreg %{{.*}})
+// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias 
writable sret{{.*}} align 1 %{{.*}}, [4 x i64] %{{.*}})
 
 // Check that a float-like aggregate type is really passed as aggregate
 struct agg_float { float a; };
 struct agg_float pass_agg_float(struct agg_float arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_float(i64 %{{.*}})
 
 // Verify that the following are *not* float-like aggregate types
 
 struct agg_nofloat2 { float a; int b; };
 struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_nofloat2(i64 %{{.*}})
 
 struct agg_nofloat3 { float a; int : 0; };
 struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_agg_nofloat3(i64 %{{.*}})
 
 char * pass_pointer(char * arg) { return arg; }
 // CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}})
@@ -299,22 +299,22 @@ struct normal_struct {
 };
 
 struct complexlike_union1 pass_complexlike_union1(struct complexlike_union1 
arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ 
float, float } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ 
float, float } %{{.*}})
 
 struct complexlike_union2 pass_complexlike_union2(struct complexlike_union2 
arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ 
float, float } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ 
float, float } %{{.*}})
 
 struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 
arg) { return arg; }
-// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ 
float, float } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ 
float, float } %{{.*}})
 
 union u1 pass_union1(union u1 arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_union1(i64 %{{.*}})
 
 union u2 pass_union2(union u2 arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_union2(i64 %{{.*}})
 
 struct normal_struct pass_normal_struct(struct normal_struct arg) { return 
arg; }
-// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 inreg %{{.*}})
+// CHECK-LABEL: define [1 x i64] @pass_normal_struct(i64 %{{.*}})
 
 // ============================================================================
 // Complex-like struct using nested typedefs with alignment attributes
@@ -335,7 +335,7 @@ struct S_A {
 struct S_A pass_S_A(struct S_A arg) { return arg; }
 // CHECK-LABEL: define void @pass_S_A(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32
-// CHECK-SAME: [4 x i64] inreg
+// CHECK-SAME: [4 x i64] 
 // CHECK: ret void
 
 // ============================================================================
@@ -353,7 +353,7 @@ struct S_B {
 struct S_B pass_S_B(struct S_B arg) { return arg; }
 // CHECK-LABEL: define void @pass_S_B(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32
-// CHECK-SAME: [8 x i64] inreg
+// CHECK-SAME: [8 x i64] 
 // CHECK: ret void
 
 // ============================================================================
@@ -372,7 +372,7 @@ struct S_C {
 struct S_C pass_S_C(struct S_C arg) { return arg; }
 // CHECK-LABEL: define void @pass_S_C(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_C) align 32
-// CHECK-SAME: [4 x i64] inreg
+// CHECK-SAME: [4 x i64] 
 // CHECK: ret void
 
 // ============================================================================
@@ -392,7 +392,7 @@ struct S_D {
 struct S_D pass_S_D(struct S_D arg) { return arg; }
 // CHECK-LABEL: define void @pass_S_D(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_D) align 32
-// CHECK-SAME: [8 x i64] inreg
+// CHECK-SAME: [8 x i64] 
 // CHECK: ret void
 
 // ============================================================================
@@ -410,5 +410,5 @@ struct S_E {
 
 struct S_E pass_S_E(struct S_E arg) { return arg; }
 // CHECK-LABEL: define %struct.S_E @pass_S_E(
-// CHECK-SAME:{ double, double } inreg %{{.*}})
+// CHECK-SAME:{ double, double } %{{.*}})
 // CHECK: ret %struct.S_E
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.cpp 
b/clang/test/CodeGen/SystemZ/zos-abi.cpp
index 0578ddfcb17cd..2e3abb71652ef 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.cpp
+++ b/clang/test/CodeGen/SystemZ/zos-abi.cpp
@@ -4,61 +4,61 @@
 
 class agg_float_class { float a; };
 class agg_float_class pass_agg_float_class(class agg_float_class arg) { return 
arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z20pass_agg_float_class15agg_float_class(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z20pass_agg_float_class15agg_float_class(i64 %arg.coerce)
 
 class agg_double_class { double a; };
 class agg_double_class pass_agg_double_class(class agg_double_class arg) { 
return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z21pass_agg_double_class16agg_double_class(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z21pass_agg_double_class16agg_double_class(i64 %arg.coerce)
 
 
 // This structure is passed in also in GPR.
 struct agg_float_cpp { float a; int : 0; };
 struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return 
arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z18pass_agg_float_cpp13agg_float_cpp(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] @_Z18pass_agg_float_cpp13agg_float_cpp(i64 
%arg.coerce)
 
 // In C++ a  data member of empty class type makes the record nonhomogeneous,
 // regardless if it's marked as [[no_unique_address]] or not.
 struct empty { };
 struct agg_nofloat_empty { float a; empty dummy; };
 struct agg_nofloat_empty pass_agg_nofloat_empty(struct agg_nofloat_empty arg) 
{ return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 %arg.coerce)
 struct complex_like_agg_nofloat_empty { struct agg_nofloat_empty a; struct 
agg_nofloat_empty b; };
 struct complex_like_agg_nofloat_empty 
pass_complex_like_agg_nofloat_empty(struct complex_like_agg_nofloat_empty arg) 
{ return arg; }
-// CHECK-LABEL: define inreg [2 x i64] 
@_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x 
i64] inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] 
@_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x 
i64] %{{.*}})
 
 struct agg_float_empty { float a; [[no_unique_address]] empty dummy; };
 struct agg_float_empty pass_agg_float_empty(struct agg_float_empty arg) { 
return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z20pass_agg_float_empty15agg_float_empty(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z20pass_agg_float_empty15agg_float_empty(i64 %arg.coerce)
 struct complex_like_agg_float_empty { struct agg_float_empty a; struct 
agg_float_empty b; };
 struct complex_like_agg_float_empty pass_complex_like_agg_float_empty(struct 
complex_like_agg_float_empty arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 inreg 
%{{.*}})
+// CHECK-LABEL: define [1 x i64] 
@_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 
%{{.*}})
 
 struct agg_nofloat_emptyarray { float a; [[no_unique_address]] empty dummy[3]; 
};
 struct agg_nofloat_emptyarray pass_agg_nofloat_emptyarray(struct 
agg_nofloat_emptyarray arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 %arg.coerce)
 
 
 // And likewise for members of base classes.
 struct noemptybase { empty dummy; };
 struct agg_nofloat_emptybase : noemptybase { float a; };
 struct agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct 
agg_nofloat_emptybase arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 %arg.coerce)
 struct complex_like_agg_nofloat_emptybase { struct agg_nofloat_emptybase a; 
struct agg_nofloat_emptybase b; };
 struct complex_like_agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct 
complex_like_agg_nofloat_emptybase arg) { return arg; }
-// CHECK-LABEL: define inreg [2 x i64] 
@_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] 
inreg %{{.*}})
+// CHECK-LABEL: define [2 x i64] 
@_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] 
%{{.*}})
 
 struct emptybase { [[no_unique_address]] empty dummy; };
 struct agg_float_emptybase : emptybase { float a; };
 struct agg_float_emptybase pass_agg_float_emptybase(struct agg_float_emptybase 
arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 inreg %arg.coerce)
+// CHECK-LABEL: define [1 x i64] 
@_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 %arg.coerce)
 struct complex_like_agg_float_emptybase { struct agg_float_emptybase a; struct 
agg_float_emptybase b; };
 struct complex_like_agg_float_emptybase pass_agg_float_emptybase(struct 
complex_like_agg_float_emptybase arg) { return arg; }
-// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase 
@_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float 
} inreg %{{.*}})
+// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase 
@_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float 
} %{{.*}})
 
 struct noemptybasearray { [[no_unique_address]] empty dummy[3]; };
 struct agg_nofloat_emptybasearray : noemptybasearray { float a; };
 struct agg_nofloat_emptybasearray pass_agg_nofloat_emptybasearray(struct 
agg_nofloat_emptybasearray arg) { return arg; }
-// CHECK-LABEL: define inreg [1 x i64] 
@_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 inreg 
%{{.*}})
+// CHECK-LABEL: define [1 x i64] 
@_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 %{{.*}})
 
 using D = double;
 using E = __attribute__((aligned(32))) D; // attribute inside the alias
@@ -67,7 +67,7 @@ struct complexlike_alias {
   double y;
 };
 struct complexlike_alias pass_complexlike_alias(struct complexlike_alias arg) 
{ return arg; }
-// CHECK-LABEL: define %struct.complexlike_alias 
@_Z22pass_complexlike_alias17complexlike_alias({ double, double } inreg %{{.*}})
+// CHECK-LABEL: define %struct.complexlike_alias 
@_Z22pass_complexlike_alias17complexlike_alias({ double, double } %{{.*}})
 
 // ============================================================================
 // Complex-like struct using alignas specifier
@@ -81,7 +81,7 @@ struct S_A {
 struct S_A pass_S_A(struct S_A arg) { return arg; }
 // CHECK-LABEL: define void @_Z8pass_S_A3S_A(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32
-// CHECK-SAME: [4 x i64] inreg
+// CHECK-SAME: [4 x i64] 
 // CHECK: ret void
 
 struct alignas(32) S_B {
@@ -92,5 +92,5 @@ struct alignas(32) S_B {
 struct S_B pass_S_B(struct S_B arg) { return arg; }
 // CHECK-LABEL: define void @_Z8pass_S_B3S_B(
 // CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32
-// CHECK-SAME: [4 x i64] inreg
+// CHECK-SAME: [4 x i64] 
 // CHECK: ret void

>From 51bafcf660b050c7185ff77c15cbdce18cdc084d Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Mon, 6 Apr 2026 19:32:36 +0000
Subject: [PATCH 4/5] Remove redundant inner if statement checking for
 isAggregateTypeForABI(Ty)

---
 clang/lib/CodeGen/Targets/SystemZ.cpp | 42 ++++++++++++---------------
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 7f1c0faa7f606..d1fc840f16f9d 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -847,7 +847,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType 
RetTy,
     RetTy = EnumTy->getDecl()->getIntegerType();
 
   return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
-                                         : ABIArgInfo::getDirect());
+                                               : ABIArgInfo::getDirect());
 }
 
 ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
@@ -895,30 +895,24 @@ ABIArgInfo 
ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
   // 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::getDirect(CoerceTy);
+    // 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::getDirect();
+    return ABIArgInfo::getDirect(CoerceTy);
   }
 
   // Non-structure compounds are passed indirectly, i.e. arrays.

>From cbd43bdfb8e0cc30dc9050fc2c0b0d5e9d15fb3e Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Mon, 6 Apr 2026 20:49:36 +0000
Subject: [PATCH 5/5] Explaining why we need to call setCanBeFlattened(false)

---
 clang/lib/CodeGen/Targets/SystemZ.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index d1fc840f16f9d..84d3feadd7c3d 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -873,6 +873,11 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
   if (IsNamedArg) {
     if (Ty->isComplexType()) {
       auto AI = ABIArgInfo::getDirect(CGT.ConvertType(Ty));
+
+      // Complex types must be preserved as opaque structs per XPLINK ABI.
+      // Without this, flattening would incorrectly split { float, float } into
+      // separate FP registers, breaking ABI compliance.
+      // Example: _Complex float pass_complex_float(_Complex float arg) { 
return arg; }
       AI.setCanBeFlattened(false);
       return AI;
     }
@@ -881,6 +886,11 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType 
Ty, bool IsNamedArg,
       llvm::Type *FPTy = CGT.ConvertType(*CompTy);
       llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
       auto AI = ABIArgInfo::getDirect(CoerceTy);
+
+      // Preserve { float, float } signature for complex-like structs per 
XPLINK ABI.
+      // Flattening would incorrectly decompose into separate FP registers.
+      // Example: struct complexlike_float { float re, im; };
+      //          struct complexlike_float pass_complexlike_float2(struct 
complexlike_float arg) { return arg; }
       AI.setCanBeFlattened(false);
       return AI;
     }

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

Reply via email to