| //===- LoongArch.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" |
| |
| using namespace clang; |
| using namespace clang::CodeGen; |
| |
| // LoongArch ABI Implementation. Documented at |
| // https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html |
| // |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| class LoongArchABIInfo : public DefaultABIInfo { |
| private: |
| // Size of the integer ('r') registers in bits. |
| unsigned GRLen; |
| // Size of the floating point ('f') registers in bits. |
| unsigned FRLen; |
| // Number of general-purpose argument registers. |
| static const int NumGARs = 8; |
| // Number of floating-point argument registers. |
| 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; |
| |
| Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
| QualType Ty) 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; |
| }; |
| } // end anonymous namespace |
| |
| void LoongArchABIInfo::computeInfo(CGFunctionInfo &FI) const { |
| QualType RetTy = FI.getReturnType(); |
| if (!getCXXABI().classifyReturnType(FI)) |
| FI.getReturnInfo() = classifyReturnType(RetTy); |
| |
| // IsRetIndirect is true if classifyArgumentType indicated the value should |
| // be passed indirect, or if the type size is a scalar greater than 2*GRLen |
| // and not a complex type with elements <= FRLen. e.g. fp128 is passed direct |
| // in LLVM IR, relying on the backend lowering code to rewrite the argument |
| // list and pass indirectly on LA32. |
| 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 { |
| // This is a normal scalar > 2*GRLen, such as fp128 on LA32. |
| IsRetIndirect = true; |
| } |
| } |
| |
| // We must track the number of GARs and FARs used in order to conform to the |
| // LoongArch ABI. As GAR usage is different for variadic arguments, we must |
| // also track whether we are examining a vararg or not. |
| 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, /*IsFixed=*/ArgNum < NumFixedArgs, GARsLeft, FARsLeft); |
| ArgNum++; |
| } |
| } |
| |
| // Returns true if the struct is a potential candidate to be passed in FARs (and |
| // GARs). If this function returns true, the caller is responsible for checking |
| // that if there is only a single field then that field is a float. |
| 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; |
| // Can't be eligible if larger than the FP registers. Half precision isn't |
| // currently supported on LoongArch and the ABI hasn't been confirmed, so |
| // default to the integer ABI in that case. |
| if (IsFloat && (Size > FRLen || Size < 32)) |
| return false; |
| // Can't be eligible if an integer type was already found (int+int pairs |
| // are not eligible). |
| 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->getSize().getZExtValue(); |
| QualType EltTy = ATy->getElementType(); |
| // Non-zero-length arrays of empty records make the struct ineligible to be |
| // passed via FARs in C++. |
| 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>()) { |
| // Structures with either a non-trivial destructor or a non-trivial |
| // copy constructor are not eligible for the FP calling convention. |
| if (getRecordArgABI(Ty, CGT.getCXXABI())) |
| return false; |
| if (isEmptyRecord(getContext(), Ty, true, true)) |
| return true; |
| const RecordDecl *RD = RTy->getDecl(); |
| // Unions aren't eligible unless they're empty (which is caught above). |
| if (RD->isUnion()) |
| return false; |
| const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); |
| // If this is a C++ record, check the bases first. |
| 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()); |
| // Zero-width bitfields are ignored. |
| if (BitWidth == 0) |
| continue; |
| // Allow a bitfield with a type greater than GRLen as long as the |
| // bitwidth is GRLen or less. |
| 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; |
| } |
| |
| // Determine if a struct is eligible to be passed in FARs (and GARs) (i.e., when |
| // flattened it contains a single fp value, fp+fp, or int+fp of appropriate |
| // size). If so, NeededFARs and NeededGARs are incremented appropriately. |
| 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; |
| // Not really a candidate if we have a single int but no float. |
| 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; |
| } |
| |
| // Call getCoerceAndExpand for the two-element flattened struct described by |
| // Field1Ty, Field1Off, Field2Ty, Field2Off. This method will create an |
| // appropriate coerceToType and unpaddedCoerceToType. |
| 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); |
| |
| // Structures with either a non-trivial destructor or a non-trivial |
| // copy constructor are always passed indirectly. |
| if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) { |
| if (GARsLeft) |
| GARsLeft -= 1; |
| return getNaturalAlignIndirect(Ty, /*ByVal=*/RAA == |
| CGCXXABI::RAA_DirectInMemory); |
| } |
| |
| // Ignore empty structs/unions. |
| if (isEmptyRecord(getContext(), Ty, true)) |
| return ABIArgInfo::getIgnore(); |
| |
| uint64_t Size = getContext().getTypeSize(Ty); |
| |
| // Pass floating point values via FARs if possible. |
| if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() && |
| FRLen >= Size && FARsLeft) { |
| FARsLeft--; |
| return ABIArgInfo::getDirect(); |
| } |
| |
| // Complex types for the *f or *d ABI must be passed directly rather than |
| // using CoerceAndExpand. |
| 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); |
| // Determine the number of GARs needed to pass the current argument |
| // according to the ABI. 2*GRLen-aligned varargs are passed in "aligned" |
| // register pairs, so may consume 3 registers. |
| 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()) { |
| // Treat an enum type as its underlying type. |
| if (const EnumType *EnumTy = Ty->getAs<EnumType>()) |
| Ty = EnumTy->getDecl()->getIntegerType(); |
| |
| // All integral types are promoted to GRLen width. |
| 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, /*ByVal=*/false); |
| } |
| |
| return ABIArgInfo::getDirect(); |
| } |
| |
| // Aggregates which are <= 2*GRLen will be passed in registers if possible, |
| // so coerce to integers. |
| if (Size <= 2 * GRLen) { |
| // Use a single GRLen int if possible, 2*GRLen if 2*GRLen alignment is |
| // required, and a 2-element GRLen array if only GRLen alignment is |
| // required. |
| 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, /*ByVal=*/false); |
| } |
| |
| ABIArgInfo LoongArchABIInfo::classifyReturnType(QualType RetTy) const { |
| if (RetTy->isVoidType()) |
| return ABIArgInfo::getIgnore(); |
| // The rules for return and argument types are the same, so defer to |
| // classifyArgumentType. |
| int GARsLeft = 2; |
| int FARsLeft = FRLen ? 2 : 0; |
| return classifyArgumentType(RetTy, /*IsFixed=*/true, GARsLeft, FARsLeft); |
| } |
| |
| Address LoongArchABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
| QualType Ty) const { |
| CharUnits SlotSize = CharUnits::fromQuantity(GRLen / 8); |
| |
| // Empty records are ignored for parameter passing purposes. |
| if (isEmptyRecord(getContext(), Ty, true)) |
| return Address(CGF.Builder.CreateLoad(VAListAddr), |
| CGF.ConvertTypeForMem(Ty), SlotSize); |
| |
| auto TInfo = getContext().getTypeInfoInChars(Ty); |
| |
| // Arguments bigger than 2*GRLen bytes are passed indirectly. |
| return emitVoidPtrVAArg(CGF, VAListAddr, Ty, |
| /*IsIndirect=*/TInfo.Width > 2 * SlotSize, TInfo, |
| SlotSize, |
| /*AllowHigherAlign=*/true); |
| } |
| |
| ABIArgInfo LoongArchABIInfo::extendType(QualType Ty) const { |
| int TySize = getContext().getTypeSize(Ty); |
| // LA64 ABI requires unsigned 32 bit integers to be sign extended. |
| 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)) {} |
| }; |
| } // namespace |
| |
| std::unique_ptr<TargetCodeGenInfo> |
| CodeGen::createLoongArchTargetCodeGenInfo(CodeGenModule &CGM, unsigned GRLen, |
| unsigned FLen) { |
| return std::make_unique<LoongArchTargetCodeGenInfo>(CGM.getTypes(), GRLen, |
| FLen); |
| } |