[clang] [SystemZ][z/OS] Implement z/OS XPLINK ABI (PR #101024)

Abhina Sree via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 7 06:06:36 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);
----------------
abhina-sree wrote:

I was trying to implement the suggested change here https://github.com/llvm/llvm-project/pull/91384#discussion_r1692208155, I will revert it if it is incorrect for the XPLINK ABI

https://github.com/llvm/llvm-project/pull/101024


More information about the cfe-commits mailing list