| 1 | //===- LoongArch.cpp ------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "ABIInfoImpl.h" |
| 10 | #include "TargetInfo.h" |
| 11 | |
| 12 | using namespace clang; |
| 13 | using namespace clang::CodeGen; |
| 14 | |
| 15 | // LoongArch ABI Implementation. Documented at |
| 16 | // https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html |
| 17 | // |
| 18 | //===----------------------------------------------------------------------===// |
| 19 | |
| 20 | namespace { |
| 21 | class LoongArchABIInfo : public DefaultABIInfo { |
| 22 | private: |
| 23 | // Size of the integer ('r') registers in bits. |
| 24 | unsigned GRLen; |
| 25 | // Size of the floating point ('f') registers in bits. |
| 26 | unsigned FRLen; |
| 27 | // Number of general-purpose argument registers. |
| 28 | static const int NumGARs = 8; |
| 29 | // Number of floating-point argument registers. |
| 30 | static const int NumFARs = 8; |
| 31 | bool detectFARsEligibleStructHelper(QualType Ty, CharUnits CurOff, |
| 32 | llvm::Type *&Field1Ty, |
| 33 | CharUnits &Field1Off, |
| 34 | llvm::Type *&Field2Ty, |
| 35 | CharUnits &Field2Off) const; |
| 36 | |
| 37 | public: |
| 38 | LoongArchABIInfo(CodeGen::CodeGenTypes &CGT, unsigned GRLen, unsigned FRLen) |
| 39 | : DefaultABIInfo(CGT), GRLen(GRLen), FRLen(FRLen) {} |
| 40 | |
| 41 | void computeInfo(CGFunctionInfo &FI) const override; |
| 42 | |
| 43 | ABIArgInfo classifyArgumentType(QualType Ty, bool IsFixed, int &GARsLeft, |
| 44 | int &FARsLeft) const; |
| 45 | ABIArgInfo classifyReturnType(QualType RetTy) const; |
| 46 | |
| 47 | RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, |
| 48 | AggValueSlot Slot) const override; |
| 49 | |
| 50 | ABIArgInfo extendType(QualType Ty) const; |
| 51 | |
| 52 | bool detectFARsEligibleStruct(QualType Ty, llvm::Type *&Field1Ty, |
| 53 | CharUnits &Field1Off, llvm::Type *&Field2Ty, |
| 54 | CharUnits &Field2Off, int &NeededArgGPRs, |
| 55 | int &NeededArgFPRs) const; |
| 56 | ABIArgInfo coerceAndExpandFARsEligibleStruct(llvm::Type *Field1Ty, |
| 57 | CharUnits Field1Off, |
| 58 | llvm::Type *Field2Ty, |
| 59 | CharUnits Field2Off) const; |
| 60 | }; |
| 61 | } // end anonymous namespace |
| 62 | |
| 63 | void LoongArchABIInfo::computeInfo(CGFunctionInfo &FI) const { |
| 64 | QualType RetTy = FI.getReturnType(); |
| 65 | if (!getCXXABI().classifyReturnType(FI)) |
| 66 | FI.getReturnInfo() = classifyReturnType(RetTy); |
| 67 | |
| 68 | // IsRetIndirect is true if classifyArgumentType indicated the value should |
| 69 | // be passed indirect, or if the type size is a scalar greater than 2*GRLen |
| 70 | // and not a complex type with elements <= FRLen. e.g. fp128 is passed direct |
| 71 | // in LLVM IR, relying on the backend lowering code to rewrite the argument |
| 72 | // list and pass indirectly on LA32. |
| 73 | bool IsRetIndirect = FI.getReturnInfo().getKind() == ABIArgInfo::Indirect; |
| 74 | if (!IsRetIndirect && RetTy->isScalarType() && |
| 75 | getContext().getTypeSize(T: RetTy) > (2 * GRLen)) { |
| 76 | if (RetTy->isComplexType() && FRLen) { |
| 77 | QualType EltTy = RetTy->castAs<ComplexType>()->getElementType(); |
| 78 | IsRetIndirect = getContext().getTypeSize(T: EltTy) > FRLen; |
| 79 | } else { |
| 80 | // This is a normal scalar > 2*GRLen, such as fp128 on LA32. |
| 81 | IsRetIndirect = true; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | // We must track the number of GARs and FARs used in order to conform to the |
| 86 | // LoongArch ABI. As GAR usage is different for variadic arguments, we must |
| 87 | // also track whether we are examining a vararg or not. |
| 88 | int GARsLeft = IsRetIndirect ? NumGARs - 1 : NumGARs; |
| 89 | int FARsLeft = FRLen ? NumFARs : 0; |
| 90 | int NumFixedArgs = FI.getNumRequiredArgs(); |
| 91 | |
| 92 | int ArgNum = 0; |
| 93 | for (auto &ArgInfo : FI.arguments()) { |
| 94 | ArgInfo.info = classifyArgumentType( |
| 95 | Ty: ArgInfo.type, /*IsFixed=*/ArgNum < NumFixedArgs, GARsLeft, FARsLeft); |
| 96 | ArgNum++; |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | // Returns true if the struct is a potential candidate to be passed in FARs (and |
| 101 | // GARs). If this function returns true, the caller is responsible for checking |
| 102 | // that if there is only a single field then that field is a float. |
| 103 | bool LoongArchABIInfo::detectFARsEligibleStructHelper( |
| 104 | QualType Ty, CharUnits CurOff, llvm::Type *&Field1Ty, CharUnits &Field1Off, |
| 105 | llvm::Type *&Field2Ty, CharUnits &Field2Off) const { |
| 106 | bool IsInt = Ty->isIntegralOrEnumerationType(); |
| 107 | bool IsFloat = Ty->isRealFloatingType(); |
| 108 | |
| 109 | if (IsInt || IsFloat) { |
| 110 | uint64_t Size = getContext().getTypeSize(T: Ty); |
| 111 | if (IsInt && Size > GRLen) |
| 112 | return false; |
| 113 | // Can't be eligible if larger than the FP registers. Handling of half |
| 114 | // precision values has been specified in the ABI, so don't block those. |
| 115 | if (IsFloat && Size > FRLen) |
| 116 | return false; |
| 117 | // Can't be eligible if an integer type was already found (int+int pairs |
| 118 | // are not eligible). |
| 119 | if (IsInt && Field1Ty && Field1Ty->isIntegerTy()) |
| 120 | return false; |
| 121 | if (!Field1Ty) { |
| 122 | Field1Ty = CGT.ConvertType(T: Ty); |
| 123 | Field1Off = CurOff; |
| 124 | return true; |
| 125 | } |
| 126 | if (!Field2Ty) { |
| 127 | Field2Ty = CGT.ConvertType(T: Ty); |
| 128 | Field2Off = CurOff; |
| 129 | return true; |
| 130 | } |
| 131 | return false; |
| 132 | } |
| 133 | |
| 134 | if (auto CTy = Ty->getAs<ComplexType>()) { |
| 135 | if (Field1Ty) |
| 136 | return false; |
| 137 | QualType EltTy = CTy->getElementType(); |
| 138 | if (getContext().getTypeSize(T: EltTy) > FRLen) |
| 139 | return false; |
| 140 | Field1Ty = CGT.ConvertType(T: EltTy); |
| 141 | Field1Off = CurOff; |
| 142 | Field2Ty = Field1Ty; |
| 143 | Field2Off = Field1Off + getContext().getTypeSizeInChars(T: EltTy); |
| 144 | return true; |
| 145 | } |
| 146 | |
| 147 | if (const ConstantArrayType *ATy = getContext().getAsConstantArrayType(T: Ty)) { |
| 148 | uint64_t ArraySize = ATy->getZExtSize(); |
| 149 | QualType EltTy = ATy->getElementType(); |
| 150 | // Non-zero-length arrays of empty records make the struct ineligible to be |
| 151 | // passed via FARs in C++. |
| 152 | if (const auto *RTy = EltTy->getAs<RecordType>()) { |
| 153 | if (ArraySize != 0 && isa<CXXRecordDecl>(Val: RTy->getDecl()) && |
| 154 | isEmptyRecord(Context&: getContext(), T: EltTy, AllowArrays: true, AsIfNoUniqueAddr: true)) |
| 155 | return false; |
| 156 | } |
| 157 | CharUnits EltSize = getContext().getTypeSizeInChars(T: EltTy); |
| 158 | for (uint64_t i = 0; i < ArraySize; ++i) { |
| 159 | if (!detectFARsEligibleStructHelper(Ty: EltTy, CurOff, Field1Ty, Field1Off, |
| 160 | Field2Ty, Field2Off)) |
| 161 | return false; |
| 162 | CurOff += EltSize; |
| 163 | } |
| 164 | return true; |
| 165 | } |
| 166 | |
| 167 | if (const auto *RTy = Ty->getAs<RecordType>()) { |
| 168 | // Structures with either a non-trivial destructor or a non-trivial |
| 169 | // copy constructor are not eligible for the FP calling convention. |
| 170 | if (getRecordArgABI(T: Ty, CXXABI&: CGT.getCXXABI())) |
| 171 | return false; |
| 172 | const RecordDecl *RD = RTy->getDecl(); |
| 173 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true, AsIfNoUniqueAddr: true) && |
| 174 | (!RD->isUnion() || !isa<CXXRecordDecl>(Val: RD))) |
| 175 | return true; |
| 176 | // Unions aren't eligible unless they're empty in C (which is caught above). |
| 177 | if (RD->isUnion()) |
| 178 | return false; |
| 179 | const ASTRecordLayout &Layout = getContext().getASTRecordLayout(D: RD); |
| 180 | // If this is a C++ record, check the bases first. |
| 181 | if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(Val: RD)) { |
| 182 | for (const CXXBaseSpecifier &B : CXXRD->bases()) { |
| 183 | const auto *BDecl = |
| 184 | cast<CXXRecordDecl>(Val: B.getType()->castAs<RecordType>()->getDecl()); |
| 185 | if (!detectFARsEligibleStructHelper( |
| 186 | Ty: B.getType(), CurOff: CurOff + Layout.getBaseClassOffset(Base: BDecl), |
| 187 | Field1Ty, Field1Off, Field2Ty, Field2Off)) |
| 188 | return false; |
| 189 | } |
| 190 | } |
| 191 | for (const FieldDecl *FD : RD->fields()) { |
| 192 | QualType QTy = FD->getType(); |
| 193 | if (FD->isBitField()) { |
| 194 | unsigned BitWidth = FD->getBitWidthValue(); |
| 195 | // Zero-width bitfields are ignored. |
| 196 | if (BitWidth == 0) |
| 197 | continue; |
| 198 | // Allow a bitfield with a type greater than GRLen as long as the |
| 199 | // bitwidth is GRLen or less. |
| 200 | if (getContext().getTypeSize(T: QTy) > GRLen && BitWidth <= GRLen) { |
| 201 | QTy = getContext().getIntTypeForBitwidth(DestWidth: GRLen, Signed: false); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | if (!detectFARsEligibleStructHelper( |
| 206 | Ty: QTy, |
| 207 | CurOff: CurOff + getContext().toCharUnitsFromBits( |
| 208 | BitSize: Layout.getFieldOffset(FieldNo: FD->getFieldIndex())), |
| 209 | Field1Ty, Field1Off, Field2Ty, Field2Off)) |
| 210 | return false; |
| 211 | } |
| 212 | return Field1Ty != nullptr; |
| 213 | } |
| 214 | |
| 215 | return false; |
| 216 | } |
| 217 | |
| 218 | // Determine if a struct is eligible to be passed in FARs (and GARs) (i.e., when |
| 219 | // flattened it contains a single fp value, fp+fp, or int+fp of appropriate |
| 220 | // size). If so, NeededFARs and NeededGARs are incremented appropriately. |
| 221 | bool LoongArchABIInfo::detectFARsEligibleStruct( |
| 222 | QualType Ty, llvm::Type *&Field1Ty, CharUnits &Field1Off, |
| 223 | llvm::Type *&Field2Ty, CharUnits &Field2Off, int &NeededGARs, |
| 224 | int &NeededFARs) const { |
| 225 | Field1Ty = nullptr; |
| 226 | Field2Ty = nullptr; |
| 227 | NeededGARs = 0; |
| 228 | NeededFARs = 0; |
| 229 | if (!detectFARsEligibleStructHelper(Ty, CurOff: CharUnits::Zero(), Field1Ty, |
| 230 | Field1Off, Field2Ty, Field2Off)) |
| 231 | return false; |
| 232 | if (!Field1Ty) |
| 233 | return false; |
| 234 | // Not really a candidate if we have a single int but no float. |
| 235 | if (Field1Ty && !Field2Ty && !Field1Ty->isFloatingPointTy()) |
| 236 | return false; |
| 237 | if (Field1Ty && Field1Ty->isFloatingPointTy()) |
| 238 | NeededFARs++; |
| 239 | else if (Field1Ty) |
| 240 | NeededGARs++; |
| 241 | if (Field2Ty && Field2Ty->isFloatingPointTy()) |
| 242 | NeededFARs++; |
| 243 | else if (Field2Ty) |
| 244 | NeededGARs++; |
| 245 | return true; |
| 246 | } |
| 247 | |
| 248 | // Call getCoerceAndExpand for the two-element flattened struct described by |
| 249 | // Field1Ty, Field1Off, Field2Ty, Field2Off. This method will create an |
| 250 | // appropriate coerceToType and unpaddedCoerceToType. |
| 251 | ABIArgInfo LoongArchABIInfo::coerceAndExpandFARsEligibleStruct( |
| 252 | llvm::Type *Field1Ty, CharUnits Field1Off, llvm::Type *Field2Ty, |
| 253 | CharUnits Field2Off) const { |
| 254 | SmallVector<llvm::Type *, 3> CoerceElts; |
| 255 | SmallVector<llvm::Type *, 2> UnpaddedCoerceElts; |
| 256 | if (!Field1Off.isZero()) |
| 257 | CoerceElts.push_back(Elt: llvm::ArrayType::get( |
| 258 | ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), NumElements: Field1Off.getQuantity())); |
| 259 | |
| 260 | CoerceElts.push_back(Elt: Field1Ty); |
| 261 | UnpaddedCoerceElts.push_back(Elt: Field1Ty); |
| 262 | |
| 263 | if (!Field2Ty) { |
| 264 | return ABIArgInfo::getCoerceAndExpand( |
| 265 | coerceToType: llvm::StructType::get(Context&: getVMContext(), Elements: CoerceElts, isPacked: !Field1Off.isZero()), |
| 266 | unpaddedCoerceToType: UnpaddedCoerceElts[0]); |
| 267 | } |
| 268 | |
| 269 | CharUnits Field2Align = |
| 270 | CharUnits::fromQuantity(Quantity: getDataLayout().getABITypeAlign(Ty: Field2Ty)); |
| 271 | CharUnits Field1End = |
| 272 | Field1Off + |
| 273 | CharUnits::fromQuantity(Quantity: getDataLayout().getTypeStoreSize(Ty: Field1Ty)); |
| 274 | CharUnits Field2OffNoPadNoPack = Field1End.alignTo(Align: Field2Align); |
| 275 | |
| 276 | CharUnits Padding = CharUnits::Zero(); |
| 277 | if (Field2Off > Field2OffNoPadNoPack) |
| 278 | Padding = Field2Off - Field2OffNoPadNoPack; |
| 279 | else if (Field2Off != Field2Align && Field2Off > Field1End) |
| 280 | Padding = Field2Off - Field1End; |
| 281 | |
| 282 | bool IsPacked = !Field2Off.isMultipleOf(N: Field2Align); |
| 283 | |
| 284 | if (!Padding.isZero()) |
| 285 | CoerceElts.push_back(Elt: llvm::ArrayType::get( |
| 286 | ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), NumElements: Padding.getQuantity())); |
| 287 | |
| 288 | CoerceElts.push_back(Elt: Field2Ty); |
| 289 | UnpaddedCoerceElts.push_back(Elt: Field2Ty); |
| 290 | |
| 291 | return ABIArgInfo::getCoerceAndExpand( |
| 292 | coerceToType: llvm::StructType::get(Context&: getVMContext(), Elements: CoerceElts, isPacked: IsPacked), |
| 293 | unpaddedCoerceToType: llvm::StructType::get(Context&: getVMContext(), Elements: UnpaddedCoerceElts, isPacked: IsPacked)); |
| 294 | } |
| 295 | |
| 296 | ABIArgInfo LoongArchABIInfo::classifyArgumentType(QualType Ty, bool IsFixed, |
| 297 | int &GARsLeft, |
| 298 | int &FARsLeft) const { |
| 299 | assert(GARsLeft <= NumGARs && "GAR tracking underflow" ); |
| 300 | Ty = useFirstFieldIfTransparentUnion(Ty); |
| 301 | |
| 302 | // Structures with either a non-trivial destructor or a non-trivial |
| 303 | // copy constructor are always passed indirectly. |
| 304 | if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(T: Ty, CXXABI&: getCXXABI())) { |
| 305 | if (GARsLeft) |
| 306 | GARsLeft -= 1; |
| 307 | return getNaturalAlignIndirect( |
| 308 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 309 | /*ByVal=*/RAA == CGCXXABI::RAA_DirectInMemory); |
| 310 | } |
| 311 | |
| 312 | uint64_t Size = getContext().getTypeSize(T: Ty); |
| 313 | |
| 314 | // Ignore empty struct or union whose size is zero, e.g. `struct { }` in C or |
| 315 | // `struct { int a[0]; }` in C++. In C++, `struct { }` is empty but it's size |
| 316 | // is 1 byte and g++ doesn't ignore it; clang++ matches this behaviour. |
| 317 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true) && Size == 0) |
| 318 | return ABIArgInfo::getIgnore(); |
| 319 | |
| 320 | // Pass floating point values via FARs if possible. |
| 321 | if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() && |
| 322 | FRLen >= Size && FARsLeft) { |
| 323 | FARsLeft--; |
| 324 | return ABIArgInfo::getDirect(); |
| 325 | } |
| 326 | |
| 327 | // Complex types for the *f or *d ABI must be passed directly rather than |
| 328 | // using CoerceAndExpand. |
| 329 | if (IsFixed && Ty->isComplexType() && FRLen && FARsLeft >= 2) { |
| 330 | QualType EltTy = Ty->castAs<ComplexType>()->getElementType(); |
| 331 | if (getContext().getTypeSize(T: EltTy) <= FRLen) { |
| 332 | FARsLeft -= 2; |
| 333 | return ABIArgInfo::getDirect(); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | if (IsFixed && FRLen && Ty->isStructureOrClassType()) { |
| 338 | llvm::Type *Field1Ty = nullptr; |
| 339 | llvm::Type *Field2Ty = nullptr; |
| 340 | CharUnits Field1Off = CharUnits::Zero(); |
| 341 | CharUnits Field2Off = CharUnits::Zero(); |
| 342 | int NeededGARs = 0; |
| 343 | int NeededFARs = 0; |
| 344 | bool IsCandidate = detectFARsEligibleStruct( |
| 345 | Ty, Field1Ty, Field1Off, Field2Ty, Field2Off, NeededGARs, NeededFARs); |
| 346 | if (IsCandidate && NeededGARs <= GARsLeft && NeededFARs <= FARsLeft) { |
| 347 | GARsLeft -= NeededGARs; |
| 348 | FARsLeft -= NeededFARs; |
| 349 | return coerceAndExpandFARsEligibleStruct(Field1Ty, Field1Off, Field2Ty, |
| 350 | Field2Off); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | uint64_t NeededAlign = getContext().getTypeAlign(T: Ty); |
| 355 | // Determine the number of GARs needed to pass the current argument |
| 356 | // according to the ABI. 2*GRLen-aligned varargs are passed in "aligned" |
| 357 | // register pairs, so may consume 3 registers. |
| 358 | int NeededGARs = 1; |
| 359 | if (!IsFixed && NeededAlign == 2 * GRLen) |
| 360 | NeededGARs = 2 + (GARsLeft % 2); |
| 361 | else if (Size > GRLen && Size <= 2 * GRLen) |
| 362 | NeededGARs = 2; |
| 363 | |
| 364 | if (NeededGARs > GARsLeft) |
| 365 | NeededGARs = GARsLeft; |
| 366 | |
| 367 | GARsLeft -= NeededGARs; |
| 368 | |
| 369 | if (!isAggregateTypeForABI(T: Ty) && !Ty->isVectorType()) { |
| 370 | // Treat an enum type as its underlying type. |
| 371 | if (const EnumType *EnumTy = Ty->getAs<EnumType>()) |
| 372 | Ty = EnumTy->getDecl()->getIntegerType(); |
| 373 | |
| 374 | // All integral types are promoted to GRLen width. |
| 375 | if (Size < GRLen && Ty->isIntegralOrEnumerationType()) |
| 376 | return extendType(Ty); |
| 377 | |
| 378 | if (const auto *EIT = Ty->getAs<BitIntType>()) { |
| 379 | if (EIT->getNumBits() < GRLen) |
| 380 | return extendType(Ty); |
| 381 | if (EIT->getNumBits() > 128 || |
| 382 | (!getContext().getTargetInfo().hasInt128Type() && |
| 383 | EIT->getNumBits() > 64)) |
| 384 | return getNaturalAlignIndirect( |
| 385 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 386 | /*ByVal=*/false); |
| 387 | } |
| 388 | |
| 389 | return ABIArgInfo::getDirect(); |
| 390 | } |
| 391 | |
| 392 | // Aggregates which are <= 2*GRLen will be passed in registers if possible, |
| 393 | // so coerce to integers. |
| 394 | if (Size <= 2 * GRLen) { |
| 395 | // Use a single GRLen int if possible, 2*GRLen if 2*GRLen alignment is |
| 396 | // required, and a 2-element GRLen array if only GRLen alignment is |
| 397 | // required. |
| 398 | if (Size <= GRLen) { |
| 399 | return ABIArgInfo::getDirect( |
| 400 | T: llvm::IntegerType::get(C&: getVMContext(), NumBits: GRLen)); |
| 401 | } |
| 402 | if (getContext().getTypeAlign(T: Ty) == 2 * GRLen) { |
| 403 | return ABIArgInfo::getDirect( |
| 404 | T: llvm::IntegerType::get(C&: getVMContext(), NumBits: 2 * GRLen)); |
| 405 | } |
| 406 | return ABIArgInfo::getDirect( |
| 407 | T: llvm::ArrayType::get(ElementType: llvm::IntegerType::get(C&: getVMContext(), NumBits: GRLen), NumElements: 2)); |
| 408 | } |
| 409 | return getNaturalAlignIndirect( |
| 410 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 411 | /*ByVal=*/false); |
| 412 | } |
| 413 | |
| 414 | ABIArgInfo LoongArchABIInfo::classifyReturnType(QualType RetTy) const { |
| 415 | if (RetTy->isVoidType()) |
| 416 | return ABIArgInfo::getIgnore(); |
| 417 | // The rules for return and argument types are the same, so defer to |
| 418 | // classifyArgumentType. |
| 419 | int GARsLeft = 2; |
| 420 | int FARsLeft = FRLen ? 2 : 0; |
| 421 | return classifyArgumentType(Ty: RetTy, /*IsFixed=*/true, GARsLeft, FARsLeft); |
| 422 | } |
| 423 | |
| 424 | RValue LoongArchABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
| 425 | QualType Ty, AggValueSlot Slot) const { |
| 426 | CharUnits SlotSize = CharUnits::fromQuantity(Quantity: GRLen / 8); |
| 427 | |
| 428 | // Empty records are ignored for parameter passing purposes. |
| 429 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true)) |
| 430 | return Slot.asRValue(); |
| 431 | |
| 432 | auto TInfo = getContext().getTypeInfoInChars(T: Ty); |
| 433 | |
| 434 | // Arguments bigger than 2*GRLen bytes are passed indirectly. |
| 435 | return emitVoidPtrVAArg(CGF, VAListAddr, ValueTy: Ty, |
| 436 | /*IsIndirect=*/TInfo.Width > 2 * SlotSize, ValueInfo: TInfo, |
| 437 | SlotSizeAndAlign: SlotSize, |
| 438 | /*AllowHigherAlign=*/true, Slot); |
| 439 | } |
| 440 | |
| 441 | ABIArgInfo LoongArchABIInfo::extendType(QualType Ty) const { |
| 442 | int TySize = getContext().getTypeSize(T: Ty); |
| 443 | // LA64 ABI requires unsigned 32 bit integers to be sign extended. |
| 444 | if (GRLen == 64 && Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32) |
| 445 | return ABIArgInfo::getSignExtend(Ty); |
| 446 | return ABIArgInfo::getExtend(Ty); |
| 447 | } |
| 448 | |
| 449 | namespace { |
| 450 | class LoongArchTargetCodeGenInfo : public TargetCodeGenInfo { |
| 451 | public: |
| 452 | LoongArchTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned GRLen, |
| 453 | unsigned FRLen) |
| 454 | : TargetCodeGenInfo( |
| 455 | std::make_unique<LoongArchABIInfo>(args&: CGT, args&: GRLen, args&: FRLen)) {} |
| 456 | }; |
| 457 | } // namespace |
| 458 | |
| 459 | std::unique_ptr<TargetCodeGenInfo> |
| 460 | CodeGen::createLoongArchTargetCodeGenInfo(CodeGenModule &CGM, unsigned GRLen, |
| 461 | unsigned FLen) { |
| 462 | return std::make_unique<LoongArchTargetCodeGenInfo>(args&: CGM.getTypes(), args&: GRLen, |
| 463 | args&: FLen); |
| 464 | } |
| 465 | |