blob: a27fee633ead23a46a8ee42cdd9813e09066e51e [file]
//===- 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;
RValue EmitZOSVAArg(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);
}
RValue ZOSXPLinkABIInfo::EmitZOSVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType ArgTy, AggValueSlot Slot) const {
// Assume that va_list type is correct; should be pointer
// to LLVM type: [2 x i8*].
llvm::Type *VAListTy =
CGF.ConvertType(getContext().getBuiltinZOSVaListType());
VAListAddr = VAListAddr.withElementType(VAListTy);
Address Curr = CGF.Builder.CreateConstArrayGEP(VAListAddr, 0, "va_list.curr");
Address Next = CGF.Builder.CreateConstArrayGEP(VAListAddr, 1, "va_list.next");
// Get information about the argument type.
auto ArgTyInfo = CGF.getContext().getTypeInfoInChars(ArgTy);
CharUnits ArgTySize = ArgTyInfo.Width;
llvm::Type *Ty = CGF.ConvertTypeForMem(ArgTy);
// Slot size is the same as the size of a pointer.
CharUnits SlotSize = CGF.getPointerSize();
// Align next and copy to curr.
CharUnits PtrAlign = CGF.getPointerAlign();
llvm::Value *OldNext = CGF.Builder.CreateLoad(Next, "arg.next");
Address Addr = Address(emitRoundPointerUpToAlignment(CGF, OldNext, PtrAlign),
CGF.Int8Ty, PtrAlign);
CGF.Builder.CreateStore(Addr.emitRawPointer(CGF), Curr);
// Advance next and store.
Address NextPtr = CGF.Builder.CreateConstInBoundsByteGEP(
Addr, ArgTySize.isZero() ? SlotSize : ArgTySize, "arg.next.next");
CGF.Builder.CreateStore(NextPtr.emitRawPointer(CGF), Next);
// Fetch next argument.
if (ArgTySize < SlotSize && !isAggregateTypeForABI(ArgTy))
Addr = CGF.Builder.CreateConstInBoundsByteGEP(Addr, SlotSize - ArgTySize);
return CGF.EmitLoadOfAnyValue(
CGF.MakeAddrLValue(Addr.withElementType(Ty), ArgTy), 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);
}