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::FloatType::getF128(context); 576 if (partByteSize > 4) 577 return mlir::FloatType::getF64(context); 578 if (partByteSize > 2) 579 return mlir::FloatType::getF32(context); 580 return mlir::FloatType::getF16(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 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 792 using GenericTarget::GenericTarget; 793 794 static constexpr int defaultWidth = 64; 795 796 CodeGenSpecifics::Marshalling 797 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 798 CodeGenSpecifics::Marshalling marshal; 799 const auto *sem = &floatToSemantics(kindMap, eleTy); 800 if (sem == &llvm::APFloat::IEEEsingle() || 801 sem == &llvm::APFloat::IEEEdouble() || 802 sem == &llvm::APFloat::IEEEquad()) { 803 // [2 x t] array of 2 eleTy 804 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 805 } else { 806 typeTodo(sem, loc, "argument"); 807 } 808 return marshal; 809 } 810 811 CodeGenSpecifics::Marshalling 812 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 813 CodeGenSpecifics::Marshalling marshal; 814 const auto *sem = &floatToSemantics(kindMap, eleTy); 815 if (sem == &llvm::APFloat::IEEEsingle() || 816 sem == &llvm::APFloat::IEEEdouble() || 817 sem == &llvm::APFloat::IEEEquad()) { 818 // Use a type that will be translated into LLVM as: 819 // { t, t } struct of 2 eleTy 820 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 821 mlir::TypeRange{eleTy, eleTy}), 822 AT{}); 823 } else { 824 typeTodo(sem, loc, "return"); 825 } 826 return marshal; 827 } 828 829 // Flatten a RecordType::TypeList containing more record types or array types 830 static std::optional<std::vector<mlir::Type>> 831 flattenTypeList(const RecordType::TypeList &types) { 832 std::vector<mlir::Type> flatTypes; 833 // The flat list will be at least the same size as the non-flat list. 834 flatTypes.reserve(types.size()); 835 for (auto [c, type] : types) { 836 // Flatten record type 837 if (auto recTy = mlir::dyn_cast<RecordType>(type)) { 838 auto subTypeList = flattenTypeList(recTy.getTypeList()); 839 if (!subTypeList) 840 return std::nullopt; 841 llvm::copy(*subTypeList, std::back_inserter(flatTypes)); 842 continue; 843 } 844 845 // Flatten array type 846 if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) { 847 if (seqTy.hasDynamicExtents()) 848 return std::nullopt; 849 std::size_t n = seqTy.getConstantArraySize(); 850 auto eleTy = seqTy.getElementType(); 851 // Flatten array of record types 852 if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) { 853 auto subTypeList = flattenTypeList(recTy.getTypeList()); 854 if (!subTypeList) 855 return std::nullopt; 856 for (std::size_t i = 0; i < n; ++i) 857 llvm::copy(*subTypeList, std::back_inserter(flatTypes)); 858 } else { 859 std::fill_n(std::back_inserter(flatTypes), 860 seqTy.getConstantArraySize(), eleTy); 861 } 862 continue; 863 } 864 865 // Other types are already flat 866 flatTypes.push_back(type); 867 } 868 return flatTypes; 869 } 870 871 // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An 872 // HFA is a record type with up to 4 floating-point members of the same type. 873 static bool isHFA(fir::RecordType ty) { 874 RecordType::TypeList types = ty.getTypeList(); 875 if (types.empty() || types.size() > 4) 876 return false; 877 878 std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types); 879 if (!flatTypes || flatTypes->size() > 4) { 880 return false; 881 } 882 883 if (!isa_real(flatTypes->front())) { 884 return false; 885 } 886 887 return llvm::all_equal(*flatTypes); 888 } 889 890 // AArch64 procedure call ABI: 891 // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing 892 CodeGenSpecifics::Marshalling 893 structReturnType(mlir::Location loc, fir::RecordType ty) const override { 894 CodeGenSpecifics::Marshalling marshal; 895 896 if (isHFA(ty)) { 897 // Just return the existing record type 898 marshal.emplace_back(ty, AT{}); 899 return marshal; 900 } 901 902 auto [size, align] = 903 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap); 904 905 // return in registers if size <= 16 bytes 906 if (size <= 16) { 907 std::size_t dwordSize = (size + 7) / 8; 908 auto newTy = fir::SequenceType::get( 909 dwordSize, mlir::IntegerType::get(ty.getContext(), 64)); 910 marshal.emplace_back(newTy, AT{}); 911 return marshal; 912 } 913 914 unsigned short stackAlign = std::max<unsigned short>(align, 8u); 915 marshal.emplace_back(fir::ReferenceType::get(ty), 916 AT{stackAlign, false, true}); 917 return marshal; 918 } 919 }; 920 } // namespace 921 922 //===----------------------------------------------------------------------===// 923 // PPC64 (AIX 64 bit) target specifics. 924 //===----------------------------------------------------------------------===// 925 926 namespace { 927 struct TargetPPC64 : public GenericTarget<TargetPPC64> { 928 using GenericTarget::GenericTarget; 929 930 static constexpr int defaultWidth = 64; 931 932 CodeGenSpecifics::Marshalling 933 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 934 CodeGenSpecifics::Marshalling marshal; 935 // two distinct element type arguments (re, im) 936 marshal.emplace_back(eleTy, AT{}); 937 marshal.emplace_back(eleTy, AT{}); 938 return marshal; 939 } 940 941 CodeGenSpecifics::Marshalling 942 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 943 CodeGenSpecifics::Marshalling marshal; 944 // Use a type that will be translated into LLVM as: 945 // { t, t } struct of 2 element type 946 marshal.emplace_back( 947 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 948 AT{}); 949 return marshal; 950 } 951 }; 952 } // namespace 953 954 //===----------------------------------------------------------------------===// 955 // PPC64le linux target specifics. 956 //===----------------------------------------------------------------------===// 957 958 namespace { 959 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 960 using GenericTarget::GenericTarget; 961 962 static constexpr int defaultWidth = 64; 963 964 CodeGenSpecifics::Marshalling 965 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 966 CodeGenSpecifics::Marshalling marshal; 967 // two distinct element type arguments (re, im) 968 marshal.emplace_back(eleTy, AT{}); 969 marshal.emplace_back(eleTy, AT{}); 970 return marshal; 971 } 972 973 CodeGenSpecifics::Marshalling 974 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 975 CodeGenSpecifics::Marshalling marshal; 976 // Use a type that will be translated into LLVM as: 977 // { t, t } struct of 2 element type 978 marshal.emplace_back( 979 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 980 AT{}); 981 return marshal; 982 } 983 }; 984 } // namespace 985 986 //===----------------------------------------------------------------------===// 987 // sparc (sparc 32 bit) target specifics. 988 //===----------------------------------------------------------------------===// 989 990 namespace { 991 struct TargetSparc : public GenericTarget<TargetSparc> { 992 using GenericTarget::GenericTarget; 993 994 static constexpr int defaultWidth = 32; 995 996 CodeGenSpecifics::Marshalling 997 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 998 assert(fir::isa_real(eleTy)); 999 CodeGenSpecifics::Marshalling marshal; 1000 // Use a type that will be translated into LLVM as: 1001 // { t, t } struct of 2 eleTy 1002 auto structTy = 1003 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 1004 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); 1005 return marshal; 1006 } 1007 1008 CodeGenSpecifics::Marshalling 1009 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1010 assert(fir::isa_real(eleTy)); 1011 CodeGenSpecifics::Marshalling marshal; 1012 // Use a type that will be translated into LLVM as: 1013 // { t, t } struct of 2 eleTy, byval 1014 auto structTy = 1015 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); 1016 marshal.emplace_back(fir::ReferenceType::get(structTy), 1017 AT{/*alignment=*/0, /*byval=*/true}); 1018 return marshal; 1019 } 1020 }; 1021 } // namespace 1022 1023 //===----------------------------------------------------------------------===// 1024 // sparcv9 (sparc 64 bit) target specifics. 1025 //===----------------------------------------------------------------------===// 1026 1027 namespace { 1028 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { 1029 using GenericTarget::GenericTarget; 1030 1031 static constexpr int defaultWidth = 64; 1032 1033 CodeGenSpecifics::Marshalling 1034 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1035 CodeGenSpecifics::Marshalling marshal; 1036 const auto *sem = &floatToSemantics(kindMap, eleTy); 1037 if (sem == &llvm::APFloat::IEEEsingle() || 1038 sem == &llvm::APFloat::IEEEdouble()) { 1039 // two distinct float, double arguments 1040 marshal.emplace_back(eleTy, AT{}); 1041 marshal.emplace_back(eleTy, AT{}); 1042 } else if (sem == &llvm::APFloat::IEEEquad()) { 1043 // Use a type that will be translated into LLVM as: 1044 // { fp128, fp128 } struct of 2 fp128, byval, align 16 1045 marshal.emplace_back( 1046 fir::ReferenceType::get(mlir::TupleType::get( 1047 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1048 AT{/*align=*/16, /*byval=*/true}); 1049 } else { 1050 typeTodo(sem, loc, "argument"); 1051 } 1052 return marshal; 1053 } 1054 1055 CodeGenSpecifics::Marshalling 1056 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1057 CodeGenSpecifics::Marshalling marshal; 1058 // Use a type that will be translated into LLVM as: 1059 // { eleTy, eleTy } struct of 2 eleTy 1060 marshal.emplace_back( 1061 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), 1062 AT{}); 1063 return marshal; 1064 } 1065 }; 1066 } // namespace 1067 1068 //===----------------------------------------------------------------------===// 1069 // RISCV64 linux target specifics. 1070 //===----------------------------------------------------------------------===// 1071 1072 namespace { 1073 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { 1074 using GenericTarget::GenericTarget; 1075 1076 static constexpr int defaultWidth = 64; 1077 1078 CodeGenSpecifics::Marshalling 1079 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1080 CodeGenSpecifics::Marshalling marshal; 1081 const auto *sem = &floatToSemantics(kindMap, eleTy); 1082 if (sem == &llvm::APFloat::IEEEsingle() || 1083 sem == &llvm::APFloat::IEEEdouble()) { 1084 // Two distinct element type arguments (re, im) 1085 marshal.emplace_back(eleTy, AT{}); 1086 marshal.emplace_back(eleTy, AT{}); 1087 } else { 1088 typeTodo(sem, loc, "argument"); 1089 } 1090 return marshal; 1091 } 1092 1093 CodeGenSpecifics::Marshalling 1094 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1095 CodeGenSpecifics::Marshalling marshal; 1096 const auto *sem = &floatToSemantics(kindMap, eleTy); 1097 if (sem == &llvm::APFloat::IEEEsingle() || 1098 sem == &llvm::APFloat::IEEEdouble()) { 1099 // Use a type that will be translated into LLVM as: 1100 // { t, t } struct of 2 eleTy, byVal 1101 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 1102 mlir::TypeRange{eleTy, eleTy}), 1103 AT{/*alignment=*/0, /*byval=*/true}); 1104 } else { 1105 typeTodo(sem, loc, "return"); 1106 } 1107 return marshal; 1108 } 1109 }; 1110 } // namespace 1111 1112 //===----------------------------------------------------------------------===// 1113 // AMDGPU linux target specifics. 1114 //===----------------------------------------------------------------------===// 1115 1116 namespace { 1117 struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> { 1118 using GenericTarget::GenericTarget; 1119 1120 // Default size (in bits) of the index type for strings. 1121 static constexpr int defaultWidth = 64; 1122 1123 CodeGenSpecifics::Marshalling 1124 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1125 CodeGenSpecifics::Marshalling marshal; 1126 TODO(loc, "handle complex argument types"); 1127 return marshal; 1128 } 1129 1130 CodeGenSpecifics::Marshalling 1131 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1132 CodeGenSpecifics::Marshalling marshal; 1133 TODO(loc, "handle complex return types"); 1134 return marshal; 1135 } 1136 }; 1137 } // namespace 1138 1139 //===----------------------------------------------------------------------===// 1140 // NVPTX linux target specifics. 1141 //===----------------------------------------------------------------------===// 1142 1143 namespace { 1144 struct TargetNVPTX : public GenericTarget<TargetNVPTX> { 1145 using GenericTarget::GenericTarget; 1146 1147 // Default size (in bits) of the index type for strings. 1148 static constexpr int defaultWidth = 64; 1149 1150 CodeGenSpecifics::Marshalling 1151 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1152 CodeGenSpecifics::Marshalling marshal; 1153 TODO(loc, "handle complex argument types"); 1154 return marshal; 1155 } 1156 1157 CodeGenSpecifics::Marshalling 1158 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1159 CodeGenSpecifics::Marshalling marshal; 1160 TODO(loc, "handle complex return types"); 1161 return marshal; 1162 } 1163 }; 1164 } // namespace 1165 1166 //===----------------------------------------------------------------------===// 1167 // LoongArch64 linux target specifics. 1168 //===----------------------------------------------------------------------===// 1169 1170 namespace { 1171 struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> { 1172 using GenericTarget::GenericTarget; 1173 1174 static constexpr int defaultWidth = 64; 1175 static constexpr int GRLen = defaultWidth; /* eight bytes */ 1176 static constexpr int GRLenInChar = GRLen / 8; 1177 static constexpr int FRLen = defaultWidth; /* eight bytes */ 1178 1179 CodeGenSpecifics::Marshalling 1180 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 1181 CodeGenSpecifics::Marshalling marshal; 1182 const auto *sem = &floatToSemantics(kindMap, eleTy); 1183 if (sem == &llvm::APFloat::IEEEsingle() || 1184 sem == &llvm::APFloat::IEEEdouble()) { 1185 // Two distinct element type arguments (re, im) 1186 marshal.emplace_back(eleTy, AT{}); 1187 marshal.emplace_back(eleTy, AT{}); 1188 } else if (sem == &llvm::APFloat::IEEEquad()) { 1189 // Use a type that will be translated into LLVM as: 1190 // { fp128, fp128 } struct of 2 fp128, byval 1191 marshal.emplace_back( 1192 fir::ReferenceType::get(mlir::TupleType::get( 1193 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1194 AT{/*align=*/16, /*byval=*/true}); 1195 } else { 1196 typeTodo(sem, loc, "argument"); 1197 } 1198 return marshal; 1199 } 1200 1201 CodeGenSpecifics::Marshalling 1202 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 1203 CodeGenSpecifics::Marshalling marshal; 1204 const auto *sem = &floatToSemantics(kindMap, eleTy); 1205 if (sem == &llvm::APFloat::IEEEsingle() || 1206 sem == &llvm::APFloat::IEEEdouble()) { 1207 // Use a type that will be translated into LLVM as: 1208 // { t, t } struct of 2 eleTy, byVal 1209 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), 1210 mlir::TypeRange{eleTy, eleTy}), 1211 AT{/*alignment=*/0, /*byval=*/true}); 1212 } else if (sem == &llvm::APFloat::IEEEquad()) { 1213 // Use a type that will be translated into LLVM as: 1214 // { fp128, fp128 } struct of 2 fp128, sret, align 16 1215 marshal.emplace_back( 1216 fir::ReferenceType::get(mlir::TupleType::get( 1217 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), 1218 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 1219 } else { 1220 typeTodo(sem, loc, "return"); 1221 } 1222 return marshal; 1223 } 1224 1225 CodeGenSpecifics::Marshalling 1226 integerArgumentType(mlir::Location loc, 1227 mlir::IntegerType argTy) const override { 1228 if (argTy.getWidth() == 32) { 1229 // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended. 1230 // Therefore, Flang also follows it if a function needs to be 1231 // interoperable with C. 1232 // 1233 // Currently, it only adds `signext` attribute to the dummy arguments and 1234 // return values in the function signatures, but it does not add the 1235 // corresponding attribute to the actual arguments and return values in 1236 // `fir.call` instruction. Thanks to LLVM's integration of all these 1237 // attributes, the modification is still effective. 1238 CodeGenSpecifics::Marshalling marshal; 1239 AT::IntegerExtension intExt = AT::IntegerExtension::Sign; 1240 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, 1241 /*sret=*/false, /*append=*/false, 1242 /*intExt=*/intExt}); 1243 return marshal; 1244 } 1245 1246 return GenericTarget::integerArgumentType(loc, argTy); 1247 } 1248 1249 /// Flatten non-basic types, resulting in an array of types containing only 1250 /// `IntegerType` and `FloatType`. 1251 llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc, 1252 const mlir::Type type) const { 1253 llvm::SmallVector<mlir::Type> flatTypes; 1254 1255 llvm::TypeSwitch<mlir::Type>(type) 1256 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 1257 if (intTy.getWidth() != 0) 1258 flatTypes.push_back(intTy); 1259 }) 1260 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) { 1261 if (floatTy.getWidth() != 0) 1262 flatTypes.push_back(floatTy); 1263 }) 1264 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) { 1265 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); 1266 if (sem == &llvm::APFloat::IEEEsingle() || 1267 sem == &llvm::APFloat::IEEEdouble() || 1268 sem == &llvm::APFloat::IEEEquad()) 1269 std::fill_n(std::back_inserter(flatTypes), 2, 1270 cmplx.getElementType()); 1271 else 1272 TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, " 1273 "IEEEquad) as a structure component for BIND(C), " 1274 "VALUE derived type argument and type return"); 1275 }) 1276 .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) { 1277 const unsigned width = 1278 kindMap.getLogicalBitsize(logicalTy.getFKind()); 1279 if (width != 0) 1280 flatTypes.push_back( 1281 mlir::IntegerType::get(type.getContext(), width)); 1282 }) 1283 .template Case<fir::CharacterType>([&](fir::CharacterType charTy) { 1284 assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 && 1285 "the bit size of characterType as an interoperable type must " 1286 "not exceed 8"); 1287 for (unsigned i = 0; i < charTy.getLen(); ++i) 1288 flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8)); 1289 }) 1290 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { 1291 if (!seqTy.hasDynamicExtents()) { 1292 const std::uint64_t numOfEle = seqTy.getConstantArraySize(); 1293 mlir::Type eleTy = seqTy.getEleTy(); 1294 if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) { 1295 llvm::SmallVector<mlir::Type> subTypeList = 1296 flattenTypeList(loc, eleTy); 1297 if (subTypeList.size() != 0) 1298 for (std::uint64_t i = 0; i < numOfEle; ++i) 1299 llvm::copy(subTypeList, std::back_inserter(flatTypes)); 1300 } else { 1301 std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy); 1302 } 1303 } else 1304 TODO(loc, "unsupported dynamic extent sequence type as a structure " 1305 "component for BIND(C), " 1306 "VALUE derived type argument and type return"); 1307 }) 1308 .template Case<fir::RecordType>([&](fir::RecordType recTy) { 1309 for (auto &component : recTy.getTypeList()) { 1310 mlir::Type eleTy = component.second; 1311 llvm::SmallVector<mlir::Type> subTypeList = 1312 flattenTypeList(loc, eleTy); 1313 if (subTypeList.size() != 0) 1314 llvm::copy(subTypeList, std::back_inserter(flatTypes)); 1315 } 1316 }) 1317 .template Case<fir::VectorType>([&](fir::VectorType vecTy) { 1318 auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash( 1319 loc, vecTy, getDataLayout(), kindMap); 1320 if (sizeAndAlign.first == 2 * GRLenInChar) 1321 flatTypes.push_back( 1322 mlir::IntegerType::get(type.getContext(), 2 * GRLen)); 1323 else 1324 TODO(loc, "unsupported vector width(must be 128 bits)"); 1325 }) 1326 .Default([&](mlir::Type ty) { 1327 if (fir::conformsWithPassByRef(ty)) 1328 flatTypes.push_back( 1329 mlir::IntegerType::get(type.getContext(), GRLen)); 1330 else 1331 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 1332 "type argument and type return"); 1333 }); 1334 1335 return flatTypes; 1336 } 1337 1338 /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e., 1339 /// when flattened it contains a single fp value, fp+fp, or int+fp of 1340 /// appropriate size). 1341 bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy, 1342 mlir::Type &field1Ty, 1343 mlir::Type &field2Ty) const { 1344 field1Ty = field2Ty = nullptr; 1345 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy); 1346 size_t flatSize = flatTypes.size(); 1347 1348 // Cannot be eligible if the number of flattened types is equal to 0 or 1349 // greater than 2. 1350 if (flatSize == 0 || flatSize > 2) 1351 return false; 1352 1353 bool isFirstAvaliableFloat = false; 1354 1355 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) && 1356 "Type must be integerType or floatType after flattening"); 1357 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) { 1358 const unsigned Size = floatTy.getWidth(); 1359 // Can't be eligible if larger than the FP registers. Half precision isn't 1360 // currently supported on LoongArch and the ABI hasn't been confirmed, so 1361 // default to the integer ABI in that case. 1362 if (Size > FRLen || Size < 32) 1363 return false; 1364 isFirstAvaliableFloat = true; 1365 field1Ty = floatTy; 1366 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) { 1367 if (intTy.getWidth() > GRLen) 1368 return false; 1369 field1Ty = intTy; 1370 } 1371 1372 // flatTypes has two elements 1373 if (flatSize == 2) { 1374 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) && 1375 "Type must be integerType or floatType after flattening"); 1376 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) { 1377 const unsigned Size = floatTy.getWidth(); 1378 if (Size > FRLen || Size < 32) 1379 return false; 1380 field2Ty = floatTy; 1381 return true; 1382 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) { 1383 // Can't be eligible if an integer type was already found (int+int pairs 1384 // are not eligible). 1385 if (!isFirstAvaliableFloat) 1386 return false; 1387 if (intTy.getWidth() > GRLen) 1388 return false; 1389 field2Ty = intTy; 1390 return true; 1391 } 1392 } 1393 1394 // return isFirstAvaliableFloat if flatTypes only has one element 1395 return isFirstAvaliableFloat; 1396 } 1397 1398 bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft, 1399 const mlir::Type type) const { 1400 if (!type) 1401 return true; 1402 1403 llvm::TypeSwitch<mlir::Type>(type) 1404 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { 1405 const unsigned width = intTy.getWidth(); 1406 if (width > 128) 1407 TODO(loc, 1408 "integerType with width exceeding 128 bits is unsupported"); 1409 if (width == 0) 1410 return; 1411 if (width <= GRLen) 1412 --GARsLeft; 1413 else if (width <= 2 * GRLen) 1414 GARsLeft = GARsLeft - 2; 1415 }) 1416 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) { 1417 const unsigned width = floatTy.getWidth(); 1418 if (width > 128) 1419 TODO(loc, "floatType with width exceeding 128 bits is unsupported"); 1420 if (width == 0) 1421 return; 1422 if (width == 32 || width == 64) 1423 --FARsLeft; 1424 else if (width <= GRLen) 1425 --GARsLeft; 1426 else if (width <= 2 * GRLen) 1427 GARsLeft = GARsLeft - 2; 1428 }) 1429 .Default([&](mlir::Type ty) { 1430 if (fir::conformsWithPassByRef(ty)) 1431 --GARsLeft; // Pointers. 1432 else 1433 TODO(loc, "unsupported component type for BIND(C), VALUE derived " 1434 "type argument and type return"); 1435 }); 1436 1437 return GARsLeft >= 0 && FARsLeft >= 0; 1438 } 1439 1440 bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft, 1441 const Marshalling &previousArguments, 1442 const mlir::Type &field1Ty, 1443 const mlir::Type &field2Ty) const { 1444 for (auto &typeAndAttr : previousArguments) { 1445 const auto &attr = std::get<Attributes>(typeAndAttr); 1446 if (attr.isByVal()) { 1447 // Previous argument passed on the stack, and its address is passed in 1448 // GAR. 1449 --GARsLeft; 1450 continue; 1451 } 1452 1453 // Previous aggregate arguments were marshalled into simpler arguments. 1454 const auto &type = std::get<mlir::Type>(typeAndAttr); 1455 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type); 1456 1457 for (auto &flatTy : flatTypes) { 1458 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy)) 1459 return false; 1460 } 1461 } 1462 1463 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty)) 1464 return false; 1465 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty)) 1466 return false; 1467 return true; 1468 } 1469 1470 /// LoongArch64 subroutine calling sequence ABI in: 1471 /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence 1472 CodeGenSpecifics::Marshalling 1473 classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft, 1474 int FARsLeft, bool isResult, 1475 const Marshalling &previousArguments) const { 1476 CodeGenSpecifics::Marshalling marshal; 1477 1478 auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash( 1479 loc, recTy, getDataLayout(), kindMap); 1480 mlir::MLIRContext *context = recTy.getContext(); 1481 1482 if (recSize == 0) { 1483 TODO(loc, "unsupported empty struct type for BIND(C), " 1484 "VALUE derived type argument and type return"); 1485 } 1486 1487 if (recSize > 2 * GRLenInChar) { 1488 marshal.emplace_back( 1489 fir::ReferenceType::get(recTy), 1490 AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult}); 1491 return marshal; 1492 } 1493 1494 // Pass by FARs(and GARs) 1495 mlir::Type field1Ty = nullptr, field2Ty = nullptr; 1496 if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) && 1497 hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty, 1498 field2Ty)) { 1499 if (!isResult) { 1500 if (field1Ty) 1501 marshal.emplace_back(field1Ty, AT{}); 1502 if (field2Ty) 1503 marshal.emplace_back(field2Ty, AT{}); 1504 } else { 1505 // field1Ty is always preferred over field2Ty for assignment, so there 1506 // will never be a case where field1Ty == nullptr and field2Ty != 1507 // nullptr. 1508 if (field1Ty && !field2Ty) 1509 marshal.emplace_back(field1Ty, AT{}); 1510 else if (field1Ty && field2Ty) 1511 marshal.emplace_back( 1512 mlir::TupleType::get(context, 1513 mlir::TypeRange{field1Ty, field2Ty}), 1514 AT{/*alignment=*/0, /*byval=*/true}); 1515 } 1516 return marshal; 1517 } 1518 1519 if (recSize <= GRLenInChar) { 1520 marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{}); 1521 return marshal; 1522 } 1523 1524 if (recAlign == 2 * GRLenInChar) { 1525 marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{}); 1526 return marshal; 1527 } 1528 1529 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar 1530 marshal.emplace_back( 1531 fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)), 1532 AT{}); 1533 return marshal; 1534 } 1535 1536 /// Marshal a derived type passed by value like a C struct. 1537 CodeGenSpecifics::Marshalling 1538 structArgumentType(mlir::Location loc, fir::RecordType recTy, 1539 const Marshalling &previousArguments) const override { 1540 int GARsLeft = 8; 1541 int FARsLeft = FRLen ? 8 : 0; 1542 1543 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false, 1544 previousArguments); 1545 } 1546 1547 CodeGenSpecifics::Marshalling 1548 structReturnType(mlir::Location loc, fir::RecordType recTy) const override { 1549 // The rules for return and argument types are the same. 1550 int GARsLeft = 2; 1551 int FARsLeft = FRLen ? 2 : 0; 1552 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true, 1553 {}); 1554 } 1555 }; 1556 } // namespace 1557 1558 // Instantiate the overloaded target instance based on the triple value. 1559 // TODO: Add other targets to this file as needed. 1560 std::unique_ptr<fir::CodeGenSpecifics> 1561 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 1562 KindMapping &&kindMap, llvm::StringRef targetCPU, 1563 mlir::LLVM::TargetFeaturesAttr targetFeatures, 1564 const mlir::DataLayout &dl) { 1565 switch (trp.getArch()) { 1566 default: 1567 break; 1568 case llvm::Triple::ArchType::x86: 1569 if (trp.isOSWindows()) 1570 return std::make_unique<TargetI386Win>(ctx, std::move(trp), 1571 std::move(kindMap), targetCPU, 1572 targetFeatures, dl); 1573 else 1574 return std::make_unique<TargetI386>(ctx, std::move(trp), 1575 std::move(kindMap), targetCPU, 1576 targetFeatures, dl); 1577 case llvm::Triple::ArchType::x86_64: 1578 if (trp.isOSWindows()) 1579 return std::make_unique<TargetX86_64Win>(ctx, std::move(trp), 1580 std::move(kindMap), targetCPU, 1581 targetFeatures, dl); 1582 else 1583 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 1584 std::move(kindMap), targetCPU, 1585 targetFeatures, dl); 1586 case llvm::Triple::ArchType::aarch64: 1587 return std::make_unique<TargetAArch64>( 1588 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1589 case llvm::Triple::ArchType::ppc64: 1590 return std::make_unique<TargetPPC64>( 1591 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1592 case llvm::Triple::ArchType::ppc64le: 1593 return std::make_unique<TargetPPC64le>( 1594 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1595 case llvm::Triple::ArchType::sparc: 1596 return std::make_unique<TargetSparc>( 1597 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1598 case llvm::Triple::ArchType::sparcv9: 1599 return std::make_unique<TargetSparcV9>( 1600 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1601 case llvm::Triple::ArchType::riscv64: 1602 return std::make_unique<TargetRISCV64>( 1603 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1604 case llvm::Triple::ArchType::amdgcn: 1605 return std::make_unique<TargetAMDGPU>( 1606 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1607 case llvm::Triple::ArchType::nvptx64: 1608 return std::make_unique<TargetNVPTX>( 1609 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1610 case llvm::Triple::ArchType::loongarch64: 1611 return std::make_unique<TargetLoongArch64>( 1612 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1613 } 1614 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 1615 } 1616 1617 std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get( 1618 mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap, 1619 llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures, 1620 const mlir::DataLayout &dl, llvm::StringRef tuneCPU) { 1621 std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get( 1622 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); 1623 1624 CGS->tuneCPU = tuneCPU; 1625 return CGS; 1626 } 1627