https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/188501
>From 5b53674ff92729eea5b3479764ecef9c670ba4c9 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 9 Mar 2026 22:17:03 +0000 Subject: [PATCH 1/7] Add new class, ZOSXPLinkABIInfo, for XPLink ABI on z/OS --- clang/lib/CodeGen/CodeGenModule.cpp | 2 + clang/lib/CodeGen/TargetInfo.h | 4 + clang/lib/CodeGen/Targets/SystemZ.cpp | 410 ++++++++++++++++++++++++ clang/test/CodeGen/SystemZ/zos-abi.c | 414 +++++++++++++++++++++++++ clang/test/CodeGen/SystemZ/zos-abi.cpp | 96 ++++++ 5 files changed, 926 insertions(+) create mode 100644 clang/test/CodeGen/SystemZ/zos-abi.c create mode 100644 clang/test/CodeGen/SystemZ/zos-abi.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index daaa846bf42bc..bbc96b0b3916b 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -242,6 +242,8 @@ createTargetCodeGenInfo(CodeGenModule &CGM) { case llvm::Triple::systemz: { bool SoftFloat = CodeGenOpts.FloatABI == "soft"; bool HasVector = !SoftFloat && Target.getABI() == "vector"; + if (Triple.getOS() == llvm::Triple::ZOS) + return createSystemZ_ZOS_TargetCodeGenInfo(CGM, HasVector, SoftFloat); return createSystemZTargetCodeGenInfo(CGM, HasVector, SoftFloat); } diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h index 98ee894fe557f..0be1cb225858c 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -572,6 +572,10 @@ std::unique_ptr<TargetCodeGenInfo> createSystemZTargetCodeGenInfo(CodeGenModule &CGM, bool HasVector, bool SoftFloatABI); +std::unique_ptr<TargetCodeGenInfo> +createSystemZ_ZOS_TargetCodeGenInfo(CodeGenModule &CGM, bool HasVector, + bool SoftFloatABI); + std::unique_ptr<TargetCodeGenInfo> createTCETargetCodeGenInfo(CodeGenModule &CGM); diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index e50f06c2f548c..879e11ea4108d 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -544,9 +544,419 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty, return false; } +//===----------------------------------------------------------------------===// +// z/OS XPLINK ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { + +class ZOSXPLinkABIInfo : public ABIInfo { + const unsigned GPRBits = 64; + bool HasVector; + +public: + ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) + : ABIInfo(CGT), HasVector(HV) {} + + bool isPromotableIntegerType(QualType Ty) const; + bool isCompoundType(QualType Ty) const; + bool isVectorArgumentType(QualType Ty) const; + bool isFPArgumentType(QualType Ty) const; + QualType getSingleElementType(QualType Ty) const; + unsigned getMaxAlignFromTypeDefs(QualType Ty) const; + std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const; + + ABIArgInfo classifyReturnType(QualType RetTy, + unsigned functionCallConv) const; + ABIArgInfo classifyArgumentType(QualType ArgTy, bool IsNamedArg, + unsigned functionCallConv) const; + + void computeInfo(CGFunctionInfo &FI) const override { + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = + classifyReturnType(FI.getReturnType(), FI.getCallingConvention()); + + unsigned NumRequiredArgs = FI.getNumRequiredArgs(); + unsigned ArgNo = 0; + + for (auto &I : FI.arguments()) { + bool IsNamedArg = ArgNo < NumRequiredArgs; + I.info = + classifyArgumentType(I.type, IsNamedArg, FI.getCallingConvention()); + ++ArgNo; + } + } + + RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, + AggValueSlot Slot) const override; +}; + +class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo { +public: + ZOSXPLinkTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector) + : TargetCodeGenInfo(std::make_unique<ZOSXPLinkABIInfo>(CGT, HasVector)) { + SwiftInfo = + std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false); + } +}; + +} // namespace + +// Return true if the ABI requires Ty to be passed sign- or zero- +// extended to 64 bits. +bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const { + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = Ty->getAs<EnumType>()) + Ty = EnumTy->getDecl()->getIntegerType(); + + // Promotable integer types are required to be promoted by the ABI. + if (getContext().isPromotableIntegerType(Ty)) + return true; + + // In addition to the usual promotable integer types, we also need to + // extend all 32-bit types, since the ABI requires promotion to 64 bits. + if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Int: + case BuiltinType::UInt: + return true; + default: + break; + } + + return false; +} + +bool ZOSXPLinkABIInfo::isCompoundType(QualType Ty) const { + return (Ty->isAnyComplexType() || Ty->isVectorType() || + isAggregateTypeForABI(Ty)); +} + +bool ZOSXPLinkABIInfo::isVectorArgumentType(QualType Ty) const { + return (HasVector && Ty->isVectorType() && + getContext().getTypeSize(Ty) <= 128); +} + +bool ZOSXPLinkABIInfo::isFPArgumentType(QualType Ty) const { + if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + return true; + default: + return false; + } + + return false; +} + +QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const { + // Unions just containing a floating point type, e.g. union { float f1, f2; }; + // are treated as a single floating point number. Check if the union only + // consists of a single type (handling embedded unions recursively), and + // return that type. + if (const RecordType *RT = Ty->getAsUnionType()) { + QualType Found; + // Check the fields. + const RecordDecl *RD = RT->getDecl(); + for (const auto *FD : RD->fields()) { + if (Found.isNull()) + Found = getSingleElementType(FD->getType()); + else if (Found != getSingleElementType(FD->getType())) + return Ty; + } + return Found.isNull() ? Ty : Found; + } + + if (const RecordType *RT = Ty->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + QualType Found; + + // If this is a C++ class/struct, inspect its base classes first. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) + for (const auto &I : CXXRD->bases()) { + QualType Base = I.getType(); + + // Skip empty base classes, they cannot contribute a data member. + if (isEmptyRecord(getContext(), Base, true)) + continue; + + // A candidate base type was already found; encountering another non‑empty + // base means the choice is no longer unique. Return the type from the + // first candidate. + if (!Found.isNull()) + return Ty; + Found = getSingleElementType(Base); + } + + // Now inspect the record's own fields. We allow at most one field to + // contribute a single element type. If we've already recorded one + // candidate, encountering another field immediately disqualifies the + // record from being a single element aggregate. + for (const auto *FD : RD->fields()) { + if (!Found.isNull()) + return Ty; // more than one field, not a single-element + Found = getSingleElementType(FD->getType()); + } + return Found.isNull() ? Ty : Found; + } + return Ty; // not record/union, unchanged +} + +unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType Ty) const { + unsigned MaxAlign = 0; + clang::QualType Cur = Ty; + + while (true) { + if (auto *TypedefTy = dyn_cast<TypedefType>(Cur.getTypePtr())) { + auto *TyDecl = TypedefTy->getDecl(); + unsigned CurrAlign = TyDecl->getMaxAlignment(); + MaxAlign = std::max(CurrAlign, MaxAlign); + } + // Peel exactly one sugar layer (Typedef, Attributed, Paren, Elaborated, etc.). + clang::QualType Next = Cur.getSingleStepDesugaredType(getContext()); + if (Next == Cur) // no more sugar to peel + break; + Cur = Next; + } + return MaxAlign; +} + +std::optional<QualType> +ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const { + if (const RecordType *RT = Ty->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + + // Check for non-empty base classes. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) + if (CXXRD->hasDefinition()) + for (const auto &I : CXXRD->bases()) { + QualType Base = I.getType(); + if (!isEmptyRecord(getContext(), Base, true)) + return std::nullopt; + } + + // Check for exactly two elements with exactly the same floating point type. + // A single-element struct containing only a float, double, or long double + // counts as a field of that type. If the struct has one field consisting + // of a complex type, it does not count. This design may be somewhat + // inconsistent but it matches the behavior of the legacy C compiler. + int Count = 0; + clang::BuiltinType::Kind elemKind; + QualType RetTy; + for (const auto *FD : RD->fields()) { + if (Count >= 2) + return std::nullopt; + + unsigned MaxAlignOnDecl = FD->getMaxAlignment(); + QualType FT = FD->getType(); + QualType FTSingleTy = getSingleElementType(FT); + unsigned MaxAlign = + std::max(getMaxAlignFromTypeDefs(FTSingleTy), MaxAlignOnDecl); + + // The first element of a complex type may have an alignment enforced + // that is less strict than twice its size, since that would be naturally + // enforced by any complex type anyways. The second element may have an + // alignment enforced that is less strict than its size. + if (Count == 0) { + if (MaxAlign > 2 * getContext().getTypeSize(FTSingleTy)) + return std::nullopt; + } + else if (Count == 1) { + if (MaxAlign > getContext().getTypeSize(FTSingleTy)) + return std::nullopt; + } + + if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) { + switch (BT->getKind()) { + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + if (Count == 0) { + elemKind = BT->getKind(); + RetTy = FTSingleTy; + break; + } else if (elemKind == BT->getKind()) + break; + else + return std::nullopt; + default: + return std::nullopt; + } + } else + return std::nullopt; + + Count++; + fprintf(stderr, "Count increased to %d\n", Count); + } + + if (Count == 2) { + // The last thing that needs to be checked is the alignment of the struct. + // If we have to emit any padding (eg. because of attribute aligned), this + // disqualifies the type from being complex. + unsigned MaxAlign = RT->getDecl()->getMaxAlignment(); + unsigned ElemSize = getContext().getTypeSize(RetTy); + if (MaxAlign > 2 * ElemSize) + return std::nullopt; + return RetTy; + } + } + return std::nullopt; +} + +ABIArgInfo +ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, + unsigned CallConv) const { + + // Ignore void types. + if (RetTy->isVoidType()) + return ABIArgInfo::getIgnore(); + + // For non-C calling convention, indirect by value for structs and complex. + if ((CallConv != llvm::CallingConv::C) && + (isAggregateTypeForABI(RetTy) || RetTy->isAnyComplexType())) { + return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace()); + } + + // Vectors are returned directly. + if (isVectorArgumentType(RetTy)) + return ABIArgInfo::getDirect(); + + // Complex types are returned by value as per the XPLINK docs. + // Their members will be placed in FPRs. + if (RetTy->isAnyComplexType()) + return ABIArgInfo::getDirect(); + + // Complex LIKE structures are returned by value as per the XPLINK docs. + // Their members will be placed in FPRs. + if (RetTy->getAs<RecordType>()) { + if (getFPTypeOfComplexLikeType(RetTy)) + return ABIArgInfo::getDirect(); + } + + // Aggregates with a size of less than 3 GPRs are returned in GRPs 1, 2 and 3. + // Other aggregates are passed in memory as an implicit first parameter. + if (isAggregateTypeForABI(RetTy)) { + uint64_t AggregateTypeSize = getContext().getTypeSize(RetTy); + + if (AggregateTypeSize <= 3 * GPRBits) { + uint64_t NumElements = + AggregateTypeSize / GPRBits + (AggregateTypeSize % GPRBits != 0); + + // Types up to 8 bytes are passed as an integer type in GPR1. + // Types between 8 and 16 bytes are passed as integer types in GPR1, 2. + // Types between 16 and 24 bytes are passed as integer types in GPR1, 2 + // and 3. + llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); + CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements); + return ABIArgInfo::getDirectInReg(CoerceTy); + } else + return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace()); + } + + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = RetTy->getAs<EnumType>()) + RetTy = EnumTy->getDecl()->getIntegerType(); + + return (isPromotableIntegerType(RetTy) ? ABIArgInfo::getExtend(RetTy) + : ABIArgInfo::getDirect()); +} + +ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, + unsigned CallConv) const { + + // Handle the generic C++ ABI. + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) + return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), RAA == CGCXXABI::RAA_DirectInMemory); + + // Integers and enums are extended to full register width. + if (isPromotableIntegerType(Ty)) + return ABIArgInfo::getExtend(Ty); + + // For non-C calling conventions, compound types passed by address copy. + if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty)) + return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), + /*ByVal=*/false); + + // Complex types are passed by value as per the XPLINK docs. + // If place available, their members will be placed in FPRs. + auto CompTy = getFPTypeOfComplexLikeType(Ty); + if (IsNamedArg) { + if (Ty->isComplexType()) { + auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty)); + AI.setCanBeFlattened(false); + return AI; + } + + if (CompTy.has_value()) { + llvm::Type *FPTy = CGT.ConvertType(*CompTy); + llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy); + auto AI = ABIArgInfo::getDirectInReg(CoerceTy); + AI.setCanBeFlattened(false); + return AI; + } + } + + // Vectors are passed directly. + if (isVectorArgumentType(Ty)) + return ABIArgInfo::getDirect(); + + // Handle structures. They are returned by value. + // If not complex like types, they are passed in GPRs, if possible. + // If place available, complex like types will have their members + // placed in FPRs. + if (Ty->getAs<RecordType>() || Ty->isAnyComplexType() || CompTy.has_value()) { + if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType() || CompTy.has_value()) { + // Since an aggregate may end up in registers, pass the aggregate as + // array. This is usually beneficial since we avoid forcing the back-end + // to store the argument to memory. + uint64_t Bits = getContext().getTypeSize(Ty); + llvm::Type *CoerceTy; + + // Struct types up to 8 bytes are passed as integer type (which will be + // properly aligned in the argument save area doubleword). + if (Bits <= GPRBits) + CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); + // Larger types are passed as arrays, with the base type selected + // according to the required alignment in the save area. + else { + uint64_t NumRegs = llvm::alignTo(Bits, GPRBits) / GPRBits; + llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), GPRBits); + CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); + } + + return ABIArgInfo::getDirectInReg(CoerceTy); + } + + return ABIArgInfo::getDirectInReg(); + } + + // Non-structure compounds are passed indirectly, i.e. arrays. + if (isCompoundType(Ty)) + return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), /*ByVal=*/false); + + return ABIArgInfo::getDirect(); +} + +RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty, AggValueSlot Slot) const { + return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, + CGF.getContext().getTypeInfoInChars(Ty), + CGF.getPointerSize(), + /*allowHigherAlign*/ false, Slot); +} + std::unique_ptr<TargetCodeGenInfo> CodeGen::createSystemZTargetCodeGenInfo(CodeGenModule &CGM, bool HasVector, bool SoftFloatABI) { return std::make_unique<SystemZTargetCodeGenInfo>(CGM.getTypes(), HasVector, SoftFloatABI); } + +std::unique_ptr<TargetCodeGenInfo> +CodeGen::createSystemZ_ZOS_TargetCodeGenInfo(CodeGenModule &CGM, bool HasVector, + bool SoftFloatABI) { + return std::make_unique<ZOSXPLinkTargetCodeGenInfo>(CGM.getTypes(), + HasVector); +} diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c new file mode 100644 index 0000000000000..999b8f07814c1 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/zos-abi.c @@ -0,0 +1,414 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKNOVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC + +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \ +// RUN: -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC,CHECK-ZVEC + +// Scalar types + +signed char pass_schar(signed char arg) { return arg; } +// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}}) + +unsigned char pass_uchar(unsigned char arg) { return arg; } +// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}}) + +short pass_short(short arg) { return arg; } +// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}}) + +int pass_int(int arg) { return arg; } +// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}}) + +long pass_long(long arg) { return arg; } +// CHECK-LABEL: define i64 @pass_long(i64 %{{.*}}) + +long long pass_longlong(long long arg) { return arg; } +// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}}) + +float pass_float(float arg) { return arg; } +// CHECK-LABEL: define float @pass_float(float %{{.*}}) + +double pass_double(double arg) { return arg; } +// CHECK-LABEL: define double @pass_double(double %{{.*}}) + +long double pass_longdouble(long double arg) { return arg; } +// CHECK-LABEL: define fp128 @pass_longdouble(fp128 %{{.*}}) + +enum Color { Red, Blue }; +enum Color pass_enum(enum Color arg) { return arg; } +// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}}) + +#ifdef TEST_VEC +vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; +// CHECK-ZVEC-LABEL: define <4 x i32> @pass_vector(<4 x i32> %{{.*}}) + +struct SingleVec { vector unsigned int v; }; +struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; }; +// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] inreg %{{.*}}) +#endif + +// Complex types + +_Complex float pass_complex_float(_Complex float arg) { return arg; } +// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } inreg %{{.*}}) + +_Complex double pass_complex_double(_Complex double arg) { return arg; } +// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } inreg %{{.*}}) + +_Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } +// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } inreg %{{.*}}) + +// Verify that the following are complex-like types +struct complexlike_float { float re, im; }; +struct complexlike_float pass_complexlike_float2(struct complexlike_float arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ float, float } inreg %{{.*}}) + +struct complexlike_double { double re, im; }; +struct complexlike_double pass_complexlike_double(struct complexlike_double arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } inreg %{{.*}}) + +struct complexlike_longdouble { long double re, im; }; +struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_longdouble arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } inreg %{{.*}}) + +struct single_element_float { float f; }; +struct complexlike_struct { + struct single_element_float x; + struct single_element_float y; +}; +struct complexlike_struct pass_complexlike_struct(struct complexlike_struct arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ float, float } inreg %{{.*}}) + +struct single_element_float_arr { + unsigned int :0; + float f[1]; +}; +struct complexlike_struct2 { + struct single_element_float_arr x; + struct single_element_float_arr y; +}; +struct complexlike_struct2 pass_complexlike_struct2(struct complexlike_struct2 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct2(i64 inreg %{{.*}}) + +struct float_and_empties { + struct S {} s; + int a[0]; + float f; +}; +struct complexlike_struct3 { + struct float_and_empties x; + struct float_and_empties y; +}; +struct complexlike_struct3 pass_complexlike_struct3(struct complexlike_struct3 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 inreg %{{.*}}) + +union two_float_union { float a; float b; }; +struct complexlike_struct_with_union { + float a; + union two_float_union b; +}; +struct complexlike_struct_with_union pass_complexlike_struct_with_union(struct complexlike_struct_with_union arg) { return arg; } +// CHECK-LABEL: struct.complexlike_struct_with_union @pass_complexlike_struct_with_union({ float, float } inreg %{{.*}}) + +// structures with one field as complex type are not considered complex types. +struct single_complex_struct { + _Complex float f; +}; +struct single_complex_struct pass_single_complex_struct(struct single_complex_struct arg) {return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_single_complex_struct(i64 inreg %{{.*}}) + +// Structures with extra padding are not considered complex types. +struct complexlike_float_padded1 { + float x __attribute__((aligned(8))); + float y __attribute__((aligned(8))); +}; +struct complexlike_float_padded1 pass_complexlike_float_padded1(struct complexlike_float_padded1 arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded1([2 x i64] inreg %{{.*}}) + +struct complexlike_float_padded2 { + float x; + float y; +} __attribute__((aligned(16))); +struct complexlike_float_padded2 pass_complexlike_float_padded2(struct complexlike_float_padded2 arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded2([2 x i64] inreg %{{.*}}) + + +struct single_padded_struct { + float f; + unsigned int :2; +}; +struct complexlike_float_padded3 { + struct single_padded_struct x; + struct single_padded_struct y; +}; +struct complexlike_float_padded3 pass_complexlike_float_padded3(struct complexlike_float_padded3 arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded3([2 x i64] inreg %{{.*}}) + +struct multi_element_float_arr { float f[2]; }; +struct complexlike_struct4 { + struct multi_element_float_arr x; + struct multi_element_float_arr y; +}; +struct complexlike_struct4 pass_complexlike_struct4(struct complexlike_struct4 arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] inreg %{{.*}}) + +typedef double align32_double __attribute__((aligned(32))); +struct complexlike_double_padded { + align32_double x; + double y; +}; +struct complexlike_double_padded pass_complexlike_double_padded(struct complexlike_double_padded arg) { return arg; } +// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] inreg %{{.*}}) + +struct complexlike_double_padded2 { + __attribute__((aligned(256))) align32_double x; // attribute on both type and typedef + double y; +}; + +struct complexlike_double_padded2 pass_complexlike_double_padded2(struct complexlike_double_padded2 arg) { return arg; } +// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] inreg %{{.*}}) + +struct complexlike_double_padded3 { + __attribute__((aligned(32))) double x; // attribute on type only + double y; +}; +struct complexlike_double_padded3 pass_complexlike_double_padded3(struct complexlike_double_padded3 arg) { return arg; } +// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] inreg %{{.*}}) + + +// Aggregate types + +struct agg_1byte { char a[1]; }; +struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 inreg %{{.*}}) + +struct agg_2byte { char a[2]; }; +struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 inreg %{{.*}}) + +struct agg_3byte { char a[3]; }; +struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 inreg %{{.*}}) + +struct agg_4byte { char a[4]; }; +struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 inreg %{{.*}}) + +struct agg_5byte { char a[5]; }; +struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 inreg %{{.*}}) + +struct agg_6byte { char a[6]; }; +struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 inreg %{{.*}}) + +struct agg_7byte { char a[7]; }; +struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 inreg %{{.*}}) + +struct agg_8byte { char a[8]; }; +struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 inreg %{{.*}}) + +struct agg_9byte { char a[9]; }; +struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] inreg %{{.*}}) + +struct agg_16byte { char a[16]; }; +struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] inreg %{{.*}}) + +struct agg_24byte { char a[24]; }; +struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; } +// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] inreg %{{.*}}) + +struct agg_25byte { char a[25]; }; +struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; } +// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] inreg %{{.*}}) + +// Check that a float-like aggregate type is really passed as aggregate +struct agg_float { float a; }; +struct agg_float pass_agg_float(struct agg_float arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 inreg %{{.*}}) + +// Verify that the following are *not* float-like aggregate types + +struct agg_nofloat2 { float a; int b; }; +struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 inreg %{{.*}}) + +struct agg_nofloat3 { float a; int : 0; }; +struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 inreg %{{.*}}) + +char * pass_pointer(char * arg) { return arg; } +// CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}}) + +typedef int vecint __attribute__ ((vector_size(16))); +vecint pass_vector_type(vecint arg) { return arg; } +// CHECKNOVEC-LABEL: define <4 x i32> @pass_vector_type(ptr dead_on_return %{{.*}}) +// CHECKVEC-LABEL: define <4 x i32> @pass_vector_type(<4 x i32> %{{.*}}) + +// Union with just a single float element are treated as float inside a struct. +union u1 { + float m1, m2; +}; + +union u2 { + float m1; + union u1 m2; +}; + +union u3 { + float m1; + int m2; +}; + +struct complexlike_union1 { + float m1; + union u1 m2; +}; + +struct complexlike_union2 { + float m1; + union u2 m2; +}; + +struct complexlike_union3 { + union u1 m1; + union u2 m2; +}; + +struct normal_struct { + float m1; + union u3 m2; +}; + +struct complexlike_union1 pass_complexlike_union1(struct complexlike_union1 arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ float, float } inreg %{{.*}}) + +struct complexlike_union2 pass_complexlike_union2(struct complexlike_union2 arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ float, float } inreg %{{.*}}) + +struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ float, float } inreg %{{.*}}) + +union u1 pass_union1(union u1 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 inreg %{{.*}}) + +union u2 pass_union2(union u2 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 inreg %{{.*}}) + +struct normal_struct pass_normal_struct(struct normal_struct arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 inreg %{{.*}}) + +// ============================================================================ +// Complex-like struct using nested typedefs with alignment attributes +// applied at different typedef layers. +// ============================================================================ +typedef double raw_double; + +// ============================================================================ +// Alignment at first typedef layer +// ============================================================================ +typedef align32_double nested_A; + +struct S_A { + nested_A re; + double im; +}; + +struct S_A pass_S_A(struct S_A arg) { return arg; } +// CHECK-LABEL: define void @pass_S_A( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32 +// CHECK-SAME: [4 x i64] inreg +// CHECK: ret void + +// ============================================================================ +// Alignment applied at middle typedef layer +// ============================================================================ +typedef double raw_B; +typedef raw_B middle_B __attribute__((aligned(32))); +typedef middle_B nested_B; + +struct S_B { + double im; + nested_B re; +}; + +struct S_B pass_S_B(struct S_B arg) { return arg; } +// CHECK-LABEL: define void @pass_S_B( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32 +// CHECK-SAME: [8 x i64] inreg +// CHECK: ret void + +// ============================================================================ +// Alignment applied only at deepest typedef +// ============================================================================ +typedef float raw_C0; +typedef raw_C0 raw_C1; +typedef raw_C1 raw_C2; +typedef raw_C2 nested_C_final __attribute__((aligned(32))); + +struct S_C { + nested_C_final re; + float im; +}; + +struct S_C pass_S_C(struct S_C arg) { return arg; } +// CHECK-LABEL: define void @pass_S_C( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_C) align 32 +// CHECK-SAME: [4 x i64] inreg +// CHECK: ret void + +// ============================================================================ +// Alignment on an intermediate typedef in a deeper chain +// ============================================================================ +typedef float raw_D0; +typedef raw_D0 nested_D1; +typedef nested_D1 nested_D2 __attribute__((aligned(32))); +typedef nested_D2 nested_D3; +typedef nested_D3 nested_D; + +struct S_D { + float im; + nested_D re; +}; + +struct S_D pass_S_D(struct S_D arg) { return arg; } +// CHECK-LABEL: define void @pass_S_D( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_D) align 32 +// CHECK-SAME: [8 x i64] inreg +// CHECK: ret void + +// ============================================================================ +// NO alignment anywhere in the typedef chain +// Should NOT get 32-byte ABI alignment. +// ============================================================================ +typedef double nested_E1; +typedef nested_E1 nested_E2; +typedef nested_E2 nested_E; + +struct S_E { + nested_E re; + double im; +}; + +struct S_E pass_S_E(struct S_E arg) { return arg; } +// CHECK-LABEL: define %struct.S_E @pass_S_E( +// CHECK-SAME:{ double, double } inreg %{{.*}}) +// CHECK: ret %struct.S_E diff --git a/clang/test/CodeGen/SystemZ/zos-abi.cpp b/clang/test/CodeGen/SystemZ/zos-abi.cpp new file mode 100644 index 0000000000000..0578ddfcb17cd --- /dev/null +++ b/clang/test/CodeGen/SystemZ/zos-abi.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos -emit-llvm -no-enable-noundef-analysis -x c++ -o - %s | FileCheck %s + +// Verify that class types are recognized as float-like aggregate types and passed in GPR. + +class agg_float_class { float a; }; +class agg_float_class pass_agg_float_class(class agg_float_class arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z20pass_agg_float_class15agg_float_class(i64 inreg %arg.coerce) + +class agg_double_class { double a; }; +class agg_double_class pass_agg_double_class(class agg_double_class arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z21pass_agg_double_class16agg_double_class(i64 inreg %arg.coerce) + + +// This structure is passed in also in GPR. +struct agg_float_cpp { float a; int : 0; }; +struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z18pass_agg_float_cpp13agg_float_cpp(i64 inreg %arg.coerce) + +// In C++ a data member of empty class type makes the record nonhomogeneous, +// regardless if it's marked as [[no_unique_address]] or not. +struct empty { }; +struct agg_nofloat_empty { float a; empty dummy; }; +struct agg_nofloat_empty pass_agg_nofloat_empty(struct agg_nofloat_empty arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 inreg %arg.coerce) +struct complex_like_agg_nofloat_empty { struct agg_nofloat_empty a; struct agg_nofloat_empty b; }; +struct complex_like_agg_nofloat_empty pass_complex_like_agg_nofloat_empty(struct complex_like_agg_nofloat_empty arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x i64] inreg %{{.*}}) + +struct agg_float_empty { float a; [[no_unique_address]] empty dummy; }; +struct agg_float_empty pass_agg_float_empty(struct agg_float_empty arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z20pass_agg_float_empty15agg_float_empty(i64 inreg %arg.coerce) +struct complex_like_agg_float_empty { struct agg_float_empty a; struct agg_float_empty b; }; +struct complex_like_agg_float_empty pass_complex_like_agg_float_empty(struct complex_like_agg_float_empty arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 inreg %{{.*}}) + +struct agg_nofloat_emptyarray { float a; [[no_unique_address]] empty dummy[3]; }; +struct agg_nofloat_emptyarray pass_agg_nofloat_emptyarray(struct agg_nofloat_emptyarray arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 inreg %arg.coerce) + + +// And likewise for members of base classes. +struct noemptybase { empty dummy; }; +struct agg_nofloat_emptybase : noemptybase { float a; }; +struct agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct agg_nofloat_emptybase arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 inreg %arg.coerce) +struct complex_like_agg_nofloat_emptybase { struct agg_nofloat_emptybase a; struct agg_nofloat_emptybase b; }; +struct complex_like_agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct complex_like_agg_nofloat_emptybase arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] inreg %{{.*}}) + +struct emptybase { [[no_unique_address]] empty dummy; }; +struct agg_float_emptybase : emptybase { float a; }; +struct agg_float_emptybase pass_agg_float_emptybase(struct agg_float_emptybase arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 inreg %arg.coerce) +struct complex_like_agg_float_emptybase { struct agg_float_emptybase a; struct agg_float_emptybase b; }; +struct complex_like_agg_float_emptybase pass_agg_float_emptybase(struct complex_like_agg_float_emptybase arg) { return arg; } +// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase @_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float } inreg %{{.*}}) + +struct noemptybasearray { [[no_unique_address]] empty dummy[3]; }; +struct agg_nofloat_emptybasearray : noemptybasearray { float a; }; +struct agg_nofloat_emptybasearray pass_agg_nofloat_emptybasearray(struct agg_nofloat_emptybasearray arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 inreg %{{.*}}) + +using D = double; +using E = __attribute__((aligned(32))) D; // attribute inside the alias +struct complexlike_alias { + E x; // Using alias with attributed underlying type + double y; +}; +struct complexlike_alias pass_complexlike_alias(struct complexlike_alias arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_alias @_Z22pass_complexlike_alias17complexlike_alias({ double, double } inreg %{{.*}}) + +// ============================================================================ +// Complex-like struct using alignas specifier +// ============================================================================ + +struct S_A { + double re alignas(32);; + double im; +}; + +struct S_A pass_S_A(struct S_A arg) { return arg; } +// CHECK-LABEL: define void @_Z8pass_S_A3S_A( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32 +// CHECK-SAME: [4 x i64] inreg +// CHECK: ret void + +struct alignas(32) S_B { + double re; + double im; +}; + +struct S_B pass_S_B(struct S_B arg) { return arg; } +// CHECK-LABEL: define void @_Z8pass_S_B3S_B( +// CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32 +// CHECK-SAME: [4 x i64] inreg +// CHECK: ret void >From 908b45a9abef33837147dd8ea5cbaaee36a929f4 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Wed, 25 Mar 2026 16:05:06 +0000 Subject: [PATCH 2/7] fix formatting --- clang/lib/CodeGen/Targets/SystemZ.cpp | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 879e11ea4108d..309380862bc9f 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -555,8 +555,7 @@ class ZOSXPLinkABIInfo : public ABIInfo { bool HasVector; public: - ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) - : ABIInfo(CGT), HasVector(HV) {} + ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {} bool isPromotableIntegerType(QualType Ty) const; bool isCompoundType(QualType Ty) const; @@ -682,9 +681,9 @@ QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const { if (isEmptyRecord(getContext(), Base, true)) continue; - // A candidate base type was already found; encountering another non‑empty - // base means the choice is no longer unique. Return the type from the - // first candidate. + // A candidate base type was already found; encountering another + // non-empty base means the choice is no longer unique. Return the type + // from the first candidate. if (!Found.isNull()) return Ty; Found = getSingleElementType(Base); @@ -714,7 +713,8 @@ unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType Ty) const { unsigned CurrAlign = TyDecl->getMaxAlignment(); MaxAlign = std::max(CurrAlign, MaxAlign); } - // Peel exactly one sugar layer (Typedef, Attributed, Paren, Elaborated, etc.). + // Peel exactly one sugar layer (Typedef, Attributed, Paren, Elaborated, + // etc.). clang::QualType Next = Cur.getSingleStepDesugaredType(getContext()); if (Next == Cur) // no more sugar to peel break; @@ -762,8 +762,7 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const { if (Count == 0) { if (MaxAlign > 2 * getContext().getTypeSize(FTSingleTy)) return std::nullopt; - } - else if (Count == 1) { + } else if (Count == 1) { if (MaxAlign > getContext().getTypeSize(FTSingleTy)) return std::nullopt; } @@ -805,9 +804,8 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const { return std::nullopt; } -ABIArgInfo -ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, - unsigned CallConv) const { +ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, + unsigned CallConv) const { // Ignore void types. if (RetTy->isVoidType()) @@ -852,7 +850,8 @@ ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements); return ABIArgInfo::getDirectInReg(CoerceTy); } else - return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace()); + return getNaturalAlignIndirect(RetTy, + getDataLayout().getAllocaAddrSpace()); } // Treat an enum type as its underlying type. @@ -868,7 +867,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, // Handle the generic C++ ABI. if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) - return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), RAA == CGCXXABI::RAA_DirectInMemory); + return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), + RAA == CGCXXABI::RAA_DirectInMemory); // Integers and enums are extended to full register width. if (isPromotableIntegerType(Ty)) @@ -907,7 +907,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, // If place available, complex like types will have their members // placed in FPRs. if (Ty->getAs<RecordType>() || Ty->isAnyComplexType() || CompTy.has_value()) { - if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType() || CompTy.has_value()) { + if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType() || + CompTy.has_value()) { // Since an aggregate may end up in registers, pass the aggregate as // array. This is usually beneficial since we avoid forcing the back-end // to store the argument to memory. @@ -934,13 +935,14 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, // Non-structure compounds are passed indirectly, i.e. arrays. if (isCompoundType(Ty)) - return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), /*ByVal=*/false); + return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), + /*ByVal=*/false); return ABIArgInfo::getDirect(); } RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, - QualType Ty, AggValueSlot Slot) const { + QualType Ty, AggValueSlot Slot) const { return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, CGF.getContext().getTypeInfoInChars(Ty), CGF.getPointerSize(), >From 7d6d9b50084fd5bee96784f860686b9ba0a3fb14 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 6 Apr 2026 18:37:39 +0000 Subject: [PATCH 3/7] Remove inreg remker and address other comments --- clang/lib/CodeGen/Targets/SystemZ.cpp | 40 ++++-------- clang/test/CodeGen/SystemZ/zos-abi.c | 90 +++++++++++++------------- clang/test/CodeGen/SystemZ/zos-abi.cpp | 32 ++++----- 3 files changed, 75 insertions(+), 87 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 309380862bc9f..7f1c0faa7f606 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -557,10 +557,9 @@ class ZOSXPLinkABIInfo : public ABIInfo { public: ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {} - bool isPromotableIntegerType(QualType Ty) const; + bool isPromotableIntegerTypeForABI(QualType Ty) const; bool isCompoundType(QualType Ty) const; bool isVectorArgumentType(QualType Ty) const; - bool isFPArgumentType(QualType Ty) const; QualType getSingleElementType(QualType Ty) const; unsigned getMaxAlignFromTypeDefs(QualType Ty) const; std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const; @@ -603,15 +602,19 @@ class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo { // Return true if the ABI requires Ty to be passed sign- or zero- // extended to 64 bits. -bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const { +bool ZOSXPLinkABIInfo::isPromotableIntegerTypeForABI(QualType Ty) const { // Treat an enum type as its underlying type. if (const EnumType *EnumTy = Ty->getAs<EnumType>()) Ty = EnumTy->getDecl()->getIntegerType(); // Promotable integer types are required to be promoted by the ABI. - if (getContext().isPromotableIntegerType(Ty)) + if (ABIInfo::isPromotableIntegerTypeForABI(Ty)) return true; + if (const auto *EIT = Ty->getAs<BitIntType>()) + if (EIT->getNumBits() < 64) + return true; + // In addition to the usual promotable integer types, we also need to // extend all 32-bit types, since the ABI requires promotion to 64 bits. if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) @@ -636,20 +639,6 @@ bool ZOSXPLinkABIInfo::isVectorArgumentType(QualType Ty) const { getContext().getTypeSize(Ty) <= 128); } -bool ZOSXPLinkABIInfo::isFPArgumentType(QualType Ty) const { - if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) - switch (BT->getKind()) { - case BuiltinType::Float: - case BuiltinType::Double: - case BuiltinType::LongDouble: - return true; - default: - return false; - } - - return false; -} - QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const { // Unions just containing a floating point type, e.g. union { float f1, f2; }; // are treated as a single floating point number. Check if the union only @@ -787,7 +776,6 @@ ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const { return std::nullopt; Count++; - fprintf(stderr, "Count increased to %d\n", Count); } if (Count == 2) { @@ -848,7 +836,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, // and 3. llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements); - return ABIArgInfo::getDirectInReg(CoerceTy); + return ABIArgInfo::getDirect(CoerceTy); } else return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace()); @@ -858,7 +846,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, if (const EnumType *EnumTy = RetTy->getAs<EnumType>()) RetTy = EnumTy->getDecl()->getIntegerType(); - return (isPromotableIntegerType(RetTy) ? ABIArgInfo::getExtend(RetTy) + return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy) : ABIArgInfo::getDirect()); } @@ -871,7 +859,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, RAA == CGCXXABI::RAA_DirectInMemory); // Integers and enums are extended to full register width. - if (isPromotableIntegerType(Ty)) + if (isPromotableIntegerTypeForABI(Ty)) return ABIArgInfo::getExtend(Ty); // For non-C calling conventions, compound types passed by address copy. @@ -884,7 +872,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, auto CompTy = getFPTypeOfComplexLikeType(Ty); if (IsNamedArg) { if (Ty->isComplexType()) { - auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty)); + auto AI = ABIArgInfo::getDirect(CGT.ConvertType(Ty)); AI.setCanBeFlattened(false); return AI; } @@ -892,7 +880,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, if (CompTy.has_value()) { llvm::Type *FPTy = CGT.ConvertType(*CompTy); llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy); - auto AI = ABIArgInfo::getDirectInReg(CoerceTy); + auto AI = ABIArgInfo::getDirect(CoerceTy); AI.setCanBeFlattened(false); return AI; } @@ -927,10 +915,10 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); } - return ABIArgInfo::getDirectInReg(CoerceTy); + return ABIArgInfo::getDirect(CoerceTy); } - return ABIArgInfo::getDirectInReg(); + return ABIArgInfo::getDirect(); } // Non-structure compounds are passed indirectly, i.e. arrays. diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c index 999b8f07814c1..4e7bd8171bbff 100644 --- a/clang/test/CodeGen/SystemZ/zos-abi.c +++ b/clang/test/CodeGen/SystemZ/zos-abi.c @@ -58,32 +58,32 @@ vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; struct SingleVec { vector unsigned int v; }; struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; }; -// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] inreg %{{.*}}) +// CHECK-ZVEC-LABEL: define [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}}) #endif // Complex types _Complex float pass_complex_float(_Complex float arg) { return arg; } -// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } %{{.*}}) _Complex double pass_complex_double(_Complex double arg) { return arg; } -// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } inreg %{{.*}}) +// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } %{{.*}}) _Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } -// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } inreg %{{.*}}) +// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } %{{.*}}) // Verify that the following are complex-like types struct complexlike_float { float re, im; }; struct complexlike_float pass_complexlike_float2(struct complexlike_float arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float2({ float, float } %{{.*}}) struct complexlike_double { double re, im; }; struct complexlike_double pass_complexlike_double(struct complexlike_double arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } %{{.*}}) struct complexlike_longdouble { long double re, im; }; struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_longdouble arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } %{{.*}}) struct single_element_float { float f; }; struct complexlike_struct { @@ -91,7 +91,7 @@ struct complexlike_struct { struct single_element_float y; }; struct complexlike_struct pass_complexlike_struct(struct complexlike_struct arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ float, float } %{{.*}}) struct single_element_float_arr { unsigned int :0; @@ -102,7 +102,7 @@ struct complexlike_struct2 { struct single_element_float_arr y; }; struct complexlike_struct2 pass_complexlike_struct2(struct complexlike_struct2 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct2(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_complexlike_struct2(i64 %{{.*}}) struct float_and_empties { struct S {} s; @@ -114,7 +114,7 @@ struct complexlike_struct3 { struct float_and_empties y; }; struct complexlike_struct3 pass_complexlike_struct3(struct complexlike_struct3 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_complexlike_struct3(i64 %{{.*}}) union two_float_union { float a; float b; }; struct complexlike_struct_with_union { @@ -122,14 +122,14 @@ struct complexlike_struct_with_union { union two_float_union b; }; struct complexlike_struct_with_union pass_complexlike_struct_with_union(struct complexlike_struct_with_union arg) { return arg; } -// CHECK-LABEL: struct.complexlike_struct_with_union @pass_complexlike_struct_with_union({ float, float } inreg %{{.*}}) +// CHECK-LABEL: struct.complexlike_struct_with_union @pass_complexlike_struct_with_union({ float, float } %{{.*}}) // structures with one field as complex type are not considered complex types. struct single_complex_struct { _Complex float f; }; struct single_complex_struct pass_single_complex_struct(struct single_complex_struct arg) {return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_single_complex_struct(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_single_complex_struct(i64 %{{.*}}) // Structures with extra padding are not considered complex types. struct complexlike_float_padded1 { @@ -137,14 +137,14 @@ struct complexlike_float_padded1 { float y __attribute__((aligned(8))); }; struct complexlike_float_padded1 pass_complexlike_float_padded1(struct complexlike_float_padded1 arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded1([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded1([2 x i64] %{{.*}}) struct complexlike_float_padded2 { float x; float y; } __attribute__((aligned(16))); struct complexlike_float_padded2 pass_complexlike_float_padded2(struct complexlike_float_padded2 arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded2([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded2([2 x i64] %{{.*}}) struct single_padded_struct { @@ -156,7 +156,7 @@ struct complexlike_float_padded3 { struct single_padded_struct y; }; struct complexlike_float_padded3 pass_complexlike_float_padded3(struct complexlike_float_padded3 arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded3([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_complexlike_float_padded3([2 x i64] %{{.*}}) struct multi_element_float_arr { float f[2]; }; struct complexlike_struct4 { @@ -164,7 +164,7 @@ struct complexlike_struct4 { struct multi_element_float_arr y; }; struct complexlike_struct4 pass_complexlike_struct4(struct complexlike_struct4 arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_complexlike_struct4([2 x i64] %{{.*}}) typedef double align32_double __attribute__((aligned(32))); struct complexlike_double_padded { @@ -172,7 +172,7 @@ struct complexlike_double_padded { double y; }; struct complexlike_double_padded pass_complexlike_double_padded(struct complexlike_double_padded arg) { return arg; } -// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] inreg %{{.*}}) +// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] %{{.*}}) struct complexlike_double_padded2 { __attribute__((aligned(256))) align32_double x; // attribute on both type and typedef @@ -180,80 +180,80 @@ struct complexlike_double_padded2 { }; struct complexlike_double_padded2 pass_complexlike_double_padded2(struct complexlike_double_padded2 arg) { return arg; } -// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] inreg %{{.*}}) +// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] %{{.*}}) struct complexlike_double_padded3 { __attribute__((aligned(32))) double x; // attribute on type only double y; }; struct complexlike_double_padded3 pass_complexlike_double_padded3(struct complexlike_double_padded3 arg) { return arg; } -// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] inreg %{{.*}}) +// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] %{{.*}}) // Aggregate types struct agg_1byte { char a[1]; }; struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_1byte(i64 %{{.*}}) struct agg_2byte { char a[2]; }; struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_2byte(i64 %{{.*}}) struct agg_3byte { char a[3]; }; struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_3byte(i64 %{{.*}}) struct agg_4byte { char a[4]; }; struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_4byte(i64 %{{.*}}) struct agg_5byte { char a[5]; }; struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_5byte(i64 %{{.*}}) struct agg_6byte { char a[6]; }; struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_6byte(i64 %{{.*}}) struct agg_7byte { char a[7]; }; struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_7byte(i64 %{{.*}}) struct agg_8byte { char a[8]; }; struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_8byte(i64 %{{.*}}) struct agg_9byte { char a[9]; }; struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}}) struct agg_16byte { char a[16]; }; struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}}) struct agg_24byte { char a[24]; }; struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; } -// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}}) struct agg_25byte { char a[25]; }; struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] inreg %{{.*}}) +// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] %{{.*}}) // Check that a float-like aggregate type is really passed as aggregate struct agg_float { float a; }; struct agg_float pass_agg_float(struct agg_float arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_float(i64 %{{.*}}) // Verify that the following are *not* float-like aggregate types struct agg_nofloat2 { float a; int b; }; struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_nofloat2(i64 %{{.*}}) struct agg_nofloat3 { float a; int : 0; }; struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_agg_nofloat3(i64 %{{.*}}) char * pass_pointer(char * arg) { return arg; } // CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}}) @@ -299,22 +299,22 @@ struct normal_struct { }; struct complexlike_union1 pass_complexlike_union1(struct complexlike_union1 arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ float, float } %{{.*}}) struct complexlike_union2 pass_complexlike_union2(struct complexlike_union2 arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ float, float } %{{.*}}) struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ float, float } %{{.*}}) union u1 pass_union1(union u1 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_union1(i64 %{{.*}}) union u2 pass_union2(union u2 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_union2(i64 %{{.*}}) struct normal_struct pass_normal_struct(struct normal_struct arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @pass_normal_struct(i64 %{{.*}}) // ============================================================================ // Complex-like struct using nested typedefs with alignment attributes @@ -335,7 +335,7 @@ struct S_A { struct S_A pass_S_A(struct S_A arg) { return arg; } // CHECK-LABEL: define void @pass_S_A( // CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32 -// CHECK-SAME: [4 x i64] inreg +// CHECK-SAME: [4 x i64] // CHECK: ret void // ============================================================================ @@ -353,7 +353,7 @@ struct S_B { struct S_B pass_S_B(struct S_B arg) { return arg; } // CHECK-LABEL: define void @pass_S_B( // CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32 -// CHECK-SAME: [8 x i64] inreg +// CHECK-SAME: [8 x i64] // CHECK: ret void // ============================================================================ @@ -372,7 +372,7 @@ struct S_C { struct S_C pass_S_C(struct S_C arg) { return arg; } // CHECK-LABEL: define void @pass_S_C( // CHECK-SAME: ptr {{.*}} sret(%struct.S_C) align 32 -// CHECK-SAME: [4 x i64] inreg +// CHECK-SAME: [4 x i64] // CHECK: ret void // ============================================================================ @@ -392,7 +392,7 @@ struct S_D { struct S_D pass_S_D(struct S_D arg) { return arg; } // CHECK-LABEL: define void @pass_S_D( // CHECK-SAME: ptr {{.*}} sret(%struct.S_D) align 32 -// CHECK-SAME: [8 x i64] inreg +// CHECK-SAME: [8 x i64] // CHECK: ret void // ============================================================================ @@ -410,5 +410,5 @@ struct S_E { struct S_E pass_S_E(struct S_E arg) { return arg; } // CHECK-LABEL: define %struct.S_E @pass_S_E( -// CHECK-SAME:{ double, double } inreg %{{.*}}) +// CHECK-SAME:{ double, double } %{{.*}}) // CHECK: ret %struct.S_E diff --git a/clang/test/CodeGen/SystemZ/zos-abi.cpp b/clang/test/CodeGen/SystemZ/zos-abi.cpp index 0578ddfcb17cd..2e3abb71652ef 100644 --- a/clang/test/CodeGen/SystemZ/zos-abi.cpp +++ b/clang/test/CodeGen/SystemZ/zos-abi.cpp @@ -4,61 +4,61 @@ class agg_float_class { float a; }; class agg_float_class pass_agg_float_class(class agg_float_class arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z20pass_agg_float_class15agg_float_class(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z20pass_agg_float_class15agg_float_class(i64 %arg.coerce) class agg_double_class { double a; }; class agg_double_class pass_agg_double_class(class agg_double_class arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z21pass_agg_double_class16agg_double_class(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z21pass_agg_double_class16agg_double_class(i64 %arg.coerce) // This structure is passed in also in GPR. struct agg_float_cpp { float a; int : 0; }; struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z18pass_agg_float_cpp13agg_float_cpp(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z18pass_agg_float_cpp13agg_float_cpp(i64 %arg.coerce) // In C++ a data member of empty class type makes the record nonhomogeneous, // regardless if it's marked as [[no_unique_address]] or not. struct empty { }; struct agg_nofloat_empty { float a; empty dummy; }; struct agg_nofloat_empty pass_agg_nofloat_empty(struct agg_nofloat_empty arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z22pass_agg_nofloat_empty17agg_nofloat_empty(i64 %arg.coerce) struct complex_like_agg_nofloat_empty { struct agg_nofloat_empty a; struct agg_nofloat_empty b; }; struct complex_like_agg_nofloat_empty pass_complex_like_agg_nofloat_empty(struct complex_like_agg_nofloat_empty arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @_Z35pass_complex_like_agg_nofloat_empty30complex_like_agg_nofloat_empty([2 x i64] %{{.*}}) struct agg_float_empty { float a; [[no_unique_address]] empty dummy; }; struct agg_float_empty pass_agg_float_empty(struct agg_float_empty arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z20pass_agg_float_empty15agg_float_empty(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z20pass_agg_float_empty15agg_float_empty(i64 %arg.coerce) struct complex_like_agg_float_empty { struct agg_float_empty a; struct agg_float_empty b; }; struct complex_like_agg_float_empty pass_complex_like_agg_float_empty(struct complex_like_agg_float_empty arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @_Z33pass_complex_like_agg_float_empty28complex_like_agg_float_empty(i64 %{{.*}}) struct agg_nofloat_emptyarray { float a; [[no_unique_address]] empty dummy[3]; }; struct agg_nofloat_emptyarray pass_agg_nofloat_emptyarray(struct agg_nofloat_emptyarray arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z27pass_agg_nofloat_emptyarray22agg_nofloat_emptyarray(i64 %arg.coerce) // And likewise for members of base classes. struct noemptybase { empty dummy; }; struct agg_nofloat_emptybase : noemptybase { float a; }; struct agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct agg_nofloat_emptybase arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z26pass_agg_nofloat_emptybase21agg_nofloat_emptybase(i64 %arg.coerce) struct complex_like_agg_nofloat_emptybase { struct agg_nofloat_emptybase a; struct agg_nofloat_emptybase b; }; struct complex_like_agg_nofloat_emptybase pass_agg_nofloat_emptybase(struct complex_like_agg_nofloat_emptybase arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] inreg %{{.*}}) +// CHECK-LABEL: define [2 x i64] @_Z26pass_agg_nofloat_emptybase34complex_like_agg_nofloat_emptybase([2 x i64] %{{.*}}) struct emptybase { [[no_unique_address]] empty dummy; }; struct agg_float_emptybase : emptybase { float a; }; struct agg_float_emptybase pass_agg_float_emptybase(struct agg_float_emptybase arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 inreg %arg.coerce) +// CHECK-LABEL: define [1 x i64] @_Z24pass_agg_float_emptybase19agg_float_emptybase(i64 %arg.coerce) struct complex_like_agg_float_emptybase { struct agg_float_emptybase a; struct agg_float_emptybase b; }; struct complex_like_agg_float_emptybase pass_agg_float_emptybase(struct complex_like_agg_float_emptybase arg) { return arg; } -// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase @_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complex_like_agg_float_emptybase @_Z24pass_agg_float_emptybase32complex_like_agg_float_emptybase({ float, float } %{{.*}}) struct noemptybasearray { [[no_unique_address]] empty dummy[3]; }; struct agg_nofloat_emptybasearray : noemptybasearray { float a; }; struct agg_nofloat_emptybasearray pass_agg_nofloat_emptybasearray(struct agg_nofloat_emptybasearray arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 inreg %{{.*}}) +// CHECK-LABEL: define [1 x i64] @_Z31pass_agg_nofloat_emptybasearray26agg_nofloat_emptybasearray(i64 %{{.*}}) using D = double; using E = __attribute__((aligned(32))) D; // attribute inside the alias @@ -67,7 +67,7 @@ struct complexlike_alias { double y; }; struct complexlike_alias pass_complexlike_alias(struct complexlike_alias arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_alias @_Z22pass_complexlike_alias17complexlike_alias({ double, double } inreg %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_alias @_Z22pass_complexlike_alias17complexlike_alias({ double, double } %{{.*}}) // ============================================================================ // Complex-like struct using alignas specifier @@ -81,7 +81,7 @@ struct S_A { struct S_A pass_S_A(struct S_A arg) { return arg; } // CHECK-LABEL: define void @_Z8pass_S_A3S_A( // CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32 -// CHECK-SAME: [4 x i64] inreg +// CHECK-SAME: [4 x i64] // CHECK: ret void struct alignas(32) S_B { @@ -92,5 +92,5 @@ struct alignas(32) S_B { struct S_B pass_S_B(struct S_B arg) { return arg; } // CHECK-LABEL: define void @_Z8pass_S_B3S_B( // CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32 -// CHECK-SAME: [4 x i64] inreg +// CHECK-SAME: [4 x i64] // CHECK: ret void >From 51bafcf660b050c7185ff77c15cbdce18cdc084d Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 6 Apr 2026 19:32:36 +0000 Subject: [PATCH 4/7] Remove redundant inner if statement checking for isAggregateTypeForABI(Ty) --- clang/lib/CodeGen/Targets/SystemZ.cpp | 42 ++++++++++++--------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 7f1c0faa7f606..d1fc840f16f9d 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -847,7 +847,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, RetTy = EnumTy->getDecl()->getIntegerType(); return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy) - : ABIArgInfo::getDirect()); + : ABIArgInfo::getDirect()); } ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, @@ -895,30 +895,24 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, // If place available, complex like types will have their members // placed in FPRs. if (Ty->getAs<RecordType>() || Ty->isAnyComplexType() || CompTy.has_value()) { - if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType() || - CompTy.has_value()) { - // Since an aggregate may end up in registers, pass the aggregate as - // array. This is usually beneficial since we avoid forcing the back-end - // to store the argument to memory. - uint64_t Bits = getContext().getTypeSize(Ty); - llvm::Type *CoerceTy; - - // Struct types up to 8 bytes are passed as integer type (which will be - // properly aligned in the argument save area doubleword). - if (Bits <= GPRBits) - CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); - // Larger types are passed as arrays, with the base type selected - // according to the required alignment in the save area. - else { - uint64_t NumRegs = llvm::alignTo(Bits, GPRBits) / GPRBits; - llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), GPRBits); - CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); - } - - return ABIArgInfo::getDirect(CoerceTy); + // Since an aggregate may end up in registers, pass the aggregate as + // array. This is usually beneficial since we avoid forcing the back-end + // to store the argument to memory. + uint64_t Bits = getContext().getTypeSize(Ty); + llvm::Type *CoerceTy; + + // Struct types up to 8 bytes are passed as integer type (which will be + // properly aligned in the argument save area doubleword). + if (Bits <= GPRBits) + CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); + // Larger types are passed as arrays, with the base type selected + // according to the required alignment in the save area. + else { + uint64_t NumRegs = llvm::alignTo(Bits, GPRBits) / GPRBits; + llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), GPRBits); + CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); } - - return ABIArgInfo::getDirect(); + return ABIArgInfo::getDirect(CoerceTy); } // Non-structure compounds are passed indirectly, i.e. arrays. >From cbd43bdfb8e0cc30dc9050fc2c0b0d5e9d15fb3e Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 6 Apr 2026 20:49:36 +0000 Subject: [PATCH 5/7] 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 6856ecb9979dd1d223bababd26b062bc9b6a1ffd Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Tue, 7 Apr 2026 20:51:45 +0000 Subject: [PATCH 6/7] 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 98c6f65600553c774d8631f3c3d5889949dbf394 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Wed, 8 Apr 2026 20:18:01 +0000 Subject: [PATCH 7/7] 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
