1 //===-- Target.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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Optimizer/CodeGen/Target.h" 14 #include "flang/Optimizer/Builder/Todo.h" 15 #include "flang/Optimizer/Dialect/FIRType.h" 16 #include "flang/Optimizer/Dialect/Support/KindMapping.h" 17 #include "flang/Optimizer/Support/FatalError.h" 18 #include "flang/Optimizer/Support/Utils.h" 19 #include "mlir/IR/BuiltinTypes.h" 20 #include "mlir/IR/TypeRange.h" 21 #include "llvm/ADT/TypeSwitch.h" 22 23 #define DEBUG_TYPE "flang-codegen-target" 24 25 using namespace fir; 26 27 namespace fir::details { 28 llvm::StringRef Attributes::getIntExtensionAttrName() const { 29 // The attribute names are available via LLVM dialect interfaces 30 // like getZExtAttrName(), getByValAttrName(), etc., so we'd better 31 // use them than literals. 32 if (isZeroExt()) 33 return "llvm.zeroext"; 34 else if (isSignExt()) 35 return "llvm.signext"; 36 return {}; 37 } 38 } // namespace fir::details 39 40 // Reduce a REAL/float type to the floating point semantics. 41 static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, 42 mlir::Type type) { 43 assert(isa_real(type)); 44 if (auto ty = mlir::dyn_cast<fir::RealType>(type)) 45 return kindMap.getFloatSemantics(ty.getFKind()); 46 return mlir::cast<mlir::FloatType>(type).getFloatSemantics(); 47 } 48 49 static void typeTodo(const llvm::fltSemantics *sem, mlir::Location loc, 50 const std::string &context) { 51 if (sem == &llvm::APFloat::IEEEhalf()) { 52 TODO(loc, "COMPLEX(KIND=2): for " + context + " type"); 53 } else if (sem == &llvm::APFloat::BFloat()) { 54 TODO(loc, "COMPLEX(KIND=3): " + context + " type"); 55 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 56 TODO(loc, "COMPLEX(KIND=10): " + context + " type"); 57 } else { 58 TODO(loc, "complex for this precision for " + context + " type"); 59 } 60 } 61 62 namespace { 63 template <typename S> 64 struct GenericTarget : public CodeGenSpecifics { 65 using CodeGenSpecifics::CodeGenSpecifics; 66 using AT = CodeGenSpecifics::Attributes; 67 68 mlir::Type complexMemoryType(mlir::Type eleTy) const override { 69 assert(fir::isa_real(eleTy)); 70 // Use a type that will be translated into LLVM as: 71 // { t, t } struct of 2 eleTy 72 return mlir::TupleType::get(eleTy.getContext(), 73 mlir::TypeRange{eleTy, eleTy}); 74 } 75 76 mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { 77 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 78 auto ptrTy = fir::ReferenceType::get(eleTy); 79 // Use a type that will be translated into LLVM as: 80 // { t*, index } 81 return mlir::TupleType::get(eleTy.getContext(), 82 mlir::TypeRange{ptrTy, idxTy}); 83 } 84 85 Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { 86 CodeGenSpecifics::Marshalling marshal; 87 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 88 auto ptrTy = fir::ReferenceType::get(eleTy); 89 marshal.emplace_back(ptrTy, AT{}); 90 // Return value arguments are grouped as a pair. Others are passed in a 91 // split format with all pointers first (in the declared position) and all 92 // LEN arguments appended after all of the dummy arguments. 93 // NB: Other conventions/ABIs can/should be supported via options. 94 marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, 95 /*sret=*/sret, /*append=*/!sret}); 96 return marshal; 97 } 98 99 CodeGenSpecifics::Marshalling 100 structArgumentType(mlir::Location loc, fir::RecordType, 101 const Marshalling &) const override { 102 TODO(loc, "passing VALUE BIND(C) derived type for this target"); 103 } 104 105 CodeGenSpecifics::Marshalling 106 integerArgumentType(mlir::Location loc, 107 mlir::IntegerType argTy) const override { 108 CodeGenSpecifics::Marshalling marshal; 109 AT::IntegerExtension intExt = AT::IntegerExtension::None; 110 if (argTy.getWidth() < getCIntTypeWidth()) { 111 // isSigned() and isUnsigned() branches below are dead code currently. 112 // If needed, we can generate calls with signed/unsigned argument types 113 // to more precisely match C side (e.g. for Fortran runtime functions 114 // with 'unsigned short' arguments). 115 if (argTy.isSigned()) 116 intExt = AT::IntegerExtension::Sign; 117 else if (argTy.isUnsigned()) 118 intExt = AT::IntegerExtension::Zero; 119 else if (argTy.isSignless()) { 120 // Zero extend for 'i1' and sign extend for other types. 121 if (argTy.getWidth() == 1) 122 intExt = AT::IntegerExtension::Zero; 123 else 124 intExt = AT::IntegerExtension::Sign; 125 } 126 } 127 128 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, 129 /*sret=*/false, /*append=*/false, 130 /*intExt=*/intExt}); 131 return marshal; 132 } 133 134 CodeGenSpecifics::Marshalling 135 integerReturnType(mlir::Location loc, 136 mlir::IntegerType argTy) const override { 137 return integerArgumentType(loc, argTy); 138 } 139 140 // Width of 'int' type is 32-bits for almost all targets, except 141 // for AVR and MSP430 (see TargetInfo initializations 142 // in clang/lib/Basic/Targets). 143 unsigned char getCIntTypeWidth() const override { return 32; } 144 }; 145 } // namespace 146 147 //===----------------------------------------------------------------------===// 148 // i386 (x86 32 bit) linux target specifics. 149 //===----------------------------------------------------------------------===// 150 151 namespace { 152 struct TargetI386 : public GenericTarget<TargetI386> { 153 using GenericTarget::GenericTarget; 154 155 static constexpr int defaultWidth = 32; 156 157 CodeGenSpecifics::Marshalling 158 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 159 assert(fir::isa_real(eleTy)); 160 CodeGenSpecifics::Marshalling marshal; 161 // Use a type that will be translated into LLVM as: 162 // { t, t } struct of 2 eleTy, byval, align 4 163 auto structTy = 164 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 165 marshal.emplace_back(fir::ReferenceType::get(structTy), 166 AT{/*alignment=*/4, /*byval=*/true}); 167 return marshal; 168 } 169 170 CodeGenSpecifics::Marshalling 171 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 172 assert(fir::isa_real(eleTy)); 173 CodeGenSpecifics::Marshalling marshal; 174 const auto *sem = &floatToSemantics(kindMap, eleTy); 175 if (sem == &llvm::APFloat::IEEEsingle()) { 176 // i64 pack both floats in a 64-bit GPR 177 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 178 AT{}); 179 } else if (sem == &llvm::APFloat::IEEEdouble()) { 180 // Use a type that will be translated into LLVM as: 181 // { t, t } struct of 2 eleTy, sret, align 4 182 auto structTy = mlir::TupleType::get(eleTy.getContext(), 183 mlir::TypeRange{eleTy, eleTy}); 184 marshal.emplace_back(fir::ReferenceType::get(structTy), 185 AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); 186 } else { 187 typeTodo(sem, loc, "return"); 188 } 189 return marshal; 190 } 191 }; 192 } // namespace 193 194 //===----------------------------------------------------------------------===// 195 // i386 (x86 32 bit) Windows target specifics. 196 //===----------------------------------------------------------------------===// 197 198 namespace { 199 struct TargetI386Win : public GenericTarget<TargetI386Win> { 200 using GenericTarget::GenericTarget; 201 202 static constexpr int defaultWidth = 32; 203 204 CodeGenSpecifics::Marshalling 205 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 206 CodeGenSpecifics::Marshalling marshal; 207 // Use a type that will be translated into LLVM as: 208 // { t, t } struct of 2 eleTy, byval, align 4 209 auto structTy = 210 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 211 marshal.emplace_back(fir::ReferenceType::get(structTy), 212 AT{/*align=*/4, /*byval=*/true}); 213 return marshal; 214 } 215 216 CodeGenSpecifics::Marshalling 217 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 218 CodeGenSpecifics::Marshalling marshal; 219 const auto *sem = &floatToSemantics(kindMap, eleTy); 220 if (sem == &llvm::APFloat::IEEEsingle()) { 221 // i64 pack both floats in a 64-bit GPR 222 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 223 AT{}); 224 } else if (sem == &llvm::APFloat::IEEEdouble()) { 225 // Use a type that will be translated into LLVM as: 226 // { double, double } struct of 2 double, sret, align 8 227 marshal.emplace_back( 228 fir::ReferenceType::get(mlir::TupleType::get( 229 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 230 AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); 231 } else if (sem == &llvm::APFloat::IEEEquad()) { 232 // Use a type that will be translated into LLVM as: 233 // { fp128, fp128 } struct of 2 fp128, sret, align 16 234 marshal.emplace_back( 235 fir::ReferenceType::get(mlir::TupleType::get( 236 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 237 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 238 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 239 // Use a type that will be translated into LLVM as: 240 // { x86_fp80, x86_fp80 } struct of 2 x86_fp80, sret, align 4 241 marshal.emplace_back( 242 fir::ReferenceType::get(mlir::TupleType::get( 243 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 244 AT{/*align=*/4, /*byval=*/false, /*sret=*/true}); 245 } else { 246 typeTodo(sem, loc, "return"); 247 } 248 return marshal; 249 } 250 }; 251 } // namespace 252 253 //===----------------------------------------------------------------------===// 254 // x86_64 (x86 64 bit) linux target specifics. 255 //===----------------------------------------------------------------------===// 256 257 namespace { 258 struct TargetX86_64 : public GenericTarget<TargetX86_64> { 259 using GenericTarget::GenericTarget; 260 261 static constexpr int defaultWidth = 64; 262 263 CodeGenSpecifics::Marshalling 264 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 265 CodeGenSpecifics::Marshalling marshal; 266 const auto *sem = &floatToSemantics(kindMap, eleTy); 267 if (sem == &llvm::APFloat::IEEEsingle()) { 268 // <2 x t> vector of 2 eleTy 269 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 270 } else if (sem == &llvm::APFloat::IEEEdouble()) { 271 // FIXME: In case of SSE register exhaustion, the ABI here may be 272 // incorrect since LLVM may pass the real via register and the imaginary 273 // part via the stack while the ABI it should be all in register or all 274 // in memory. Register occupancy must be analyzed here. 275 // two distinct double arguments 276 marshal.emplace_back(eleTy, AT{}); 277 marshal.emplace_back(eleTy, AT{}); 278 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 279 // Use a type that will be translated into LLVM as: 280 // { x86_fp80, x86_fp80 } struct of 2 fp128, byval, align 16 281 marshal.emplace_back( 282 fir::ReferenceType::get(mlir::TupleType::get( 283 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 284 AT{/*align=*/16, /*byval=*/true}); 285 } else if (sem == &llvm::APFloat::IEEEquad()) { 286 // Use a type that will be translated into LLVM as: 287 // { fp128, fp128 } struct of 2 fp128, byval, align 16 288 marshal.emplace_back( 289 fir::ReferenceType::get(mlir::TupleType::get( 290 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 291 AT{/*align=*/16, /*byval=*/true}); 292 } else { 293 typeTodo(sem, loc, "argument"); 294 } 295 return marshal; 296 } 297 298 CodeGenSpecifics::Marshalling 299 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 300 CodeGenSpecifics::Marshalling marshal; 301 const auto *sem = &floatToSemantics(kindMap, eleTy); 302 if (sem == &llvm::APFloat::IEEEsingle()) { 303 // <2 x t> vector of 2 eleTy 304 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 305 } else if (sem == &llvm::APFloat::IEEEdouble()) { 306 // Use a type that will be translated into LLVM as: 307 // { double, double } struct of 2 double 308 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 309 mlir::TypeRange{eleTy, eleTy}), 310 AT{}); 311 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 312 // { x86_fp80, x86_fp80 } 313 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 314 mlir::TypeRange{eleTy, eleTy}), 315 AT{}); 316 } else if (sem == &llvm::APFloat::IEEEquad()) { 317 // Use a type that will be translated into LLVM as: 318 // { fp128, fp128 } struct of 2 fp128, sret, align 16 319 marshal.emplace_back( 320 fir::ReferenceType::get(mlir::TupleType::get( 321 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 322 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 323 } else { 324 typeTodo(sem, loc, "return"); 325 } 326 return marshal; 327 } 328 329 /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3. 330 enum ArgClass { 331 Integer = 0, 332 SSE, 333 SSEUp, 334 X87, 335 X87Up, 336 ComplexX87, 337 NoClass, 338 Memory 339 }; 340 341 /// Classify an argument type or a field of an aggregate type argument. 342 /// See System V ABI version 1.0 section 3.2.3. 343 /// The Lo and Hi class are set to the class of the lower eight eightbytes 344 /// and upper eight eightbytes on return. 345 /// If this is called for an aggregate field, the caller is responsible to 346 /// do the post-merge. 347 void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset, 348 ArgClass &Lo, ArgClass &Hi) const { 349 Hi = Lo = ArgClass::NoClass; 350 ArgClass ¤t = byteOffset < 8 ? Lo : Hi; 351 // System V AMD64 ABI 3.2.3. version 1.0 352 llvm::TypeSwitch<mlir::Type>(type) 353 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 354 if (intTy.getWidth() == 128) 355 Hi = Lo = ArgClass::Integer; 356 else 357 current = ArgClass::Integer; 358 }) 359 .template Case<mlir::FloatType, fir::RealType>([&](mlir::Type floatTy) { 360 const auto *sem = &floatToSemantics(kindMap, floatTy); 361 if (sem == &llvm::APFloat::x87DoubleExtended()) { 362 Lo = ArgClass::X87; 363 Hi = ArgClass::X87Up; 364 } else if (sem == &llvm::APFloat::IEEEquad()) { 365 Lo = ArgClass::SSE; 366 Hi = ArgClass::SSEUp; 367 } else { 368 current = ArgClass::SSE; 369 } 370 }) 371 .template Case<fir::ComplexType>([&](fir::ComplexType cmplx) { 372 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); 373 if (sem == &llvm::APFloat::x87DoubleExtended()) { 374 current = ArgClass::ComplexX87; 375 } else { 376 fir::SequenceType::Shape shape{2}; 377 classifyArray(loc, 378 fir::SequenceType::get(shape, cmplx.getElementType()), 379 byteOffset, Lo, Hi); 380 } 381 }) 382 .template Case<fir::LogicalType>([&](fir::LogicalType logical) { 383 if (kindMap.getLogicalBitsize(logical.getFKind()) == 128) 384 Hi = Lo = ArgClass::Integer; 385 else 386 current = ArgClass::Integer; 387 }) 388 .template Case<fir::CharacterType>( 389 [&](fir::CharacterType character) { current = ArgClass::Integer; }) 390 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { 391 // Array component. 392 classifyArray(loc, seqTy, byteOffset, Lo, Hi); 393 }) 394 .template Case<fir::RecordType>([&](fir::RecordType recTy) { 395 // Component that is a derived type. 396 classifyStruct(loc, recTy, byteOffset, Lo, Hi); 397 }) 398 .template Case<fir::VectorType>([&](fir::VectorType vecTy) { 399 // Previously marshalled SSE eight byte for a previous struct 400 // argument. 401 auto *sem = fir::isa_real(vecTy.getEleTy()) 402 ? &floatToSemantics(kindMap, vecTy.getEleTy()) 403 : nullptr; 404 // Not expecting to hit this todo in standard code (it would 405 // require some vector type extension). 406 if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) && 407 !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4)) 408 TODO(loc, "passing vector argument to C by value"); 409 current = SSE; 410 }) 411 .Default([&](mlir::Type ty) { 412 if (fir::conformsWithPassByRef(ty)) 413 current = ArgClass::Integer; // Pointers. 414 else 415 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 416 "type argument"); 417 }); 418 } 419 420 // Classify fields of a derived type starting at \p offset. Returns the new 421 // offset. Post-merge is left to the caller. 422 std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy, 423 std::uint64_t byteOffset, ArgClass &Lo, 424 ArgClass &Hi) const { 425 for (auto component : recTy.getTypeList()) { 426 if (byteOffset > 16) { 427 // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger 428 // than 16 bytes here, it is not a single _m256 and or _m512 entity 429 // that could fit in AVX registers. 430 Lo = Hi = ArgClass::Memory; 431 return byteOffset; 432 } 433 mlir::Type compType = component.second; 434 auto [compSize, compAlign] = 435 fir::getTypeSizeAndAlignment(loc, compType, getDataLayout(), kindMap); 436 byteOffset = llvm::alignTo(byteOffset, compAlign); 437 ArgClass LoComp, HiComp; 438 classify(loc, compType, byteOffset, LoComp, HiComp); 439 Lo = mergeClass(Lo, LoComp); 440 Hi = mergeClass(Hi, HiComp); 441 byteOffset = byteOffset + llvm::alignTo(compSize, compAlign); 442 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) 443 return byteOffset; 444 } 445 return byteOffset; 446 } 447 448 // Classify fields of a constant size array type starting at \p offset. 449 // Returns the new offset. Post-merge is left to the caller. 450 void classifyArray(mlir::Location loc, fir::SequenceType seqTy, 451 std::uint64_t byteOffset, ArgClass &Lo, 452 ArgClass &Hi) const { 453 mlir::Type eleTy = seqTy.getEleTy(); 454 const std::uint64_t arraySize = seqTy.getConstantArraySize(); 455 auto [eleSize, eleAlign] = 456 fir::getTypeSizeAndAlignment(loc, eleTy, getDataLayout(), kindMap); 457 std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign); 458 for (std::uint64_t i = 0; i < arraySize; ++i) { 459 byteOffset = llvm::alignTo(byteOffset, eleAlign); 460 if (byteOffset > 16) { 461 // See 3.2.3 p. 1 and note 15. Same as in classifyStruct. 462 Lo = Hi = ArgClass::Memory; 463 return; 464 } 465 ArgClass LoComp, HiComp; 466 classify(loc, eleTy, byteOffset, LoComp, HiComp); 467 Lo = mergeClass(Lo, LoComp); 468 Hi = mergeClass(Hi, HiComp); 469 byteOffset = byteOffset + eleStorageSize; 470 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) 471 return; 472 } 473 } 474 475 // Goes through the previously marshalled arguments and count the 476 // register occupancy to check if there are enough registers left. 477 bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters, 478 int neededSSERegisters, 479 const Marshalling &previousArguments) const { 480 int availIntRegisters = 6; 481 int availSSERegisters = 8; 482 for (auto typeAndAttr : previousArguments) { 483 const auto &attr = std::get<Attributes>(typeAndAttr); 484 if (attr.isByVal()) 485 continue; // Previous argument passed on the stack. 486 ArgClass Lo, Hi; 487 Lo = Hi = ArgClass::NoClass; 488 classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi); 489 // post merge is not needed here since previous aggregate arguments 490 // were marshalled into simpler arguments. 491 if (Lo == ArgClass::Integer) 492 --availIntRegisters; 493 else if (Lo == SSE) 494 --availSSERegisters; 495 if (Hi == ArgClass::Integer) 496 --availIntRegisters; 497 else if (Hi == ArgClass::SSE) 498 --availSSERegisters; 499 } 500 return availSSERegisters >= neededSSERegisters && 501 availIntRegisters >= neededIntRegisters; 502 } 503 504 /// Argument class merging as described in System V ABI 3.2.3 point 4. 505 ArgClass mergeClass(ArgClass accum, ArgClass field) const { 506 assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) && 507 "Invalid accumulated classification during merge."); 508 if (accum == field || field == NoClass) 509 return accum; 510 if (field == ArgClass::Memory) 511 return ArgClass::Memory; 512 if (accum == NoClass) 513 return field; 514 if (accum == Integer || field == Integer) 515 return ArgClass::Integer; 516 if (field == ArgClass::X87 || field == ArgClass::X87Up || 517 field == ArgClass::ComplexX87 || accum == ArgClass::X87 || 518 accum == ArgClass::X87Up) 519 return Memory; 520 return SSE; 521 } 522 523 /// Argument class post merging as described in System V ABI 3.2.3 point 5. 524 void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const { 525 if (Hi == ArgClass::Memory) 526 Lo = ArgClass::Memory; 527 if (Hi == ArgClass::X87Up && Lo != ArgClass::X87) 528 Lo = ArgClass::Memory; 529 if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp)) 530 Lo = ArgClass::Memory; 531 if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE) 532 Hi = SSE; 533 } 534 535 /// When \p recTy is a one field record type that can be passed 536 /// like the field on its own, returns the field type. Returns 537 /// a null type otherwise. 538 mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy) const { 539 auto typeList = recTy.getTypeList(); 540 if (typeList.size() != 1) 541 return {}; 542 mlir::Type fieldType = typeList[0].second; 543 if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::RealType, 544 fir::CharacterType, fir::LogicalType>(fieldType)) 545 return fieldType; 546 // Complex field that needs to be split, or array. 547 return {}; 548 } 549 550 mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context, 551 ArgClass argClass, 552 std::uint64_t partByteSize) const { 553 if (argClass == ArgClass::SSE) { 554 if (partByteSize > 16) 555 TODO(loc, "passing struct as a real > 128 bits in register"); 556 // Clang uses vector type when several fp fields are marshalled 557 // into a single SSE register (like <n x smallest fp field> ). 558 // It should make no difference from an ABI point of view to just 559 // select an fp type of the right size, and it makes things simpler 560 // here. 561 if (partByteSize > 8) 562 return mlir::FloatType::getF128(context); 563 if (partByteSize > 4) 564 return mlir::FloatType::getF64(context); 565 if (partByteSize > 2) 566 return mlir::FloatType::getF32(context); 567 return mlir::FloatType::getF16(context); 568 } 569 assert(partByteSize <= 8 && 570 "expect integer part of aggregate argument to fit into eight bytes"); 571 if (partByteSize > 4) 572 return mlir::IntegerType::get(context, 64); 573 if (partByteSize > 2) 574 return mlir::IntegerType::get(context, 32); 575 if (partByteSize > 1) 576 return mlir::IntegerType::get(context, 16); 577 return mlir::IntegerType::get(context, 8); 578 } 579 580 /// Marshal a derived type passed by value like a C struct. 581 CodeGenSpecifics::Marshalling 582 structArgumentType(mlir::Location loc, fir::RecordType recTy, 583 const Marshalling &previousArguments) const override { 584 std::uint64_t byteOffset = 0; 585 ArgClass Lo, Hi; 586 Lo = Hi = ArgClass::NoClass; 587 byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi); 588 postMerge(byteOffset, Lo, Hi); 589 if (Lo == ArgClass::Memory || Lo == ArgClass::X87 || 590 Lo == ArgClass::ComplexX87) 591 return passOnTheStack(loc, recTy); 592 int neededIntRegisters = 0; 593 int neededSSERegisters = 0; 594 if (Lo == ArgClass::SSE) 595 ++neededSSERegisters; 596 else if (Lo == ArgClass::Integer) 597 ++neededIntRegisters; 598 if (Hi == ArgClass::SSE) 599 ++neededSSERegisters; 600 else if (Hi == ArgClass::Integer) 601 ++neededIntRegisters; 602 // C struct should not be split into LLVM registers if LLVM codegen is not 603 // able to later assign actual registers to all of them (struct passing is 604 // all in registers or all on the stack). 605 if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters, 606 previousArguments)) 607 return passOnTheStack(loc, recTy); 608 609 if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) { 610 CodeGenSpecifics::Marshalling marshal; 611 marshal.emplace_back(fieldType, AT{}); 612 return marshal; 613 } 614 if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) { 615 // Pass a single integer or floating point argument. 616 mlir::Type lowType = 617 pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset); 618 CodeGenSpecifics::Marshalling marshal; 619 marshal.emplace_back(lowType, AT{}); 620 return marshal; 621 } 622 // Split into two integer or floating point arguments. 623 // Note that for the first argument, this will always pick i64 or f64 which 624 // may be bigger than needed if some struct padding ends the first eight 625 // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and 626 // semantic point of view, but it may not match the LLVM IR interface clang 627 // would produce for the equivalent C code (the assembly will still be 628 // compatible). This allows keeping the logic simpler here since it 629 // avoids computing the "data" size of the Lo part. 630 mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u); 631 mlir::Type hiType = 632 pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u); 633 CodeGenSpecifics::Marshalling marshal; 634 marshal.emplace_back(lowType, AT{}); 635 marshal.emplace_back(hiType, AT{}); 636 return marshal; 637 } 638 639 /// Marshal an argument that must be passed on the stack. 640 CodeGenSpecifics::Marshalling passOnTheStack(mlir::Location loc, 641 mlir::Type ty) const { 642 CodeGenSpecifics::Marshalling marshal; 643 auto sizeAndAlign = 644 fir::getTypeSizeAndAlignment(loc, ty, getDataLayout(), kindMap); 645 // The stack is always 8 byte aligned (note 14 in 3.2.3). 646 unsigned short align = 647 std::max(sizeAndAlign.second, static_cast<unsigned short>(8)); 648 marshal.emplace_back(fir::ReferenceType::get(ty), 649 AT{align, /*byval=*/true, /*sret=*/false}); 650 return marshal; 651 } 652 }; 653 } // namespace 654 655 //===----------------------------------------------------------------------===// 656 // x86_64 (x86 64 bit) Windows target specifics. 657 //===----------------------------------------------------------------------===// 658 659 namespace { 660 struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> { 661 using GenericTarget::GenericTarget; 662 663 static constexpr int defaultWidth = 64; 664 665 CodeGenSpecifics::Marshalling 666 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 667 CodeGenSpecifics::Marshalling marshal; 668 const auto *sem = &floatToSemantics(kindMap, eleTy); 669 if (sem == &llvm::APFloat::IEEEsingle()) { 670 // i64 pack both floats in a 64-bit GPR 671 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 672 AT{}); 673 } else if (sem == &llvm::APFloat::IEEEdouble()) { 674 // Use a type that will be translated into LLVM as: 675 // { double, double } struct of 2 double, byval, align 8 676 marshal.emplace_back( 677 fir::ReferenceType::get(mlir::TupleType::get( 678 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 679 AT{/*align=*/8, /*byval=*/true}); 680 } else if (sem == &llvm::APFloat::IEEEquad() || 681 sem == &llvm::APFloat::x87DoubleExtended()) { 682 // Use a type that will be translated into LLVM as: 683 // { t, t } struct of 2 eleTy, byval, align 16 684 marshal.emplace_back( 685 fir::ReferenceType::get(mlir::TupleType::get( 686 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 687 AT{/*align=*/16, /*byval=*/true}); 688 } else { 689 typeTodo(sem, loc, "argument"); 690 } 691 return marshal; 692 } 693 694 CodeGenSpecifics::Marshalling 695 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 696 CodeGenSpecifics::Marshalling marshal; 697 const auto *sem = &floatToSemantics(kindMap, eleTy); 698 if (sem == &llvm::APFloat::IEEEsingle()) { 699 // i64 pack both floats in a 64-bit GPR 700 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 701 AT{}); 702 } else if (sem == &llvm::APFloat::IEEEdouble()) { 703 // Use a type that will be translated into LLVM as: 704 // { double, double } struct of 2 double, sret, align 8 705 marshal.emplace_back( 706 fir::ReferenceType::get(mlir::TupleType::get( 707 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 708 AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); 709 } else if (sem == &llvm::APFloat::IEEEquad() || 710 sem == &llvm::APFloat::x87DoubleExtended()) { 711 // Use a type that will be translated into LLVM as: 712 // { t, t } struct of 2 eleTy, sret, align 16 713 marshal.emplace_back( 714 fir::ReferenceType::get(mlir::TupleType::get( 715 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 716 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 717 } else { 718 typeTodo(sem, loc, "return"); 719 } 720 return marshal; 721 } 722 }; 723 } // namespace 724 725 //===----------------------------------------------------------------------===// 726 // AArch64 linux target specifics. 727 //===----------------------------------------------------------------------===// 728 729 namespace { 730 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 731 using GenericTarget::GenericTarget; 732 733 static constexpr int defaultWidth = 64; 734 735 CodeGenSpecifics::Marshalling 736 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 737 CodeGenSpecifics::Marshalling marshal; 738 const auto *sem = &floatToSemantics(kindMap, eleTy); 739 if (sem == &llvm::APFloat::IEEEsingle() || 740 sem == &llvm::APFloat::IEEEdouble() || 741 sem == &llvm::APFloat::IEEEquad()) { 742 // [2 x t] array of 2 eleTy 743 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 744 } else { 745 typeTodo(sem, loc, "argument"); 746 } 747 return marshal; 748 } 749 750 CodeGenSpecifics::Marshalling 751 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 752 CodeGenSpecifics::Marshalling marshal; 753 const auto *sem = &floatToSemantics(kindMap, eleTy); 754 if (sem == &llvm::APFloat::IEEEsingle() || 755 sem == &llvm::APFloat::IEEEdouble() || 756 sem == &llvm::APFloat::IEEEquad()) { 757 // Use a type that will be translated into LLVM as: 758 // { t, t } struct of 2 eleTy 759 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 760 mlir::TypeRange{eleTy, eleTy}), 761 AT{}); 762 } else { 763 typeTodo(sem, loc, "return"); 764 } 765 return marshal; 766 } 767 }; 768 } // namespace 769 770 //===----------------------------------------------------------------------===// 771 // PPC64 (AIX 64 bit) target specifics. 772 //===----------------------------------------------------------------------===// 773 774 namespace { 775 struct TargetPPC64 : public GenericTarget<TargetPPC64> { 776 using GenericTarget::GenericTarget; 777 778 static constexpr int defaultWidth = 64; 779 780 CodeGenSpecifics::Marshalling 781 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 782 CodeGenSpecifics::Marshalling marshal; 783 // two distinct element type arguments (re, im) 784 marshal.emplace_back(eleTy, AT{}); 785 marshal.emplace_back(eleTy, AT{}); 786 return marshal; 787 } 788 789 CodeGenSpecifics::Marshalling 790 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 791 CodeGenSpecifics::Marshalling marshal; 792 // Use a type that will be translated into LLVM as: 793 // { t, t } struct of 2 element type 794 marshal.emplace_back( 795 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 796 AT{}); 797 return marshal; 798 } 799 }; 800 } // namespace 801 802 //===----------------------------------------------------------------------===// 803 // PPC64le linux target specifics. 804 //===----------------------------------------------------------------------===// 805 806 namespace { 807 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 808 using GenericTarget::GenericTarget; 809 810 static constexpr int defaultWidth = 64; 811 812 CodeGenSpecifics::Marshalling 813 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 814 CodeGenSpecifics::Marshalling marshal; 815 // two distinct element type arguments (re, im) 816 marshal.emplace_back(eleTy, AT{}); 817 marshal.emplace_back(eleTy, AT{}); 818 return marshal; 819 } 820 821 CodeGenSpecifics::Marshalling 822 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 823 CodeGenSpecifics::Marshalling marshal; 824 // Use a type that will be translated into LLVM as: 825 // { t, t } struct of 2 element type 826 marshal.emplace_back( 827 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 828 AT{}); 829 return marshal; 830 } 831 }; 832 } // namespace 833 834 //===----------------------------------------------------------------------===// 835 // sparc (sparc 32 bit) target specifics. 836 //===----------------------------------------------------------------------===// 837 838 namespace { 839 struct TargetSparc : public GenericTarget<TargetSparc> { 840 using GenericTarget::GenericTarget; 841 842 static constexpr int defaultWidth = 32; 843 844 CodeGenSpecifics::Marshalling 845 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 846 assert(fir::isa_real(eleTy)); 847 CodeGenSpecifics::Marshalling marshal; 848 // Use a type that will be translated into LLVM as: 849 // { t, t } struct of 2 eleTy 850 auto structTy = 851 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 852 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); 853 return marshal; 854 } 855 856 CodeGenSpecifics::Marshalling 857 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 858 assert(fir::isa_real(eleTy)); 859 CodeGenSpecifics::Marshalling marshal; 860 // Use a type that will be translated into LLVM as: 861 // { t, t } struct of 2 eleTy, byval 862 auto structTy = 863 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 864 marshal.emplace_back(fir::ReferenceType::get(structTy), 865 AT{/*alignment=*/0, /*byval=*/true}); 866 return marshal; 867 } 868 }; 869 } // namespace 870 871 //===----------------------------------------------------------------------===// 872 // sparcv9 (sparc 64 bit) target specifics. 873 //===----------------------------------------------------------------------===// 874 875 namespace { 876 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { 877 using GenericTarget::GenericTarget; 878 879 static constexpr int defaultWidth = 64; 880 881 CodeGenSpecifics::Marshalling 882 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 883 CodeGenSpecifics::Marshalling marshal; 884 const auto *sem = &floatToSemantics(kindMap, eleTy); 885 if (sem == &llvm::APFloat::IEEEsingle() || 886 sem == &llvm::APFloat::IEEEdouble()) { 887 // two distinct float, double arguments 888 marshal.emplace_back(eleTy, AT{}); 889 marshal.emplace_back(eleTy, AT{}); 890 } else if (sem == &llvm::APFloat::IEEEquad()) { 891 // Use a type that will be translated into LLVM as: 892 // { fp128, fp128 } struct of 2 fp128, byval, align 16 893 marshal.emplace_back( 894 fir::ReferenceType::get(mlir::TupleType::get( 895 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 896 AT{/*align=*/16, /*byval=*/true}); 897 } else { 898 typeTodo(sem, loc, "argument"); 899 } 900 return marshal; 901 } 902 903 CodeGenSpecifics::Marshalling 904 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 905 CodeGenSpecifics::Marshalling marshal; 906 // Use a type that will be translated into LLVM as: 907 // { eleTy, eleTy } struct of 2 eleTy 908 marshal.emplace_back( 909 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 910 AT{}); 911 return marshal; 912 } 913 }; 914 } // namespace 915 916 //===----------------------------------------------------------------------===// 917 // RISCV64 linux target specifics. 918 //===----------------------------------------------------------------------===// 919 920 namespace { 921 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { 922 using GenericTarget::GenericTarget; 923 924 static constexpr int defaultWidth = 64; 925 926 CodeGenSpecifics::Marshalling 927 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 928 CodeGenSpecifics::Marshalling marshal; 929 const auto *sem = &floatToSemantics(kindMap, eleTy); 930 if (sem == &llvm::APFloat::IEEEsingle() || 931 sem == &llvm::APFloat::IEEEdouble()) { 932 // Two distinct element type arguments (re, im) 933 marshal.emplace_back(eleTy, AT{}); 934 marshal.emplace_back(eleTy, AT{}); 935 } else { 936 typeTodo(sem, loc, "argument"); 937 } 938 return marshal; 939 } 940 941 CodeGenSpecifics::Marshalling 942 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 943 CodeGenSpecifics::Marshalling marshal; 944 const auto *sem = &floatToSemantics(kindMap, eleTy); 945 if (sem == &llvm::APFloat::IEEEsingle() || 946 sem == &llvm::APFloat::IEEEdouble()) { 947 // Use a type that will be translated into LLVM as: 948 // { t, t } struct of 2 eleTy, byVal 949 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 950 mlir::TypeRange{eleTy, eleTy}), 951 AT{/*alignment=*/0, /*byval=*/true}); 952 } else { 953 typeTodo(sem, loc, "return"); 954 } 955 return marshal; 956 } 957 }; 958 } // namespace 959 960 //===----------------------------------------------------------------------===// 961 // AMDGPU linux target specifics. 962 //===----------------------------------------------------------------------===// 963 964 namespace { 965 struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> { 966 using GenericTarget::GenericTarget; 967 968 // Default size (in bits) of the index type for strings. 969 static constexpr int defaultWidth = 64; 970 971 CodeGenSpecifics::Marshalling 972 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 973 CodeGenSpecifics::Marshalling marshal; 974 TODO(loc, "handle complex argument types"); 975 return marshal; 976 } 977 978 CodeGenSpecifics::Marshalling 979 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 980 CodeGenSpecifics::Marshalling marshal; 981 TODO(loc, "handle complex return types"); 982 return marshal; 983 } 984 }; 985 } // namespace 986 987 //===----------------------------------------------------------------------===// 988 // NVPTX linux target specifics. 989 //===----------------------------------------------------------------------===// 990 991 namespace { 992 struct TargetNVPTX : public GenericTarget<TargetNVPTX> { 993 using GenericTarget::GenericTarget; 994 995 // Default size (in bits) of the index type for strings. 996 static constexpr int defaultWidth = 64; 997 998 CodeGenSpecifics::Marshalling 999 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1000 CodeGenSpecifics::Marshalling marshal; 1001 TODO(loc, "handle complex argument types"); 1002 return marshal; 1003 } 1004 1005 CodeGenSpecifics::Marshalling 1006 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1007 CodeGenSpecifics::Marshalling marshal; 1008 TODO(loc, "handle complex return types"); 1009 return marshal; 1010 } 1011 }; 1012 } // namespace 1013 1014 //===----------------------------------------------------------------------===// 1015 // LoongArch64 linux target specifics. 1016 //===----------------------------------------------------------------------===// 1017 1018 namespace { 1019 struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> { 1020 using GenericTarget::GenericTarget; 1021 1022 static constexpr int defaultWidth = 64; 1023 1024 CodeGenSpecifics::Marshalling 1025 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1026 CodeGenSpecifics::Marshalling marshal; 1027 const auto *sem = &floatToSemantics(kindMap, eleTy); 1028 if (sem == &llvm::APFloat::IEEEsingle() || 1029 sem == &llvm::APFloat::IEEEdouble()) { 1030 // Two distinct element type arguments (re, im) 1031 marshal.emplace_back(eleTy, AT{}); 1032 marshal.emplace_back(eleTy, AT{}); 1033 } else { 1034 typeTodo(sem, loc, "argument"); 1035 } 1036 return marshal; 1037 } 1038 1039 CodeGenSpecifics::Marshalling 1040 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1041 CodeGenSpecifics::Marshalling marshal; 1042 const auto *sem = &floatToSemantics(kindMap, eleTy); 1043 if (sem == &llvm::APFloat::IEEEsingle() || 1044 sem == &llvm::APFloat::IEEEdouble()) { 1045 // Use a type that will be translated into LLVM as: 1046 // { t, t } struct of 2 eleTy, byVal 1047 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 1048 mlir::TypeRange{eleTy, eleTy}), 1049 AT{/*alignment=*/0, /*byval=*/true}); 1050 } else { 1051 typeTodo(sem, loc, "return"); 1052 } 1053 return marshal; 1054 } 1055 }; 1056 } // namespace 1057 1058 // Instantiate the overloaded target instance based on the triple value. 1059 // TODO: Add other targets to this file as needed. 1060 std::unique_ptr<fir::CodeGenSpecifics> 1061 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 1062 KindMapping &&kindMap, llvm::StringRef targetCPU, 1063 mlir::LLVM::TargetFeaturesAttr targetFeatures, 1064 const mlir::DataLayout &dl) { 1065 switch (trp.getArch()) { 1066 default: 1067 break; 1068 case llvm::Triple::ArchType::x86: 1069 if (trp.isOSWindows()) 1070 return std::make_unique<TargetI386Win>(ctx, std::move(trp), 1071 std::move(kindMap), targetCPU, 1072 targetFeatures, dl); 1073 else 1074 return std::make_unique<TargetI386>(ctx, std::move(trp), 1075 std::move(kindMap), targetCPU, 1076 targetFeatures, dl); 1077 case llvm::Triple::ArchType::x86_64: 1078 if (trp.isOSWindows()) 1079 return std::make_unique<TargetX86_64Win>(ctx, std::move(trp), 1080 std::move(kindMap), targetCPU, 1081 targetFeatures, dl); 1082 else 1083 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 1084 std::move(kindMap), targetCPU, 1085 targetFeatures, dl); 1086 case llvm::Triple::ArchType::aarch64: 1087 return std::make_unique<TargetAArch64>( 1088 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1089 case llvm::Triple::ArchType::ppc64: 1090 return std::make_unique<TargetPPC64>( 1091 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1092 case llvm::Triple::ArchType::ppc64le: 1093 return std::make_unique<TargetPPC64le>( 1094 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1095 case llvm::Triple::ArchType::sparc: 1096 return std::make_unique<TargetSparc>( 1097 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1098 case llvm::Triple::ArchType::sparcv9: 1099 return std::make_unique<TargetSparcV9>( 1100 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1101 case llvm::Triple::ArchType::riscv64: 1102 return std::make_unique<TargetRISCV64>( 1103 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1104 case llvm::Triple::ArchType::amdgcn: 1105 return std::make_unique<TargetAMDGPU>( 1106 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1107 case llvm::Triple::ArchType::nvptx64: 1108 return std::make_unique<TargetNVPTX>( 1109 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1110 case llvm::Triple::ArchType::loongarch64: 1111 return std::make_unique<TargetLoongArch64>( 1112 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1113 } 1114 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 1115 } 1116