#include "ABIInfoImpl.h"
#include "TargetInfo.h"
using namespace clang;
using namespace clang::CodeGen;
namespace {
class LoongArchABIInfo : public DefaultABIInfo {
private:
unsigned GRLen;
unsigned FRLen;
static const int NumGARs = 8;
static const int NumFARs = 8;
bool detectFARsEligibleStructHelper(QualType Ty, CharUnits CurOff,
llvm::Type *&Field1Ty,
CharUnits &Field1Off,
llvm::Type *&Field2Ty,
CharUnits &Field2Off) const;
public:
LoongArchABIInfo(CodeGen::CodeGenTypes &CGT, unsigned GRLen, unsigned FRLen)
: DefaultABIInfo(CGT), GRLen(GRLen), FRLen(FRLen) {}
void computeInfo(CGFunctionInfo &FI) const override;
ABIArgInfo classifyArgumentType(QualType Ty, bool IsFixed, int &GARsLeft,
int &FARsLeft) const;
ABIArgInfo classifyReturnType(QualType RetTy) const;
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
AggValueSlot Slot) const override;
ABIArgInfo extendType(QualType Ty) const;
bool detectFARsEligibleStruct(QualType Ty, llvm::Type *&Field1Ty,
CharUnits &Field1Off, llvm::Type *&Field2Ty,
CharUnits &Field2Off, int &NeededArgGPRs,
int &NeededArgFPRs) const;
ABIArgInfo coerceAndExpandFARsEligibleStruct(llvm::Type *Field1Ty,
CharUnits Field1Off,
llvm::Type *Field2Ty,
CharUnits Field2Off) const;
};
}
void LoongArchABIInfo::computeInfo(CGFunctionInfo &FI) const {
QualType RetTy = FI.getReturnType();
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(RetTy);
bool IsRetIndirect = FI.getReturnInfo().getKind() == ABIArgInfo::Indirect;
if (!IsRetIndirect && RetTy->isScalarType() &&
getContext().getTypeSize(RetTy) > (2 * GRLen)) {
if (RetTy->isComplexType() && FRLen) {
QualType EltTy = RetTy->castAs<ComplexType>()->getElementType();
IsRetIndirect = getContext().getTypeSize(EltTy) > FRLen;
} else {
IsRetIndirect = true;
}
}
int GARsLeft = IsRetIndirect ? NumGARs - 1 : NumGARs;
int FARsLeft = FRLen ? NumFARs : 0;
int NumFixedArgs = FI.getNumRequiredArgs();
int ArgNum = 0;
for (auto &ArgInfo : FI.arguments()) {
ArgInfo.info = classifyArgumentType(
ArgInfo.type, ArgNum < NumFixedArgs, GARsLeft, FARsLeft);
ArgNum++;
}
}
bool LoongArchABIInfo::detectFARsEligibleStructHelper(
QualType Ty, CharUnits CurOff, llvm::Type *&Field1Ty, CharUnits &Field1Off,
llvm::Type *&Field2Ty, CharUnits &Field2Off) const {
bool IsInt = Ty->isIntegralOrEnumerationType();
bool IsFloat = Ty->isRealFloatingType();
if (IsInt || IsFloat) {
uint64_t Size = getContext().getTypeSize(Ty);
if (IsInt && Size > GRLen)
return false;
if (IsFloat && (Size > FRLen || Size < 32))
return false;
if (IsInt && Field1Ty && Field1Ty->isIntegerTy())
return false;
if (!Field1Ty) {
Field1Ty = CGT.ConvertType(Ty);
Field1Off = CurOff;
return true;
}
if (!Field2Ty) {
Field2Ty = CGT.ConvertType(Ty);
Field2Off = CurOff;
return true;
}
return false;
}
if (auto CTy = Ty->getAs<ComplexType>()) {
if (Field1Ty)
return false;
QualType EltTy = CTy->getElementType();
if (getContext().getTypeSize(EltTy) > FRLen)
return false;
Field1Ty = CGT.ConvertType(EltTy);
Field1Off = CurOff;
Field2Ty = Field1Ty;
Field2Off = Field1Off + getContext().getTypeSizeInChars(EltTy);
return true;
}
if (const ConstantArrayType *ATy = getContext().getAsConstantArrayType(Ty)) {
uint64_t ArraySize = ATy->getZExtSize();
QualType EltTy = ATy->getElementType();
if (const auto *RTy = EltTy->getAs<RecordType>()) {
if (ArraySize != 0 && isa<CXXRecordDecl>(RTy->getDecl()) &&
isEmptyRecord(getContext(), EltTy, true, true))
return false;
}
CharUnits EltSize = getContext().getTypeSizeInChars(EltTy);
for (uint64_t i = 0; i < ArraySize; ++i) {
if (!detectFARsEligibleStructHelper(EltTy, CurOff, Field1Ty, Field1Off,
Field2Ty, Field2Off))
return false;
CurOff += EltSize;
}
return true;
}
if (const auto *RTy = Ty->getAs<RecordType>()) {
if (getRecordArgABI(Ty, CGT.getCXXABI()))
return false;
const RecordDecl *RD = RTy->getDecl();
if (isEmptyRecord(getContext(), Ty, true, true) &&
(!RD->isUnion() || !isa<CXXRecordDecl>(RD)))
return true;
if (RD->isUnion())
return false;
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
for (const CXXBaseSpecifier &B : CXXRD->bases()) {
const auto *BDecl =
cast<CXXRecordDecl>(B.getType()->castAs<RecordType>()->getDecl());
if (!detectFARsEligibleStructHelper(
B.getType(), CurOff + Layout.getBaseClassOffset(BDecl),
Field1Ty, Field1Off, Field2Ty, Field2Off))
return false;
}
}
for (const FieldDecl *FD : RD->fields()) {
QualType QTy = FD->getType();
if (FD->isBitField()) {
unsigned BitWidth = FD->getBitWidthValue(getContext());
if (BitWidth == 0)
continue;
if (getContext().getTypeSize(QTy) > GRLen && BitWidth <= GRLen) {
QTy = getContext().getIntTypeForBitwidth(GRLen, false);
}
}
if (!detectFARsEligibleStructHelper(
QTy,
CurOff + getContext().toCharUnitsFromBits(
Layout.getFieldOffset(FD->getFieldIndex())),
Field1Ty, Field1Off, Field2Ty, Field2Off))
return false;
}
return Field1Ty != nullptr;
}
return false;
}
bool LoongArchABIInfo::detectFARsEligibleStruct(
QualType Ty, llvm::Type *&Field1Ty, CharUnits &Field1Off,
llvm::Type *&Field2Ty, CharUnits &Field2Off, int &NeededGARs,
int &NeededFARs) const {
Field1Ty = nullptr;
Field2Ty = nullptr;
NeededGARs = 0;
NeededFARs = 0;
if (!detectFARsEligibleStructHelper(Ty, CharUnits::Zero(), Field1Ty,
Field1Off, Field2Ty, Field2Off))
return false;
if (!Field1Ty)
return false;
if (Field1Ty && !Field2Ty && !Field1Ty->isFloatingPointTy())
return false;
if (Field1Ty && Field1Ty->isFloatingPointTy())
NeededFARs++;
else if (Field1Ty)
NeededGARs++;
if (Field2Ty && Field2Ty->isFloatingPointTy())
NeededFARs++;
else if (Field2Ty)
NeededGARs++;
return true;
}
ABIArgInfo LoongArchABIInfo::coerceAndExpandFARsEligibleStruct(
llvm::Type *Field1Ty, CharUnits Field1Off, llvm::Type *Field2Ty,
CharUnits Field2Off) const {
SmallVector<llvm::Type *, 3> CoerceElts;
SmallVector<llvm::Type *, 2> UnpaddedCoerceElts;
if (!Field1Off.isZero())
CoerceElts.push_back(llvm::ArrayType::get(
llvm::Type::getInt8Ty(getVMContext()), Field1Off.getQuantity()));
CoerceElts.push_back(Field1Ty);
UnpaddedCoerceElts.push_back(Field1Ty);
if (!Field2Ty) {
return ABIArgInfo::getCoerceAndExpand(
llvm::StructType::get(getVMContext(), CoerceElts, !Field1Off.isZero()),
UnpaddedCoerceElts[0]);
}
CharUnits Field2Align =
CharUnits::fromQuantity(getDataLayout().getABITypeAlign(Field2Ty));
CharUnits Field1End =
Field1Off +
CharUnits::fromQuantity(getDataLayout().getTypeStoreSize(Field1Ty));
CharUnits Field2OffNoPadNoPack = Field1End.alignTo(Field2Align);
CharUnits Padding = CharUnits::Zero();
if (Field2Off > Field2OffNoPadNoPack)
Padding = Field2Off - Field2OffNoPadNoPack;
else if (Field2Off != Field2Align && Field2Off > Field1End)
Padding = Field2Off - Field1End;
bool IsPacked = !Field2Off.isMultipleOf(Field2Align);
if (!Padding.isZero())
CoerceElts.push_back(llvm::ArrayType::get(
llvm::Type::getInt8Ty(getVMContext()), Padding.getQuantity()));
CoerceElts.push_back(Field2Ty);
UnpaddedCoerceElts.push_back(Field2Ty);
return ABIArgInfo::getCoerceAndExpand(
llvm::StructType::get(getVMContext(), CoerceElts, IsPacked),
llvm::StructType::get(getVMContext(), UnpaddedCoerceElts, IsPacked));
}
ABIArgInfo LoongArchABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
int &GARsLeft,
int &FARsLeft) const {
assert(GARsLeft <= NumGARs && "GAR tracking underflow");
Ty = useFirstFieldIfTransparentUnion(Ty);
if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
if (GARsLeft)
GARsLeft -= 1;
return getNaturalAlignIndirect(Ty, RAA ==
CGCXXABI::RAA_DirectInMemory);
}
uint64_t Size = getContext().getTypeSize(Ty);
if (isEmptyRecord(getContext(), Ty, true) && Size == 0)
return ABIArgInfo::getIgnore();
if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() &&
FRLen >= Size && FARsLeft) {
FARsLeft--;
return ABIArgInfo::getDirect();
}
if (IsFixed && Ty->isComplexType() && FRLen && FARsLeft >= 2) {
QualType EltTy = Ty->castAs<ComplexType>()->getElementType();
if (getContext().getTypeSize(EltTy) <= FRLen) {
FARsLeft -= 2;
return ABIArgInfo::getDirect();
}
}
if (IsFixed && FRLen && Ty->isStructureOrClassType()) {
llvm::Type *Field1Ty = nullptr;
llvm::Type *Field2Ty = nullptr;
CharUnits Field1Off = CharUnits::Zero();
CharUnits Field2Off = CharUnits::Zero();
int NeededGARs = 0;
int NeededFARs = 0;
bool IsCandidate = detectFARsEligibleStruct(
Ty, Field1Ty, Field1Off, Field2Ty, Field2Off, NeededGARs, NeededFARs);
if (IsCandidate && NeededGARs <= GARsLeft && NeededFARs <= FARsLeft) {
GARsLeft -= NeededGARs;
FARsLeft -= NeededFARs;
return coerceAndExpandFARsEligibleStruct(Field1Ty, Field1Off, Field2Ty,
Field2Off);
}
}
uint64_t NeededAlign = getContext().getTypeAlign(Ty);
int NeededGARs = 1;
if (!IsFixed && NeededAlign == 2 * GRLen)
NeededGARs = 2 + (GARsLeft % 2);
else if (Size > GRLen && Size <= 2 * GRLen)
NeededGARs = 2;
if (NeededGARs > GARsLeft)
NeededGARs = GARsLeft;
GARsLeft -= NeededGARs;
if (!isAggregateTypeForABI(Ty) && !Ty->isVectorType()) {
if (const EnumType *EnumTy = Ty->getAs<EnumType>())
Ty = EnumTy->getDecl()->getIntegerType();
if (Size < GRLen && Ty->isIntegralOrEnumerationType())
return extendType(Ty);
if (const auto *EIT = Ty->getAs<BitIntType>()) {
if (EIT->getNumBits() < GRLen)
return extendType(Ty);
if (EIT->getNumBits() > 128 ||
(!getContext().getTargetInfo().hasInt128Type() &&
EIT->getNumBits() > 64))
return getNaturalAlignIndirect(Ty, false);
}
return ABIArgInfo::getDirect();
}
if (Size <= 2 * GRLen) {
if (Size <= GRLen) {
return ABIArgInfo::getDirect(
llvm::IntegerType::get(getVMContext(), GRLen));
}
if (getContext().getTypeAlign(Ty) == 2 * GRLen) {
return ABIArgInfo::getDirect(
llvm::IntegerType::get(getVMContext(), 2 * GRLen));
}
return ABIArgInfo::getDirect(
llvm::ArrayType::get(llvm::IntegerType::get(getVMContext(), GRLen), 2));
}
return getNaturalAlignIndirect(Ty, false);
}
ABIArgInfo LoongArchABIInfo::classifyReturnType(QualType RetTy) const {
if (RetTy->isVoidType())
return ABIArgInfo::getIgnore();
int GARsLeft = 2;
int FARsLeft = FRLen ? 2 : 0;
return classifyArgumentType(RetTy, true, GARsLeft, FARsLeft);
}
RValue LoongArchABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty, AggValueSlot Slot) const {
CharUnits SlotSize = CharUnits::fromQuantity(GRLen / 8);
if (isEmptyRecord(getContext(), Ty, true))
return Slot.asRValue();
auto TInfo = getContext().getTypeInfoInChars(Ty);
return emitVoidPtrVAArg(CGF, VAListAddr, Ty,
TInfo.Width > 2 * SlotSize, TInfo,
SlotSize,
true, Slot);
}
ABIArgInfo LoongArchABIInfo::extendType(QualType Ty) const {
int TySize = getContext().getTypeSize(Ty);
if (GRLen == 64 && Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32)
return ABIArgInfo::getSignExtend(Ty);
return ABIArgInfo::getExtend(Ty);
}
namespace {
class LoongArchTargetCodeGenInfo : public TargetCodeGenInfo {
public:
LoongArchTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned GRLen,
unsigned FRLen)
: TargetCodeGenInfo(
std::make_unique<LoongArchABIInfo>(CGT, GRLen, FRLen)) {}
};
}
std::unique_ptr<TargetCodeGenInfo>
CodeGen::createLoongArchTargetCodeGenInfo(CodeGenModule &CGM, unsigned GRLen,
unsigned FLen) {
return std::make_unique<LoongArchTargetCodeGenInfo>(CGM.getTypes(), GRLen,
FLen);
}