https://github.com/fanbo-meng updated https://github.com/llvm/llvm-project/pull/91384
>From 7b40fa0aab937dfc0ab8db48ed93db1a5debef0b Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 7 May 2024 13:36:38 -0400 Subject: [PATCH 1/8] [SystemZ][z/OS] Implement z/OS XPLINK ABI The XPLINK calling convention is specified in the Language Environment Vendor Interface, chapter 22, (https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.cee/cee.htm) and in Redbook XPLink: OS/390 Extra Performance Linkage (http://www.redbooks.ibm.com/abstracts/sg245991.html?Open) --- clang/lib/CodeGen/CodeGenModule.cpp | 2 + clang/lib/CodeGen/TargetInfo.h | 4 + clang/lib/CodeGen/Targets/SystemZ.cpp | 317 ++++++++++++++++++++++++++ clang/test/CodeGen/zos-abi.c | 137 +++++++++++ 4 files changed, 460 insertions(+) create mode 100644 clang/test/CodeGen/zos-abi.c diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c8898ce196c1e..39491d699f6d2 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -241,6 +241,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 f242d9e36ed40..e15f9bdf39356 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -527,6 +527,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 deaafc85a3157..903e7391b314d 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -529,9 +529,326 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty, return false; } +//===----------------------------------------------------------------------===// +// z/OS XPLINK ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { + +class ZOSXPLinkABIInfo : public ABIInfo { + static 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; + bool IsLikeComplexType(QualType Ty) const; + + ABIArgInfo classifyReturnType(QualType RetTy) const; + ABIArgInfo classifyArgumentType(QualType ArgTy, bool IsNamedArg) const; + + void computeInfo(CGFunctionInfo &FI) const override { + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + + unsigned NumRequiredArgs = FI.getNumRequiredArgs(); + unsigned ArgNo = 0; + + for (auto &I : FI.arguments()) { + bool IsNamedArg = ArgNo < NumRequiredArgs; + I.info = classifyArgumentType(I.type, IsNamedArg); + ++ArgNo; + } + } + + Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) 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 { + if (const RecordType *RT = Ty->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + QualType Found; + + // If this is a C++ record, check the bases first. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) + for (const auto &I : CXXRD->bases()) { + QualType Base = I.getType(); + + // Empty bases don't affect things either way. + if (isEmptyRecord(getContext(), Base, true)) + continue; + + if (!Found.isNull()) + return Ty; + Found = GetSingleElementType(Base); + } + + // Check the fields. + for (const auto *FD : RD->fields()) { + // For compatibility with GCC, ignore empty bitfields in C++ mode. + // Unlike isSingleElementStruct(), empty structure and array fields + // do count. So do anonymous bitfields that aren't zero-sized. + if (getContext().getLangOpts().CPlusPlus && + FD->isZeroLengthBitField(getContext())) + continue; + + // Unlike isSingleElementStruct(), arrays do not count. + // Nested structures still do though. + if (!Found.isNull()) + return Ty; + Found = GetSingleElementType(FD->getType()); + } + + // Unlike isSingleElementStruct(), trailing padding is allowed. + // An 8-byte aligned struct s { float f; } is passed as a double. + if (!Found.isNull()) + return Found; + } + + return Ty; +} + +bool ZOSXPLinkABIInfo::IsLikeComplexType(QualType Ty) const { + if (const RecordType *RT = Ty->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + int i = 0; + clang::BuiltinType::Kind elemKind; + + // Check for exactly two elements with exactly the same floating point type. + for (const auto *FD : RD->fields()) { + if (i >= 2) + return false; + + QualType FT = FD->getType(); + if (const BuiltinType *BT = FT->getAs<BuiltinType>()) { + switch (BT->getKind()) { + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + if (i == 0) { + elemKind = BT->getKind(); + break; + } else if (elemKind == BT->getKind()) + break; + else + return false; + default: + return false; + } + } else + return false; + + i++; + } + + return i == 2; + } + return false; +} + +ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { + + // Ignore void types. + if (RetTy->isVoidType()) + return ABIArgInfo::getIgnore(); + + // 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 (IsLikeComplexType(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); + } + + // 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) const { + // Handle the generic C++ ABI. + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) + return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory); + + // Integers and enums are extended to full register width. + if (isPromotableIntegerType(Ty)) + return ABIArgInfo::getExtend(Ty); + + // Complex types are passed by value as per the XPLINK docs. + // If place available, their members will be placed in FPRs. + if (Ty->isAnyComplexType() && IsNamedArg) + return ABIArgInfo::getDirect(); + + // Handle vector types and vector-like structure types. Note that + // as opposed to float-like structure types, we do not allow any + // padding for vector-like structures, so verify the sizes match. + uint64_t Size = getContext().getTypeSize(Ty); + QualType SingleElementTy = GetSingleElementType(Ty); + if (isVectorArgumentType(SingleElementTy) && + getContext().getTypeSize(SingleElementTy) == Size) + return ABIArgInfo::getDirect(CGT.ConvertType(SingleElementTy)); + + // 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()) { + if (IsLikeComplexType(Ty) && IsNamedArg) + return ABIArgInfo::getDirect(); + + if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType()) { + // MVS64 alligns on 8 bytes. + uint64_t ABIAlign = CharUnits::fromQuantity(8).getQuantity(); + const uint64_t RegBits = ABIAlign * 8; + + // 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(), RegBits); + // 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, RegBits) / RegBits; + llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), RegBits); + 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, /*ByVal=*/false); + + return ABIArgInfo::getDirect(); +} + +Address ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const { + return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, + CGF.getContext().getTypeInfoInChars(Ty), + CharUnits::fromQuantity(8), + /*allowHigherAlign*/ false); +} + 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/zos-abi.c b/clang/test/CodeGen/zos-abi.c new file mode 100644 index 0000000000000..9c2fb1308523d --- /dev/null +++ b/clang/test/CodeGen/zos-abi.c @@ -0,0 +1,137 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s + +// Scalar types + +char pass_char(char arg) { return arg; } +// CHECK-LABEL: define signext i8 @pass_char(i8 signext %{{.*}}) + +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 %{{.*}}) + +// Complex types + +// TODO: Add tests for complex integer types + +_Complex float pass_complex_float(_Complex float arg) { return arg; } +// 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 %{{.*}}) + +_Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } +// 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_float(struct complexlike_float arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float(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 %{{.*}}) + +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 %{{.*}}) + +// 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 %{{.*}}) >From d5a673b42c8ae9f89f018bf04e9720ebf7df0d4a Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 7 May 2024 15:41:08 -0400 Subject: [PATCH 2/8] apply clang-format --- clang/lib/CodeGen/Targets/SystemZ.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 903e7391b314d..1eef5aa83e6e7 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -540,8 +540,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; @@ -575,9 +574,9 @@ 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); - } + SwiftInfo = + std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false); + } }; } // namespace >From b79c247abbc34d79e52d7239348fd49cb3666bbe Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Wed, 8 May 2024 09:43:57 -0400 Subject: [PATCH 3/8] improve format; remove redundant code; Do not use 1 element array for return type --- clang/lib/CodeGen/Targets/SystemZ.cpp | 25 ++++++++++++------------- clang/test/CodeGen/zos-abi.c | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 1eef5aa83e6e7..fd35161498031 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -694,15 +694,17 @@ bool ZOSXPLinkABIInfo::IsLikeComplexType(QualType Ty) const { if (i == 0) { elemKind = BT->getKind(); break; - } else if (elemKind == BT->getKind()) + } else if (elemKind == BT->getKind()) { break; - else + } else { return false; + } default: return false; } - } else + } else { return false; + } i++; } @@ -748,16 +750,13 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { // 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); + if (NumElements > 1) + CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements); return ABIArgInfo::getDirectInReg(CoerceTy); - } else - return getNaturalAlignIndirect(RetTy); + } + return getNaturalAlignIndirect(RetTy); } - // 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()); } @@ -805,13 +804,13 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, uint64_t Bits = getContext().getTypeSize(Ty); llvm::Type *CoerceTy; - // Struct types up to 8 bytes are passed as integer type (which will be + // 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) + if (Bits <= GPRBits) { CoerceTy = llvm::IntegerType::get(getVMContext(), RegBits); // Larger types are passed as arrays, with the base type selected // according to the required alignment in the save area. - else { + } else { uint64_t NumRegs = llvm::alignTo(Bits, RegBits) / RegBits; llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), RegBits); CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); diff --git a/clang/test/CodeGen/zos-abi.c b/clang/test/CodeGen/zos-abi.c index 9c2fb1308523d..0c6532d2a1fd6 100644 --- a/clang/test/CodeGen/zos-abi.c +++ b/clang/test/CodeGen/zos-abi.c @@ -75,35 +75,35 @@ struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_lon 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 inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg i64 @pass_agg_8byte(i64 inreg %{{.*}}) struct agg_9byte { char a[9]; }; struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; } @@ -124,14 +124,14 @@ 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 inreg [1 x i64] @pass_agg_float(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg 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 %{{.*}}) +// CHECK-LABEL: define inreg i64 @pass_agg_nofloat3(i64 inreg %{{.*}}) >From a249ac36960569c43cc63d0f3262398e16b42c52 Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Wed, 8 May 2024 09:55:53 -0400 Subject: [PATCH 4/8] Make formatter happy, fix typo --- clang/lib/CodeGen/Targets/SystemZ.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index fd35161498031..70127851ff3ea 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -736,7 +736,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { return ABIArgInfo::getDirect(); } - // Aggregates with a size of less than 3 GPRs are returned in GRPs 1, 2 and 3. + // Aggregates with a size of less than 3 GPRs are returned in GPRs 1, 2 and 3. // Other aggregates are passed in memory as an implicit first parameter. if (isAggregateTypeForABI(RetTy)) { uint64_t AggregateTypeSize = getContext().getTypeSize(RetTy); @@ -804,13 +804,13 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, 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) { + // Struct types up to 8 bytes are passed as integer type (which will be + // properly aligned in the argument save area doubleword). CoerceTy = llvm::IntegerType::get(getVMContext(), RegBits); - // Larger types are passed as arrays, with the base type selected - // according to the required alignment in the save area. } else { + // Larger types are passed as arrays, with the base type selected + // according to the required alignment in the save area. uint64_t NumRegs = llvm::alignTo(Bits, RegBits) / RegBits; llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), RegBits); CoerceTy = llvm::ArrayType::get(RegTy, NumRegs); >From b9e4ae074598de071745d3118b6e4f72a4cd21ef Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 14 May 2024 13:10:36 -0400 Subject: [PATCH 5/8] Addressing comments --- clang/lib/CodeGen/Targets/SystemZ.cpp | 184 ++++++++++++++++++-------- clang/test/CodeGen/zos-abi.c | 64 +++++---- 2 files changed, 171 insertions(+), 77 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 70127851ff3ea..4f5de20e860ab 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -10,6 +10,7 @@ #include "TargetInfo.h" #include "clang/Basic/Builtins.h" #include "llvm/IR/IntrinsicsS390.h" +#include <optional> using namespace clang; using namespace clang::CodeGen; @@ -536,7 +537,7 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty, namespace { class ZOSXPLinkABIInfo : public ABIInfo { - static const unsigned GPRBits = 64; + const unsigned GPRBits = 64; bool HasVector; public: @@ -547,21 +548,26 @@ class ZOSXPLinkABIInfo : public ABIInfo { bool isVectorArgumentType(QualType Ty) const; bool isFPArgumentType(QualType Ty) const; QualType GetSingleElementType(QualType Ty) const; - bool IsLikeComplexType(QualType Ty) const; + unsigned getMaxAlignFromTypeDefs(QualType Ty) const; + std::optional<QualType> GetFPTypeOfComplexLikeType(QualType Ty) const; - ABIArgInfo classifyReturnType(QualType RetTy) const; - ABIArgInfo classifyArgumentType(QualType ArgTy, bool IsNamedArg) 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.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); + I.info = + classifyArgumentType(I.type, IsNamedArg, FI.getCallingConvention()); ++ArgNo; } } @@ -592,6 +598,10 @@ bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const { if (getContext().isPromotableIntegerType(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>()) @@ -651,7 +661,6 @@ QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { // Check the fields. for (const auto *FD : RD->fields()) { - // For compatibility with GCC, ignore empty bitfields in C++ mode. // Unlike isSingleElementStruct(), empty structure and array fields // do count. So do anonymous bitfields that aren't zero-sized. if (getContext().getLangOpts().CPlusPlus && @@ -666,7 +675,6 @@ QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { } // Unlike isSingleElementStruct(), trailing padding is allowed. - // An 8-byte aligned struct s { float f; } is passed as a double. if (!Found.isNull()) return Found; } @@ -674,52 +682,117 @@ QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { return Ty; } -bool ZOSXPLinkABIInfo::IsLikeComplexType(QualType Ty) const { +unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType Ty) const { + unsigned MaxAlign = 0; + while (Ty != Ty.getSingleStepDesugaredType(getContext())) { + auto *DesugaredType = + Ty.getSingleStepDesugaredType(getContext()).getTypePtr(); + if (auto *TypedefTy = dyn_cast<TypedefType>(DesugaredType)) { + auto *TyDecl = TypedefTy->getDecl(); + unsigned CurrAlign = TyDecl->getMaxAlignment(); + MaxAlign = std::max(CurrAlign, MaxAlign); + } + Ty = Ty.getSingleStepDesugaredType(getContext()); + } + return MaxAlign; +} + +std::optional<QualType> +ZOSXPLinkABIInfo::GetFPTypeOfComplexLikeType(QualType Ty) const { if (const RecordType *RT = Ty->getAsStructureType()) { const RecordDecl *RD = RT->getDecl(); - int i = 0; - clang::BuiltinType::Kind elemKind; + + // 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 (i >= 2) - return false; + if (Count >= 2) + return std::nullopt; + unsigned MaxAlignOnDecl = FD->getMaxAlignment(); QualType FT = FD->getType(); - if (const BuiltinType *BT = FT->getAs<BuiltinType>()) { + 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 (i == 0) { + if (Count == 0) { elemKind = BT->getKind(); + RetTy = FTSingleTy; break; } else if (elemKind == BT->getKind()) { break; } else { - return false; + return std::nullopt; } default: - return false; + return std::nullopt; } } else { - return false; + return std::nullopt; } - i++; + 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 i == 2; } - return false; + return std::nullopt; } -ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { +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); + } + // Vectors are returned directly. if (isVectorArgumentType(RetTy)) return ABIArgInfo::getDirect(); @@ -732,11 +805,11 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { // Complex LIKE structures are returned by value as per the XPLINK docs. // Their members will be placed in FPRs. if (RetTy->getAs<RecordType>()) { - if (IsLikeComplexType(RetTy)) + if (GetFPTypeOfComplexLikeType(RetTy)) return ABIArgInfo::getDirect(); } - // Aggregates with a size of less than 3 GPRs are returned in GPRs 1, 2 and 3. + // 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); @@ -761,8 +834,8 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy) const { : ABIArgInfo::getDirect()); } -ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, - bool IsNamedArg) const { +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, RAA == CGCXXABI::RAA_DirectInMemory); @@ -771,55 +844,62 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, 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, /*ByVal=*/false); + // Complex types are passed by value as per the XPLINK docs. // If place available, their members will be placed in FPRs. - if (Ty->isAnyComplexType() && IsNamedArg) - return ABIArgInfo::getDirect(); + auto CompTy = GetFPTypeOfComplexLikeType(Ty); + if (IsNamedArg) { + if (Ty->isComplexType()) { + auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty)); + AI.setCanBeFlattened(false); + return AI; + } - // Handle vector types and vector-like structure types. Note that - // as opposed to float-like structure types, we do not allow any - // padding for vector-like structures, so verify the sizes match. - uint64_t Size = getContext().getTypeSize(Ty); - QualType SingleElementTy = GetSingleElementType(Ty); - if (isVectorArgumentType(SingleElementTy) && - getContext().getTypeSize(SingleElementTy) == Size) - return ABIArgInfo::getDirect(CGT.ConvertType(SingleElementTy)); + 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()) { - if (IsLikeComplexType(Ty) && IsNamedArg) - return ABIArgInfo::getDirect(); - - if (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType()) { - // MVS64 alligns on 8 bytes. - uint64_t ABIAlign = CharUnits::fromQuantity(8).getQuantity(); - const uint64_t RegBits = ABIAlign * 8; - + 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; + if (Bits <= GPRBits) { // Struct types up to 8 bytes are passed as integer type (which will be // properly aligned in the argument save area doubleword). - CoerceTy = llvm::IntegerType::get(getVMContext(), RegBits); + CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits); } else { // Larger types are passed as arrays, with the base type selected // according to the required alignment in the save area. - uint64_t NumRegs = llvm::alignTo(Bits, RegBits) / RegBits; - llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), RegBits); + 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::getDirect(CoerceTy); } - return ABIArgInfo::getDirectInReg(); + return ABIArgInfo::getDirect(); } // Non-structure compounds are passed indirectly, i.e. arrays. @@ -833,7 +913,7 @@ Address ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty) const { return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, CGF.getContext().getTypeInfoInChars(Ty), - CharUnits::fromQuantity(8), + CGF.getPointerSize(), /*allowHigherAlign*/ false); } diff --git a/clang/test/CodeGen/zos-abi.c b/clang/test/CodeGen/zos-abi.c index 0c6532d2a1fd6..cee4f05c42381 100644 --- a/clang/test/CodeGen/zos-abi.c +++ b/clang/test/CodeGen/zos-abi.c @@ -15,10 +15,17 @@ // RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \ // RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \ +// RUN: -o - %s | FileCheck --check-prefixes=CHECKVEC %s + // Scalar types -char pass_char(char arg) { return arg; } -// CHECK-LABEL: define signext i8 @pass_char(i8 signext %{{.*}}) +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 %{{.*}}) @@ -45,93 +52,100 @@ enum Color { Red, Blue }; enum Color pass_enum(enum Color arg) { return arg; } // CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}}) -// Complex types +#ifdef TEST_VEC +vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; +// CHECKVEC-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; }; +// CHECKVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}}) +#endif -// TODO: Add tests for complex integer types +// Complex types _Complex float pass_complex_float(_Complex float arg) { return arg; } -// CHECK-LABEL: define { float, float } @pass_complex_float(float %{{.*}}, float %{{.*}}) +// 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 %{{.*}}) +// 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 %{{.*}}) +// 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_float(struct complexlike_float arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float(float %{{.*}}, float %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float({ 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 %{{.*}}) +// 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 %{{.*}}) +// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } 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 i64 @pass_agg_1byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_2byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_3byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_4byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_5byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_6byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_7byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_8byte(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 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 inreg [2 x i64] @pass_agg_16byte([2 x i64] inreg %{{.*}}) +// 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 inreg [3 x i64] @pass_agg_24byte([3 x i64] inreg %{{.*}}) +// 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; } -// 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 i64 @pass_agg_float(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_nofloat2(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg 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 i64 @pass_agg_nofloat3(i64 inreg %{{.*}}) +// CHECK-LABEL: define inreg i64 @pass_agg_nofloat3(i64 %{{.*}}) >From f37cfeee8b364d4a86dae7cc71a8e3dd6904060b Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 14 May 2024 13:14:21 -0400 Subject: [PATCH 6/8] fix coding style --- clang/lib/CodeGen/Targets/SystemZ.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 4f5de20e860ab..68571b241be42 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -547,9 +547,9 @@ class ZOSXPLinkABIInfo : public ABIInfo { bool isCompoundType(QualType Ty) const; bool isVectorArgumentType(QualType Ty) const; bool isFPArgumentType(QualType Ty) const; - QualType GetSingleElementType(QualType Ty) const; + QualType getSingleElementType(QualType Ty) const; unsigned getMaxAlignFromTypeDefs(QualType Ty) const; - std::optional<QualType> GetFPTypeOfComplexLikeType(QualType Ty) const; + std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const; ABIArgInfo classifyReturnType(QualType RetTy, unsigned functionCallConv) const; @@ -640,7 +640,7 @@ bool ZOSXPLinkABIInfo::isFPArgumentType(QualType Ty) const { return false; } -QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { +QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const { if (const RecordType *RT = Ty->getAsStructureType()) { const RecordDecl *RD = RT->getDecl(); QualType Found; @@ -656,7 +656,7 @@ QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { if (!Found.isNull()) return Ty; - Found = GetSingleElementType(Base); + Found = getSingleElementType(Base); } // Check the fields. @@ -671,7 +671,7 @@ QualType ZOSXPLinkABIInfo::GetSingleElementType(QualType Ty) const { // Nested structures still do though. if (!Found.isNull()) return Ty; - Found = GetSingleElementType(FD->getType()); + Found = getSingleElementType(FD->getType()); } // Unlike isSingleElementStruct(), trailing padding is allowed. @@ -698,7 +698,7 @@ unsigned ZOSXPLinkABIInfo::getMaxAlignFromTypeDefs(QualType Ty) const { } std::optional<QualType> -ZOSXPLinkABIInfo::GetFPTypeOfComplexLikeType(QualType Ty) const { +ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const { if (const RecordType *RT = Ty->getAsStructureType()) { const RecordDecl *RD = RT->getDecl(); @@ -725,7 +725,7 @@ ZOSXPLinkABIInfo::GetFPTypeOfComplexLikeType(QualType Ty) const { unsigned MaxAlignOnDecl = FD->getMaxAlignment(); QualType FT = FD->getType(); - QualType FTSingleTy = GetSingleElementType(FT); + QualType FTSingleTy = getSingleElementType(FT); unsigned MaxAlign = std::max(getMaxAlignFromTypeDefs(FTSingleTy), MaxAlignOnDecl); @@ -805,7 +805,7 @@ ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy, // 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)) + if (getFPTypeOfComplexLikeType(RetTy)) return ABIArgInfo::getDirect(); } @@ -850,7 +850,7 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, // 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); + auto CompTy = getFPTypeOfComplexLikeType(Ty); if (IsNamedArg) { if (Ty->isComplexType()) { auto AI = ABIArgInfo::getDirectInReg(CGT.ConvertType(Ty)); >From cffe40a198e2a9f78df5c9295c56237ce448bf19 Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 14 May 2024 13:43:56 -0400 Subject: [PATCH 7/8] add tests for unnamed arguments --- clang/test/CodeGen/zos-abi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang/test/CodeGen/zos-abi.c b/clang/test/CodeGen/zos-abi.c index cee4f05c42381..1fee351475b34 100644 --- a/clang/test/CodeGen/zos-abi.c +++ b/clang/test/CodeGen/zos-abi.c @@ -85,6 +85,17 @@ 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 %{{.*}}) +// Unnamed types + +int pass_unnamed_int(int) { return 0; } +// CHECK-LABEL: define signext i32 @pass_unnamed_int(i32 signext %{{.*}}) + +signed char pass_unnamed_schar(signed char) { return '0'; } +// CHECK-LABEL: define signext i8 @pass_unnamed_schar(i8 signext %{{.*}}) + +long double pass_unnamed_longdouble(long double) { return 0; } +// CHECK-LABEL: define fp128 @pass_unnamed_longdouble(fp128 %{{.*}}) + // Aggregate types struct agg_1byte { char a[1]; }; >From 23ba3ee77914830a5cb8c5d5a1ce6fe2c558a994 Mon Sep 17 00:00:00 2001 From: Fanbo Meng <fanbo.m...@ibm.com> Date: Tue, 14 May 2024 13:46:52 -0400 Subject: [PATCH 8/8] clang-format --- clang/lib/CodeGen/Targets/SystemZ.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 68571b241be42..d420286c71c16 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -736,8 +736,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; } @@ -779,9 +778,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()) @@ -876,14 +874,14 @@ 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. uint64_t Bits = getContext().getTypeSize(Ty); llvm::Type *CoerceTy; - if (Bits <= GPRBits) { // Struct types up to 8 bytes are passed as integer type (which will be // properly aligned in the argument save area doubleword). _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits