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