[clang] [SystemZ][z/OS] Implement z/OS XPLINK ABI (PR #101024)
Ulrich Weigand via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 6 04:24:42 PDT 2024
================
@@ -532,9 +532,371 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty,
return false;
}
+//===----------------------------------------------------------------------===//
+// z/OS XPLINK ABI Implementation
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class ZOSXPLinkABIInfo : public ABIInfo {
+ const unsigned GPRBits = 64;
+ bool HasVector;
+
+public:
+ ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {}
+
+ bool isPromotableIntegerType(QualType Ty) const;
+ bool isVectorArgumentType(QualType Ty) const;
+ bool isFPArgumentType(QualType Ty) const;
+ QualType getSingleElementType(QualType Ty) const;
+ QualType getFPTypeOfComplexLikeType(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;
+ }
+ }
+
+ RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
+ AggValueSlot Slot) const override;
+};
+
+class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo {
+public:
+ ZOSXPLinkTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector)
+ : TargetCodeGenInfo(std::make_unique<ZOSXPLinkABIInfo>(CGT, HasVector)) {
+ SwiftInfo =
+ std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
+ }
+};
+
+} // namespace
+
+// Return true if the ABI requires Ty to be passed sign- or zero-
+// extended to 64 bits.
+bool ZOSXPLinkABIInfo::isPromotableIntegerType(QualType Ty) const {
+ // Treat an enum type as its underlying type.
+ if (const EnumType *EnumTy = Ty->getAs<EnumType>())
+ Ty = EnumTy->getDecl()->getIntegerType();
+
+ // Promotable integer types are required to be promoted by the ABI.
+ if (getContext().isPromotableIntegerType(Ty))
+ return true;
+
+ 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>())
+ switch (BT->getKind()) {
+ case BuiltinType::Int:
+ case BuiltinType::UInt:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool ZOSXPLinkABIInfo::isVectorArgumentType(QualType Ty) const {
+ return (HasVector && Ty->isVectorType() &&
+ getContext().getTypeSize(Ty) <= 128);
+}
+
+bool ZOSXPLinkABIInfo::isFPArgumentType(QualType Ty) const {
+ if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
+ switch (BT->getKind()) {
+ case BuiltinType::Float:
+ case BuiltinType::Double:
+ case BuiltinType::LongDouble:
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+QualType ZOSXPLinkABIInfo::getSingleElementType(QualType Ty) const {
+ // Unions just containing a floating point type, e.g. union { float f1, f2; };
+ // are treated as a single floating point number. Check if the union only
+ // consists of a single type (handling embedded unions recursively), and
+ // return that type.
+ if (const RecordType *RT = Ty->getAsUnionType()) {
+ QualType Found;
+ // Check the fields.
+ const RecordDecl *RD = RT->getDecl();
+ for (const auto *FD : RD->fields()) {
+ if (Found.isNull())
+ Found = getSingleElementType(FD->getType());
+ else if (Found != getSingleElementType(FD->getType()))
+ return Ty;
+ }
+ return Found.isNull() ? Ty : Found;
+ }
+
+ const RecordType *RT = Ty->getAs<RecordType>();
+
+ if (RT && RT->isStructureOrClassType()) {
+ 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))
+ if (CXXRD->hasDefinition())
+ 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()) {
+ QualType FT = FD->getType();
+
+ // Ignore empty fields.
+ if (isEmptyField(getContext(), FD, true))
+ continue;
+
+ if (!Found.isNull())
+ return Ty;
+
+ // Treat single element arrays as the element.
+ while (const ConstantArrayType *AT =
+ getContext().getAsConstantArrayType(FT)) {
+ if (AT->getZExtSize() != 1)
+ break;
+ FT = AT->getElementType();
+ }
+
+ Found = getSingleElementType(FT);
+ }
+
+ // Unlike isSingleElementStruct(), trailing padding is allowed.
+ if (!Found.isNull())
+ return Found;
+ }
+
+ return Ty;
+}
+
+QualType ZOSXPLinkABIInfo::getFPTypeOfComplexLikeType(QualType Ty) const {
+ if (const RecordType *RT = Ty->getAsStructureType()) {
+ const RecordDecl *RD = RT->getDecl();
+
+ // Check for non-empty base classes.
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+ if (CXXRD->hasDefinition())
+ for (const auto &I : CXXRD->bases()) {
+ QualType Base = I.getType();
+ if (!isEmptyRecord(getContext(), Base, true))
+ return QualType();
+ }
+
+ // Check for exactly two elements with exactly the same floating point type.
+ // A single-element struct containing only a float, double, or long double
+ // counts as a field of that type. If the struct has one field consisting
+ // of a complex type, it does not count. This design may be somewhat
+ // inconsistent but it matches the behavior of the legacy C compiler.
+ int Count = 0;
+ clang::BuiltinType::Kind ElemKind;
+ QualType RetTy;
+ for (const auto *FD : RD->fields()) {
+ if (Count >= 2)
+ return QualType();
+
+ QualType FTSingleTy = FD->getType();
+ if (isAggregateTypeForABI(FTSingleTy)) {
+ QualType Ty = getSingleElementType(FTSingleTy);
+ if (Ty.isNull())
+ return QualType();
+ FTSingleTy = Ty;
+ }
+
+ if (isFPArgumentType(FTSingleTy)) {
+ clang::BuiltinType::Kind Kind =
+ FTSingleTy->getAs<BuiltinType>()->getKind();
+ if (Count == 0) {
+ ElemKind = Kind;
+ RetTy = FTSingleTy;
+ } else if (ElemKind != Kind)
+ return QualType();
+ } else
+ return QualType();
+
+ Count++;
+ }
+ if (Count == 2) {
+ // The last thing that needs to be checked is the size of the struct.
+ // If we have to emit any padding (eg. because of attribute aligned), this
+ // disqualifies the type from being complex.
+ unsigned RecordSize = getContext().getTypeSize(RT);
+ unsigned ElemSize = getContext().getTypeSize(RetTy);
+ if (RecordSize > 2 * ElemSize)
+ return QualType();
+ return RetTy;
+ }
+ }
+ return QualType();
+}
+
+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>()) {
+ auto CompTy = getFPTypeOfComplexLikeType(RetTy);
+ if (!CompTy.isNull()) {
+ llvm::Type *FPTy = CGT.ConvertType(CompTy);
+ llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
+ auto AI = ABIArgInfo::getDirect(CoerceTy);
+ AI.setCanBeFlattened(false);
+ return AI;
+ }
+ }
+
+ // 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);
+ if (NumElements > 1)
+ CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
+ return ABIArgInfo::getDirectInReg(CoerceTy);
+ }
+ return getNaturalAlignIndirect(RetTy);
+ }
+
+ return (isPromotableIntegerType(RetTy) ? ABIArgInfo::getExtend(RetTy)
+ : ABIArgInfo::getDirect());
+}
+
+ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty,
+ bool IsNamedArg) const {
+ // Handle transparent union types.
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ // 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, CGT.ConvertType(Ty));
+
+ // Complex types are passed by value as per the XPLINK docs.
+ // If place available, their members will be placed in FPRs.
+ if (IsNamedArg) {
+ if (Ty->isComplexType()) {
+ auto AI = ABIArgInfo::getDirect(CGT.ConvertType(Ty));
+ AI.setCanBeFlattened(false);
+ return AI;
+ }
+
+ auto CompTy = getFPTypeOfComplexLikeType(Ty);
+ if (!CompTy.isNull()) {
+ llvm::Type *FPTy = CGT.ConvertType(CompTy);
+ llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
+ auto AI = ABIArgInfo::getDirect(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 (isAggregateTypeForABI(Ty) || Ty->isAnyComplexType()) {
+ // 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(), GPRBits);
+ } else if (Bits > 64)
+ return getNaturalAlignIndirect(Ty, /*ByVal=*/true);
----------------
uweigand wrote:
I don't understand this change. First of all, GPRBits is 64, so the last `else` branch now will never be used?
Also, this change now passes large struct via *pointer*. I think this is not correct for the XPLINK ABI, which requires structs to be passed directly in the argument list, with the first up to three words of the argument list passed in registers as usual.
https://github.com/llvm/llvm-project/pull/101024
More information about the cfe-commits
mailing list