CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
923 lines
34 KiB
C++
923 lines
34 KiB
C++
//===- SystemZ.cpp --------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ABIInfoImpl.h"
|
|
#include "TargetInfo.h"
|
|
#include "clang/Basic/Builtins.h"
|
|
#include "llvm/IR/IntrinsicsS390.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::CodeGen;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SystemZ ABI Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class SystemZABIInfo : public ABIInfo {
|
|
bool HasVector;
|
|
bool IsSoftFloatABI;
|
|
|
|
public:
|
|
SystemZABIInfo(CodeGenTypes &CGT, bool HV, bool SF)
|
|
: ABIInfo(CGT), HasVector(HV), IsSoftFloatABI(SF) {}
|
|
|
|
bool isPromotableIntegerTypeForABI(QualType Ty) const;
|
|
bool isCompoundType(QualType Ty) const;
|
|
bool isVectorArgumentType(QualType Ty) const;
|
|
llvm::Type *getFPArgumentType(QualType Ty, uint64_t Size) const;
|
|
QualType GetSingleElementType(QualType Ty) const;
|
|
|
|
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
|
ABIArgInfo classifyArgumentType(QualType ArgTy) const;
|
|
|
|
void computeInfo(CGFunctionInfo &FI) const override;
|
|
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
|
|
AggValueSlot Slot) const override;
|
|
};
|
|
|
|
class SystemZTargetCodeGenInfo : public TargetCodeGenInfo {
|
|
ASTContext &Ctx;
|
|
|
|
// These are used for speeding up the search for a visible vector ABI.
|
|
mutable bool HasVisibleVecABIFlag = false;
|
|
mutable std::set<const Type *> SeenTypes;
|
|
|
|
// Returns true (the first time) if Ty is, or is found to include, a vector
|
|
// type that exposes the vector ABI. This is any vector >=16 bytes which
|
|
// with vector support are aligned to only 8 bytes. When IsParam is true,
|
|
// the type belongs to a value as passed between functions. If it is a
|
|
// vector <=16 bytes it will be passed in a vector register (if supported).
|
|
bool isVectorTypeBased(const Type *Ty, bool IsParam) const;
|
|
|
|
public:
|
|
SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector, bool SoftFloatABI)
|
|
: TargetCodeGenInfo(
|
|
std::make_unique<SystemZABIInfo>(CGT, HasVector, SoftFloatABI)),
|
|
Ctx(CGT.getContext()) {
|
|
SwiftInfo =
|
|
std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
|
|
}
|
|
|
|
// The vector ABI is different when the vector facility is present and when
|
|
// a module e.g. defines an externally visible vector variable, a flag
|
|
// indicating a visible vector ABI is added. Eventually this will result in
|
|
// a GNU attribute indicating the vector ABI of the module. Ty is the type
|
|
// of a variable or function parameter that is globally visible.
|
|
void handleExternallyVisibleObjABI(const Type *Ty, CodeGen::CodeGenModule &M,
|
|
bool IsParam) const {
|
|
if (!HasVisibleVecABIFlag && isVectorTypeBased(Ty, IsParam)) {
|
|
M.getModule().addModuleFlag(llvm::Module::Warning,
|
|
"s390x-visible-vector-ABI", 1);
|
|
HasVisibleVecABIFlag = true;
|
|
}
|
|
}
|
|
|
|
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
|
|
CodeGen::CodeGenModule &M) const override {
|
|
if (!D)
|
|
return;
|
|
|
|
// Check if the vector ABI becomes visible by an externally visible
|
|
// variable or function.
|
|
if (const auto *VD = dyn_cast<VarDecl>(D)) {
|
|
if (VD->isExternallyVisible())
|
|
handleExternallyVisibleObjABI(VD->getType().getTypePtr(), M,
|
|
/*IsParam*/false);
|
|
}
|
|
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
if (FD->isExternallyVisible())
|
|
handleExternallyVisibleObjABI(FD->getType().getTypePtr(), M,
|
|
/*IsParam*/false);
|
|
}
|
|
}
|
|
|
|
llvm::Value *testFPKind(llvm::Value *V, unsigned BuiltinID,
|
|
CGBuilderTy &Builder,
|
|
CodeGenModule &CGM) const override {
|
|
assert(V->getType()->isFloatingPointTy() && "V should have an FP type.");
|
|
// Only use TDC in constrained FP mode.
|
|
if (!Builder.getIsFPConstrained())
|
|
return nullptr;
|
|
|
|
llvm::Type *Ty = V->getType();
|
|
if (Ty->isHalfTy() || Ty->isFloatTy() || Ty->isDoubleTy() ||
|
|
Ty->isFP128Ty()) {
|
|
llvm::Module &M = CGM.getModule();
|
|
auto &Ctx = M.getContext();
|
|
llvm::Function *TDCFunc = llvm::Intrinsic::getOrInsertDeclaration(
|
|
&M, llvm::Intrinsic::s390_tdc, Ty);
|
|
unsigned TDCBits = 0;
|
|
switch (BuiltinID) {
|
|
case Builtin::BI__builtin_isnan:
|
|
TDCBits = 0xf;
|
|
break;
|
|
case Builtin::BIfinite:
|
|
case Builtin::BI__finite:
|
|
case Builtin::BIfinitef:
|
|
case Builtin::BI__finitef:
|
|
case Builtin::BIfinitel:
|
|
case Builtin::BI__finitel:
|
|
case Builtin::BI__builtin_isfinite:
|
|
TDCBits = 0xfc0;
|
|
break;
|
|
case Builtin::BI__builtin_isinf:
|
|
TDCBits = 0x30;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (TDCBits)
|
|
return Builder.CreateCall(
|
|
TDCFunc,
|
|
{V, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), TDCBits)});
|
|
}
|
|
return nullptr;
|
|
}
|
|
};
|
|
}
|
|
|
|
bool SystemZABIInfo::isPromotableIntegerTypeForABI(QualType Ty) const {
|
|
// Treat an enum type as its underlying type.
|
|
if (const auto *ED = Ty->getAsEnumDecl())
|
|
Ty = ED->getIntegerType();
|
|
|
|
// Promotable integer types are required to be promoted by the ABI.
|
|
if (ABIInfo::isPromotableIntegerTypeForABI(Ty))
|
|
return true;
|
|
|
|
if (const auto *EIT = Ty->getAs<BitIntType>())
|
|
if (EIT->getNumBits() < 64)
|
|
return true;
|
|
|
|
// 32-bit values must also be promoted.
|
|
if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
|
|
switch (BT->getKind()) {
|
|
case BuiltinType::Int:
|
|
case BuiltinType::UInt:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SystemZABIInfo::isCompoundType(QualType Ty) const {
|
|
return (Ty->isAnyComplexType() ||
|
|
Ty->isVectorType() ||
|
|
isAggregateTypeForABI(Ty));
|
|
}
|
|
|
|
bool SystemZABIInfo::isVectorArgumentType(QualType Ty) const {
|
|
return (HasVector &&
|
|
Ty->isVectorType() &&
|
|
getContext().getTypeSize(Ty) <= 128);
|
|
}
|
|
|
|
// The Size argument will in case of af an overaligned single element struct
|
|
// reflect the overalignment value. In such a case the argument will be
|
|
// passed using the type matching Size.
|
|
llvm::Type *SystemZABIInfo::getFPArgumentType(QualType Ty,
|
|
uint64_t Size) const {
|
|
if (IsSoftFloatABI)
|
|
return nullptr;
|
|
|
|
if (const BuiltinType *BT = Ty->getAs<BuiltinType>())
|
|
switch (BT->getKind()) {
|
|
case BuiltinType::Float16:
|
|
if (Size == 16)
|
|
return llvm::Type::getHalfTy(getVMContext());
|
|
[[fallthrough]];
|
|
case BuiltinType::Float:
|
|
if (Size == 32)
|
|
return llvm::Type::getFloatTy(getVMContext());
|
|
[[fallthrough]];
|
|
case BuiltinType::Double:
|
|
return llvm::Type::getDoubleTy(getVMContext());
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
QualType SystemZABIInfo::GetSingleElementType(QualType Ty) const {
|
|
const auto *RD = Ty->getAsRecordDecl();
|
|
if (RD && RD->isStructureOrClass()) {
|
|
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()) {
|
|
// Unlike isSingleElementStruct(), empty structure and array fields
|
|
// do count. So do anonymous bitfields that aren't zero-sized.
|
|
|
|
// Like isSingleElementStruct(), ignore C++20 empty data members.
|
|
if (FD->hasAttr<NoUniqueAddressAttr>() &&
|
|
isEmptyRecord(getContext(), FD->getType(), true))
|
|
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;
|
|
}
|
|
|
|
RValue SystemZABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
|
QualType Ty, AggValueSlot Slot) const {
|
|
// Assume that va_list type is correct; should be pointer to LLVM type:
|
|
// struct {
|
|
// i64 __gpr;
|
|
// i64 __fpr;
|
|
// i8 *__overflow_arg_area;
|
|
// i8 *__reg_save_area;
|
|
// };
|
|
|
|
// Every non-vector argument occupies 8 bytes and is passed by preference
|
|
// in either GPRs or FPRs. Vector arguments occupy 8 or 16 bytes and are
|
|
// always passed on the stack.
|
|
const SystemZTargetCodeGenInfo &SZCGI =
|
|
static_cast<const SystemZTargetCodeGenInfo &>(
|
|
CGT.getCGM().getTargetCodeGenInfo());
|
|
Ty = getContext().getCanonicalType(Ty);
|
|
auto TyInfo = getContext().getTypeInfoInChars(Ty);
|
|
llvm::Type *ArgTy = CGF.ConvertTypeForMem(Ty);
|
|
llvm::Type *DirectTy = ArgTy;
|
|
ABIArgInfo AI = classifyArgumentType(Ty);
|
|
bool IsIndirect = AI.isIndirect();
|
|
bool InFPRs = false;
|
|
bool IsVector = false;
|
|
CharUnits UnpaddedSize;
|
|
CharUnits DirectAlign;
|
|
SZCGI.handleExternallyVisibleObjABI(Ty.getTypePtr(), CGT.getCGM(),
|
|
/*IsParam*/true);
|
|
if (IsIndirect) {
|
|
DirectTy = llvm::PointerType::getUnqual(DirectTy->getContext());
|
|
UnpaddedSize = DirectAlign = CharUnits::fromQuantity(8);
|
|
} else {
|
|
if (AI.getCoerceToType())
|
|
ArgTy = AI.getCoerceToType();
|
|
InFPRs = (!IsSoftFloatABI &&
|
|
(ArgTy->isHalfTy() || ArgTy->isFloatTy() || ArgTy->isDoubleTy()));
|
|
IsVector = ArgTy->isVectorTy();
|
|
UnpaddedSize = TyInfo.Width;
|
|
DirectAlign = TyInfo.Align;
|
|
}
|
|
CharUnits PaddedSize = CharUnits::fromQuantity(8);
|
|
if (IsVector && UnpaddedSize > PaddedSize)
|
|
PaddedSize = CharUnits::fromQuantity(16);
|
|
assert((UnpaddedSize <= PaddedSize) && "Invalid argument size.");
|
|
|
|
CharUnits Padding = (PaddedSize - UnpaddedSize);
|
|
|
|
llvm::Type *IndexTy = CGF.Int64Ty;
|
|
llvm::Value *PaddedSizeV =
|
|
llvm::ConstantInt::get(IndexTy, PaddedSize.getQuantity());
|
|
|
|
if (IsVector) {
|
|
// Work out the address of a vector argument on the stack.
|
|
// Vector arguments are always passed in the high bits of a
|
|
// single (8 byte) or double (16 byte) stack slot.
|
|
Address OverflowArgAreaPtr =
|
|
CGF.Builder.CreateStructGEP(VAListAddr, 2, "overflow_arg_area_ptr");
|
|
Address OverflowArgArea =
|
|
Address(CGF.Builder.CreateLoad(OverflowArgAreaPtr, "overflow_arg_area"),
|
|
CGF.Int8Ty, TyInfo.Align);
|
|
Address MemAddr = OverflowArgArea.withElementType(DirectTy);
|
|
|
|
// Update overflow_arg_area_ptr pointer
|
|
llvm::Value *NewOverflowArgArea = CGF.Builder.CreateGEP(
|
|
OverflowArgArea.getElementType(), OverflowArgArea.emitRawPointer(CGF),
|
|
PaddedSizeV, "overflow_arg_area");
|
|
CGF.Builder.CreateStore(NewOverflowArgArea, OverflowArgAreaPtr);
|
|
|
|
return CGF.EmitLoadOfAnyValue(CGF.MakeAddrLValue(MemAddr, Ty), Slot);
|
|
}
|
|
|
|
assert(PaddedSize.getQuantity() == 8);
|
|
|
|
unsigned MaxRegs, RegCountField, RegSaveIndex;
|
|
CharUnits RegPadding;
|
|
if (InFPRs) {
|
|
MaxRegs = 4; // Maximum of 4 FPR arguments
|
|
RegCountField = 1; // __fpr
|
|
RegSaveIndex = 16; // save offset for f0
|
|
RegPadding = CharUnits(); // floats are passed in the high bits of an FPR
|
|
} else {
|
|
MaxRegs = 5; // Maximum of 5 GPR arguments
|
|
RegCountField = 0; // __gpr
|
|
RegSaveIndex = 2; // save offset for r2
|
|
RegPadding = Padding; // values are passed in the low bits of a GPR
|
|
}
|
|
|
|
Address RegCountPtr =
|
|
CGF.Builder.CreateStructGEP(VAListAddr, RegCountField, "reg_count_ptr");
|
|
llvm::Value *RegCount = CGF.Builder.CreateLoad(RegCountPtr, "reg_count");
|
|
llvm::Value *MaxRegsV = llvm::ConstantInt::get(IndexTy, MaxRegs);
|
|
llvm::Value *InRegs = CGF.Builder.CreateICmpULT(RegCount, MaxRegsV,
|
|
"fits_in_regs");
|
|
|
|
llvm::BasicBlock *InRegBlock = CGF.createBasicBlock("vaarg.in_reg");
|
|
llvm::BasicBlock *InMemBlock = CGF.createBasicBlock("vaarg.in_mem");
|
|
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("vaarg.end");
|
|
CGF.Builder.CreateCondBr(InRegs, InRegBlock, InMemBlock);
|
|
|
|
// Emit code to load the value if it was passed in registers.
|
|
CGF.EmitBlock(InRegBlock);
|
|
|
|
// Work out the address of an argument register.
|
|
llvm::Value *ScaledRegCount =
|
|
CGF.Builder.CreateMul(RegCount, PaddedSizeV, "scaled_reg_count");
|
|
llvm::Value *RegBase =
|
|
llvm::ConstantInt::get(IndexTy, RegSaveIndex * PaddedSize.getQuantity()
|
|
+ RegPadding.getQuantity());
|
|
llvm::Value *RegOffset =
|
|
CGF.Builder.CreateAdd(ScaledRegCount, RegBase, "reg_offset");
|
|
Address RegSaveAreaPtr =
|
|
CGF.Builder.CreateStructGEP(VAListAddr, 3, "reg_save_area_ptr");
|
|
llvm::Value *RegSaveArea =
|
|
CGF.Builder.CreateLoad(RegSaveAreaPtr, "reg_save_area");
|
|
Address RawRegAddr(
|
|
CGF.Builder.CreateGEP(CGF.Int8Ty, RegSaveArea, RegOffset, "raw_reg_addr"),
|
|
CGF.Int8Ty, PaddedSize);
|
|
Address RegAddr = RawRegAddr.withElementType(DirectTy);
|
|
|
|
// Update the register count
|
|
llvm::Value *One = llvm::ConstantInt::get(IndexTy, 1);
|
|
llvm::Value *NewRegCount =
|
|
CGF.Builder.CreateAdd(RegCount, One, "reg_count");
|
|
CGF.Builder.CreateStore(NewRegCount, RegCountPtr);
|
|
CGF.EmitBranch(ContBlock);
|
|
|
|
// Emit code to load the value if it was passed in memory.
|
|
CGF.EmitBlock(InMemBlock);
|
|
|
|
// Work out the address of a stack argument.
|
|
Address OverflowArgAreaPtr =
|
|
CGF.Builder.CreateStructGEP(VAListAddr, 2, "overflow_arg_area_ptr");
|
|
Address OverflowArgArea =
|
|
Address(CGF.Builder.CreateLoad(OverflowArgAreaPtr, "overflow_arg_area"),
|
|
CGF.Int8Ty, PaddedSize);
|
|
Address RawMemAddr =
|
|
CGF.Builder.CreateConstByteGEP(OverflowArgArea, Padding, "raw_mem_addr");
|
|
Address MemAddr = RawMemAddr.withElementType(DirectTy);
|
|
|
|
// Update overflow_arg_area_ptr pointer
|
|
llvm::Value *NewOverflowArgArea = CGF.Builder.CreateGEP(
|
|
OverflowArgArea.getElementType(), OverflowArgArea.emitRawPointer(CGF),
|
|
PaddedSizeV, "overflow_arg_area");
|
|
CGF.Builder.CreateStore(NewOverflowArgArea, OverflowArgAreaPtr);
|
|
CGF.EmitBranch(ContBlock);
|
|
|
|
// Return the appropriate result.
|
|
CGF.EmitBlock(ContBlock);
|
|
Address ResAddr = emitMergePHI(CGF, RegAddr, InRegBlock, MemAddr, InMemBlock,
|
|
"va_arg.addr");
|
|
|
|
if (IsIndirect)
|
|
ResAddr = Address(CGF.Builder.CreateLoad(ResAddr, "indirect_arg"), ArgTy,
|
|
TyInfo.Align);
|
|
|
|
return CGF.EmitLoadOfAnyValue(CGF.MakeAddrLValue(ResAddr, Ty), Slot);
|
|
}
|
|
|
|
ABIArgInfo SystemZABIInfo::classifyReturnType(QualType RetTy) const {
|
|
if (RetTy->isVoidType())
|
|
return ABIArgInfo::getIgnore();
|
|
if (isVectorArgumentType(RetTy))
|
|
return ABIArgInfo::getDirect();
|
|
if (isCompoundType(RetTy) || getContext().getTypeSize(RetTy) > 64)
|
|
return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace());
|
|
return (isPromotableIntegerTypeForABI(RetTy) ? ABIArgInfo::getExtend(RetTy)
|
|
: ABIArgInfo::getDirect());
|
|
}
|
|
|
|
ABIArgInfo SystemZABIInfo::classifyArgumentType(QualType Ty) const {
|
|
// Handle transparent union types.
|
|
Ty = useFirstFieldIfTransparentUnion(Ty);
|
|
|
|
// Handle the generic C++ ABI.
|
|
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
RAA == CGCXXABI::RAA_DirectInMemory);
|
|
|
|
// Integers and enums are extended to full register width.
|
|
if (isPromotableIntegerTypeForABI(Ty))
|
|
return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty));
|
|
|
|
// 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));
|
|
|
|
// Values that are not 1, 2, 4 or 8 bytes in size are passed indirectly.
|
|
if (Size != 8 && Size != 16 && Size != 32 && Size != 64)
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
// Handle small structures.
|
|
if (const auto *RD = Ty->getAsRecordDecl()) {
|
|
// Structures with flexible arrays have variable length, so really
|
|
// fail the size test above.
|
|
if (RD->hasFlexibleArrayMember())
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
// The structure is passed as an unextended integer, a half, a float,
|
|
// or a double.
|
|
if (llvm::Type *FPArgTy = getFPArgumentType(SingleElementTy, Size)) {
|
|
assert(Size == 16 || Size == 32 || Size == 64);
|
|
return ABIArgInfo::getDirect(FPArgTy);
|
|
} else {
|
|
llvm::IntegerType *PassTy = llvm::IntegerType::get(getVMContext(), Size);
|
|
return Size <= 32 ? ABIArgInfo::getNoExtend(PassTy)
|
|
: ABIArgInfo::getDirect(PassTy);
|
|
}
|
|
}
|
|
|
|
// Non-structure compounds are passed indirectly.
|
|
if (isCompoundType(Ty))
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
return ABIArgInfo::getDirect(nullptr);
|
|
}
|
|
|
|
void SystemZABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
|
const SystemZTargetCodeGenInfo &SZCGI =
|
|
static_cast<const SystemZTargetCodeGenInfo &>(
|
|
CGT.getCGM().getTargetCodeGenInfo());
|
|
if (!getCXXABI().classifyReturnType(FI))
|
|
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
|
|
unsigned Idx = 0;
|
|
for (auto &I : FI.arguments()) {
|
|
I.info = classifyArgumentType(I.type);
|
|
if (FI.isVariadic() && Idx++ >= FI.getNumRequiredArgs())
|
|
// Check if a vararg vector argument is passed, in which case the
|
|
// vector ABI becomes visible as the va_list could be passed on to
|
|
// other functions.
|
|
SZCGI.handleExternallyVisibleObjABI(I.type.getTypePtr(), CGT.getCGM(),
|
|
/*IsParam*/true);
|
|
}
|
|
}
|
|
|
|
bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty,
|
|
bool IsParam) const {
|
|
if (!SeenTypes.insert(Ty).second)
|
|
return false;
|
|
|
|
if (IsParam) {
|
|
// A narrow (<16 bytes) vector will as a parameter also expose the ABI as
|
|
// it will be passed in a vector register. A wide (>16 bytes) vector will
|
|
// be passed via "hidden" pointer where any extra alignment is not
|
|
// required (per GCC).
|
|
const Type *SingleEltTy = getABIInfo<SystemZABIInfo>()
|
|
.GetSingleElementType(QualType(Ty, 0))
|
|
.getTypePtr();
|
|
bool SingleVecEltStruct = SingleEltTy != Ty && SingleEltTy->isVectorType() &&
|
|
Ctx.getTypeSize(SingleEltTy) == Ctx.getTypeSize(Ty);
|
|
if (Ty->isVectorType() || SingleVecEltStruct)
|
|
return Ctx.getTypeSize(Ty) / 8 <= 16;
|
|
}
|
|
|
|
// Assume pointers are dereferenced.
|
|
while (Ty->isPointerType() || Ty->isArrayType())
|
|
Ty = Ty->getPointeeOrArrayElementType();
|
|
|
|
// Vectors >= 16 bytes expose the ABI through alignment requirements.
|
|
if (Ty->isVectorType() && Ctx.getTypeSize(Ty) / 8 >= 16)
|
|
return true;
|
|
|
|
if (const auto *RD = Ty->getAsRecordDecl()) {
|
|
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
|
|
if (CXXRD->hasDefinition())
|
|
for (const auto &I : CXXRD->bases())
|
|
if (isVectorTypeBased(I.getType().getTypePtr(), /*IsParam*/false))
|
|
return true;
|
|
for (const auto *FD : RD->fields())
|
|
if (isVectorTypeBased(FD->getType().getTypePtr(), /*IsParam*/false))
|
|
return true;
|
|
}
|
|
|
|
if (const auto *FT = Ty->getAs<FunctionType>())
|
|
if (isVectorTypeBased(FT->getReturnType().getTypePtr(), /*IsParam*/true))
|
|
return true;
|
|
if (const FunctionProtoType *Proto = Ty->getAs<FunctionProtoType>())
|
|
for (const auto &ParamType : Proto->getParamTypes())
|
|
if (isVectorTypeBased(ParamType.getTypePtr(), /*IsParam*/true))
|
|
return true;
|
|
|
|
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 isPromotableIntegerTypeForABI(QualType Ty) const;
|
|
bool isCompoundType(QualType Ty) const;
|
|
bool isVectorArgumentType(QualType Ty) const;
|
|
QualType getSingleElementType(QualType Ty) const;
|
|
std::optional<QualType> getFPTypeOfComplexLikeType(QualType Ty) 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.getCallingConvention());
|
|
|
|
unsigned NumRequiredArgs = FI.getNumRequiredArgs();
|
|
unsigned ArgNo = 0;
|
|
|
|
for (auto &I : FI.arguments()) {
|
|
bool IsNamedArg = ArgNo < NumRequiredArgs;
|
|
I.info =
|
|
classifyArgumentType(I.type, IsNamedArg, FI.getCallingConvention());
|
|
++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::isPromotableIntegerTypeForABI(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 (ABIInfo::isPromotableIntegerTypeForABI(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:
|
|
case BuiltinType::ULong:
|
|
case BuiltinType::Long:
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (const RecordType *RT = Ty->getAsStructureType()) {
|
|
const RecordDecl *RD = RT->getDecl();
|
|
QualType Found;
|
|
|
|
// If this is a C++ class/struct, inspect its base classes first.
|
|
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
|
|
for (const auto &I : CXXRD->bases()) {
|
|
QualType Base = I.getType();
|
|
|
|
// Skip empty base classes, they cannot contribute a data member.
|
|
if (isEmptyRecord(getContext(), Base, true))
|
|
continue;
|
|
|
|
// A candidate base type was already found; encountering another
|
|
// non-empty base means the choice is no longer unique. Return the type
|
|
// from the first candidate.
|
|
if (!Found.isNull())
|
|
return Ty;
|
|
Found = getSingleElementType(Base);
|
|
}
|
|
|
|
// Inspect the record's fields. A struct qualifies as single-element if it
|
|
// has exactly one non-empty field. Empty fields (zero-size structs, arrays,
|
|
// bitfields, or [[no_unique_address]] members) don't affect this
|
|
// classification. Validated by zos-abi.cpp tests.
|
|
for (const auto *FD : RD->fields()) {
|
|
if (!Found.isNull())
|
|
return Ty; // Multiple non-empty fields found
|
|
Found = getSingleElementType(FD->getType());
|
|
}
|
|
return Found.isNull() ? Ty : Found;
|
|
}
|
|
return Ty; // not record/union, unchanged
|
|
}
|
|
|
|
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 (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) {
|
|
// A record can be treated as complex-like if its size is exactly
|
|
// 2 * sizeof(T), matching the layout of two adjacent FP elements.
|
|
auto &Ctx = getContext();
|
|
if (Ctx.getTypeSize(Ty) == 2 * Ctx.getTypeSize(RetTy))
|
|
return RetTy;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
ABIArgInfo ZOSXPLinkABIInfo::classifyReturnType(QualType RetTy,
|
|
unsigned CallConv) const {
|
|
|
|
// Ignore void types.
|
|
if (RetTy->isVoidType())
|
|
return ABIArgInfo::getIgnore();
|
|
|
|
// Handle transparent union types.
|
|
RetTy = useFirstFieldIfTransparentUnion(RetTy);
|
|
|
|
// For non-C calling convention, indirect by value for structs and complex.
|
|
if ((CallConv != llvm::CallingConv::C) &&
|
|
(isAggregateTypeForABI(RetTy) || RetTy->isAnyComplexType())) {
|
|
return getNaturalAlignIndirect(RetTy, getDataLayout().getAllocaAddrSpace());
|
|
}
|
|
|
|
// 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);
|
|
CoerceTy = llvm::ArrayType::get(CoerceTy, NumElements);
|
|
return ABIArgInfo::getDirectInReg(CoerceTy);
|
|
} else
|
|
return getNaturalAlignIndirect(RetTy,
|
|
getDataLayout().getAllocaAddrSpace());
|
|
}
|
|
return (isPromotableIntegerTypeForABI(RetTy)
|
|
? ABIArgInfo::getExtend(RetTy, CGT.ConvertType(RetTy))
|
|
: ABIArgInfo::getDirect(CGT.ConvertType(RetTy)));
|
|
}
|
|
|
|
ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg,
|
|
unsigned CallConv) const {
|
|
// Handle transparent union types.
|
|
Ty = useFirstFieldIfTransparentUnion(Ty);
|
|
|
|
// Handle the generic C++ ABI.
|
|
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI()))
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
RAA == CGCXXABI::RAA_DirectInMemory);
|
|
|
|
// Integers and enums are extended to full register width.
|
|
if (isPromotableIntegerTypeForABI(Ty))
|
|
return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty));
|
|
|
|
// For non-C calling conventions, compound types passed by address copy.
|
|
if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty))
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
// 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);
|
|
if (IsNamedArg) {
|
|
if (Ty->isComplexType()) {
|
|
auto AI = ABIArgInfo::getDirect(CGT.ConvertType(Ty));
|
|
|
|
// Complex types must be preserved as opaque structs per XPLINK ABI.
|
|
// Without this, flattening would incorrectly split { float, float } into
|
|
// separate FP registers, breaking ABI compliance.
|
|
// Example: _Complex float pass_complex_float(_Complex float arg) { return
|
|
// arg; }
|
|
AI.setCanBeFlattened(false);
|
|
return AI;
|
|
}
|
|
|
|
if (CompTy.has_value()) {
|
|
llvm::Type *FPTy = CGT.ConvertType(*CompTy);
|
|
llvm::Type *CoerceTy = llvm::StructType::get(FPTy, FPTy);
|
|
auto AI = ABIArgInfo::getDirect(CoerceTy);
|
|
|
|
// Preserve { float, float } signature for complex-like structs per XPLINK
|
|
// ABI. Flattening would incorrectly decompose into separate FP registers.
|
|
// Example: struct complexlike_float { float re, im; };
|
|
// struct complexlike_float pass_complexlike_float2(struct
|
|
// complexlike_float arg) { return arg; }
|
|
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() || 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;
|
|
|
|
// 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(), GPRBits);
|
|
// 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, GPRBits) / GPRBits;
|
|
llvm::Type *RegTy = llvm::IntegerType::get(getVMContext(), GPRBits);
|
|
CoerceTy = llvm::ArrayType::get(RegTy, NumRegs);
|
|
}
|
|
return ABIArgInfo::getDirect(CoerceTy);
|
|
}
|
|
|
|
// Non-structure compounds are passed indirectly, i.e. arrays.
|
|
if (isCompoundType(Ty))
|
|
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
|
|
/*ByVal=*/false);
|
|
|
|
return ABIArgInfo::getDirect();
|
|
}
|
|
|
|
RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
|
|
QualType Ty, AggValueSlot Slot) const {
|
|
return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false,
|
|
CGF.getContext().getTypeInfoInChars(Ty),
|
|
CGF.getPointerSize(),
|
|
/*allowHigherAlign*/ false, Slot);
|
|
}
|
|
|
|
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);
|
|
}
|