[clang] [SystemZ][z/OS] Implement z/OS XPLINK ABI (PR #91384)
Fanbo Meng via cfe-commits
cfe-commits at lists.llvm.org
Tue May 14 10:47:07 PDT 2024
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.Meng at 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.Meng at 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.Meng at 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.Meng at 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.Meng at 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.Meng at 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.Meng at 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.Meng at 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).
More information about the cfe-commits
mailing list