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) 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 // Characters are passed in a split format with all pointers first (in the 89 // declared position) and all LEN arguments appended after all of the dummy 90 // arguments. 91 // NB: Other conventions/ABIs can/should be supported via options. 92 marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, 93 /*sret=*/false, /*append=*/true}); 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 structReturnType(mlir::Location loc, fir::RecordType ty) const override { 105 TODO(loc, "returning BIND(C) derived type for this target"); 106 } 107 108 CodeGenSpecifics::Marshalling 109 integerArgumentType(mlir::Location loc, 110 mlir::IntegerType argTy) const override { 111 CodeGenSpecifics::Marshalling marshal; 112 AT::IntegerExtension intExt = AT::IntegerExtension::None; 113 if (argTy.getWidth() < getCIntTypeWidth()) { 114 // isSigned() and isUnsigned() branches below are dead code currently. 115 // If needed, we can generate calls with signed/unsigned argument types 116 // to more precisely match C side (e.g. for Fortran runtime functions 117 // with 'unsigned short' arguments). 118 if (argTy.isSigned()) 119 intExt = AT::IntegerExtension::Sign; 120 else if (argTy.isUnsigned()) 121 intExt = AT::IntegerExtension::Zero; 122 else if (argTy.isSignless()) { 123 // Zero extend for 'i1' and sign extend for other types. 124 if (argTy.getWidth() == 1) 125 intExt = AT::IntegerExtension::Zero; 126 else 127 intExt = AT::IntegerExtension::Sign; 128 } 129 } 130 131 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, 132 /*sret=*/false, /*append=*/false, 133 /*intExt=*/intExt}); 134 return marshal; 135 } 136 137 CodeGenSpecifics::Marshalling 138 integerReturnType(mlir::Location loc, 139 mlir::IntegerType argTy) const override { 140 return integerArgumentType(loc, argTy); 141 } 142 143 // Width of 'int' type is 32-bits for almost all targets, except 144 // for AVR and MSP430 (see TargetInfo initializations 145 // in clang/lib/Basic/Targets). 146 unsigned char getCIntTypeWidth() const override { return 32; } 147 }; 148 } // namespace 149 150 //===----------------------------------------------------------------------===// 151 // i386 (x86 32 bit) linux target specifics. 152 //===----------------------------------------------------------------------===// 153 154 namespace { 155 struct TargetI386 : public GenericTarget<TargetI386> { 156 using GenericTarget::GenericTarget; 157 158 static constexpr int defaultWidth = 32; 159 160 CodeGenSpecifics::Marshalling 161 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 162 assert(fir::isa_real(eleTy)); 163 CodeGenSpecifics::Marshalling marshal; 164 // Use a type that will be translated into LLVM as: 165 // { t, t } struct of 2 eleTy, byval, align 4 166 auto structTy = 167 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 168 marshal.emplace_back(fir::ReferenceType::get(structTy), 169 AT{/*alignment=*/4, /*byval=*/true}); 170 return marshal; 171 } 172 173 CodeGenSpecifics::Marshalling 174 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 175 assert(fir::isa_real(eleTy)); 176 CodeGenSpecifics::Marshalling marshal; 177 const auto *sem = &floatToSemantics(kindMap, eleTy); 178 if (sem == &llvm::APFloat::IEEEsingle()) { 179 // i64 pack both floats in a 64-bit GPR 180 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 181 AT{}); 182 } else if (sem == &llvm::APFloat::IEEEdouble()) { 183 // Use a type that will be translated into LLVM as: 184 // { t, t } struct of 2 eleTy, sret, align 4 185 auto structTy = mlir::TupleType::get(eleTy.getContext(), 186 mlir::TypeRange{eleTy, eleTy}); 187 marshal.emplace_back(fir::ReferenceType::get(structTy), 188 AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); 189 } else { 190 typeTodo(sem, loc, "return"); 191 } 192 return marshal; 193 } 194 }; 195 } // namespace 196 197 //===----------------------------------------------------------------------===// 198 // i386 (x86 32 bit) Windows target specifics. 199 //===----------------------------------------------------------------------===// 200 201 namespace { 202 struct TargetI386Win : public GenericTarget<TargetI386Win> { 203 using GenericTarget::GenericTarget; 204 205 static constexpr int defaultWidth = 32; 206 207 CodeGenSpecifics::Marshalling 208 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 209 CodeGenSpecifics::Marshalling marshal; 210 // Use a type that will be translated into LLVM as: 211 // { t, t } struct of 2 eleTy, byval, align 4 212 auto structTy = 213 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 214 marshal.emplace_back(fir::ReferenceType::get(structTy), 215 AT{/*align=*/4, /*byval=*/true}); 216 return marshal; 217 } 218 219 CodeGenSpecifics::Marshalling 220 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 221 CodeGenSpecifics::Marshalling marshal; 222 const auto *sem = &floatToSemantics(kindMap, eleTy); 223 if (sem == &llvm::APFloat::IEEEsingle()) { 224 // i64 pack both floats in a 64-bit GPR 225 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 226 AT{}); 227 } else if (sem == &llvm::APFloat::IEEEdouble()) { 228 // Use a type that will be translated into LLVM as: 229 // { double, double } struct of 2 double, sret, align 8 230 marshal.emplace_back( 231 fir::ReferenceType::get(mlir::TupleType::get( 232 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 233 AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); 234 } else if (sem == &llvm::APFloat::IEEEquad()) { 235 // Use a type that will be translated into LLVM as: 236 // { fp128, fp128 } struct of 2 fp128, sret, align 16 237 marshal.emplace_back( 238 fir::ReferenceType::get(mlir::TupleType::get( 239 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 240 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 241 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 242 // Use a type that will be translated into LLVM as: 243 // { x86_fp80, x86_fp80 } struct of 2 x86_fp80, sret, align 4 244 marshal.emplace_back( 245 fir::ReferenceType::get(mlir::TupleType::get( 246 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 247 AT{/*align=*/4, /*byval=*/false, /*sret=*/true}); 248 } else { 249 typeTodo(sem, loc, "return"); 250 } 251 return marshal; 252 } 253 }; 254 } // namespace 255 256 //===----------------------------------------------------------------------===// 257 // x86_64 (x86 64 bit) linux target specifics. 258 //===----------------------------------------------------------------------===// 259 260 namespace { 261 struct TargetX86_64 : public GenericTarget<TargetX86_64> { 262 using GenericTarget::GenericTarget; 263 264 static constexpr int defaultWidth = 64; 265 266 CodeGenSpecifics::Marshalling 267 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 268 CodeGenSpecifics::Marshalling marshal; 269 const auto *sem = &floatToSemantics(kindMap, eleTy); 270 if (sem == &llvm::APFloat::IEEEsingle()) { 271 // <2 x t> vector of 2 eleTy 272 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 273 } else if (sem == &llvm::APFloat::IEEEdouble()) { 274 // FIXME: In case of SSE register exhaustion, the ABI here may be 275 // incorrect since LLVM may pass the real via register and the imaginary 276 // part via the stack while the ABI it should be all in register or all 277 // in memory. Register occupancy must be analyzed here. 278 // two distinct double arguments 279 marshal.emplace_back(eleTy, AT{}); 280 marshal.emplace_back(eleTy, AT{}); 281 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 282 // Use a type that will be translated into LLVM as: 283 // { x86_fp80, x86_fp80 } struct of 2 fp128, byval, align 16 284 marshal.emplace_back( 285 fir::ReferenceType::get(mlir::TupleType::get( 286 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 287 AT{/*align=*/16, /*byval=*/true}); 288 } else if (sem == &llvm::APFloat::IEEEquad()) { 289 // Use a type that will be translated into LLVM as: 290 // { fp128, fp128 } struct of 2 fp128, byval, align 16 291 marshal.emplace_back( 292 fir::ReferenceType::get(mlir::TupleType::get( 293 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 294 AT{/*align=*/16, /*byval=*/true}); 295 } else { 296 typeTodo(sem, loc, "argument"); 297 } 298 return marshal; 299 } 300 301 CodeGenSpecifics::Marshalling 302 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 303 CodeGenSpecifics::Marshalling marshal; 304 const auto *sem = &floatToSemantics(kindMap, eleTy); 305 if (sem == &llvm::APFloat::IEEEsingle()) { 306 // <2 x t> vector of 2 eleTy 307 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 308 } else if (sem == &llvm::APFloat::IEEEdouble()) { 309 // Use a type that will be translated into LLVM as: 310 // { double, double } struct of 2 double 311 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 312 mlir::TypeRange{eleTy, eleTy}), 313 AT{}); 314 } else if (sem == &llvm::APFloat::x87DoubleExtended()) { 315 // { x86_fp80, x86_fp80 } 316 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 317 mlir::TypeRange{eleTy, eleTy}), 318 AT{}); 319 } else if (sem == &llvm::APFloat::IEEEquad()) { 320 // Use a type that will be translated into LLVM as: 321 // { fp128, fp128 } struct of 2 fp128, sret, align 16 322 marshal.emplace_back( 323 fir::ReferenceType::get(mlir::TupleType::get( 324 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 325 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 326 } else { 327 typeTodo(sem, loc, "return"); 328 } 329 return marshal; 330 } 331 332 /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3. 333 enum ArgClass { 334 Integer = 0, 335 SSE, 336 SSEUp, 337 X87, 338 X87Up, 339 ComplexX87, 340 NoClass, 341 Memory 342 }; 343 344 /// Classify an argument type or a field of an aggregate type argument. 345 /// See System V ABI version 1.0 section 3.2.3. 346 /// The Lo and Hi class are set to the class of the lower eight eightbytes 347 /// and upper eight eightbytes on return. 348 /// If this is called for an aggregate field, the caller is responsible to 349 /// do the post-merge. 350 void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset, 351 ArgClass &Lo, ArgClass &Hi) const { 352 Hi = Lo = ArgClass::NoClass; 353 ArgClass ¤t = byteOffset < 8 ? Lo : Hi; 354 // System V AMD64 ABI 3.2.3. version 1.0 355 llvm::TypeSwitch<mlir::Type>(type) 356 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 357 if (intTy.getWidth() == 128) 358 Hi = Lo = ArgClass::Integer; 359 else 360 current = ArgClass::Integer; 361 }) 362 .template Case<mlir::FloatType>([&](mlir::Type floatTy) { 363 const auto *sem = &floatToSemantics(kindMap, floatTy); 364 if (sem == &llvm::APFloat::x87DoubleExtended()) { 365 Lo = ArgClass::X87; 366 Hi = ArgClass::X87Up; 367 } else if (sem == &llvm::APFloat::IEEEquad()) { 368 Lo = ArgClass::SSE; 369 Hi = ArgClass::SSEUp; 370 } else { 371 current = ArgClass::SSE; 372 } 373 }) 374 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) { 375 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); 376 if (sem == &llvm::APFloat::x87DoubleExtended()) { 377 current = ArgClass::ComplexX87; 378 } else { 379 fir::SequenceType::Shape shape{2}; 380 classifyArray(loc, 381 fir::SequenceType::get(shape, cmplx.getElementType()), 382 byteOffset, Lo, Hi); 383 } 384 }) 385 .template Case<fir::LogicalType>([&](fir::LogicalType logical) { 386 if (kindMap.getLogicalBitsize(logical.getFKind()) == 128) 387 Hi = Lo = ArgClass::Integer; 388 else 389 current = ArgClass::Integer; 390 }) 391 .template Case<fir::CharacterType>( 392 [&](fir::CharacterType character) { current = ArgClass::Integer; }) 393 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { 394 // Array component. 395 classifyArray(loc, seqTy, byteOffset, Lo, Hi); 396 }) 397 .template Case<fir::RecordType>([&](fir::RecordType recTy) { 398 // Component that is a derived type. 399 classifyStruct(loc, recTy, byteOffset, Lo, Hi); 400 }) 401 .template Case<fir::VectorType>([&](fir::VectorType vecTy) { 402 // Previously marshalled SSE eight byte for a previous struct 403 // argument. 404 auto *sem = fir::isa_real(vecTy.getEleTy()) 405 ? &floatToSemantics(kindMap, vecTy.getEleTy()) 406 : nullptr; 407 // Not expecting to hit this todo in standard code (it would 408 // require some vector type extension). 409 if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) && 410 !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4)) 411 TODO(loc, "passing vector argument to C by value"); 412 current = SSE; 413 }) 414 .Default([&](mlir::Type ty) { 415 if (fir::conformsWithPassByRef(ty)) 416 current = ArgClass::Integer; // Pointers. 417 else 418 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 419 "type argument"); 420 }); 421 } 422 423 // Classify fields of a derived type starting at \p offset. Returns the new 424 // offset. Post-merge is left to the caller. 425 std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy, 426 std::uint64_t byteOffset, ArgClass &Lo, 427 ArgClass &Hi) const { 428 for (auto component : recTy.getTypeList()) { 429 if (byteOffset > 16) { 430 // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger 431 // than 16 bytes here, it is not a single _m256 and or _m512 entity 432 // that could fit in AVX registers. 433 Lo = Hi = ArgClass::Memory; 434 return byteOffset; 435 } 436 mlir::Type compType = component.second; 437 auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash( 438 loc, compType, getDataLayout(), kindMap); 439 byteOffset = llvm::alignTo(byteOffset, compAlign); 440 ArgClass LoComp, HiComp; 441 classify(loc, compType, byteOffset, LoComp, HiComp); 442 Lo = mergeClass(Lo, LoComp); 443 Hi = mergeClass(Hi, HiComp); 444 byteOffset = byteOffset + llvm::alignTo(compSize, compAlign); 445 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) 446 return byteOffset; 447 } 448 return byteOffset; 449 } 450 451 // Classify fields of a constant size array type starting at \p offset. 452 // Returns the new offset. Post-merge is left to the caller. 453 void classifyArray(mlir::Location loc, fir::SequenceType seqTy, 454 std::uint64_t byteOffset, ArgClass &Lo, 455 ArgClass &Hi) const { 456 mlir::Type eleTy = seqTy.getEleTy(); 457 const std::uint64_t arraySize = seqTy.getConstantArraySize(); 458 auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash( 459 loc, eleTy, getDataLayout(), kindMap); 460 std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign); 461 for (std::uint64_t i = 0; i < arraySize; ++i) { 462 byteOffset = llvm::alignTo(byteOffset, eleAlign); 463 if (byteOffset > 16) { 464 // See 3.2.3 p. 1 and note 15. Same as in classifyStruct. 465 Lo = Hi = ArgClass::Memory; 466 return; 467 } 468 ArgClass LoComp, HiComp; 469 classify(loc, eleTy, byteOffset, LoComp, HiComp); 470 Lo = mergeClass(Lo, LoComp); 471 Hi = mergeClass(Hi, HiComp); 472 byteOffset = byteOffset + eleStorageSize; 473 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) 474 return; 475 } 476 } 477 478 // Goes through the previously marshalled arguments and count the 479 // register occupancy to check if there are enough registers left. 480 bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters, 481 int neededSSERegisters, 482 const Marshalling &previousArguments) const { 483 int availIntRegisters = 6; 484 int availSSERegisters = 8; 485 for (auto typeAndAttr : previousArguments) { 486 const auto &attr = std::get<Attributes>(typeAndAttr); 487 if (attr.isByVal()) 488 continue; // Previous argument passed on the stack. 489 ArgClass Lo, Hi; 490 Lo = Hi = ArgClass::NoClass; 491 classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi); 492 // post merge is not needed here since previous aggregate arguments 493 // were marshalled into simpler arguments. 494 if (Lo == ArgClass::Integer) 495 --availIntRegisters; 496 else if (Lo == SSE) 497 --availSSERegisters; 498 if (Hi == ArgClass::Integer) 499 --availIntRegisters; 500 else if (Hi == ArgClass::SSE) 501 --availSSERegisters; 502 } 503 return availSSERegisters >= neededSSERegisters && 504 availIntRegisters >= neededIntRegisters; 505 } 506 507 /// Argument class merging as described in System V ABI 3.2.3 point 4. 508 ArgClass mergeClass(ArgClass accum, ArgClass field) const { 509 assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) && 510 "Invalid accumulated classification during merge."); 511 if (accum == field || field == NoClass) 512 return accum; 513 if (field == ArgClass::Memory) 514 return ArgClass::Memory; 515 if (accum == NoClass) 516 return field; 517 if (accum == Integer || field == Integer) 518 return ArgClass::Integer; 519 if (field == ArgClass::X87 || field == ArgClass::X87Up || 520 field == ArgClass::ComplexX87 || accum == ArgClass::X87 || 521 accum == ArgClass::X87Up) 522 return Memory; 523 return SSE; 524 } 525 526 /// Argument class post merging as described in System V ABI 3.2.3 point 5. 527 void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const { 528 if (Hi == ArgClass::Memory) 529 Lo = ArgClass::Memory; 530 if (Hi == ArgClass::X87Up && Lo != ArgClass::X87) 531 Lo = ArgClass::Memory; 532 if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp)) 533 Lo = ArgClass::Memory; 534 if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE) 535 Hi = SSE; 536 } 537 538 /// When \p recTy is a one field record type that can be passed 539 /// like the field on its own, returns the field type. Returns 540 /// a null type otherwise. 541 mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy, 542 bool allowComplex = false) const { 543 auto typeList = recTy.getTypeList(); 544 if (typeList.size() != 1) 545 return {}; 546 mlir::Type fieldType = typeList[0].second; 547 if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::LogicalType>( 548 fieldType)) 549 return fieldType; 550 if (allowComplex && mlir::isa<mlir::ComplexType>(fieldType)) 551 return fieldType; 552 if (mlir::isa<fir::CharacterType>(fieldType)) { 553 // Only CHARACTER(1) are expected in BIND(C) contexts, which is the only 554 // contexts where derived type may be passed in registers. 555 assert(mlir::cast<fir::CharacterType>(fieldType).getLen() == 1 && 556 "fir.type value arg character components must have length 1"); 557 return fieldType; 558 } 559 // Complex field that needs to be split, or array. 560 return {}; 561 } 562 563 mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context, 564 ArgClass argClass, 565 std::uint64_t partByteSize) const { 566 if (argClass == ArgClass::SSE) { 567 if (partByteSize > 16) 568 TODO(loc, "passing struct as a real > 128 bits in register"); 569 // Clang uses vector type when several fp fields are marshalled 570 // into a single SSE register (like <n x smallest fp field> ). 571 // It should make no difference from an ABI point of view to just 572 // select an fp type of the right size, and it makes things simpler 573 // here. 574 if (partByteSize > 8) 575 return mlir::Float128Type::get(context); 576 if (partByteSize > 4) 577 return mlir::Float64Type::get(context); 578 if (partByteSize > 2) 579 return mlir::Float32Type::get(context); 580 return mlir::Float16Type::get(context); 581 } 582 assert(partByteSize <= 8 && 583 "expect integer part of aggregate argument to fit into eight bytes"); 584 if (partByteSize > 4) 585 return mlir::IntegerType::get(context, 64); 586 if (partByteSize > 2) 587 return mlir::IntegerType::get(context, 32); 588 if (partByteSize > 1) 589 return mlir::IntegerType::get(context, 16); 590 return mlir::IntegerType::get(context, 8); 591 } 592 593 /// Marshal a derived type passed by value like a C struct. 594 CodeGenSpecifics::Marshalling 595 structArgumentType(mlir::Location loc, fir::RecordType recTy, 596 const Marshalling &previousArguments) const override { 597 std::uint64_t byteOffset = 0; 598 ArgClass Lo, Hi; 599 Lo = Hi = ArgClass::NoClass; 600 byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi); 601 postMerge(byteOffset, Lo, Hi); 602 if (Lo == ArgClass::Memory || Lo == ArgClass::X87 || 603 Lo == ArgClass::ComplexX87) 604 return passOnTheStack(loc, recTy, /*isResult=*/false); 605 int neededIntRegisters = 0; 606 int neededSSERegisters = 0; 607 if (Lo == ArgClass::SSE) 608 ++neededSSERegisters; 609 else if (Lo == ArgClass::Integer) 610 ++neededIntRegisters; 611 if (Hi == ArgClass::SSE) 612 ++neededSSERegisters; 613 else if (Hi == ArgClass::Integer) 614 ++neededIntRegisters; 615 // C struct should not be split into LLVM registers if LLVM codegen is not 616 // able to later assign actual registers to all of them (struct passing is 617 // all in registers or all on the stack). 618 if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters, 619 previousArguments)) 620 return passOnTheStack(loc, recTy, /*isResult=*/false); 621 622 if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) { 623 CodeGenSpecifics::Marshalling marshal; 624 marshal.emplace_back(fieldType, AT{}); 625 return marshal; 626 } 627 if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) { 628 // Pass a single integer or floating point argument. 629 mlir::Type lowType = 630 pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset); 631 CodeGenSpecifics::Marshalling marshal; 632 marshal.emplace_back(lowType, AT{}); 633 return marshal; 634 } 635 // Split into two integer or floating point arguments. 636 // Note that for the first argument, this will always pick i64 or f64 which 637 // may be bigger than needed if some struct padding ends the first eight 638 // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and 639 // semantic point of view, but it may not match the LLVM IR interface clang 640 // would produce for the equivalent C code (the assembly will still be 641 // compatible). This allows keeping the logic simpler here since it 642 // avoids computing the "data" size of the Lo part. 643 mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u); 644 mlir::Type hiType = 645 pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u); 646 CodeGenSpecifics::Marshalling marshal; 647 marshal.emplace_back(lowType, AT{}); 648 marshal.emplace_back(hiType, AT{}); 649 return marshal; 650 } 651 652 CodeGenSpecifics::Marshalling 653 structReturnType(mlir::Location loc, fir::RecordType recTy) const override { 654 std::uint64_t byteOffset = 0; 655 ArgClass Lo, Hi; 656 Lo = Hi = ArgClass::NoClass; 657 byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi); 658 mlir::MLIRContext *context = recTy.getContext(); 659 postMerge(byteOffset, Lo, Hi); 660 if (Lo == ArgClass::Memory) 661 return passOnTheStack(loc, recTy, /*isResult=*/true); 662 663 // Note that X87/ComplexX87 are passed in memory, but returned via %st0 664 // %st1 registers. Here, they are returned as fp80 or {fp80, fp80} by 665 // passAsFieldIfOneFieldStruct, and LLVM will use the expected registers. 666 667 // Note that {_Complex long double} is not 100% clear from an ABI 668 // perspective because the aggregate post merger rules say it should be 669 // passed in memory because it is bigger than 2 eight bytes. This has the 670 // funny effect of 671 // {_Complex long double} return to be dealt with differently than 672 // _Complex long double. 673 674 if (auto fieldType = 675 passAsFieldIfOneFieldStruct(recTy, /*allowComplex=*/true)) { 676 if (auto complexType = mlir::dyn_cast<mlir::ComplexType>(fieldType)) 677 return complexReturnType(loc, complexType.getElementType()); 678 CodeGenSpecifics::Marshalling marshal; 679 marshal.emplace_back(fieldType, AT{}); 680 return marshal; 681 } 682 683 if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) { 684 // Return a single integer or floating point argument. 685 mlir::Type lowType = pickLLVMArgType(loc, context, Lo, byteOffset); 686 CodeGenSpecifics::Marshalling marshal; 687 marshal.emplace_back(lowType, AT{}); 688 return marshal; 689 } 690 // Will be returned in two different registers. Generate {lowTy, HiTy} for 691 // the LLVM IR result type. 692 CodeGenSpecifics::Marshalling marshal; 693 mlir::Type lowType = pickLLVMArgType(loc, context, Lo, 8u); 694 mlir::Type hiType = pickLLVMArgType(loc, context, Hi, byteOffset - 8u); 695 marshal.emplace_back(mlir::TupleType::get(context, {lowType, hiType}), 696 AT{}); 697 return marshal; 698 } 699 700 /// Marshal an argument that must be passed on the stack. 701 CodeGenSpecifics::Marshalling 702 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const { 703 CodeGenSpecifics::Marshalling marshal; 704 auto sizeAndAlign = 705 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap); 706 // The stack is always 8 byte aligned (note 14 in 3.2.3). 707 unsigned short align = 708 std::max(sizeAndAlign.second, static_cast<unsigned short>(8)); 709 marshal.emplace_back(fir::ReferenceType::get(ty), 710 AT{align, /*byval=*/!isResult, /*sret=*/isResult}); 711 return marshal; 712 } 713 }; 714 } // namespace 715 716 //===----------------------------------------------------------------------===// 717 // x86_64 (x86 64 bit) Windows target specifics. 718 //===----------------------------------------------------------------------===// 719 720 namespace { 721 struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> { 722 using GenericTarget::GenericTarget; 723 724 static constexpr int defaultWidth = 64; 725 726 CodeGenSpecifics::Marshalling 727 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 728 CodeGenSpecifics::Marshalling marshal; 729 const auto *sem = &floatToSemantics(kindMap, eleTy); 730 if (sem == &llvm::APFloat::IEEEsingle()) { 731 // i64 pack both floats in a 64-bit GPR 732 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 733 AT{}); 734 } else if (sem == &llvm::APFloat::IEEEdouble()) { 735 // Use a type that will be translated into LLVM as: 736 // { double, double } struct of 2 double, byval, align 8 737 marshal.emplace_back( 738 fir::ReferenceType::get(mlir::TupleType::get( 739 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 740 AT{/*align=*/8, /*byval=*/true}); 741 } else if (sem == &llvm::APFloat::IEEEquad() || 742 sem == &llvm::APFloat::x87DoubleExtended()) { 743 // Use a type that will be translated into LLVM as: 744 // { t, t } struct of 2 eleTy, byval, align 16 745 marshal.emplace_back( 746 fir::ReferenceType::get(mlir::TupleType::get( 747 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 748 AT{/*align=*/16, /*byval=*/true}); 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 // i64 pack both floats in a 64-bit GPR 761 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 762 AT{}); 763 } else if (sem == &llvm::APFloat::IEEEdouble()) { 764 // Use a type that will be translated into LLVM as: 765 // { double, double } struct of 2 double, sret, align 8 766 marshal.emplace_back( 767 fir::ReferenceType::get(mlir::TupleType::get( 768 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 769 AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); 770 } else if (sem == &llvm::APFloat::IEEEquad() || 771 sem == &llvm::APFloat::x87DoubleExtended()) { 772 // Use a type that will be translated into LLVM as: 773 // { t, t } struct of 2 eleTy, sret, align 16 774 marshal.emplace_back( 775 fir::ReferenceType::get(mlir::TupleType::get( 776 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 777 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 778 } else { 779 typeTodo(sem, loc, "return"); 780 } 781 return marshal; 782 } 783 }; 784 } // namespace 785 786 //===----------------------------------------------------------------------===// 787 // AArch64 linux target specifics. 788 //===----------------------------------------------------------------------===// 789 790 namespace { 791 // AArch64 procedure call standard: 792 // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing 793 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 794 using GenericTarget::GenericTarget; 795 796 static constexpr int defaultWidth = 64; 797 798 CodeGenSpecifics::Marshalling 799 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 800 CodeGenSpecifics::Marshalling marshal; 801 const auto *sem = &floatToSemantics(kindMap, eleTy); 802 if (sem == &llvm::APFloat::IEEEsingle() || 803 sem == &llvm::APFloat::IEEEdouble() || 804 sem == &llvm::APFloat::IEEEquad()) { 805 // [2 x t] array of 2 eleTy 806 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 807 } else { 808 typeTodo(sem, loc, "argument"); 809 } 810 return marshal; 811 } 812 813 CodeGenSpecifics::Marshalling 814 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 815 CodeGenSpecifics::Marshalling marshal; 816 const auto *sem = &floatToSemantics(kindMap, eleTy); 817 if (sem == &llvm::APFloat::IEEEsingle() || 818 sem == &llvm::APFloat::IEEEdouble() || 819 sem == &llvm::APFloat::IEEEquad()) { 820 // Use a type that will be translated into LLVM as: 821 // { t, t } struct of 2 eleTy 822 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 823 mlir::TypeRange{eleTy, eleTy}), 824 AT{}); 825 } else { 826 typeTodo(sem, loc, "return"); 827 } 828 return marshal; 829 } 830 831 // Flatten a RecordType::TypeList containing more record types or array type 832 static std::optional<std::vector<mlir::Type>> 833 flattenTypeList(const RecordType::TypeList &types) { 834 std::vector<mlir::Type> flatTypes; 835 // The flat list will be at least the same size as the non-flat list. 836 flatTypes.reserve(types.size()); 837 for (auto [c, type] : types) { 838 // Flatten record type 839 if (auto recTy = mlir::dyn_cast<RecordType>(type)) { 840 auto subTypeList = flattenTypeList(recTy.getTypeList()); 841 if (!subTypeList) 842 return std::nullopt; 843 llvm::copy(*subTypeList, std::back_inserter(flatTypes)); 844 continue; 845 } 846 847 // Flatten array type 848 if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) { 849 if (seqTy.hasDynamicExtents()) 850 return std::nullopt; 851 std::size_t n = seqTy.getConstantArraySize(); 852 auto eleTy = seqTy.getElementType(); 853 // Flatten array of record types 854 if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) { 855 auto subTypeList = flattenTypeList(recTy.getTypeList()); 856 if (!subTypeList) 857 return std::nullopt; 858 for (std::size_t i = 0; i < n; ++i) 859 llvm::copy(*subTypeList, std::back_inserter(flatTypes)); 860 } else { 861 std::fill_n(std::back_inserter(flatTypes), 862 seqTy.getConstantArraySize(), eleTy); 863 } 864 continue; 865 } 866 867 // Other types are already flat 868 flatTypes.push_back(type); 869 } 870 return flatTypes; 871 } 872 873 // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An 874 // HFA is a record type with up to 4 floating-point members of the same type. 875 static std::optional<int> usedRegsForHFA(fir::RecordType ty) { 876 RecordType::TypeList types = ty.getTypeList(); 877 if (types.empty() || types.size() > 4) 878 return std::nullopt; 879 880 std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types); 881 if (!flatTypes || flatTypes->size() > 4) { 882 return std::nullopt; 883 } 884 885 if (!isa_real(flatTypes->front())) { 886 return std::nullopt; 887 } 888 889 return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()} 890 : std::nullopt; 891 } 892 893 struct NRegs { 894 int n{0}; 895 bool isSimd{false}; 896 }; 897 898 NRegs usedRegsForRecordType(mlir::Location loc, fir::RecordType type) const { 899 if (std::optional<int> size = usedRegsForHFA(type)) 900 return {*size, true}; 901 902 auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash( 903 loc, type, getDataLayout(), kindMap); 904 905 if (size <= 16) 906 return {static_cast<int>((size + 7) / 8), false}; 907 908 // Pass on the stack, i.e. no registers used 909 return {}; 910 } 911 912 NRegs usedRegsForType(mlir::Location loc, mlir::Type type) const { 913 return llvm::TypeSwitch<mlir::Type, NRegs>(type) 914 .Case<mlir::IntegerType>([&](auto intTy) { 915 return intTy.getWidth() == 128 ? NRegs{2, false} : NRegs{1, false}; 916 }) 917 .Case<mlir::FloatType>([&](auto) { return NRegs{1, true}; }) 918 .Case<mlir::ComplexType>([&](auto) { return NRegs{2, true}; }) 919 .Case<fir::LogicalType>([&](auto) { return NRegs{1, false}; }) 920 .Case<fir::CharacterType>([&](auto) { return NRegs{1, false}; }) 921 .Case<fir::SequenceType>([&](auto ty) { 922 assert(ty.getShape().size() == 1 && 923 "invalid array dimensions in BIND(C)"); 924 NRegs nregs = usedRegsForType(loc, ty.getEleTy()); 925 nregs.n *= ty.getShape()[0]; 926 return nregs; 927 }) 928 .Case<fir::RecordType>( 929 [&](auto ty) { return usedRegsForRecordType(loc, ty); }) 930 .Case<fir::VectorType>([&](auto) { 931 TODO(loc, "passing vector argument to C by value is not supported"); 932 return NRegs{}; 933 }); 934 } 935 936 bool hasEnoughRegisters(mlir::Location loc, fir::RecordType type, 937 const Marshalling &previousArguments) const { 938 int availIntRegisters = 8; 939 int availSIMDRegisters = 8; 940 941 // Check previous arguments to see how many registers are used already 942 for (auto [type, attr] : previousArguments) { 943 if (availIntRegisters <= 0 || availSIMDRegisters <= 0) 944 break; 945 946 if (attr.isByVal()) 947 continue; // Previous argument passed on the stack 948 949 NRegs nregs = usedRegsForType(loc, type); 950 if (nregs.isSimd) 951 availSIMDRegisters -= nregs.n; 952 else 953 availIntRegisters -= nregs.n; 954 } 955 956 NRegs nregs = usedRegsForRecordType(loc, type); 957 958 if (nregs.isSimd) 959 return nregs.n <= availSIMDRegisters; 960 961 return nregs.n <= availIntRegisters; 962 } 963 964 CodeGenSpecifics::Marshalling 965 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const { 966 CodeGenSpecifics::Marshalling marshal; 967 auto sizeAndAlign = 968 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap); 969 // The stack is always 8 byte aligned 970 unsigned short align = 971 std::max(sizeAndAlign.second, static_cast<unsigned short>(8)); 972 marshal.emplace_back(fir::ReferenceType::get(ty), 973 AT{align, /*byval=*/!isResult, /*sret=*/isResult}); 974 return marshal; 975 } 976 977 CodeGenSpecifics::Marshalling 978 structType(mlir::Location loc, fir::RecordType type, bool isResult) const { 979 NRegs nregs = usedRegsForRecordType(loc, type); 980 981 // If the type needs no registers it must need to be passed on the stack 982 if (nregs.n == 0) 983 return passOnTheStack(loc, type, isResult); 984 985 CodeGenSpecifics::Marshalling marshal; 986 987 mlir::Type pcsType; 988 if (nregs.isSimd) { 989 pcsType = type; 990 } else { 991 pcsType = fir::SequenceType::get( 992 nregs.n, mlir::IntegerType::get(type.getContext(), 64)); 993 } 994 995 marshal.emplace_back(pcsType, AT{}); 996 return marshal; 997 } 998 999 CodeGenSpecifics::Marshalling 1000 structArgumentType(mlir::Location loc, fir::RecordType ty, 1001 const Marshalling &previousArguments) const override { 1002 if (!hasEnoughRegisters(loc, ty, previousArguments)) { 1003 return passOnTheStack(loc, ty, /*isResult=*/false); 1004 } 1005 1006 return structType(loc, ty, /*isResult=*/false); 1007 } 1008 1009 CodeGenSpecifics::Marshalling 1010 structReturnType(mlir::Location loc, fir::RecordType ty) const override { 1011 return structType(loc, ty, /*isResult=*/true); 1012 } 1013 }; 1014 } // namespace 1015 1016 //===----------------------------------------------------------------------===// 1017 // PPC64 (AIX 64 bit) target specifics. 1018 //===----------------------------------------------------------------------===// 1019 1020 namespace { 1021 struct TargetPPC64 : public GenericTarget<TargetPPC64> { 1022 using GenericTarget::GenericTarget; 1023 1024 static constexpr int defaultWidth = 64; 1025 1026 CodeGenSpecifics::Marshalling 1027 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 1028 CodeGenSpecifics::Marshalling marshal; 1029 // two distinct element type arguments (re, im) 1030 marshal.emplace_back(eleTy, AT{}); 1031 marshal.emplace_back(eleTy, AT{}); 1032 return marshal; 1033 } 1034 1035 CodeGenSpecifics::Marshalling 1036 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 1037 CodeGenSpecifics::Marshalling marshal; 1038 // Use a type that will be translated into LLVM as: 1039 // { t, t } struct of 2 element type 1040 marshal.emplace_back( 1041 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 1042 AT{}); 1043 return marshal; 1044 } 1045 }; 1046 } // namespace 1047 1048 //===----------------------------------------------------------------------===// 1049 // PPC64le linux target specifics. 1050 //===----------------------------------------------------------------------===// 1051 1052 namespace { 1053 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 1054 using GenericTarget::GenericTarget; 1055 1056 static constexpr int defaultWidth = 64; 1057 1058 CodeGenSpecifics::Marshalling 1059 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 1060 CodeGenSpecifics::Marshalling marshal; 1061 // two distinct element type arguments (re, im) 1062 marshal.emplace_back(eleTy, AT{}); 1063 marshal.emplace_back(eleTy, AT{}); 1064 return marshal; 1065 } 1066 1067 CodeGenSpecifics::Marshalling 1068 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 1069 CodeGenSpecifics::Marshalling marshal; 1070 // Use a type that will be translated into LLVM as: 1071 // { t, t } struct of 2 element type 1072 marshal.emplace_back( 1073 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 1074 AT{}); 1075 return marshal; 1076 } 1077 }; 1078 } // namespace 1079 1080 //===----------------------------------------------------------------------===// 1081 // sparc (sparc 32 bit) target specifics. 1082 //===----------------------------------------------------------------------===// 1083 1084 namespace { 1085 struct TargetSparc : public GenericTarget<TargetSparc> { 1086 using GenericTarget::GenericTarget; 1087 1088 static constexpr int defaultWidth = 32; 1089 1090 CodeGenSpecifics::Marshalling 1091 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 1092 assert(fir::isa_real(eleTy)); 1093 CodeGenSpecifics::Marshalling marshal; 1094 // Use a type that will be translated into LLVM as: 1095 // { t, t } struct of 2 eleTy 1096 auto structTy = 1097 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 1098 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); 1099 return marshal; 1100 } 1101 1102 CodeGenSpecifics::Marshalling 1103 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1104 assert(fir::isa_real(eleTy)); 1105 CodeGenSpecifics::Marshalling marshal; 1106 // Use a type that will be translated into LLVM as: 1107 // { t, t } struct of 2 eleTy, byval 1108 auto structTy = 1109 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 1110 marshal.emplace_back(fir::ReferenceType::get(structTy), 1111 AT{/*alignment=*/0, /*byval=*/true}); 1112 return marshal; 1113 } 1114 }; 1115 } // namespace 1116 1117 //===----------------------------------------------------------------------===// 1118 // sparcv9 (sparc 64 bit) target specifics. 1119 //===----------------------------------------------------------------------===// 1120 1121 namespace { 1122 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { 1123 using GenericTarget::GenericTarget; 1124 1125 static constexpr int defaultWidth = 64; 1126 1127 CodeGenSpecifics::Marshalling 1128 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1129 CodeGenSpecifics::Marshalling marshal; 1130 const auto *sem = &floatToSemantics(kindMap, eleTy); 1131 if (sem == &llvm::APFloat::IEEEsingle() || 1132 sem == &llvm::APFloat::IEEEdouble()) { 1133 // two distinct float, double arguments 1134 marshal.emplace_back(eleTy, AT{}); 1135 marshal.emplace_back(eleTy, AT{}); 1136 } else if (sem == &llvm::APFloat::IEEEquad()) { 1137 // Use a type that will be translated into LLVM as: 1138 // { fp128, fp128 } struct of 2 fp128, byval, align 16 1139 marshal.emplace_back( 1140 fir::ReferenceType::get(mlir::TupleType::get( 1141 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1142 AT{/*align=*/16, /*byval=*/true}); 1143 } else { 1144 typeTodo(sem, loc, "argument"); 1145 } 1146 return marshal; 1147 } 1148 1149 CodeGenSpecifics::Marshalling 1150 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1151 CodeGenSpecifics::Marshalling marshal; 1152 // Use a type that will be translated into LLVM as: 1153 // { eleTy, eleTy } struct of 2 eleTy 1154 marshal.emplace_back( 1155 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 1156 AT{}); 1157 return marshal; 1158 } 1159 }; 1160 } // namespace 1161 1162 //===----------------------------------------------------------------------===// 1163 // RISCV64 linux target specifics. 1164 //===----------------------------------------------------------------------===// 1165 1166 namespace { 1167 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { 1168 using GenericTarget::GenericTarget; 1169 1170 static constexpr int defaultWidth = 64; 1171 1172 CodeGenSpecifics::Marshalling 1173 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1174 CodeGenSpecifics::Marshalling marshal; 1175 const auto *sem = &floatToSemantics(kindMap, eleTy); 1176 if (sem == &llvm::APFloat::IEEEsingle() || 1177 sem == &llvm::APFloat::IEEEdouble()) { 1178 // Two distinct element type arguments (re, im) 1179 marshal.emplace_back(eleTy, AT{}); 1180 marshal.emplace_back(eleTy, AT{}); 1181 } else { 1182 typeTodo(sem, loc, "argument"); 1183 } 1184 return marshal; 1185 } 1186 1187 CodeGenSpecifics::Marshalling 1188 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1189 CodeGenSpecifics::Marshalling marshal; 1190 const auto *sem = &floatToSemantics(kindMap, eleTy); 1191 if (sem == &llvm::APFloat::IEEEsingle() || 1192 sem == &llvm::APFloat::IEEEdouble()) { 1193 // Use a type that will be translated into LLVM as: 1194 // { t, t } struct of 2 eleTy, byVal 1195 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 1196 mlir::TypeRange{eleTy, eleTy}), 1197 AT{/*alignment=*/0, /*byval=*/true}); 1198 } else { 1199 typeTodo(sem, loc, "return"); 1200 } 1201 return marshal; 1202 } 1203 }; 1204 } // namespace 1205 1206 //===----------------------------------------------------------------------===// 1207 // AMDGPU linux target specifics. 1208 //===----------------------------------------------------------------------===// 1209 1210 namespace { 1211 struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> { 1212 using GenericTarget::GenericTarget; 1213 1214 // Default size (in bits) of the index type for strings. 1215 static constexpr int defaultWidth = 64; 1216 1217 CodeGenSpecifics::Marshalling 1218 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1219 CodeGenSpecifics::Marshalling marshal; 1220 TODO(loc, "handle complex argument types"); 1221 return marshal; 1222 } 1223 1224 CodeGenSpecifics::Marshalling 1225 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1226 CodeGenSpecifics::Marshalling marshal; 1227 TODO(loc, "handle complex return types"); 1228 return marshal; 1229 } 1230 }; 1231 } // namespace 1232 1233 //===----------------------------------------------------------------------===// 1234 // NVPTX linux target specifics. 1235 //===----------------------------------------------------------------------===// 1236 1237 namespace { 1238 struct TargetNVPTX : public GenericTarget<TargetNVPTX> { 1239 using GenericTarget::GenericTarget; 1240 1241 // Default size (in bits) of the index type for strings. 1242 static constexpr int defaultWidth = 64; 1243 1244 CodeGenSpecifics::Marshalling 1245 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1246 CodeGenSpecifics::Marshalling marshal; 1247 TODO(loc, "handle complex argument types"); 1248 return marshal; 1249 } 1250 1251 CodeGenSpecifics::Marshalling 1252 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1253 CodeGenSpecifics::Marshalling marshal; 1254 TODO(loc, "handle complex return types"); 1255 return marshal; 1256 } 1257 }; 1258 } // namespace 1259 1260 //===----------------------------------------------------------------------===// 1261 // LoongArch64 linux target specifics. 1262 //===----------------------------------------------------------------------===// 1263 1264 namespace { 1265 struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> { 1266 using GenericTarget::GenericTarget; 1267 1268 static constexpr int defaultWidth = 64; 1269 static constexpr int GRLen = defaultWidth; /* eight bytes */ 1270 static constexpr int GRLenInChar = GRLen / 8; 1271 static constexpr int FRLen = defaultWidth; /* eight bytes */ 1272 1273 CodeGenSpecifics::Marshalling 1274 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1275 CodeGenSpecifics::Marshalling marshal; 1276 const auto *sem = &floatToSemantics(kindMap, eleTy); 1277 if (sem == &llvm::APFloat::IEEEsingle() || 1278 sem == &llvm::APFloat::IEEEdouble()) { 1279 // Two distinct element type arguments (re, im) 1280 marshal.emplace_back(eleTy, AT{}); 1281 marshal.emplace_back(eleTy, AT{}); 1282 } else if (sem == &llvm::APFloat::IEEEquad()) { 1283 // Use a type that will be translated into LLVM as: 1284 // { fp128, fp128 } struct of 2 fp128, byval 1285 marshal.emplace_back( 1286 fir::ReferenceType::get(mlir::TupleType::get( 1287 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1288 AT{/*align=*/16, /*byval=*/true}); 1289 } else { 1290 typeTodo(sem, loc, "argument"); 1291 } 1292 return marshal; 1293 } 1294 1295 CodeGenSpecifics::Marshalling 1296 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1297 CodeGenSpecifics::Marshalling marshal; 1298 const auto *sem = &floatToSemantics(kindMap, eleTy); 1299 if (sem == &llvm::APFloat::IEEEsingle() || 1300 sem == &llvm::APFloat::IEEEdouble()) { 1301 // Use a type that will be translated into LLVM as: 1302 // { t, t } struct of 2 eleTy, byVal 1303 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 1304 mlir::TypeRange{eleTy, eleTy}), 1305 AT{/*alignment=*/0, /*byval=*/true}); 1306 } else if (sem == &llvm::APFloat::IEEEquad()) { 1307 // Use a type that will be translated into LLVM as: 1308 // { fp128, fp128 } struct of 2 fp128, sret, align 16 1309 marshal.emplace_back( 1310 fir::ReferenceType::get(mlir::TupleType::get( 1311 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1312 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 1313 } else { 1314 typeTodo(sem, loc, "return"); 1315 } 1316 return marshal; 1317 } 1318 1319 CodeGenSpecifics::Marshalling 1320 integerArgumentType(mlir::Location loc, 1321 mlir::IntegerType argTy) const override { 1322 if (argTy.getWidth() == 32) { 1323 // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended. 1324 // Therefore, Flang also follows it if a function needs to be 1325 // interoperable with C. 1326 // 1327 // Currently, it only adds `signext` attribute to the dummy arguments and 1328 // return values in the function signatures, but it does not add the 1329 // corresponding attribute to the actual arguments and return values in 1330 // `fir.call` instruction. Thanks to LLVM's integration of all these 1331 // attributes, the modification is still effective. 1332 CodeGenSpecifics::Marshalling marshal; 1333 AT::IntegerExtension intExt = AT::IntegerExtension::Sign; 1334 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, 1335 /*sret=*/false, /*append=*/false, 1336 /*intExt=*/intExt}); 1337 return marshal; 1338 } 1339 1340 return GenericTarget::integerArgumentType(loc, argTy); 1341 } 1342 1343 /// Flatten non-basic types, resulting in an array of types containing only 1344 /// `IntegerType` and `FloatType`. 1345 llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc, 1346 const mlir::Type type) const { 1347 llvm::SmallVector<mlir::Type> flatTypes; 1348 1349 llvm::TypeSwitch<mlir::Type>(type) 1350 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 1351 if (intTy.getWidth() != 0) 1352 flatTypes.push_back(intTy); 1353 }) 1354 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) { 1355 if (floatTy.getWidth() != 0) 1356 flatTypes.push_back(floatTy); 1357 }) 1358 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) { 1359 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); 1360 if (sem == &llvm::APFloat::IEEEsingle() || 1361 sem == &llvm::APFloat::IEEEdouble() || 1362 sem == &llvm::APFloat::IEEEquad()) 1363 std::fill_n(std::back_inserter(flatTypes), 2, 1364 cmplx.getElementType()); 1365 else 1366 TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, " 1367 "IEEEquad) as a structure component for BIND(C), " 1368 "VALUE derived type argument and type return"); 1369 }) 1370 .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) { 1371 const unsigned width = 1372 kindMap.getLogicalBitsize(logicalTy.getFKind()); 1373 if (width != 0) 1374 flatTypes.push_back( 1375 mlir::IntegerType::get(type.getContext(), width)); 1376 }) 1377 .template Case<fir::CharacterType>([&](fir::CharacterType charTy) { 1378 assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 && 1379 "the bit size of characterType as an interoperable type must " 1380 "not exceed 8"); 1381 for (unsigned i = 0; i < charTy.getLen(); ++i) 1382 flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8)); 1383 }) 1384 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { 1385 if (!seqTy.hasDynamicExtents()) { 1386 const std::uint64_t numOfEle = seqTy.getConstantArraySize(); 1387 mlir::Type eleTy = seqTy.getEleTy(); 1388 if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) { 1389 llvm::SmallVector<mlir::Type> subTypeList = 1390 flattenTypeList(loc, eleTy); 1391 if (subTypeList.size() != 0) 1392 for (std::uint64_t i = 0; i < numOfEle; ++i) 1393 llvm::copy(subTypeList, std::back_inserter(flatTypes)); 1394 } else { 1395 std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy); 1396 } 1397 } else 1398 TODO(loc, "unsupported dynamic extent sequence type as a structure " 1399 "component for BIND(C), " 1400 "VALUE derived type argument and type return"); 1401 }) 1402 .template Case<fir::RecordType>([&](fir::RecordType recTy) { 1403 for (auto &component : recTy.getTypeList()) { 1404 mlir::Type eleTy = component.second; 1405 llvm::SmallVector<mlir::Type> subTypeList = 1406 flattenTypeList(loc, eleTy); 1407 if (subTypeList.size() != 0) 1408 llvm::copy(subTypeList, std::back_inserter(flatTypes)); 1409 } 1410 }) 1411 .template Case<fir::VectorType>([&](fir::VectorType vecTy) { 1412 auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash( 1413 loc, vecTy, getDataLayout(), kindMap); 1414 if (sizeAndAlign.first == 2 * GRLenInChar) 1415 flatTypes.push_back( 1416 mlir::IntegerType::get(type.getContext(), 2 * GRLen)); 1417 else 1418 TODO(loc, "unsupported vector width(must be 128 bits)"); 1419 }) 1420 .Default([&](mlir::Type ty) { 1421 if (fir::conformsWithPassByRef(ty)) 1422 flatTypes.push_back( 1423 mlir::IntegerType::get(type.getContext(), GRLen)); 1424 else 1425 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 1426 "type argument and type return"); 1427 }); 1428 1429 return flatTypes; 1430 } 1431 1432 /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e., 1433 /// when flattened it contains a single fp value, fp+fp, or int+fp of 1434 /// appropriate size). 1435 bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy, 1436 mlir::Type &field1Ty, 1437 mlir::Type &field2Ty) const { 1438 field1Ty = field2Ty = nullptr; 1439 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy); 1440 size_t flatSize = flatTypes.size(); 1441 1442 // Cannot be eligible if the number of flattened types is equal to 0 or 1443 // greater than 2. 1444 if (flatSize == 0 || flatSize > 2) 1445 return false; 1446 1447 bool isFirstAvaliableFloat = false; 1448 1449 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) && 1450 "Type must be integerType or floatType after flattening"); 1451 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) { 1452 const unsigned Size = floatTy.getWidth(); 1453 // Can't be eligible if larger than the FP registers. Half precision isn't 1454 // currently supported on LoongArch and the ABI hasn't been confirmed, so 1455 // default to the integer ABI in that case. 1456 if (Size > FRLen || Size < 32) 1457 return false; 1458 isFirstAvaliableFloat = true; 1459 field1Ty = floatTy; 1460 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) { 1461 if (intTy.getWidth() > GRLen) 1462 return false; 1463 field1Ty = intTy; 1464 } 1465 1466 // flatTypes has two elements 1467 if (flatSize == 2) { 1468 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) && 1469 "Type must be integerType or floatType after flattening"); 1470 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) { 1471 const unsigned Size = floatTy.getWidth(); 1472 if (Size > FRLen || Size < 32) 1473 return false; 1474 field2Ty = floatTy; 1475 return true; 1476 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) { 1477 // Can't be eligible if an integer type was already found (int+int pairs 1478 // are not eligible). 1479 if (!isFirstAvaliableFloat) 1480 return false; 1481 if (intTy.getWidth() > GRLen) 1482 return false; 1483 field2Ty = intTy; 1484 return true; 1485 } 1486 } 1487 1488 // return isFirstAvaliableFloat if flatTypes only has one element 1489 return isFirstAvaliableFloat; 1490 } 1491 1492 bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft, 1493 const mlir::Type type) const { 1494 if (!type) 1495 return true; 1496 1497 llvm::TypeSwitch<mlir::Type>(type) 1498 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 1499 const unsigned width = intTy.getWidth(); 1500 if (width > 128) 1501 TODO(loc, 1502 "integerType with width exceeding 128 bits is unsupported"); 1503 if (width == 0) 1504 return; 1505 if (width <= GRLen) 1506 --GARsLeft; 1507 else if (width <= 2 * GRLen) 1508 GARsLeft = GARsLeft - 2; 1509 }) 1510 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) { 1511 const unsigned width = floatTy.getWidth(); 1512 if (width > 128) 1513 TODO(loc, "floatType with width exceeding 128 bits is unsupported"); 1514 if (width == 0) 1515 return; 1516 if (width == 32 || width == 64) 1517 --FARsLeft; 1518 else if (width <= GRLen) 1519 --GARsLeft; 1520 else if (width <= 2 * GRLen) 1521 GARsLeft = GARsLeft - 2; 1522 }) 1523 .Default([&](mlir::Type ty) { 1524 if (fir::conformsWithPassByRef(ty)) 1525 --GARsLeft; // Pointers. 1526 else 1527 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 1528 "type argument and type return"); 1529 }); 1530 1531 return GARsLeft >= 0 && FARsLeft >= 0; 1532 } 1533 1534 bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft, 1535 const Marshalling &previousArguments, 1536 const mlir::Type &field1Ty, 1537 const mlir::Type &field2Ty) const { 1538 for (auto &typeAndAttr : previousArguments) { 1539 const auto &attr = std::get<Attributes>(typeAndAttr); 1540 if (attr.isByVal()) { 1541 // Previous argument passed on the stack, and its address is passed in 1542 // GAR. 1543 --GARsLeft; 1544 continue; 1545 } 1546 1547 // Previous aggregate arguments were marshalled into simpler arguments. 1548 const auto &type = std::get<mlir::Type>(typeAndAttr); 1549 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type); 1550 1551 for (auto &flatTy : flatTypes) { 1552 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy)) 1553 return false; 1554 } 1555 } 1556 1557 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty)) 1558 return false; 1559 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty)) 1560 return false; 1561 return true; 1562 } 1563 1564 /// LoongArch64 subroutine calling sequence ABI in: 1565 /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence 1566 CodeGenSpecifics::Marshalling 1567 classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft, 1568 int FARsLeft, bool isResult, 1569 const Marshalling &previousArguments) const { 1570 CodeGenSpecifics::Marshalling marshal; 1571 1572 auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash( 1573 loc, recTy, getDataLayout(), kindMap); 1574 mlir::MLIRContext *context = recTy.getContext(); 1575 1576 if (recSize == 0) { 1577 TODO(loc, "unsupported empty struct type for BIND(C), " 1578 "VALUE derived type argument and type return"); 1579 } 1580 1581 if (recSize > 2 * GRLenInChar) { 1582 marshal.emplace_back( 1583 fir::ReferenceType::get(recTy), 1584 AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult}); 1585 return marshal; 1586 } 1587 1588 // Pass by FARs(and GARs) 1589 mlir::Type field1Ty = nullptr, field2Ty = nullptr; 1590 if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) && 1591 hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty, 1592 field2Ty)) { 1593 if (!isResult) { 1594 if (field1Ty) 1595 marshal.emplace_back(field1Ty, AT{}); 1596 if (field2Ty) 1597 marshal.emplace_back(field2Ty, AT{}); 1598 } else { 1599 // field1Ty is always preferred over field2Ty for assignment, so there 1600 // will never be a case where field1Ty == nullptr and field2Ty != 1601 // nullptr. 1602 if (field1Ty && !field2Ty) 1603 marshal.emplace_back(field1Ty, AT{}); 1604 else if (field1Ty && field2Ty) 1605 marshal.emplace_back( 1606 mlir::TupleType::get(context, 1607 mlir::TypeRange{field1Ty, field2Ty}), 1608 AT{/*alignment=*/0, /*byval=*/true}); 1609 } 1610 return marshal; 1611 } 1612 1613 if (recSize <= GRLenInChar) { 1614 marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{}); 1615 return marshal; 1616 } 1617 1618 if (recAlign == 2 * GRLenInChar) { 1619 marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{}); 1620 return marshal; 1621 } 1622 1623 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar 1624 marshal.emplace_back( 1625 fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)), 1626 AT{}); 1627 return marshal; 1628 } 1629 1630 /// Marshal a derived type passed by value like a C struct. 1631 CodeGenSpecifics::Marshalling 1632 structArgumentType(mlir::Location loc, fir::RecordType recTy, 1633 const Marshalling &previousArguments) const override { 1634 int GARsLeft = 8; 1635 int FARsLeft = FRLen ? 8 : 0; 1636 1637 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false, 1638 previousArguments); 1639 } 1640 1641 CodeGenSpecifics::Marshalling 1642 structReturnType(mlir::Location loc, fir::RecordType recTy) const override { 1643 // The rules for return and argument types are the same. 1644 int GARsLeft = 2; 1645 int FARsLeft = FRLen ? 2 : 0; 1646 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true, 1647 {}); 1648 } 1649 }; 1650 } // namespace 1651 1652 // Instantiate the overloaded target instance based on the triple value. 1653 // TODO: Add other targets to this file as needed. 1654 std::unique_ptr<fir::CodeGenSpecifics> 1655 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 1656 KindMapping &&kindMap, llvm::StringRef targetCPU, 1657 mlir::LLVM::TargetFeaturesAttr targetFeatures, 1658 const mlir::DataLayout &dl) { 1659 switch (trp.getArch()) { 1660 default: 1661 break; 1662 case llvm::Triple::ArchType::x86: 1663 if (trp.isOSWindows()) 1664 return std::make_unique<TargetI386Win>(ctx, std::move(trp), 1665 std::move(kindMap), targetCPU, 1666 targetFeatures, dl); 1667 else 1668 return std::make_unique<TargetI386>(ctx, std::move(trp), 1669 std::move(kindMap), targetCPU, 1670 targetFeatures, dl); 1671 case llvm::Triple::ArchType::x86_64: 1672 if (trp.isOSWindows()) 1673 return std::make_unique<TargetX86_64Win>(ctx, std::move(trp), 1674 std::move(kindMap), targetCPU, 1675 targetFeatures, dl); 1676 else 1677 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 1678 std::move(kindMap), targetCPU, 1679 targetFeatures, dl); 1680 case llvm::Triple::ArchType::aarch64: 1681 return std::make_unique<TargetAArch64>( 1682 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1683 case llvm::Triple::ArchType::ppc64: 1684 return std::make_unique<TargetPPC64>( 1685 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1686 case llvm::Triple::ArchType::ppc64le: 1687 return std::make_unique<TargetPPC64le>( 1688 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1689 case llvm::Triple::ArchType::sparc: 1690 return std::make_unique<TargetSparc>( 1691 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1692 case llvm::Triple::ArchType::sparcv9: 1693 return std::make_unique<TargetSparcV9>( 1694 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1695 case llvm::Triple::ArchType::riscv64: 1696 return std::make_unique<TargetRISCV64>( 1697 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1698 case llvm::Triple::ArchType::amdgcn: 1699 return std::make_unique<TargetAMDGPU>( 1700 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1701 case llvm::Triple::ArchType::nvptx64: 1702 return std::make_unique<TargetNVPTX>( 1703 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1704 case llvm::Triple::ArchType::loongarch64: 1705 return std::make_unique<TargetLoongArch64>( 1706 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1707 } 1708 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 1709 } 1710 1711 std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get( 1712 mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap, 1713 llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures, 1714 const mlir::DataLayout &dl, llvm::StringRef tuneCPU) { 1715 std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get( 1716 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1717 1718 CGS->tuneCPU = tuneCPU; 1719 return CGS; 1720 } 1721