| 1 | //===- RISCV.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 | #include "llvm/TargetParser/RISCVTargetParser.h" |
| 12 | |
| 13 | using namespace clang; |
| 14 | using namespace clang::CodeGen; |
| 15 | |
| 16 | //===----------------------------------------------------------------------===// |
| 17 | // RISC-V ABI Implementation |
| 18 | //===----------------------------------------------------------------------===// |
| 19 | |
| 20 | namespace { |
| 21 | class RISCVABIInfo : public DefaultABIInfo { |
| 22 | private: |
| 23 | // Size of the integer ('x') registers in bits. |
| 24 | unsigned XLen; |
| 25 | // Size of the floating point ('f') registers in bits. Note that the target |
| 26 | // ISA might have a wider FLen than the selected ABI (e.g. an RV32IF target |
| 27 | // with soft float ABI has FLen==0). |
| 28 | unsigned FLen; |
| 29 | const int NumArgGPRs; |
| 30 | const int NumArgFPRs; |
| 31 | const bool EABI; |
| 32 | bool detectFPCCEligibleStructHelper(QualType Ty, CharUnits CurOff, |
| 33 | llvm::Type *&Field1Ty, |
| 34 | CharUnits &Field1Off, |
| 35 | llvm::Type *&Field2Ty, |
| 36 | CharUnits &Field2Off) const; |
| 37 | |
| 38 | bool detectVLSCCEligibleStruct(QualType Ty, unsigned ABIVLen, |
| 39 | llvm::Type *&VLSType) const; |
| 40 | |
| 41 | public: |
| 42 | RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, unsigned FLen, |
| 43 | bool EABI) |
| 44 | : DefaultABIInfo(CGT), XLen(XLen), FLen(FLen), NumArgGPRs(EABI ? 6 : 8), |
| 45 | NumArgFPRs(FLen != 0 ? 8 : 0), EABI(EABI) {} |
| 46 | |
| 47 | // DefaultABIInfo's classifyReturnType and classifyArgumentType are |
| 48 | // non-virtual, but computeInfo is virtual, so we overload it. |
| 49 | void computeInfo(CGFunctionInfo &FI) const override; |
| 50 | |
| 51 | ABIArgInfo classifyArgumentType(QualType Ty, bool IsFixed, int &ArgGPRsLeft, |
| 52 | int &ArgFPRsLeft, unsigned ABIVLen) const; |
| 53 | ABIArgInfo classifyReturnType(QualType RetTy, unsigned ABIVLen) const; |
| 54 | |
| 55 | RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, |
| 56 | AggValueSlot Slot) const override; |
| 57 | |
| 58 | ABIArgInfo extendType(QualType Ty, llvm::Type *CoerceTy = nullptr) const; |
| 59 | |
| 60 | bool detectFPCCEligibleStruct(QualType Ty, llvm::Type *&Field1Ty, |
| 61 | CharUnits &Field1Off, llvm::Type *&Field2Ty, |
| 62 | CharUnits &Field2Off, int &NeededArgGPRs, |
| 63 | int &NeededArgFPRs) const; |
| 64 | ABIArgInfo coerceAndExpandFPCCEligibleStruct(llvm::Type *Field1Ty, |
| 65 | CharUnits Field1Off, |
| 66 | llvm::Type *Field2Ty, |
| 67 | CharUnits Field2Off) const; |
| 68 | |
| 69 | ABIArgInfo coerceVLSVector(QualType Ty, unsigned ABIVLen = 0) const; |
| 70 | |
| 71 | using ABIInfo::appendAttributeMangling; |
| 72 | void appendAttributeMangling(TargetClonesAttr *Attr, unsigned Index, |
| 73 | raw_ostream &Out) const override; |
| 74 | void appendAttributeMangling(StringRef AttrStr, |
| 75 | raw_ostream &Out) const override; |
| 76 | }; |
| 77 | } // end anonymous namespace |
| 78 | |
| 79 | void RISCVABIInfo::appendAttributeMangling(TargetClonesAttr *Attr, |
| 80 | unsigned Index, |
| 81 | raw_ostream &Out) const { |
| 82 | appendAttributeMangling(AttrStr: Attr->getFeatureStr(Index), Out); |
| 83 | } |
| 84 | |
| 85 | void RISCVABIInfo::appendAttributeMangling(StringRef AttrStr, |
| 86 | raw_ostream &Out) const { |
| 87 | if (AttrStr == "default" ) { |
| 88 | Out << ".default" ; |
| 89 | return; |
| 90 | } |
| 91 | |
| 92 | Out << '.'; |
| 93 | |
| 94 | SmallVector<StringRef, 8> Attrs; |
| 95 | AttrStr.split(A&: Attrs, Separator: ';'); |
| 96 | |
| 97 | // Only consider the arch string. |
| 98 | StringRef ArchStr; |
| 99 | for (auto &Attr : Attrs) { |
| 100 | if (Attr.starts_with(Prefix: "arch=" )) |
| 101 | ArchStr = Attr; |
| 102 | } |
| 103 | |
| 104 | // Extract features string. |
| 105 | SmallVector<StringRef, 8> Features; |
| 106 | ArchStr.consume_front(Prefix: "arch=" ); |
| 107 | ArchStr.split(A&: Features, Separator: ','); |
| 108 | |
| 109 | llvm::stable_sort(Range&: Features); |
| 110 | |
| 111 | for (auto Feat : Features) { |
| 112 | Feat.consume_front(Prefix: "+" ); |
| 113 | Out << "_" << Feat; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | void RISCVABIInfo::computeInfo(CGFunctionInfo &FI) const { |
| 118 | unsigned ABIVLen; |
| 119 | switch (FI.getExtInfo().getCC()) { |
| 120 | default: |
| 121 | ABIVLen = 0; |
| 122 | break; |
| 123 | #define CC_VLS_CASE(ABI_VLEN) \ |
| 124 | case CallingConv::CC_RISCVVLSCall_##ABI_VLEN: \ |
| 125 | ABIVLen = ABI_VLEN; \ |
| 126 | break; |
| 127 | CC_VLS_CASE(32) |
| 128 | CC_VLS_CASE(64) |
| 129 | CC_VLS_CASE(128) |
| 130 | CC_VLS_CASE(256) |
| 131 | CC_VLS_CASE(512) |
| 132 | CC_VLS_CASE(1024) |
| 133 | CC_VLS_CASE(2048) |
| 134 | CC_VLS_CASE(4096) |
| 135 | CC_VLS_CASE(8192) |
| 136 | CC_VLS_CASE(16384) |
| 137 | CC_VLS_CASE(32768) |
| 138 | CC_VLS_CASE(65536) |
| 139 | #undef CC_VLS_CASE |
| 140 | } |
| 141 | QualType RetTy = FI.getReturnType(); |
| 142 | if (!getCXXABI().classifyReturnType(FI)) |
| 143 | FI.getReturnInfo() = classifyReturnType(RetTy, ABIVLen); |
| 144 | |
| 145 | // IsRetIndirect is true if classifyArgumentType indicated the value should |
| 146 | // be passed indirect, or if the type size is a scalar greater than 2*XLen |
| 147 | // and not a complex type with elements <= FLen. e.g. fp128 is passed direct |
| 148 | // in LLVM IR, relying on the backend lowering code to rewrite the argument |
| 149 | // list and pass indirectly on RV32. |
| 150 | bool IsRetIndirect = FI.getReturnInfo().getKind() == ABIArgInfo::Indirect; |
| 151 | if (!IsRetIndirect && RetTy->isScalarType() && |
| 152 | getContext().getTypeSize(T: RetTy) > (2 * XLen)) { |
| 153 | if (RetTy->isComplexType() && FLen) { |
| 154 | QualType EltTy = RetTy->castAs<ComplexType>()->getElementType(); |
| 155 | IsRetIndirect = getContext().getTypeSize(T: EltTy) > FLen; |
| 156 | } else { |
| 157 | // This is a normal scalar > 2*XLen, such as fp128 on RV32. |
| 158 | IsRetIndirect = true; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | int ArgGPRsLeft = IsRetIndirect ? NumArgGPRs - 1 : NumArgGPRs; |
| 163 | int ArgFPRsLeft = NumArgFPRs; |
| 164 | int NumFixedArgs = FI.getNumRequiredArgs(); |
| 165 | |
| 166 | int ArgNum = 0; |
| 167 | for (auto &ArgInfo : FI.arguments()) { |
| 168 | bool IsFixed = ArgNum < NumFixedArgs; |
| 169 | ArgInfo.info = classifyArgumentType(Ty: ArgInfo.type, IsFixed, ArgGPRsLeft, |
| 170 | ArgFPRsLeft, ABIVLen); |
| 171 | ArgNum++; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | // Returns true if the struct is a potential candidate for the floating point |
| 176 | // calling convention. If this function returns true, the caller is |
| 177 | // responsible for checking that if there is only a single field then that |
| 178 | // field is a float. |
| 179 | bool RISCVABIInfo::detectFPCCEligibleStructHelper(QualType Ty, CharUnits CurOff, |
| 180 | llvm::Type *&Field1Ty, |
| 181 | CharUnits &Field1Off, |
| 182 | llvm::Type *&Field2Ty, |
| 183 | CharUnits &Field2Off) const { |
| 184 | bool IsInt = Ty->isIntegralOrEnumerationType(); |
| 185 | bool IsFloat = Ty->isRealFloatingType(); |
| 186 | |
| 187 | if (IsInt || IsFloat) { |
| 188 | uint64_t Size = getContext().getTypeSize(T: Ty); |
| 189 | if (IsInt && Size > XLen) |
| 190 | return false; |
| 191 | // Can't be eligible if larger than the FP registers. Handling of half |
| 192 | // precision values has been specified in the ABI, so don't block those. |
| 193 | if (IsFloat && Size > FLen) |
| 194 | return false; |
| 195 | // Can't be eligible if an integer type was already found (int+int pairs |
| 196 | // are not eligible). |
| 197 | if (IsInt && Field1Ty && Field1Ty->isIntegerTy()) |
| 198 | return false; |
| 199 | if (!Field1Ty) { |
| 200 | Field1Ty = CGT.ConvertType(T: Ty); |
| 201 | Field1Off = CurOff; |
| 202 | return true; |
| 203 | } |
| 204 | if (!Field2Ty) { |
| 205 | Field2Ty = CGT.ConvertType(T: Ty); |
| 206 | Field2Off = CurOff; |
| 207 | return true; |
| 208 | } |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | if (auto CTy = Ty->getAs<ComplexType>()) { |
| 213 | if (Field1Ty) |
| 214 | return false; |
| 215 | QualType EltTy = CTy->getElementType(); |
| 216 | if (getContext().getTypeSize(T: EltTy) > FLen) |
| 217 | return false; |
| 218 | Field1Ty = CGT.ConvertType(T: EltTy); |
| 219 | Field1Off = CurOff; |
| 220 | Field2Ty = Field1Ty; |
| 221 | Field2Off = Field1Off + getContext().getTypeSizeInChars(T: EltTy); |
| 222 | return true; |
| 223 | } |
| 224 | |
| 225 | if (const ConstantArrayType *ATy = getContext().getAsConstantArrayType(T: Ty)) { |
| 226 | uint64_t ArraySize = ATy->getZExtSize(); |
| 227 | QualType EltTy = ATy->getElementType(); |
| 228 | // Non-zero-length arrays of empty records make the struct ineligible for |
| 229 | // the FP calling convention in C++. |
| 230 | if (const auto *RTy = EltTy->getAs<RecordType>()) { |
| 231 | if (ArraySize != 0 && isa<CXXRecordDecl>(Val: RTy->getDecl()) && |
| 232 | isEmptyRecord(Context&: getContext(), T: EltTy, AllowArrays: true, AsIfNoUniqueAddr: true)) |
| 233 | return false; |
| 234 | } |
| 235 | CharUnits EltSize = getContext().getTypeSizeInChars(T: EltTy); |
| 236 | for (uint64_t i = 0; i < ArraySize; ++i) { |
| 237 | bool Ret = detectFPCCEligibleStructHelper(Ty: EltTy, CurOff, Field1Ty, |
| 238 | Field1Off, Field2Ty, Field2Off); |
| 239 | if (!Ret) |
| 240 | return false; |
| 241 | CurOff += EltSize; |
| 242 | } |
| 243 | return true; |
| 244 | } |
| 245 | |
| 246 | if (const auto *RTy = Ty->getAs<RecordType>()) { |
| 247 | // Structures with either a non-trivial destructor or a non-trivial |
| 248 | // copy constructor are not eligible for the FP calling convention. |
| 249 | if (getRecordArgABI(T: Ty, CXXABI&: CGT.getCXXABI())) |
| 250 | return false; |
| 251 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true, AsIfNoUniqueAddr: true)) |
| 252 | return true; |
| 253 | const RecordDecl *RD = RTy->getDecl(); |
| 254 | // Unions aren't eligible unless they're empty (which is caught above). |
| 255 | if (RD->isUnion()) |
| 256 | return false; |
| 257 | const ASTRecordLayout &Layout = getContext().getASTRecordLayout(D: RD); |
| 258 | // If this is a C++ record, check the bases first. |
| 259 | if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(Val: RD)) { |
| 260 | for (const CXXBaseSpecifier &B : CXXRD->bases()) { |
| 261 | const auto *BDecl = |
| 262 | cast<CXXRecordDecl>(Val: B.getType()->castAs<RecordType>()->getDecl()); |
| 263 | CharUnits BaseOff = Layout.getBaseClassOffset(Base: BDecl); |
| 264 | bool Ret = detectFPCCEligibleStructHelper(Ty: B.getType(), CurOff: CurOff + BaseOff, |
| 265 | Field1Ty, Field1Off, Field2Ty, |
| 266 | Field2Off); |
| 267 | if (!Ret) |
| 268 | return false; |
| 269 | } |
| 270 | } |
| 271 | int ZeroWidthBitFieldCount = 0; |
| 272 | for (const FieldDecl *FD : RD->fields()) { |
| 273 | uint64_t FieldOffInBits = Layout.getFieldOffset(FieldNo: FD->getFieldIndex()); |
| 274 | QualType QTy = FD->getType(); |
| 275 | if (FD->isBitField()) { |
| 276 | unsigned BitWidth = FD->getBitWidthValue(); |
| 277 | // Allow a bitfield with a type greater than XLen as long as the |
| 278 | // bitwidth is XLen or less. |
| 279 | if (getContext().getTypeSize(T: QTy) > XLen && BitWidth <= XLen) |
| 280 | QTy = getContext().getIntTypeForBitwidth(DestWidth: XLen, Signed: false); |
| 281 | if (BitWidth == 0) { |
| 282 | ZeroWidthBitFieldCount++; |
| 283 | continue; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | bool Ret = detectFPCCEligibleStructHelper( |
| 288 | Ty: QTy, CurOff: CurOff + getContext().toCharUnitsFromBits(BitSize: FieldOffInBits), |
| 289 | Field1Ty, Field1Off, Field2Ty, Field2Off); |
| 290 | if (!Ret) |
| 291 | return false; |
| 292 | |
| 293 | // As a quirk of the ABI, zero-width bitfields aren't ignored for fp+fp |
| 294 | // or int+fp structs, but are ignored for a struct with an fp field and |
| 295 | // any number of zero-width bitfields. |
| 296 | if (Field2Ty && ZeroWidthBitFieldCount > 0) |
| 297 | return false; |
| 298 | } |
| 299 | return Field1Ty != nullptr; |
| 300 | } |
| 301 | |
| 302 | return false; |
| 303 | } |
| 304 | |
| 305 | // Determine if a struct is eligible for passing according to the floating |
| 306 | // point calling convention (i.e., when flattened it contains a single fp |
| 307 | // value, fp+fp, or int+fp of appropriate size). If so, NeededArgFPRs and |
| 308 | // NeededArgGPRs are incremented appropriately. |
| 309 | bool RISCVABIInfo::detectFPCCEligibleStruct(QualType Ty, llvm::Type *&Field1Ty, |
| 310 | CharUnits &Field1Off, |
| 311 | llvm::Type *&Field2Ty, |
| 312 | CharUnits &Field2Off, |
| 313 | int &NeededArgGPRs, |
| 314 | int &NeededArgFPRs) const { |
| 315 | Field1Ty = nullptr; |
| 316 | Field2Ty = nullptr; |
| 317 | NeededArgGPRs = 0; |
| 318 | NeededArgFPRs = 0; |
| 319 | bool IsCandidate = detectFPCCEligibleStructHelper( |
| 320 | Ty, CurOff: CharUnits::Zero(), Field1Ty, Field1Off, Field2Ty, Field2Off); |
| 321 | if (!Field1Ty) |
| 322 | return false; |
| 323 | // Not really a candidate if we have a single int but no float. |
| 324 | if (Field1Ty && !Field2Ty && !Field1Ty->isFloatingPointTy()) |
| 325 | return false; |
| 326 | if (!IsCandidate) |
| 327 | return false; |
| 328 | if (Field1Ty && Field1Ty->isFloatingPointTy()) |
| 329 | NeededArgFPRs++; |
| 330 | else if (Field1Ty) |
| 331 | NeededArgGPRs++; |
| 332 | if (Field2Ty && Field2Ty->isFloatingPointTy()) |
| 333 | NeededArgFPRs++; |
| 334 | else if (Field2Ty) |
| 335 | NeededArgGPRs++; |
| 336 | return true; |
| 337 | } |
| 338 | |
| 339 | // Call getCoerceAndExpand for the two-element flattened struct described by |
| 340 | // Field1Ty, Field1Off, Field2Ty, Field2Off. This method will create an |
| 341 | // appropriate coerceToType and unpaddedCoerceToType. |
| 342 | ABIArgInfo RISCVABIInfo::coerceAndExpandFPCCEligibleStruct( |
| 343 | llvm::Type *Field1Ty, CharUnits Field1Off, llvm::Type *Field2Ty, |
| 344 | CharUnits Field2Off) const { |
| 345 | SmallVector<llvm::Type *, 3> CoerceElts; |
| 346 | SmallVector<llvm::Type *, 2> UnpaddedCoerceElts; |
| 347 | if (!Field1Off.isZero()) |
| 348 | CoerceElts.push_back(Elt: llvm::ArrayType::get( |
| 349 | ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), NumElements: Field1Off.getQuantity())); |
| 350 | |
| 351 | CoerceElts.push_back(Elt: Field1Ty); |
| 352 | UnpaddedCoerceElts.push_back(Elt: Field1Ty); |
| 353 | |
| 354 | if (!Field2Ty) { |
| 355 | return ABIArgInfo::getCoerceAndExpand( |
| 356 | coerceToType: llvm::StructType::get(Context&: getVMContext(), Elements: CoerceElts, isPacked: !Field1Off.isZero()), |
| 357 | unpaddedCoerceToType: UnpaddedCoerceElts[0]); |
| 358 | } |
| 359 | |
| 360 | CharUnits Field2Align = |
| 361 | CharUnits::fromQuantity(Quantity: getDataLayout().getABITypeAlign(Ty: Field2Ty)); |
| 362 | CharUnits Field1End = Field1Off + |
| 363 | CharUnits::fromQuantity(Quantity: getDataLayout().getTypeStoreSize(Ty: Field1Ty)); |
| 364 | CharUnits Field2OffNoPadNoPack = Field1End.alignTo(Align: Field2Align); |
| 365 | |
| 366 | CharUnits Padding = CharUnits::Zero(); |
| 367 | if (Field2Off > Field2OffNoPadNoPack) |
| 368 | Padding = Field2Off - Field2OffNoPadNoPack; |
| 369 | else if (Field2Off != Field2Align && Field2Off > Field1End) |
| 370 | Padding = Field2Off - Field1End; |
| 371 | |
| 372 | bool IsPacked = !Field2Off.isMultipleOf(N: Field2Align); |
| 373 | |
| 374 | if (!Padding.isZero()) |
| 375 | CoerceElts.push_back(Elt: llvm::ArrayType::get( |
| 376 | ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), NumElements: Padding.getQuantity())); |
| 377 | |
| 378 | CoerceElts.push_back(Elt: Field2Ty); |
| 379 | UnpaddedCoerceElts.push_back(Elt: Field2Ty); |
| 380 | |
| 381 | auto CoerceToType = |
| 382 | llvm::StructType::get(Context&: getVMContext(), Elements: CoerceElts, isPacked: IsPacked); |
| 383 | auto UnpaddedCoerceToType = |
| 384 | llvm::StructType::get(Context&: getVMContext(), Elements: UnpaddedCoerceElts, isPacked: IsPacked); |
| 385 | |
| 386 | return ABIArgInfo::getCoerceAndExpand(coerceToType: CoerceToType, unpaddedCoerceToType: UnpaddedCoerceToType); |
| 387 | } |
| 388 | |
| 389 | bool RISCVABIInfo::detectVLSCCEligibleStruct(QualType Ty, unsigned ABIVLen, |
| 390 | llvm::Type *&VLSType) const { |
| 391 | // No riscv_vls_cc attribute. |
| 392 | if (ABIVLen == 0) |
| 393 | return false; |
| 394 | |
| 395 | // Legal struct for VLS calling convention should fulfill following rules: |
| 396 | // 1. Struct element should be either "homogeneous fixed-length vectors" or "a |
| 397 | // fixed-length vector array". |
| 398 | // 2. Number of struct elements or array elements should be greater or equal |
| 399 | // to 1 and less or equal to 8 |
| 400 | // 3. Total number of vector registers needed should not exceed 8. |
| 401 | // |
| 402 | // Examples: Assume ABI_VLEN = 128. |
| 403 | // These are legal structs: |
| 404 | // a. Structs with 1~8 "same" fixed-length vectors, e.g. |
| 405 | // struct { |
| 406 | // __attribute__((vector_size(16))) int a; |
| 407 | // __attribute__((vector_size(16))) int b; |
| 408 | // } |
| 409 | // |
| 410 | // b. Structs with "single" fixed-length vector array with lengh 1~8, e.g. |
| 411 | // struct { |
| 412 | // __attribute__((vector_size(16))) int a[3]; |
| 413 | // } |
| 414 | // These are illegal structs: |
| 415 | // a. Structs with 9 fixed-length vectors, e.g. |
| 416 | // struct { |
| 417 | // __attribute__((vector_size(16))) int a; |
| 418 | // __attribute__((vector_size(16))) int b; |
| 419 | // __attribute__((vector_size(16))) int c; |
| 420 | // __attribute__((vector_size(16))) int d; |
| 421 | // __attribute__((vector_size(16))) int e; |
| 422 | // __attribute__((vector_size(16))) int f; |
| 423 | // __attribute__((vector_size(16))) int g; |
| 424 | // __attribute__((vector_size(16))) int h; |
| 425 | // __attribute__((vector_size(16))) int i; |
| 426 | // } |
| 427 | // |
| 428 | // b. Structs with "multiple" fixed-length vector array, e.g. |
| 429 | // struct { |
| 430 | // __attribute__((vector_size(16))) int a[2]; |
| 431 | // __attribute__((vector_size(16))) int b[2]; |
| 432 | // } |
| 433 | // |
| 434 | // c. Vector registers needed exceeds 8, e.g. |
| 435 | // struct { |
| 436 | // // Registers needed for single fixed-length element: |
| 437 | // // 64 * 8 / ABI_VLEN = 4 |
| 438 | // __attribute__((vector_size(64))) int a; |
| 439 | // __attribute__((vector_size(64))) int b; |
| 440 | // __attribute__((vector_size(64))) int c; |
| 441 | // __attribute__((vector_size(64))) int d; |
| 442 | // } |
| 443 | // |
| 444 | // Struct of 1 fixed-length vector is passed as a scalable vector. |
| 445 | // Struct of >1 fixed-length vectors are passed as vector tuple. |
| 446 | // Struct of 1 array of fixed-length vectors is passed as a scalable vector. |
| 447 | // Otherwise, pass the struct indirectly. |
| 448 | |
| 449 | if (llvm::StructType *STy = dyn_cast<llvm::StructType>(Val: CGT.ConvertType(T: Ty))) { |
| 450 | unsigned NumElts = STy->getStructNumElements(); |
| 451 | if (NumElts > 8) |
| 452 | return false; |
| 453 | |
| 454 | auto *FirstEltTy = STy->getElementType(N: 0); |
| 455 | if (!STy->containsHomogeneousTypes()) |
| 456 | return false; |
| 457 | |
| 458 | // Check structure of fixed-length vectors and turn them into vector tuple |
| 459 | // type if legal. |
| 460 | if (auto *FixedVecTy = dyn_cast<llvm::FixedVectorType>(Val: FirstEltTy)) { |
| 461 | if (NumElts == 1) { |
| 462 | // Handle single fixed-length vector. |
| 463 | VLSType = llvm::ScalableVectorType::get( |
| 464 | ElementType: FixedVecTy->getElementType(), |
| 465 | MinNumElts: llvm::divideCeil(Numerator: FixedVecTy->getNumElements() * |
| 466 | llvm::RISCV::RVVBitsPerBlock, |
| 467 | Denominator: ABIVLen)); |
| 468 | // Check registers needed <= 8. |
| 469 | return llvm::divideCeil( |
| 470 | Numerator: FixedVecTy->getNumElements() * |
| 471 | FixedVecTy->getElementType()->getScalarSizeInBits(), |
| 472 | Denominator: ABIVLen) <= 8; |
| 473 | } |
| 474 | // LMUL |
| 475 | // = fixed-length vector size / ABIVLen |
| 476 | // = 8 * I8EltCount / RVVBitsPerBlock |
| 477 | // => |
| 478 | // I8EltCount |
| 479 | // = (fixed-length vector size * RVVBitsPerBlock) / (ABIVLen * 8) |
| 480 | unsigned I8EltCount = llvm::divideCeil( |
| 481 | Numerator: FixedVecTy->getNumElements() * |
| 482 | FixedVecTy->getElementType()->getScalarSizeInBits() * |
| 483 | llvm::RISCV::RVVBitsPerBlock, |
| 484 | Denominator: ABIVLen * 8); |
| 485 | VLSType = llvm::TargetExtType::get( |
| 486 | Context&: getVMContext(), Name: "riscv.vector.tuple" , |
| 487 | Types: llvm::ScalableVectorType::get(ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), |
| 488 | MinNumElts: I8EltCount), |
| 489 | Ints: NumElts); |
| 490 | // Check registers needed <= 8. |
| 491 | return NumElts * |
| 492 | llvm::divideCeil( |
| 493 | Numerator: FixedVecTy->getNumElements() * |
| 494 | FixedVecTy->getElementType()->getScalarSizeInBits(), |
| 495 | Denominator: ABIVLen) <= |
| 496 | 8; |
| 497 | } |
| 498 | |
| 499 | // If elements are not fixed-length vectors, it should be an array. |
| 500 | if (NumElts != 1) |
| 501 | return false; |
| 502 | |
| 503 | // Check array of fixed-length vector and turn it into scalable vector type |
| 504 | // if legal. |
| 505 | if (auto *ArrTy = dyn_cast<llvm::ArrayType>(Val: FirstEltTy)) { |
| 506 | unsigned NumArrElt = ArrTy->getNumElements(); |
| 507 | if (NumArrElt > 8) |
| 508 | return false; |
| 509 | |
| 510 | auto *ArrEltTy = dyn_cast<llvm::FixedVectorType>(Val: ArrTy->getElementType()); |
| 511 | if (!ArrEltTy) |
| 512 | return false; |
| 513 | |
| 514 | // LMUL |
| 515 | // = NumArrElt * fixed-length vector size / ABIVLen |
| 516 | // = fixed-length vector elt size * ScalVecNumElts / RVVBitsPerBlock |
| 517 | // => |
| 518 | // ScalVecNumElts |
| 519 | // = (NumArrElt * fixed-length vector size * RVVBitsPerBlock) / |
| 520 | // (ABIVLen * fixed-length vector elt size) |
| 521 | // = NumArrElt * num fixed-length vector elt * RVVBitsPerBlock / |
| 522 | // ABIVLen |
| 523 | unsigned ScalVecNumElts = llvm::divideCeil( |
| 524 | Numerator: NumArrElt * ArrEltTy->getNumElements() * llvm::RISCV::RVVBitsPerBlock, |
| 525 | Denominator: ABIVLen); |
| 526 | VLSType = llvm::ScalableVectorType::get(ElementType: ArrEltTy->getElementType(), |
| 527 | MinNumElts: ScalVecNumElts); |
| 528 | // Check registers needed <= 8. |
| 529 | return llvm::divideCeil( |
| 530 | Numerator: ScalVecNumElts * |
| 531 | ArrEltTy->getElementType()->getScalarSizeInBits(), |
| 532 | Denominator: llvm::RISCV::RVVBitsPerBlock) <= 8; |
| 533 | } |
| 534 | } |
| 535 | return false; |
| 536 | } |
| 537 | |
| 538 | // Fixed-length RVV vectors are represented as scalable vectors in function |
| 539 | // args/return and must be coerced from fixed vectors. |
| 540 | ABIArgInfo RISCVABIInfo::coerceVLSVector(QualType Ty, unsigned ABIVLen) const { |
| 541 | assert(Ty->isVectorType() && "expected vector type!" ); |
| 542 | |
| 543 | const auto *VT = Ty->castAs<VectorType>(); |
| 544 | assert(VT->getElementType()->isBuiltinType() && "expected builtin type!" ); |
| 545 | |
| 546 | auto VScale = getContext().getTargetInfo().getVScaleRange( |
| 547 | LangOpts: getContext().getLangOpts(), Mode: TargetInfo::ArmStreamingKind::NotStreaming); |
| 548 | |
| 549 | unsigned NumElts = VT->getNumElements(); |
| 550 | llvm::Type *EltType = llvm::Type::getInt1Ty(C&: getVMContext()); |
| 551 | switch (VT->getVectorKind()) { |
| 552 | case VectorKind::RVVFixedLengthMask_1: |
| 553 | break; |
| 554 | case VectorKind::RVVFixedLengthMask_2: |
| 555 | NumElts *= 2; |
| 556 | break; |
| 557 | case VectorKind::RVVFixedLengthMask_4: |
| 558 | NumElts *= 4; |
| 559 | break; |
| 560 | case VectorKind::RVVFixedLengthMask: |
| 561 | NumElts *= 8; |
| 562 | break; |
| 563 | default: |
| 564 | assert((VT->getVectorKind() == VectorKind::Generic || |
| 565 | VT->getVectorKind() == VectorKind::RVVFixedLengthData) && |
| 566 | "Unexpected vector kind" ); |
| 567 | EltType = CGT.ConvertType(T: VT->getElementType()); |
| 568 | } |
| 569 | |
| 570 | llvm::ScalableVectorType *ResType; |
| 571 | |
| 572 | if (ABIVLen == 0) { |
| 573 | // The MinNumElts is simplified from equation: |
| 574 | // NumElts / VScale = |
| 575 | // (EltSize * NumElts / (VScale * RVVBitsPerBlock)) |
| 576 | // * (RVVBitsPerBlock / EltSize) |
| 577 | ResType = llvm::ScalableVectorType::get(ElementType: EltType, MinNumElts: NumElts / VScale->first); |
| 578 | } else { |
| 579 | // Check registers needed <= 8. |
| 580 | if ((EltType->getScalarSizeInBits() * NumElts / ABIVLen) > 8) |
| 581 | return getNaturalAlignIndirect( |
| 582 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 583 | /*ByVal=*/false); |
| 584 | |
| 585 | // Generic vector |
| 586 | // The number of elements needs to be at least 1. |
| 587 | ResType = llvm::ScalableVectorType::get( |
| 588 | ElementType: EltType, |
| 589 | MinNumElts: llvm::divideCeil(Numerator: NumElts * llvm::RISCV::RVVBitsPerBlock, Denominator: ABIVLen)); |
| 590 | |
| 591 | // If the corresponding extension is not supported, just make it an i8 |
| 592 | // vector with same LMUL. |
| 593 | const TargetInfo &TI = getContext().getTargetInfo(); |
| 594 | if ((EltType->isHalfTy() && !TI.hasFeature(Feature: "zvfhmin" )) || |
| 595 | (EltType->isBFloatTy() && !TI.hasFeature(Feature: "zvfbfmin" )) || |
| 596 | (EltType->isFloatTy() && !TI.hasFeature(Feature: "zve32f" )) || |
| 597 | (EltType->isDoubleTy() && !TI.hasFeature(Feature: "zve64d" )) || |
| 598 | (EltType->isIntegerTy(Bitwidth: 64) && !TI.hasFeature(Feature: "zve64x" )) || |
| 599 | EltType->isIntegerTy(Bitwidth: 128)) { |
| 600 | // The number of elements needs to be at least 1. |
| 601 | ResType = llvm::ScalableVectorType::get( |
| 602 | ElementType: llvm::Type::getInt8Ty(C&: getVMContext()), |
| 603 | MinNumElts: llvm::divideCeil(Numerator: EltType->getScalarSizeInBits() * NumElts * |
| 604 | llvm::RISCV::RVVBitsPerBlock, |
| 605 | Denominator: 8 * ABIVLen)); |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | return ABIArgInfo::getDirect(T: ResType); |
| 610 | } |
| 611 | |
| 612 | ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed, |
| 613 | int &ArgGPRsLeft, |
| 614 | int &ArgFPRsLeft, |
| 615 | unsigned ABIVLen) const { |
| 616 | assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow" ); |
| 617 | Ty = useFirstFieldIfTransparentUnion(Ty); |
| 618 | |
| 619 | // Structures with either a non-trivial destructor or a non-trivial |
| 620 | // copy constructor are always passed indirectly. |
| 621 | if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(T: Ty, CXXABI&: getCXXABI())) { |
| 622 | if (ArgGPRsLeft) |
| 623 | ArgGPRsLeft -= 1; |
| 624 | return getNaturalAlignIndirect( |
| 625 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 626 | /*ByVal=*/RAA == CGCXXABI::RAA_DirectInMemory); |
| 627 | } |
| 628 | |
| 629 | uint64_t Size = getContext().getTypeSize(T: Ty); |
| 630 | |
| 631 | // Ignore empty structs/unions whose size is zero. According to the calling |
| 632 | // convention empty structs/unions are required to be sized types in C++. |
| 633 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true) && Size == 0) |
| 634 | return ABIArgInfo::getIgnore(); |
| 635 | |
| 636 | // Pass floating point values via FPRs if possible. |
| 637 | if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() && |
| 638 | FLen >= Size && ArgFPRsLeft) { |
| 639 | ArgFPRsLeft--; |
| 640 | return ABIArgInfo::getDirect(); |
| 641 | } |
| 642 | |
| 643 | // Complex types for the hard float ABI must be passed direct rather than |
| 644 | // using CoerceAndExpand. |
| 645 | if (IsFixed && Ty->isComplexType() && FLen && ArgFPRsLeft >= 2) { |
| 646 | QualType EltTy = Ty->castAs<ComplexType>()->getElementType(); |
| 647 | if (getContext().getTypeSize(T: EltTy) <= FLen) { |
| 648 | ArgFPRsLeft -= 2; |
| 649 | return ABIArgInfo::getDirect(); |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | if (IsFixed && FLen && Ty->isStructureOrClassType()) { |
| 654 | llvm::Type *Field1Ty = nullptr; |
| 655 | llvm::Type *Field2Ty = nullptr; |
| 656 | CharUnits Field1Off = CharUnits::Zero(); |
| 657 | CharUnits Field2Off = CharUnits::Zero(); |
| 658 | int NeededArgGPRs = 0; |
| 659 | int NeededArgFPRs = 0; |
| 660 | bool IsCandidate = |
| 661 | detectFPCCEligibleStruct(Ty, Field1Ty, Field1Off, Field2Ty, Field2Off, |
| 662 | NeededArgGPRs, NeededArgFPRs); |
| 663 | if (IsCandidate && NeededArgGPRs <= ArgGPRsLeft && |
| 664 | NeededArgFPRs <= ArgFPRsLeft) { |
| 665 | ArgGPRsLeft -= NeededArgGPRs; |
| 666 | ArgFPRsLeft -= NeededArgFPRs; |
| 667 | return coerceAndExpandFPCCEligibleStruct(Field1Ty, Field1Off, Field2Ty, |
| 668 | Field2Off); |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | if (IsFixed && Ty->isStructureOrClassType()) { |
| 673 | llvm::Type *VLSType = nullptr; |
| 674 | if (detectVLSCCEligibleStruct(Ty, ABIVLen, VLSType)) |
| 675 | return ABIArgInfo::getDirect(T: VLSType); |
| 676 | } |
| 677 | |
| 678 | uint64_t NeededAlign = getContext().getTypeAlign(T: Ty); |
| 679 | // Determine the number of GPRs needed to pass the current argument |
| 680 | // according to the ABI. 2*XLen-aligned varargs are passed in "aligned" |
| 681 | // register pairs, so may consume 3 registers. |
| 682 | // TODO: To be compatible with GCC's behaviors, we don't align registers |
| 683 | // currently if we are using ILP32E calling convention. This behavior may be |
| 684 | // changed when RV32E/ILP32E is ratified. |
| 685 | int NeededArgGPRs = 1; |
| 686 | if (!IsFixed && NeededAlign == 2 * XLen) |
| 687 | NeededArgGPRs = 2 + (EABI && XLen == 32 ? 0 : (ArgGPRsLeft % 2)); |
| 688 | else if (Size > XLen && Size <= 2 * XLen) |
| 689 | NeededArgGPRs = 2; |
| 690 | |
| 691 | if (NeededArgGPRs > ArgGPRsLeft) { |
| 692 | NeededArgGPRs = ArgGPRsLeft; |
| 693 | } |
| 694 | |
| 695 | ArgGPRsLeft -= NeededArgGPRs; |
| 696 | |
| 697 | if (!isAggregateTypeForABI(T: Ty) && !Ty->isVectorType()) { |
| 698 | // Treat an enum type as its underlying type. |
| 699 | if (const EnumType *EnumTy = Ty->getAs<EnumType>()) |
| 700 | Ty = EnumTy->getDecl()->getIntegerType(); |
| 701 | |
| 702 | // All integral types are promoted to XLen width |
| 703 | if (Size < XLen && Ty->isIntegralOrEnumerationType()) { |
| 704 | return extendType(Ty, CoerceTy: CGT.ConvertType(T: Ty)); |
| 705 | } |
| 706 | |
| 707 | if (const auto *EIT = Ty->getAs<BitIntType>()) { |
| 708 | if (EIT->getNumBits() < XLen) |
| 709 | return extendType(Ty, CoerceTy: CGT.ConvertType(T: Ty)); |
| 710 | if (EIT->getNumBits() > 128 || |
| 711 | (!getContext().getTargetInfo().hasInt128Type() && |
| 712 | EIT->getNumBits() > 64)) |
| 713 | return getNaturalAlignIndirect( |
| 714 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 715 | /*ByVal=*/false); |
| 716 | } |
| 717 | |
| 718 | return ABIArgInfo::getDirect(); |
| 719 | } |
| 720 | |
| 721 | // TODO: _BitInt is not handled yet in VLS calling convention since _BitInt |
| 722 | // ABI is also not merged yet in RISC-V: |
| 723 | // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/419 |
| 724 | if (const VectorType *VT = Ty->getAs<VectorType>(); |
| 725 | VT && !VT->getElementType()->isBitIntType()) { |
| 726 | if (VT->getVectorKind() == VectorKind::RVVFixedLengthData || |
| 727 | VT->getVectorKind() == VectorKind::RVVFixedLengthMask || |
| 728 | VT->getVectorKind() == VectorKind::RVVFixedLengthMask_1 || |
| 729 | VT->getVectorKind() == VectorKind::RVVFixedLengthMask_2 || |
| 730 | VT->getVectorKind() == VectorKind::RVVFixedLengthMask_4) |
| 731 | return coerceVLSVector(Ty); |
| 732 | if (VT->getVectorKind() == VectorKind::Generic && ABIVLen != 0) |
| 733 | // Generic vector without riscv_vls_cc should fall through and pass by |
| 734 | // reference. |
| 735 | return coerceVLSVector(Ty, ABIVLen); |
| 736 | } |
| 737 | |
| 738 | // Aggregates which are <= 2*XLen will be passed in registers if possible, |
| 739 | // so coerce to integers. |
| 740 | if (Size <= 2 * XLen) { |
| 741 | unsigned Alignment = getContext().getTypeAlign(T: Ty); |
| 742 | |
| 743 | // Use a single XLen int if possible, 2*XLen if 2*XLen alignment is |
| 744 | // required, and a 2-element XLen array if only XLen alignment is required. |
| 745 | if (Size <= XLen) { |
| 746 | return ABIArgInfo::getDirect( |
| 747 | T: llvm::IntegerType::get(C&: getVMContext(), NumBits: XLen)); |
| 748 | } else if (Alignment == 2 * XLen) { |
| 749 | return ABIArgInfo::getDirect( |
| 750 | T: llvm::IntegerType::get(C&: getVMContext(), NumBits: 2 * XLen)); |
| 751 | } else { |
| 752 | return ABIArgInfo::getDirect(T: llvm::ArrayType::get( |
| 753 | ElementType: llvm::IntegerType::get(C&: getVMContext(), NumBits: XLen), NumElements: 2)); |
| 754 | } |
| 755 | } |
| 756 | return getNaturalAlignIndirect( |
| 757 | Ty, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(), |
| 758 | /*ByVal=*/false); |
| 759 | } |
| 760 | |
| 761 | ABIArgInfo RISCVABIInfo::classifyReturnType(QualType RetTy, |
| 762 | unsigned ABIVLen) const { |
| 763 | if (RetTy->isVoidType()) |
| 764 | return ABIArgInfo::getIgnore(); |
| 765 | |
| 766 | int ArgGPRsLeft = 2; |
| 767 | int ArgFPRsLeft = FLen ? 2 : 0; |
| 768 | |
| 769 | // The rules for return and argument types are the same, so defer to |
| 770 | // classifyArgumentType. |
| 771 | return classifyArgumentType(Ty: RetTy, /*IsFixed=*/true, ArgGPRsLeft, ArgFPRsLeft, |
| 772 | ABIVLen); |
| 773 | } |
| 774 | |
| 775 | RValue RISCVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
| 776 | QualType Ty, AggValueSlot Slot) const { |
| 777 | CharUnits SlotSize = CharUnits::fromQuantity(Quantity: XLen / 8); |
| 778 | |
| 779 | // Empty records are ignored for parameter passing purposes. |
| 780 | if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true)) |
| 781 | return Slot.asRValue(); |
| 782 | |
| 783 | auto TInfo = getContext().getTypeInfoInChars(T: Ty); |
| 784 | |
| 785 | // TODO: To be compatible with GCC's behaviors, we force arguments with |
| 786 | // 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`, |
| 787 | // `unsigned long long` and `double` to have 4-byte alignment. This |
| 788 | // behavior may be changed when RV32E/ILP32E is ratified. |
| 789 | if (EABI && XLen == 32) |
| 790 | TInfo.Align = std::min(a: TInfo.Align, b: CharUnits::fromQuantity(Quantity: 4)); |
| 791 | |
| 792 | // Arguments bigger than 2*Xlen bytes are passed indirectly. |
| 793 | bool IsIndirect = TInfo.Width > 2 * SlotSize; |
| 794 | |
| 795 | return emitVoidPtrVAArg(CGF, VAListAddr, ValueTy: Ty, IsIndirect, ValueInfo: TInfo, SlotSizeAndAlign: SlotSize, |
| 796 | /*AllowHigherAlign=*/true, Slot); |
| 797 | } |
| 798 | |
| 799 | ABIArgInfo RISCVABIInfo::extendType(QualType Ty, llvm::Type *CoerceTy) const { |
| 800 | int TySize = getContext().getTypeSize(T: Ty); |
| 801 | // RV64 ABI requires unsigned 32 bit integers to be sign extended. |
| 802 | if (XLen == 64 && Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32) |
| 803 | return ABIArgInfo::getSignExtend(Ty, T: CoerceTy); |
| 804 | return ABIArgInfo::getExtend(Ty, T: CoerceTy); |
| 805 | } |
| 806 | |
| 807 | namespace { |
| 808 | class RISCVTargetCodeGenInfo : public TargetCodeGenInfo { |
| 809 | public: |
| 810 | RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, |
| 811 | unsigned FLen, bool EABI) |
| 812 | : TargetCodeGenInfo( |
| 813 | std::make_unique<RISCVABIInfo>(args&: CGT, args&: XLen, args&: FLen, args&: EABI)) { |
| 814 | SwiftInfo = |
| 815 | std::make_unique<SwiftABIInfo>(args&: CGT, /*SwiftErrorInRegister=*/args: false); |
| 816 | } |
| 817 | |
| 818 | void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, |
| 819 | CodeGen::CodeGenModule &CGM) const override { |
| 820 | const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: D); |
| 821 | if (!FD) return; |
| 822 | |
| 823 | auto *Fn = cast<llvm::Function>(Val: GV); |
| 824 | |
| 825 | if (CGM.getCodeGenOpts().CFProtectionReturn) |
| 826 | Fn->addFnAttr(Kind: "hw-shadow-stack" ); |
| 827 | |
| 828 | const auto *Attr = FD->getAttr<RISCVInterruptAttr>(); |
| 829 | if (!Attr) |
| 830 | return; |
| 831 | |
| 832 | StringRef Kind = "machine" ; |
| 833 | bool HasSiFiveCLICPreemptible = false; |
| 834 | bool HasSiFiveCLICStackSwap = false; |
| 835 | for (RISCVInterruptAttr::InterruptType type : Attr->interrupt()) { |
| 836 | switch (type) { |
| 837 | case RISCVInterruptAttr::machine: |
| 838 | // Do not update `Kind` because `Kind` is already "machine", or the |
| 839 | // kinds also contains SiFive types which need to be applied. |
| 840 | break; |
| 841 | case RISCVInterruptAttr::supervisor: |
| 842 | Kind = "supervisor" ; |
| 843 | break; |
| 844 | case RISCVInterruptAttr::qcinest: |
| 845 | Kind = "qci-nest" ; |
| 846 | break; |
| 847 | case RISCVInterruptAttr::qcinonest: |
| 848 | Kind = "qci-nonest" ; |
| 849 | break; |
| 850 | // There are three different LLVM IR attribute values for SiFive CLIC |
| 851 | // interrupt kinds, one for each kind and one extra for their combination. |
| 852 | case RISCVInterruptAttr::SiFiveCLICPreemptible: { |
| 853 | HasSiFiveCLICPreemptible = true; |
| 854 | Kind = HasSiFiveCLICStackSwap ? "SiFive-CLIC-preemptible-stack-swap" |
| 855 | : "SiFive-CLIC-preemptible" ; |
| 856 | break; |
| 857 | } |
| 858 | case RISCVInterruptAttr::SiFiveCLICStackSwap: { |
| 859 | HasSiFiveCLICStackSwap = true; |
| 860 | Kind = HasSiFiveCLICPreemptible ? "SiFive-CLIC-preemptible-stack-swap" |
| 861 | : "SiFive-CLIC-stack-swap" ; |
| 862 | break; |
| 863 | } |
| 864 | } |
| 865 | } |
| 866 | |
| 867 | Fn->addFnAttr(Kind: "interrupt" , Val: Kind); |
| 868 | } |
| 869 | }; |
| 870 | } // namespace |
| 871 | |
| 872 | std::unique_ptr<TargetCodeGenInfo> |
| 873 | CodeGen::createRISCVTargetCodeGenInfo(CodeGenModule &CGM, unsigned XLen, |
| 874 | unsigned FLen, bool EABI) { |
| 875 | return std::make_unique<RISCVTargetCodeGenInfo>(args&: CGM.getTypes(), args&: XLen, args&: FLen, |
| 876 | args&: EABI); |
| 877 | } |
| 878 | |