[clang] [SystemZ][z/OS] Implement z/OS XPLINK ABI (PR #91384)
Kai Nacke via cfe-commits
cfe-commits at lists.llvm.org
Mon May 27 14:55:43 PDT 2024
================
@@ -529,9 +530,379 @@ bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty,
return false;
}
+//===----------------------------------------------------------------------===//
+// z/OS XPLINK ABI Implementation
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class ZOSXPLinkABIInfo : public ABIInfo {
+ const unsigned GPRBits = 64;
+ bool HasVector;
+
+public:
+ ZOSXPLinkABIInfo(CodeGenTypes &CGT, bool HV) : ABIInfo(CGT), HasVector(HV) {}
+
+ bool isPromotableIntegerType(QualType Ty) const;
+ bool isCompoundType(QualType Ty) const;
+ bool isVectorArgumentType(QualType Ty) const;
+ bool isFPArgumentType(QualType Ty) const;
+ QualType getSingleElementType(QualType Ty) const;
+ unsigned getMaxAlignFromTypeDefs(QualType Ty) const;
+ std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) const;
+
+ ABIArgInfo classifyReturnType(QualType RetTy) 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;
+
+ 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::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 {
+ 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;
+}
+
+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();
+
+ // Check for non-empty base classes.
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+ if (CXXRD->hasDefinition())
+ for (const auto &I : CXXRD->bases()) {
+ QualType Base = I.getType();
+ if (!isEmptyRecord(getContext(), Base, true))
+ return std::nullopt;
+ }
+
+ // Check for exactly two elements with exactly the same floating point type.
+ // A single-element struct containing only a float, double, or long double
+ // counts as a field of that type. If the struct has one field consisting
+ // of a complex type, it does not count. This design may be somewhat
+ // inconsistent but it matches the behavior of the legacy C compiler.
+ int Count = 0;
+ clang::BuiltinType::Kind elemKind;
+ QualType RetTy;
+ for (const auto *FD : RD->fields()) {
+ if (Count >= 2)
+ return std::nullopt;
+
+ QualType FT = FD->getType();
+ QualType FTSingleTy = getSingleElementType(FT);
+ if (getContext().getTypeSize(FTSingleTy) != getContext().getTypeSize(FT))
+ return std::nullopt;
+
+ if (const BuiltinType *BT = FTSingleTy->getAs<BuiltinType>()) {
+ switch (BT->getKind()) {
+ case BuiltinType::Float:
+ case BuiltinType::Double:
+ case BuiltinType::LongDouble:
+ if (Count == 0) {
+ elemKind = BT->getKind();
+ RetTy = FTSingleTy;
+ break;
+ } else if (elemKind == BT->getKind()) {
+ break;
+ } else {
+ return std::nullopt;
+ }
+ default:
+ return std::nullopt;
+ }
+ } else {
+ return std::nullopt;
+ }
+
+ Count++;
+ }
+ 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 std::nullopt;
+ return RetTy;
+ }
+ }
+ return std::nullopt;
+}
+
+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 (getFPTypeOfComplexLikeType(RetTy))
+ return ABIArgInfo::getDirect();
+ }
+
+ // Aggregates with a size of less than 3 GPRs are returned in GRPs 1, 2 and 3.
+ // Other aggregates are passed in memory as an implicit first parameter.
+ if (isAggregateTypeForABI(RetTy)) {
+ uint64_t AggregateTypeSize = getContext().getTypeSize(RetTy);
+
+ if (AggregateTypeSize <= 3 * GPRBits) {
+ uint64_t NumElements =
+ AggregateTypeSize / GPRBits + (AggregateTypeSize % GPRBits != 0);
+
+ // Types up to 8 bytes are passed as an integer type in GPR1.
+ // Types between 8 and 16 bytes are passed as integer types in GPR1, 2.
+ // Types between 16 and 24 bytes are passed as integer types in GPR1, 2
+ // and 3.
+ llvm::Type *CoerceTy = llvm::IntegerType::get(getVMContext(), GPRBits);
+ 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 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.
+ auto CompTy = getFPTypeOfComplexLikeType(Ty);
----------------
redstar wrote:
With the change proposed below applied, this can move inside the `if` on line 841.
https://github.com/llvm/llvm-project/pull/91384
More information about the cfe-commits
mailing list