[clang] [SystemZ][z/OS] Add XPLink ABI on z/OS (PR #188501)
Zibi Sarbinowski via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 11:53:20 PDT 2026
https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/188501
>From d74b22a5666bc92f5437ac3df7d517a2e80a6f59 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 9 Mar 2026 22:17:03 +0000
Subject: [PATCH 01/12] 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 94a52f51118a7..843bbaaaa4e92 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 b29ff03e2b96aa0c98b4ef7bdd5e5d09a37eea50 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Wed, 25 Mar 2026 16:05:06 +0000
Subject: [PATCH 02/12] 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 e944490189b8fa7f69f2f2da9a3fe3173171f142 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 6 Apr 2026 18:37:39 +0000
Subject: [PATCH 03/12] 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 d02e41c7da13d1bf7def5359cfdfdfcdd67d2608 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 6 Apr 2026 19:32:36 +0000
Subject: [PATCH 04/12] 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 2ad2939dff5082e0096fe20514df856e434904fb Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 6 Apr 2026 20:49:36 +0000
Subject: [PATCH 05/12] 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;
}
>From 055796beddb4f0d258b5f71e8f20c79014f7870b Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 7 Apr 2026 20:51:45 +0000
Subject: [PATCH 06/12] Simplify getFPTypeOfComplexLikeType based on the
comments and add more test
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 36 +++++++++----------
clang/test/CodeGen/SystemZ/zos-abi.c | 50 +++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 18 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 84d3feadd7c3d..9a5c6d6f854ac 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -738,23 +738,21 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
if (Count >= 2)
return std::nullopt;
+ // For the current ABI, a record can be treated as complex-like if no field
+ // requires an alignment stronger than a native complex type (2 * sizeof(T))
+ // and the record size is exactly 2 * sizeof(T). Under these constraints,
+ // per-field alignment cannot introduce internal or tail padding, and the
+ // layout is guaranteed to match two adjacent FP elements.
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;
- }
+ unsigned ElemSize = getContext().getTypeSize(FTSingleTy);
+ if (MaxAlignOnDecl > 2 * ElemSize)
+ return std::nullopt;
+
+ unsigned StructSize = getContext().getTypeSize(Ty);
+ if (StructSize != 2 * ElemSize)
+ return std::nullopt;
if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
switch (BT->getKind()) {
@@ -877,7 +875,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
// 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; }
+ // Example: _Complex float pass_complex_float(_Complex float arg) { return
+ // arg; }
AI.setCanBeFlattened(false);
return AI;
}
@@ -887,10 +886,11 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
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.
+ // 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; }
+ // struct complexlike_float pass_complexlike_float2(struct
+ // complexlike_float arg) { return arg; }
AI.setCanBeFlattened(false);
return AI;
}
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c
index 4e7bd8171bbff..f9e746b039b2c 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.c
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -412,3 +412,53 @@ 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 } %{{.*}})
// CHECK: ret %struct.S_E
+
+
+// ============================================================================
+// Over-aligned second field introduces internal padding, so the record
+// cannot be treated as a complex-like `{ double, double }` and must be
+// returned indirectly with 16-byte alignment.
+// ============================================================================
+
+struct Bad1 {
+ double re;
+ double im __attribute__((aligned(16)));;
+};
+struct Bad1 pass_Bad1(struct Bad1 arg) { return arg; }
+// CHECK-LABEL: define void @pass_Bad1(ptr dead_on_unwind noalias writable sret({{.*}}) align 16 %agg.result, [4 x i64] %{{.*}})
+
+
+// ============================================================================
+// Stronger over-alignment on the second field further increases the required
+// alignment and argument size, forcing indirect return with 32-byte alignment.
+// ============================================================================
+
+struct Bad2 {
+ double re;
+ double im __attribute__((aligned(32)));;
+};
+struct Bad2 pass_Bad2(struct Bad2 arg) { return arg; }
+// CHECK-LABEL: define void @pass_Bad2(ptr dead_on_unwind noalias writable sret(%{{.*}}) align 32 %agg.result, [8 x i64] %{{.*}})
+
+// ============================================================================
+// Over-alignment on the first field prevents complex classification, but the
+// record still fits in registers and is returned directly as a plain aggregate.
+// ============================================================================
+struct Bad3 {
+ double re __attribute__((aligned(16)));
+ double im;
+};
+struct Bad3 pass_Bad3(struct Bad3 arg) { return arg; }
+// CHECK-LABEL: define %struct.Bad3 @pass_Bad3({ double, double } %{{.*}})
+
+
+// ============================================================================
+// Strong over-alignment on the first field raises the required alignment of the
+// whole record, forcing indirect return and disqualifying complex-like passing.
+// ============================================================================
+struct Bad4 {
+ double re __attribute__((aligned(32)));
+ double im;
+};
+struct Bad4 pass_Bad4(struct Bad4 arg) { return arg; }
+// CHECK-LABEL: define void @pass_Bad4(ptr dead_on_unwind noalias writable sret(%{{.*}}) align 32 %agg.result, [4 x i64] %{{.*}})
>From be2fd8ea4caa8992695f1d4ec55d6887c2b8bf7d Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Wed, 8 Apr 2026 20:18:01 +0000
Subject: [PATCH 07/12] Add inreg attributer back for function return types
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 13 +++----
clang/test/CodeGen/SystemZ/zos-abi.c | 50 +++++++++++++-------------
clang/test/CodeGen/SystemZ/zos-abi.cpp | 24 ++++++-------
3 files changed, 44 insertions(+), 43 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 9a5c6d6f854ac..2e4d34f826b9a 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -738,11 +738,12 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
if (Count >= 2)
return std::nullopt;
- // For the current ABI, a record can be treated as complex-like if no field
- // requires an alignment stronger than a native complex type (2 * sizeof(T))
- // and the record size is exactly 2 * sizeof(T). Under these constraints,
- // per-field alignment cannot introduce internal or tail padding, and the
- // layout is guaranteed to match two adjacent FP elements.
+ // For the current ABI, a record can be treated as complex-like if no
+ // field requires an alignment stronger than a native complex type (2 *
+ // sizeof(T)) and the record size is exactly 2 * sizeof(T). Under these
+ // constraints, per-field alignment cannot introduce internal or tail
+ // padding, and the layout is guaranteed to match two adjacent FP
+ // elements.
unsigned MaxAlignOnDecl = FD->getMaxAlignment();
QualType FT = FD->getType();
QualType FTSingleTy = getSingleElementType(FT);
@@ -834,7 +835,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
// and 3.
llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits);
CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
- return ABIArgInfo::getDirect(CoerceTy);
+ return ABIArgInfo::getDirectInReg(CoerceTy);
} else
return getNaturalAlignIndirect(RetTy,
getDataLayout().getAllocaAddrSpace());
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c
index f9e746b039b2c..172685354803a 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.c
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -58,7 +58,7 @@ 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 [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}})
+// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}})
#endif
// Complex types
@@ -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 [1 x i64] @pass_complexlike_struct2(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_complexlike_struct3(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 %{{.*}})
union two_float_union { float a; float b; };
struct complexlike_struct_with_union {
@@ -129,7 +129,7 @@ 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 [1 x i64] @pass_single_complex_struct(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_complexlike_float_padded1([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_complexlike_float_padded2([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_complexlike_float_padded3([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_complexlike_struct4([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] %{{.*}})
typedef double align32_double __attribute__((aligned(32)));
struct complexlike_double_padded {
@@ -194,47 +194,47 @@ struct complexlike_double_padded3 pass_complexlike_double_padded3(struct complex
struct agg_1byte { char a[1]; };
struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; }
-// CHECK-LABEL: define [1 x i64] @pass_agg_1byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_2byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_3byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_4byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_5byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_6byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_7byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_8byte(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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; }
@@ -243,17 +243,17 @@ struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; }
// 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 [1 x i64] @pass_agg_float(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_nofloat2(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @pass_agg_nofloat3(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 %{{.*}})
char * pass_pointer(char * arg) { return arg; }
// CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}})
@@ -308,13 +308,13 @@ struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 arg)
// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ float, float } %{{.*}})
union u1 pass_union1(union u1 arg) { return arg; }
-// CHECK-LABEL: define [1 x i64] @pass_union1(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 %{{.*}})
union u2 pass_union2(union u2 arg) { return arg; }
-// CHECK-LABEL: define [1 x i64] @pass_union2(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 %{{.*}})
struct normal_struct pass_normal_struct(struct normal_struct arg) { return arg; }
-// CHECK-LABEL: define [1 x i64] @pass_normal_struct(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 %{{.*}})
// ============================================================================
// Complex-like struct using nested typedefs with alignment attributes
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.cpp b/clang/test/CodeGen/SystemZ/zos-abi.cpp
index 2e3abb71652ef..3c6d1d4ec1506 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.cpp
+++ b/clang/test/CodeGen/SystemZ/zos-abi.cpp
@@ -4,53 +4,53 @@
class agg_float_class { float a; };
class agg_float_class pass_agg_float_class(class agg_float_class arg) { return arg; }
-// CHECK-LABEL: define [1 x i64] @_Z20pass_agg_float_class15agg_float_class(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z21pass_agg_double_class16agg_double_class(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z18pass_agg_float_cpp13agg_float_cpp(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [2 x i64] @_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z20pass_agg_float_empty15agg_float_empty(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 [2 x i64] @_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] %{{.*}})
+// CHECK-LABEL: define inreg [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 [1 x i64] @_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 %arg.coerce)
+// CHECK-LABEL: define inreg [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 } %{{.*}})
@@ -58,7 +58,7 @@ struct complex_like_agg_float_emptybase pass_agg_float_emptybase(struct complex_
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 [1 x i64] @_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 %{{.*}})
+// CHECK-LABEL: define inreg [1 x i64] @_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 %{{.*}})
using D = double;
using E = __attribute__((aligned(32))) D; // attribute inside the alias
>From 5b2feadbda1e6db9237fc39ff3a23a13fbbe87e6 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Fri, 17 Apr 2026 20:14:44 +0000
Subject: [PATCH 08/12] Remove redundant code, simplify it, correct comment to
address the review
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 2e4d34f826b9a..8b0b63ef0333a 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -678,13 +678,13 @@ QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const {
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.
+ // Inspect the record's fields. A struct qualifies as single-element if it
+ // has exactly one non-empty field. Empty fields (zero-size structs, arrays,
+ // bitfields, or [[no_unique_address]] members) don't affect this
+ // classification. Validated by zos-abi.cpp tests.
for (const auto *FD : RD->fields()) {
if (!Found.isNull())
- return Ty; // more than one field, not a single-element
+ return Ty; // Multiple non-empty fields found
Found = getSingleElementType(FD->getType());
}
return Found.isNull() ? Ty : Found;
@@ -778,13 +778,7 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
}
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;
+ // Size was already verified in the loop - no padding exists
return RetTy;
}
}
@@ -840,11 +834,6 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
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 (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
: ABIArgInfo::getDirect());
}
>From 8b12ef2cf3d4f2c8d81ed6afe17ac4014cbc0eb5 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Thu, 7 May 2026 19:38:20 +0000
Subject: [PATCH 09/12] Include transparent-union in ZOSXPLink ABI
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 13 ++++++++++---
clang/test/CodeGen/SystemZ/zos-abi.c | 26 +++++++++++++++++++++++++-
2 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 8b0b63ef0333a..898d65aa4d146 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -621,6 +621,8 @@ bool ZOSXPLinkABIInfo::isPromotableIntegerTypeForABI(QualType Ty) const {
switch (BT->getKind()) {
case BuiltinType::Int:
case BuiltinType::UInt:
+ case BuiltinType::ULong:
+ case BuiltinType::Long:
return true;
default:
break;
@@ -792,6 +794,9 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
if (RetTy->isVoidType())
return ABIArgInfo::getIgnore();
+ // Handle transparent union types.
+ RetTy = useFirstFieldIfTransparentUnion(RetTy);
+
// For non-C calling convention, indirect by value for structs and complex.
if ((CallConv != llvm::CallingConv::C) &&
(isAggregateTypeForABI(RetTy) || RetTy->isAnyComplexType())) {
@@ -834,12 +839,14 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
return getNaturalAlignIndirect(RetTy,
getDataLayout().getAllocaAddrSpace());
}
- return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
- : ABIArgInfo::getDirect());
+ return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
+ : ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
}
ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
unsigned CallConv) const {
+ // Handle transparent union types.
+ Ty = useFirstFieldIfTransparentUnion(Ty);
// Handle the generic C++ ABI.
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
@@ -848,7 +855,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
// Integers and enums are extended to full register width.
if (isPromotableIntegerTypeForABI(Ty))
- return ABIArgInfo::getExtend(Ty);
+ return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty));
// For non-C calling conventions, compound types passed by address copy.
if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty))
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c
index 172685354803a..ac1d7b31328b6 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.c
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -34,7 +34,7 @@ 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 %{{.*}})
+// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}})
long long pass_longlong(long long arg) { return arg; }
// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}})
@@ -462,3 +462,27 @@ struct Bad4 {
};
struct Bad4 pass_Bad4(struct Bad4 arg) { return arg; }
// CHECK-LABEL: define void @pass_Bad4(ptr dead_on_unwind noalias writable sret(%{{.*}}) align 32 %agg.result, [4 x i64] %{{.*}})
+
+
+// ==================================================================
+// Verify that transparent unions are passed like their first member.
+// ==================================================================
+union tu_char { char a; } __attribute__((transparent_union));
+union tu_char pass_tu_char(union tu_char arg) { return arg; }
+// CHECK-LABEL: define{{.*}} i8 @pass_tu_char(i8 signext %{{.*}})
+
+union tu_short { short a; } __attribute__((transparent_union));
+union tu_short pass_tu_short(union tu_short arg) { return arg; }
+// CHECK-LABEL: define{{.*}} i16 @pass_tu_short(i16 signext %{{.*}})
+
+union tu_int { int a; } __attribute__((transparent_union));
+union tu_int pass_tu_int(union tu_int arg) { return arg; }
+// CHECK-LABEL: define{{.*}} i32 @pass_tu_int(i32 signext %{{.*}})
+
+union tu_long { long a; } __attribute__((transparent_union));
+union tu_long pass_tu_long(union tu_long arg) { return arg; }
+// CHECK-LABEL: define{{.*}} i64 @pass_tu_long(i64 signext %{{.*}})
+
+union tu_ptr { void *a; } __attribute__((transparent_union));
+union tu_ptr pass_tu_ptr(union tu_ptr arg) { return arg; }
+// CHECK-LABEL: define{{.*}} ptr @pass_tu_ptr(ptr %{{.*}})
>From b8c91042d3ab103541d89ef6f47964a76f9c8634 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 11 May 2026 19:19:49 +0000
Subject: [PATCH 10/12] Remove aligment check inside
getFPTypeOfComplexLikeType() + fix formatting
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 22 +++++++---------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 898d65aa4d146..d829197f39651 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -740,21 +740,12 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
if (Count >= 2)
return std::nullopt;
- // For the current ABI, a record can be treated as complex-like if no
- // field requires an alignment stronger than a native complex type (2 *
- // sizeof(T)) and the record size is exactly 2 * sizeof(T). Under these
- // constraints, per-field alignment cannot introduce internal or tail
- // padding, and the layout is guaranteed to match two adjacent FP
- // elements.
- unsigned MaxAlignOnDecl = FD->getMaxAlignment();
+ // A record can be treated as complex-like if its size is exactly
+ // 2 * sizeof(T), matching the layout of two adjacent FP elements.
QualType FT = FD->getType();
QualType FTSingleTy = getSingleElementType(FT);
- unsigned ElemSize = getContext().getTypeSize(FTSingleTy);
- if (MaxAlignOnDecl > 2 * ElemSize)
- return std::nullopt;
-
- unsigned StructSize = getContext().getTypeSize(Ty);
- if (StructSize != 2 * ElemSize)
+ auto &Ctx = getContext();
+ if (Ctx.getTypeSize(Ty)!= 2 * Ctx.getTypeSize(FTSingleTy))
return std::nullopt;
if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
@@ -839,8 +830,9 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
return getNaturalAlignIndirect(RetTy,
getDataLayout().getAllocaAddrSpace());
}
- return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
- : ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
+ return (isPromotableIntegerTypeForABI(RetTy)
+ ? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
+ : ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
}
ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
>From 546353a586f29b049aa7832674d6ef2026ab71bb Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 12 May 2026 21:02:37 +0000
Subject: [PATCH 11/12] formatting
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index d829197f39651..64419a4fb0389 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -745,7 +745,7 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
QualType FT = FD->getType();
QualType FTSingleTy = getSingleElementType(FT);
auto &Ctx = getContext();
- if (Ctx.getTypeSize(Ty)!= 2 * Ctx.getTypeSize(FTSingleTy))
+ if (Ctx.getTypeSize(Ty) != 2 * Ctx.getTypeSize(FTSingleTy))
return std::nullopt;
if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
@@ -831,8 +831,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
getDataLayout().getAllocaAddrSpace());
}
return (isPromotableIntegerTypeForABI(RetTy)
- ? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
- : ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
+ ? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
+ : ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
}
ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
>From 20315487edb7269155565eabbbd97e94c1bb207f Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 1 Jun 2026 13:38:16 -0400
Subject: [PATCH 12/12] Remove getMaxAlignFromTypeDefs and relocate size
verification for complex-like struc.
---
clang/lib/CodeGen/Targets/SystemZ.cpp | 33 ++++-----------------------
1 file changed, 5 insertions(+), 28 deletions(-)
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 64419a4fb0389..5c7485ed0944f 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -561,7 +561,6 @@ class ZOSXPLinkABIInfo : public ABIInfo {
bool isCompoundType(QualType Ty) const;
bool isVectorArgumentType(QualType Ty) const;
QualType getSingleElementType(QualType Ty) const;
- unsigned getMaxAlignFromTypeDefs(QualType Ty) const;
std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const;
ABIArgInfo classifyReturnType(QualType RetTy,
@@ -694,26 +693,6 @@ QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const {
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()) {
@@ -740,13 +719,8 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
if (Count >= 2)
return std::nullopt;
- // A record can be treated as complex-like if its size is exactly
- // 2 * sizeof(T), matching the layout of two adjacent FP elements.
QualType FT = FD->getType();
QualType FTSingleTy = getSingleElementType(FT);
- auto &Ctx = getContext();
- if (Ctx.getTypeSize(Ty) != 2 * Ctx.getTypeSize(FTSingleTy))
- return std::nullopt;
if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
switch (BT->getKind()) {
@@ -771,8 +745,11 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
}
if (Count == 2) {
- // Size was already verified in the loop - no padding exists
- return RetTy;
+ // A record can be treated as complex-like if its size is exactly
+ // 2 * sizeof(T), matching the layout of two adjacent FP elements.
+ auto &Ctx = getContext();
+ if (Ctx.getTypeSize(Ty) == 2 * Ctx.getTypeSize(RetTy))
+ return RetTy;
}
}
return std::nullopt;
More information about the cfe-commits
mailing list