1971 lines · cpp
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-exception6//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 25using namespace fir;26 27namespace fir::details {28llvm::StringRef Attributes::getIntExtensionAttrName() const {29 // The attribute names are available via LLVM dialect interfaces30 // like getZExtAttrName(), getByValAttrName(), etc., so we'd better31 // 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::details39 40// Reduce a REAL/float type to the floating point semantics.41static 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 47static 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 60namespace {61template <typename S>62struct 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 eleTy70 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 the89 // declared position) and all LEN arguments appended after all of the dummy90 // 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::Marshalling98 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::Marshalling104 structReturnType(mlir::Location loc, fir::RecordType ty) const override {105 TODO(loc, "returning BIND(C) derived type for this target");106 }107 108 CodeGenSpecifics::Marshalling109 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 types116 // to more precisely match C side (e.g. for Fortran runtime functions117 // 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 else127 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::Marshalling138 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, except144 // for AVR and MSP430 (see TargetInfo initializations145 // in clang/lib/Basic/Targets).146 unsigned char getCIntTypeWidth() const override { return 32; }147};148} // namespace149 150//===----------------------------------------------------------------------===//151// i386 (x86 32 bit) linux target specifics.152//===----------------------------------------------------------------------===//153 154namespace {155struct TargetI386 : public GenericTarget<TargetI386> {156 using GenericTarget::GenericTarget;157 158 static constexpr int defaultWidth = 32;159 160 CodeGenSpecifics::Marshalling161 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 4166 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::Marshalling174 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 GPR180 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 4185 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} // namespace196 197//===----------------------------------------------------------------------===//198// i386 (x86 32 bit) Windows target specifics.199//===----------------------------------------------------------------------===//200 201namespace {202struct TargetI386Win : public GenericTarget<TargetI386Win> {203 using GenericTarget::GenericTarget;204 205 static constexpr int defaultWidth = 32;206 207 CodeGenSpecifics::Marshalling208 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 4212 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::Marshalling220 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 GPR225 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 8230 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 16237 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 4244 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} // namespace255 256//===----------------------------------------------------------------------===//257// x86_64 (x86 64 bit) linux target specifics.258//===----------------------------------------------------------------------===//259 260namespace {261struct TargetX86_64 : public GenericTarget<TargetX86_64> {262 using GenericTarget::GenericTarget;263 264 static constexpr int defaultWidth = 64;265 266 CodeGenSpecifics::Marshalling267 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 eleTy272 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 be275 // incorrect since LLVM may pass the real via register and the imaginary276 // part via the stack while the ABI it should be all in register or all277 // in memory. Register occupancy must be analyzed here.278 // two distinct double arguments279 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 16284 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 16291 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::Marshalling302 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 eleTy307 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 double311 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 16322 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 Memory342 };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 eightbytes347 /// and upper eight eightbytes on return.348 /// If this is called for an aggregate field, the caller is responsible to349 /// 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.0355 llvm::TypeSwitch<mlir::Type>(type)356 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {357 if (intTy.getWidth() == 128)358 Hi = Lo = ArgClass::Integer;359 else360 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 else389 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 struct403 // 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 would408 // 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 else418 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 new424 // 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 bigger431 // than 16 bytes here, it is not a single _m256 and or _m512 entity432 // 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 the479 // 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 arguments493 // 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 passed539 /// like the field on its own, returns the field type. Returns540 /// 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 only554 // 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 marshalled570 // into a single SSE register (like <n x smallest fp field> ).571 // It should make no difference from an ABI point of view to just572 // select an fp type of the right size, and it makes things simpler573 // 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::Marshalling595 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 not616 // able to later assign actual registers to all of them (struct passing is617 // 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 which637 // may be bigger than needed if some struct padding ends the first eight638 // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and639 // semantic point of view, but it may not match the LLVM IR interface clang640 // would produce for the equivalent C code (the assembly will still be641 // compatible). This allows keeping the logic simpler here since it642 // 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::Marshalling653 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 %st0664 // %st1 registers. Here, they are returned as fp80 or {fp80, fp80} by665 // passAsFieldIfOneFieldStruct, and LLVM will use the expected registers.666 667 // Note that {_Complex long double} is not 100% clear from an ABI668 // perspective because the aggregate post merger rules say it should be669 // passed in memory because it is bigger than 2 eight bytes. This has the670 // funny effect of671 // {_Complex long double} return to be dealt with differently than672 // _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} for691 // 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::Marshalling702 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} // namespace715 716//===----------------------------------------------------------------------===//717// x86_64 (x86 64 bit) Windows target specifics.718//===----------------------------------------------------------------------===//719 720namespace {721struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {722 using GenericTarget::GenericTarget;723 724 static constexpr int defaultWidth = 64;725 726 CodeGenSpecifics::Marshalling727 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 GPR732 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 8737 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 16745 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::Marshalling756 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 GPR761 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 8766 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 16774 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} // namespace785 786//===----------------------------------------------------------------------===//787// AArch64 target specifics.788//===----------------------------------------------------------------------===//789 790namespace {791// AArch64 procedure call standard:792// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing793struct TargetAArch64 : public GenericTarget<TargetAArch64> {794 using GenericTarget::GenericTarget;795 796 static constexpr int defaultWidth = 64;797 798 CodeGenSpecifics::Marshalling799 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 eleTy806 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});807 } else {808 typeTodo(sem, loc, "argument");809 }810 return marshal;811 }812 813 CodeGenSpecifics::Marshalling814 integerArgumentType(mlir::Location loc,815 mlir::IntegerType argTy) const override {816 if (argTy.getWidth() < getCIntTypeWidth() && argTy.isSignless()) {817 AT::IntegerExtension intExt;818 if (argTy.getWidth() == 1) {819 // Zero extend for 'i1'.820 intExt = AT::IntegerExtension::Zero;821 } else {822 if (triple.isOSDarwin()) {823 // On Darwin, sign extend. The apple developer guide specifies this as824 // a divergence from the AArch64PCS:825 // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly826 intExt = AT::IntegerExtension::Sign;827 } else {828 // On linux, pass directly and do not extend.829 intExt = AT::IntegerExtension::None;830 }831 }832 CodeGenSpecifics::Marshalling marshal;833 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,834 /*sret=*/false, /*append=*/false,835 /*intExt=*/intExt});836 return marshal;837 }838 return GenericTarget::integerArgumentType(loc, argTy);839 }840 841 CodeGenSpecifics::Marshalling842 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {843 CodeGenSpecifics::Marshalling marshal;844 const auto *sem = &floatToSemantics(kindMap, eleTy);845 if (sem == &llvm::APFloat::IEEEsingle() ||846 sem == &llvm::APFloat::IEEEdouble() ||847 sem == &llvm::APFloat::IEEEquad()) {848 // Use a type that will be translated into LLVM as:849 // { t, t } struct of 2 eleTy850 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),851 mlir::TypeRange{eleTy, eleTy}),852 AT{});853 } else {854 typeTodo(sem, loc, "return");855 }856 return marshal;857 }858 859 // Flatten a RecordType::TypeList containing more record types or array type860 static std::optional<std::vector<mlir::Type>>861 flattenTypeList(const RecordType::TypeList &types) {862 std::vector<mlir::Type> flatTypes;863 // The flat list will be at least the same size as the non-flat list.864 flatTypes.reserve(types.size());865 for (auto [c, type] : types) {866 // Flatten record type867 if (auto recTy = mlir::dyn_cast<RecordType>(type)) {868 auto subTypeList = flattenTypeList(recTy.getTypeList());869 if (!subTypeList)870 return std::nullopt;871 llvm::copy(*subTypeList, std::back_inserter(flatTypes));872 continue;873 }874 875 // Flatten array type876 if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {877 if (seqTy.hasDynamicExtents())878 return std::nullopt;879 std::size_t n = seqTy.getConstantArraySize();880 auto eleTy = seqTy.getElementType();881 // Flatten array of record types882 if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) {883 auto subTypeList = flattenTypeList(recTy.getTypeList());884 if (!subTypeList)885 return std::nullopt;886 for (std::size_t i = 0; i < n; ++i)887 llvm::copy(*subTypeList, std::back_inserter(flatTypes));888 } else {889 std::fill_n(std::back_inserter(flatTypes),890 seqTy.getConstantArraySize(), eleTy);891 }892 continue;893 }894 895 // Other types are already flat896 flatTypes.push_back(type);897 }898 return flatTypes;899 }900 901 // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An902 // HFA is a record type with up to 4 floating-point members of the same type.903 static std::optional<int> usedRegsForHFA(fir::RecordType ty) {904 RecordType::TypeList types = ty.getTypeList();905 if (types.empty() || types.size() > 4)906 return std::nullopt;907 908 std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);909 if (!flatTypes || flatTypes->size() > 4) {910 return std::nullopt;911 }912 913 if (!isa_real(flatTypes->front())) {914 return std::nullopt;915 }916 917 return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()}918 : std::nullopt;919 }920 921 struct NRegs {922 int n{0};923 bool isSimd{false};924 };925 926 NRegs usedRegsForRecordType(mlir::Location loc, fir::RecordType type) const {927 if (std::optional<int> size = usedRegsForHFA(type))928 return {*size, true};929 930 auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash(931 loc, type, getDataLayout(), kindMap);932 933 if (size <= 16)934 return {static_cast<int>((size + 7) / 8), false};935 936 // Pass on the stack, i.e. no registers used937 return {};938 }939 940 NRegs usedRegsForType(mlir::Location loc, mlir::Type type) const {941 return llvm::TypeSwitch<mlir::Type, NRegs>(type)942 .Case<mlir::IntegerType>([&](auto intTy) {943 return intTy.getWidth() == 128 ? NRegs{2, false} : NRegs{1, false};944 })945 .Case<mlir::FloatType>([&](auto) { return NRegs{1, true}; })946 .Case<mlir::ComplexType>([&](auto) { return NRegs{2, true}; })947 .Case<fir::LogicalType>([&](auto) { return NRegs{1, false}; })948 .Case<fir::CharacterType>([&](auto) { return NRegs{1, false}; })949 .Case<fir::SequenceType>([&](auto ty) {950 assert(ty.getShape().size() == 1 &&951 "invalid array dimensions in BIND(C)");952 NRegs nregs = usedRegsForType(loc, ty.getEleTy());953 nregs.n *= ty.getShape()[0];954 return nregs;955 })956 .Case<fir::RecordType>(957 [&](auto ty) { return usedRegsForRecordType(loc, ty); })958 .Case<fir::VectorType>([&](auto) {959 TODO(loc, "passing vector argument to C by value is not supported");960 return NRegs{};961 })962 .Default([&](auto ty) {963 if (fir::conformsWithPassByRef(ty))964 return NRegs{1, false}; // Pointers take 1 integer register965 TODO(loc, "unsupported component type for BIND(C), VALUE derived "966 "type argument");967 return NRegs{};968 });969 }970 971 bool hasEnoughRegisters(mlir::Location loc, fir::RecordType type,972 const Marshalling &previousArguments) const {973 int availIntRegisters = 8;974 int availSIMDRegisters = 8;975 976 // Check previous arguments to see how many registers are used already977 for (auto [type, attr] : previousArguments) {978 if (availIntRegisters <= 0 || availSIMDRegisters <= 0)979 break;980 981 if (attr.isByVal())982 continue; // Previous argument passed on the stack983 984 NRegs nregs = usedRegsForType(loc, type);985 if (nregs.isSimd)986 availSIMDRegisters -= nregs.n;987 else988 availIntRegisters -= nregs.n;989 }990 991 NRegs nregs = usedRegsForRecordType(loc, type);992 993 if (nregs.isSimd)994 return nregs.n <= availSIMDRegisters;995 996 return nregs.n <= availIntRegisters;997 }998 999 CodeGenSpecifics::Marshalling1000 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {1001 CodeGenSpecifics::Marshalling marshal;1002 auto sizeAndAlign =1003 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);1004 // The stack is always 8 byte aligned1005 unsigned short align =1006 std::max(sizeAndAlign.second, static_cast<unsigned short>(8));1007 marshal.emplace_back(fir::ReferenceType::get(ty),1008 AT{align, /*byval=*/!isResult, /*sret=*/isResult});1009 return marshal;1010 }1011 1012 CodeGenSpecifics::Marshalling1013 structType(mlir::Location loc, fir::RecordType type, bool isResult) const {1014 NRegs nregs = usedRegsForRecordType(loc, type);1015 1016 // If the type needs no registers it must need to be passed on the stack1017 if (nregs.n == 0)1018 return passOnTheStack(loc, type, isResult);1019 1020 CodeGenSpecifics::Marshalling marshal;1021 1022 mlir::Type pcsType;1023 if (nregs.isSimd) {1024 pcsType = type;1025 } else {1026 pcsType = fir::SequenceType::get(1027 nregs.n, mlir::IntegerType::get(type.getContext(), 64));1028 }1029 1030 marshal.emplace_back(pcsType, AT{});1031 return marshal;1032 }1033 1034 CodeGenSpecifics::Marshalling1035 structArgumentType(mlir::Location loc, fir::RecordType ty,1036 const Marshalling &previousArguments) const override {1037 if (!hasEnoughRegisters(loc, ty, previousArguments)) {1038 return passOnTheStack(loc, ty, /*isResult=*/false);1039 }1040 1041 return structType(loc, ty, /*isResult=*/false);1042 }1043 1044 CodeGenSpecifics::Marshalling1045 structReturnType(mlir::Location loc, fir::RecordType ty) const override {1046 return structType(loc, ty, /*isResult=*/true);1047 }1048};1049} // namespace1050 1051//===----------------------------------------------------------------------===//1052// PPC (AIX 32 bit) target specifics.1053//===----------------------------------------------------------------------===//1054namespace {1055struct TargetPPC : public GenericTarget<TargetPPC> {1056 using GenericTarget::GenericTarget;1057 1058 static constexpr int defaultWidth = 32;1059 1060 CodeGenSpecifics::Marshalling1061 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {1062 CodeGenSpecifics::Marshalling marshal;1063 // two distinct element type arguments (re, im)1064 marshal.emplace_back(eleTy, AT{});1065 marshal.emplace_back(eleTy, AT{});1066 return marshal;1067 }1068 1069 CodeGenSpecifics::Marshalling1070 complexReturnType(mlir::Location, mlir::Type eleTy) const override {1071 CodeGenSpecifics::Marshalling marshal;1072 // Use a type that will be translated into LLVM as:1073 // { t, t } struct of 2 element type1074 marshal.emplace_back(1075 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),1076 AT{});1077 return marshal;1078 }1079};1080} // namespace1081 1082//===----------------------------------------------------------------------===//1083// PPC64 (AIX 64 bit) target specifics.1084//===----------------------------------------------------------------------===//1085 1086namespace {1087struct TargetPPC64 : public GenericTarget<TargetPPC64> {1088 using GenericTarget::GenericTarget;1089 1090 static constexpr int defaultWidth = 64;1091 1092 CodeGenSpecifics::Marshalling1093 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {1094 CodeGenSpecifics::Marshalling marshal;1095 // two distinct element type arguments (re, im)1096 marshal.emplace_back(eleTy, AT{});1097 marshal.emplace_back(eleTy, AT{});1098 return marshal;1099 }1100 1101 CodeGenSpecifics::Marshalling1102 complexReturnType(mlir::Location, mlir::Type eleTy) const override {1103 CodeGenSpecifics::Marshalling marshal;1104 // Use a type that will be translated into LLVM as:1105 // { t, t } struct of 2 element type1106 marshal.emplace_back(1107 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),1108 AT{});1109 return marshal;1110 }1111 1112 CodeGenSpecifics::Marshalling1113 structType(mlir::Location loc, fir::RecordType ty, bool isResult) const {1114 CodeGenSpecifics::Marshalling marshal;1115 auto sizeAndAlign{1116 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};1117 unsigned short align{1118 std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};1119 marshal.emplace_back(fir::ReferenceType::get(ty),1120 AT{align, /*byval*/ !isResult, /*sret*/ isResult});1121 return marshal;1122 }1123 1124 CodeGenSpecifics::Marshalling1125 structArgumentType(mlir::Location loc, fir::RecordType ty,1126 const Marshalling &previousArguments) const override {1127 return structType(loc, ty, false);1128 }1129 1130 CodeGenSpecifics::Marshalling1131 structReturnType(mlir::Location loc, fir::RecordType ty) const override {1132 return structType(loc, ty, true);1133 }1134};1135} // namespace1136 1137//===----------------------------------------------------------------------===//1138// PPC64le linux target specifics.1139//===----------------------------------------------------------------------===//1140 1141namespace {1142struct TargetPPC64le : public GenericTarget<TargetPPC64le> {1143 using GenericTarget::GenericTarget;1144 1145 static constexpr int defaultWidth{64};1146 1147 CodeGenSpecifics::Marshalling1148 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {1149 CodeGenSpecifics::Marshalling marshal;1150 // two distinct element type arguments (re, im)1151 marshal.emplace_back(eleTy, AT{});1152 marshal.emplace_back(eleTy, AT{});1153 return marshal;1154 }1155 1156 CodeGenSpecifics::Marshalling1157 complexReturnType(mlir::Location, mlir::Type eleTy) const override {1158 CodeGenSpecifics::Marshalling marshal;1159 // Use a type that will be translated into LLVM as:1160 // { t, t } struct of 2 element type1161 marshal.emplace_back(1162 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),1163 AT{});1164 return marshal;1165 }1166 1167 unsigned getElemWidth(mlir::Type ty) const {1168 unsigned width{};1169 llvm::TypeSwitch<mlir::Type>(ty)1170 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {1171 auto elemType{1172 mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};1173 width = elemType.getWidth();1174 })1175 .template Case<mlir::FloatType>(1176 [&](mlir::FloatType real) { width = real.getWidth(); });1177 return width;1178 }1179 1180 // Determine if all derived types components are of the same float type with1181 // the same width. Complex(4) is considered 2 floats and complex(8) 2 doubles.1182 bool hasSameFloatAndWidth(1183 fir::RecordType recTy,1184 std::pair<mlir::Type, unsigned> &firstTypeAndWidth) const {1185 for (auto comp : recTy.getTypeList()) {1186 mlir::Type compType{comp.second};1187 if (mlir::isa<fir::RecordType>(compType)) {1188 auto rc{hasSameFloatAndWidth(mlir::cast<fir::RecordType>(compType),1189 firstTypeAndWidth)};1190 if (!rc)1191 return false;1192 } else {1193 mlir::Type ty;1194 bool isFloatType{false};1195 if (mlir::isa<mlir::FloatType, mlir::ComplexType>(compType)) {1196 ty = compType;1197 isFloatType = true;1198 } else if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(compType)) {1199 ty = seqTy.getEleTy();1200 isFloatType = mlir::isa<mlir::FloatType, mlir::ComplexType>(ty);1201 }1202 1203 if (!isFloatType) {1204 return false;1205 }1206 auto width{getElemWidth(ty)};1207 if (firstTypeAndWidth.first == nullptr) {1208 firstTypeAndWidth.first = ty;1209 firstTypeAndWidth.second = width;1210 } else if (width != firstTypeAndWidth.second) {1211 return false;1212 }1213 }1214 }1215 return true;1216 }1217 1218 CodeGenSpecifics::Marshalling1219 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {1220 CodeGenSpecifics::Marshalling marshal;1221 auto sizeAndAlign{1222 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};1223 unsigned short align{1224 std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};1225 marshal.emplace_back(fir::ReferenceType::get(ty),1226 AT{align, /*byval=*/!isResult, /*sret=*/isResult});1227 return marshal;1228 }1229 1230 CodeGenSpecifics::Marshalling1231 structType(mlir::Location loc, fir::RecordType recTy, bool isResult) const {1232 CodeGenSpecifics::Marshalling marshal;1233 auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(1234 loc, recTy, getDataLayout(), kindMap)};1235 auto recordTypeSize{sizeAndAlign.first};1236 mlir::Type seqTy;1237 std::pair<mlir::Type, unsigned> firstTyAndWidth{nullptr, 0};1238 1239 // If there are less than or equal to 8 floats, the structure is flatten as1240 // an array of floats.1241 constexpr uint64_t maxNoOfFloats{8};1242 1243 // i64 type1244 mlir::Type elemTy{mlir::IntegerType::get(recTy.getContext(), defaultWidth)};1245 uint64_t nElem{static_cast<uint64_t>(1246 std::ceil(static_cast<float>(recordTypeSize * 8) / defaultWidth))};1247 1248 // If the derived type components contains are all floats with the same1249 // width, the argument is passed as an array of floats.1250 if (hasSameFloatAndWidth(recTy, firstTyAndWidth)) {1251 uint64_t n{};1252 auto firstType{firstTyAndWidth.first};1253 1254 // Type is either float or complex1255 if (auto cmplx = mlir::dyn_cast<mlir::ComplexType>(firstType)) {1256 auto fltType{mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};1257 n = static_cast<uint64_t>(8 * recordTypeSize / fltType.getWidth());1258 if (n <= maxNoOfFloats) {1259 nElem = n;1260 elemTy = fltType;1261 }1262 } else if (mlir::isa<mlir::FloatType>(firstType)) {1263 auto elemSizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(1264 loc, firstType, getDataLayout(), kindMap)};1265 n = static_cast<uint64_t>(recordTypeSize / elemSizeAndAlign.first);1266 if (n <= maxNoOfFloats) {1267 nElem = n;1268 elemTy = firstType;1269 }1270 }1271 // Neither float nor complex1272 assert(n > 0 && "unexpected type");1273 }1274 1275 // For function returns, only flattened if there are less than 81276 // floats in total.1277 if (isResult &&1278 ((mlir::isa<mlir::FloatType>(elemTy) && nElem > maxNoOfFloats) ||1279 !mlir::isa<mlir::FloatType>(elemTy))) {1280 return passOnTheStack(loc, recTy, isResult);1281 }1282 1283 seqTy = fir::SequenceType::get(nElem, elemTy);1284 marshal.emplace_back(seqTy, AT{});1285 return marshal;1286 }1287 1288 CodeGenSpecifics::Marshalling1289 structArgumentType(mlir::Location loc, fir::RecordType recType,1290 const Marshalling &previousArguments) const override {1291 auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(1292 loc, recType, getDataLayout(), kindMap)};1293 if (sizeAndAlign.first > 64) {1294 return passOnTheStack(loc, recType, false);1295 }1296 return structType(loc, recType, false);1297 }1298 1299 CodeGenSpecifics::Marshalling1300 structReturnType(mlir::Location loc, fir::RecordType recType) const override {1301 return structType(loc, recType, true);1302 }1303};1304} // namespace1305 1306//===----------------------------------------------------------------------===//1307// sparc (sparc 32 bit) target specifics.1308//===----------------------------------------------------------------------===//1309 1310namespace {1311struct TargetSparc : public GenericTarget<TargetSparc> {1312 using GenericTarget::GenericTarget;1313 1314 static constexpr int defaultWidth = 32;1315 1316 CodeGenSpecifics::Marshalling1317 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {1318 assert(fir::isa_real(eleTy));1319 CodeGenSpecifics::Marshalling marshal;1320 // Use a type that will be translated into LLVM as:1321 // { t, t } struct of 2 eleTy1322 auto structTy =1323 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});1324 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});1325 return marshal;1326 }1327 1328 CodeGenSpecifics::Marshalling1329 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1330 assert(fir::isa_real(eleTy));1331 CodeGenSpecifics::Marshalling marshal;1332 // Use a type that will be translated into LLVM as:1333 // { t, t } struct of 2 eleTy, byval1334 auto structTy =1335 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});1336 marshal.emplace_back(fir::ReferenceType::get(structTy),1337 AT{/*alignment=*/0, /*byval=*/true});1338 return marshal;1339 }1340};1341} // namespace1342 1343//===----------------------------------------------------------------------===//1344// sparcv9 (sparc 64 bit) target specifics.1345//===----------------------------------------------------------------------===//1346 1347namespace {1348struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {1349 using GenericTarget::GenericTarget;1350 1351 static constexpr int defaultWidth = 64;1352 1353 CodeGenSpecifics::Marshalling1354 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {1355 CodeGenSpecifics::Marshalling marshal;1356 const auto *sem = &floatToSemantics(kindMap, eleTy);1357 if (sem == &llvm::APFloat::IEEEsingle() ||1358 sem == &llvm::APFloat::IEEEdouble()) {1359 // two distinct float, double arguments1360 marshal.emplace_back(eleTy, AT{});1361 marshal.emplace_back(eleTy, AT{});1362 } else if (sem == &llvm::APFloat::IEEEquad()) {1363 // Use a type that will be translated into LLVM as:1364 // { fp128, fp128 } struct of 2 fp128, byval, align 161365 marshal.emplace_back(1366 fir::ReferenceType::get(mlir::TupleType::get(1367 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),1368 AT{/*align=*/16, /*byval=*/true});1369 } else {1370 typeTodo(sem, loc, "argument");1371 }1372 return marshal;1373 }1374 1375 CodeGenSpecifics::Marshalling1376 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1377 CodeGenSpecifics::Marshalling marshal;1378 // Use a type that will be translated into LLVM as:1379 // { eleTy, eleTy } struct of 2 eleTy1380 marshal.emplace_back(1381 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),1382 AT{});1383 return marshal;1384 }1385};1386} // namespace1387 1388//===----------------------------------------------------------------------===//1389// RISCV64 linux target specifics.1390//===----------------------------------------------------------------------===//1391 1392namespace {1393struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {1394 using GenericTarget::GenericTarget;1395 1396 static constexpr int defaultWidth = 64;1397 1398 CodeGenSpecifics::Marshalling1399 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {1400 CodeGenSpecifics::Marshalling marshal;1401 const auto *sem = &floatToSemantics(kindMap, eleTy);1402 if (sem == &llvm::APFloat::IEEEsingle() ||1403 sem == &llvm::APFloat::IEEEdouble()) {1404 // Two distinct element type arguments (re, im)1405 marshal.emplace_back(eleTy, AT{});1406 marshal.emplace_back(eleTy, AT{});1407 } else {1408 typeTodo(sem, loc, "argument");1409 }1410 return marshal;1411 }1412 1413 CodeGenSpecifics::Marshalling1414 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1415 CodeGenSpecifics::Marshalling marshal;1416 const auto *sem = &floatToSemantics(kindMap, eleTy);1417 if (sem == &llvm::APFloat::IEEEsingle() ||1418 sem == &llvm::APFloat::IEEEdouble()) {1419 // Use a type that will be translated into LLVM as:1420 // { t, t } struct of 2 eleTy, byVal1421 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),1422 mlir::TypeRange{eleTy, eleTy}),1423 AT{/*alignment=*/0, /*byval=*/true});1424 } else {1425 typeTodo(sem, loc, "return");1426 }1427 return marshal;1428 }1429};1430} // namespace1431 1432//===----------------------------------------------------------------------===//1433// AMDGPU linux target specifics.1434//===----------------------------------------------------------------------===//1435 1436namespace {1437struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {1438 using GenericTarget::GenericTarget;1439 1440 // Default size (in bits) of the index type for strings.1441 static constexpr int defaultWidth = 64;1442 1443 CodeGenSpecifics::Marshalling1444 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {1445 CodeGenSpecifics::Marshalling marshal;1446 const auto *sem = &floatToSemantics(kindMap, eleTy);1447 if (sem == &llvm::APFloat::IEEEsingle()) {1448 // Lower COMPLEX(KIND=4) as an array of two element values.1449 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});1450 } else if (sem == &llvm::APFloat::IEEEdouble()) {1451 // Pass COMPLEX(KIND=8) as two separate arguments.1452 marshal.emplace_back(eleTy, AT{});1453 marshal.emplace_back(eleTy, AT{});1454 } else {1455 typeTodo(sem, loc, "argument");1456 }1457 return marshal;1458 }1459 1460 CodeGenSpecifics::Marshalling1461 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1462 CodeGenSpecifics::Marshalling marshal;1463 const auto *sem = &floatToSemantics(kindMap, eleTy);1464 if (sem == &llvm::APFloat::IEEEsingle()) {1465 // Return COMPLEX(KIND=4) as an array of two elements.1466 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});1467 } else if (sem == &llvm::APFloat::IEEEdouble()) {1468 // Return COMPLEX(KIND=8) via an aggregate with two fields.1469 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),1470 mlir::TypeRange{eleTy, eleTy}),1471 AT{});1472 } else {1473 typeTodo(sem, loc, "return");1474 }1475 return marshal;1476 }1477};1478} // namespace1479 1480//===----------------------------------------------------------------------===//1481// NVPTX linux target specifics.1482//===----------------------------------------------------------------------===//1483 1484namespace {1485struct TargetNVPTX : public GenericTarget<TargetNVPTX> {1486 using GenericTarget::GenericTarget;1487 1488 // Default size (in bits) of the index type for strings.1489 static constexpr int defaultWidth = 64;1490 1491 CodeGenSpecifics::Marshalling1492 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {1493 CodeGenSpecifics::Marshalling marshal;1494 TODO(loc, "handle complex argument types");1495 return marshal;1496 }1497 1498 CodeGenSpecifics::Marshalling1499 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1500 CodeGenSpecifics::Marshalling marshal;1501 TODO(loc, "handle complex return types");1502 return marshal;1503 }1504};1505} // namespace1506 1507//===----------------------------------------------------------------------===//1508// LoongArch64 linux target specifics.1509//===----------------------------------------------------------------------===//1510 1511namespace {1512struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {1513 using GenericTarget::GenericTarget;1514 1515 static constexpr int defaultWidth = 64;1516 static constexpr int GRLen = defaultWidth; /* eight bytes */1517 static constexpr int GRLenInChar = GRLen / 8;1518 static constexpr int FRLen = defaultWidth; /* eight bytes */1519 1520 CodeGenSpecifics::Marshalling1521 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {1522 CodeGenSpecifics::Marshalling marshal;1523 const auto *sem = &floatToSemantics(kindMap, eleTy);1524 if (sem == &llvm::APFloat::IEEEsingle() ||1525 sem == &llvm::APFloat::IEEEdouble()) {1526 // Two distinct element type arguments (re, im)1527 marshal.emplace_back(eleTy, AT{});1528 marshal.emplace_back(eleTy, AT{});1529 } else if (sem == &llvm::APFloat::IEEEquad()) {1530 // Use a type that will be translated into LLVM as:1531 // { fp128, fp128 } struct of 2 fp128, byval1532 marshal.emplace_back(1533 fir::ReferenceType::get(mlir::TupleType::get(1534 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),1535 AT{/*align=*/16, /*byval=*/true});1536 } else {1537 typeTodo(sem, loc, "argument");1538 }1539 return marshal;1540 }1541 1542 CodeGenSpecifics::Marshalling1543 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {1544 CodeGenSpecifics::Marshalling marshal;1545 const auto *sem = &floatToSemantics(kindMap, eleTy);1546 if (sem == &llvm::APFloat::IEEEsingle() ||1547 sem == &llvm::APFloat::IEEEdouble()) {1548 // Use a type that will be translated into LLVM as:1549 // { t, t } struct of 2 eleTy, byVal1550 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),1551 mlir::TypeRange{eleTy, eleTy}),1552 AT{/*alignment=*/0, /*byval=*/true});1553 } else if (sem == &llvm::APFloat::IEEEquad()) {1554 // Use a type that will be translated into LLVM as:1555 // { fp128, fp128 } struct of 2 fp128, sret, align 161556 marshal.emplace_back(1557 fir::ReferenceType::get(mlir::TupleType::get(1558 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),1559 AT{/*align=*/16, /*byval=*/false, /*sret=*/true});1560 } else {1561 typeTodo(sem, loc, "return");1562 }1563 return marshal;1564 }1565 1566 CodeGenSpecifics::Marshalling1567 integerArgumentType(mlir::Location loc,1568 mlir::IntegerType argTy) const override {1569 if (argTy.getWidth() == 32) {1570 // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended.1571 // Therefore, Flang also follows it if a function needs to be1572 // interoperable with C.1573 //1574 // Currently, it only adds `signext` attribute to the dummy arguments and1575 // return values in the function signatures, but it does not add the1576 // corresponding attribute to the actual arguments and return values in1577 // `fir.call` instruction. Thanks to LLVM's integration of all these1578 // attributes, the modification is still effective.1579 CodeGenSpecifics::Marshalling marshal;1580 AT::IntegerExtension intExt = AT::IntegerExtension::Sign;1581 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,1582 /*sret=*/false, /*append=*/false,1583 /*intExt=*/intExt});1584 return marshal;1585 }1586 1587 return GenericTarget::integerArgumentType(loc, argTy);1588 }1589 1590 /// Flatten non-basic types, resulting in an array of types containing only1591 /// `IntegerType` and `FloatType`.1592 llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc,1593 const mlir::Type type) const {1594 llvm::SmallVector<mlir::Type> flatTypes;1595 1596 llvm::TypeSwitch<mlir::Type>(type)1597 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {1598 if (intTy.getWidth() != 0)1599 flatTypes.push_back(intTy);1600 })1601 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {1602 if (floatTy.getWidth() != 0)1603 flatTypes.push_back(floatTy);1604 })1605 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {1606 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());1607 if (sem == &llvm::APFloat::IEEEsingle() ||1608 sem == &llvm::APFloat::IEEEdouble() ||1609 sem == &llvm::APFloat::IEEEquad())1610 std::fill_n(std::back_inserter(flatTypes), 2,1611 cmplx.getElementType());1612 else1613 TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "1614 "IEEEquad) as a structure component for BIND(C), "1615 "VALUE derived type argument and type return");1616 })1617 .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {1618 const unsigned width =1619 kindMap.getLogicalBitsize(logicalTy.getFKind());1620 if (width != 0)1621 flatTypes.push_back(1622 mlir::IntegerType::get(type.getContext(), width));1623 })1624 .template Case<fir::CharacterType>([&](fir::CharacterType charTy) {1625 assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 &&1626 "the bit size of characterType as an interoperable type must "1627 "not exceed 8");1628 for (unsigned i = 0; i < charTy.getLen(); ++i)1629 flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));1630 })1631 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {1632 if (!seqTy.hasDynamicExtents()) {1633 const std::uint64_t numOfEle = seqTy.getConstantArraySize();1634 mlir::Type eleTy = seqTy.getEleTy();1635 if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {1636 llvm::SmallVector<mlir::Type> subTypeList =1637 flattenTypeList(loc, eleTy);1638 if (subTypeList.size() != 0)1639 for (std::uint64_t i = 0; i < numOfEle; ++i)1640 llvm::copy(subTypeList, std::back_inserter(flatTypes));1641 } else {1642 std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);1643 }1644 } else1645 TODO(loc, "unsupported dynamic extent sequence type as a structure "1646 "component for BIND(C), "1647 "VALUE derived type argument and type return");1648 })1649 .template Case<fir::RecordType>([&](fir::RecordType recTy) {1650 for (auto &component : recTy.getTypeList()) {1651 mlir::Type eleTy = component.second;1652 llvm::SmallVector<mlir::Type> subTypeList =1653 flattenTypeList(loc, eleTy);1654 if (subTypeList.size() != 0)1655 llvm::copy(subTypeList, std::back_inserter(flatTypes));1656 }1657 })1658 .template Case<fir::VectorType>([&](fir::VectorType vecTy) {1659 auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash(1660 loc, vecTy, getDataLayout(), kindMap);1661 if (sizeAndAlign.first == 2 * GRLenInChar)1662 flatTypes.push_back(1663 mlir::IntegerType::get(type.getContext(), 2 * GRLen));1664 else1665 TODO(loc, "unsupported vector width(must be 128 bits)");1666 })1667 .Default([&](mlir::Type ty) {1668 if (fir::conformsWithPassByRef(ty))1669 flatTypes.push_back(1670 mlir::IntegerType::get(type.getContext(), GRLen));1671 else1672 TODO(loc, "unsupported component type for BIND(C), VALUE derived "1673 "type argument and type return");1674 });1675 1676 return flatTypes;1677 }1678 1679 /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,1680 /// when flattened it contains a single fp value, fp+fp, or int+fp of1681 /// appropriate size).1682 bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,1683 mlir::Type &field1Ty,1684 mlir::Type &field2Ty) const {1685 field1Ty = field2Ty = nullptr;1686 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);1687 size_t flatSize = flatTypes.size();1688 1689 // Cannot be eligible if the number of flattened types is equal to 0 or1690 // greater than 2.1691 if (flatSize == 0 || flatSize > 2)1692 return false;1693 1694 bool isFirstAvaliableFloat = false;1695 1696 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&1697 "Type must be integerType or floatType after flattening");1698 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {1699 const unsigned Size = floatTy.getWidth();1700 // Can't be eligible if larger than the FP registers. Half precision isn't1701 // currently supported on LoongArch and the ABI hasn't been confirmed, so1702 // default to the integer ABI in that case.1703 if (Size > FRLen || Size < 32)1704 return false;1705 isFirstAvaliableFloat = true;1706 field1Ty = floatTy;1707 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {1708 if (intTy.getWidth() > GRLen)1709 return false;1710 field1Ty = intTy;1711 }1712 1713 // flatTypes has two elements1714 if (flatSize == 2) {1715 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&1716 "Type must be integerType or floatType after flattening");1717 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {1718 const unsigned Size = floatTy.getWidth();1719 if (Size > FRLen || Size < 32)1720 return false;1721 field2Ty = floatTy;1722 return true;1723 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) {1724 // Can't be eligible if an integer type was already found (int+int pairs1725 // are not eligible).1726 if (!isFirstAvaliableFloat)1727 return false;1728 if (intTy.getWidth() > GRLen)1729 return false;1730 field2Ty = intTy;1731 return true;1732 }1733 }1734 1735 // return isFirstAvaliableFloat if flatTypes only has one element1736 return isFirstAvaliableFloat;1737 }1738 1739 bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,1740 const mlir::Type type) const {1741 if (!type)1742 return true;1743 1744 llvm::TypeSwitch<mlir::Type>(type)1745 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {1746 const unsigned width = intTy.getWidth();1747 if (width > 128)1748 TODO(loc,1749 "integerType with width exceeding 128 bits is unsupported");1750 if (width == 0)1751 return;1752 if (width <= GRLen)1753 --GARsLeft;1754 else if (width <= 2 * GRLen)1755 GARsLeft = GARsLeft - 2;1756 })1757 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {1758 const unsigned width = floatTy.getWidth();1759 if (width > 128)1760 TODO(loc, "floatType with width exceeding 128 bits is unsupported");1761 if (width == 0)1762 return;1763 if (width == 32 || width == 64)1764 --FARsLeft;1765 else if (width <= GRLen)1766 --GARsLeft;1767 else if (width <= 2 * GRLen)1768 GARsLeft = GARsLeft - 2;1769 })1770 .Default([&](mlir::Type ty) {1771 if (fir::conformsWithPassByRef(ty))1772 --GARsLeft; // Pointers.1773 else1774 TODO(loc, "unsupported component type for BIND(C), VALUE derived "1775 "type argument and type return");1776 });1777 1778 return GARsLeft >= 0 && FARsLeft >= 0;1779 }1780 1781 bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,1782 const Marshalling &previousArguments,1783 const mlir::Type &field1Ty,1784 const mlir::Type &field2Ty) const {1785 for (auto &typeAndAttr : previousArguments) {1786 const auto &attr = std::get<Attributes>(typeAndAttr);1787 if (attr.isByVal()) {1788 // Previous argument passed on the stack, and its address is passed in1789 // GAR.1790 --GARsLeft;1791 continue;1792 }1793 1794 // Previous aggregate arguments were marshalled into simpler arguments.1795 const auto &type = std::get<mlir::Type>(typeAndAttr);1796 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);1797 1798 for (auto &flatTy : flatTypes) {1799 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))1800 return false;1801 }1802 }1803 1804 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))1805 return false;1806 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))1807 return false;1808 return true;1809 }1810 1811 /// LoongArch64 subroutine calling sequence ABI in:1812 /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence1813 CodeGenSpecifics::Marshalling1814 classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft,1815 int FARsLeft, bool isResult,1816 const Marshalling &previousArguments) const {1817 CodeGenSpecifics::Marshalling marshal;1818 1819 auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(1820 loc, recTy, getDataLayout(), kindMap);1821 mlir::MLIRContext *context = recTy.getContext();1822 1823 if (recSize == 0) {1824 TODO(loc, "unsupported empty struct type for BIND(C), "1825 "VALUE derived type argument and type return");1826 }1827 1828 if (recSize > 2 * GRLenInChar) {1829 marshal.emplace_back(1830 fir::ReferenceType::get(recTy),1831 AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult});1832 return marshal;1833 }1834 1835 // Pass by FARs(and GARs)1836 mlir::Type field1Ty = nullptr, field2Ty = nullptr;1837 if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) &&1838 hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty,1839 field2Ty)) {1840 if (!isResult) {1841 if (field1Ty)1842 marshal.emplace_back(field1Ty, AT{});1843 if (field2Ty)1844 marshal.emplace_back(field2Ty, AT{});1845 } else {1846 // field1Ty is always preferred over field2Ty for assignment, so there1847 // will never be a case where field1Ty == nullptr and field2Ty !=1848 // nullptr.1849 if (field1Ty && !field2Ty)1850 marshal.emplace_back(field1Ty, AT{});1851 else if (field1Ty && field2Ty)1852 marshal.emplace_back(1853 mlir::TupleType::get(context,1854 mlir::TypeRange{field1Ty, field2Ty}),1855 AT{/*alignment=*/0, /*byval=*/true});1856 }1857 return marshal;1858 }1859 1860 if (recSize <= GRLenInChar) {1861 marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});1862 return marshal;1863 }1864 1865 if (recAlign == 2 * GRLenInChar) {1866 marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});1867 return marshal;1868 }1869 1870 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar1871 marshal.emplace_back(1872 fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),1873 AT{});1874 return marshal;1875 }1876 1877 /// Marshal a derived type passed by value like a C struct.1878 CodeGenSpecifics::Marshalling1879 structArgumentType(mlir::Location loc, fir::RecordType recTy,1880 const Marshalling &previousArguments) const override {1881 int GARsLeft = 8;1882 int FARsLeft = FRLen ? 8 : 0;1883 1884 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,1885 previousArguments);1886 }1887 1888 CodeGenSpecifics::Marshalling1889 structReturnType(mlir::Location loc, fir::RecordType recTy) const override {1890 // The rules for return and argument types are the same.1891 int GARsLeft = 2;1892 int FARsLeft = FRLen ? 2 : 0;1893 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,1894 {});1895 }1896};1897} // namespace1898 1899// Instantiate the overloaded target instance based on the triple value.1900// TODO: Add other targets to this file as needed.1901std::unique_ptr<fir::CodeGenSpecifics>1902fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,1903 KindMapping &&kindMap, llvm::StringRef targetCPU,1904 mlir::LLVM::TargetFeaturesAttr targetFeatures,1905 const mlir::DataLayout &dl) {1906 switch (trp.getArch()) {1907 default:1908 break;1909 case llvm::Triple::ArchType::x86:1910 if (trp.isOSWindows())1911 return std::make_unique<TargetI386Win>(ctx, std::move(trp),1912 std::move(kindMap), targetCPU,1913 targetFeatures, dl);1914 else1915 return std::make_unique<TargetI386>(ctx, std::move(trp),1916 std::move(kindMap), targetCPU,1917 targetFeatures, dl);1918 case llvm::Triple::ArchType::x86_64:1919 if (trp.isOSWindows())1920 return std::make_unique<TargetX86_64Win>(ctx, std::move(trp),1921 std::move(kindMap), targetCPU,1922 targetFeatures, dl);1923 else1924 return std::make_unique<TargetX86_64>(ctx, std::move(trp),1925 std::move(kindMap), targetCPU,1926 targetFeatures, dl);1927 case llvm::Triple::ArchType::aarch64:1928 return std::make_unique<TargetAArch64>(1929 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1930 case llvm::Triple::ArchType::ppc:1931 return std::make_unique<TargetPPC>(ctx, std::move(trp), std::move(kindMap),1932 targetCPU, targetFeatures, dl);1933 case llvm::Triple::ArchType::ppc64:1934 return std::make_unique<TargetPPC64>(1935 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1936 case llvm::Triple::ArchType::ppc64le:1937 return std::make_unique<TargetPPC64le>(1938 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1939 case llvm::Triple::ArchType::sparc:1940 return std::make_unique<TargetSparc>(1941 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1942 case llvm::Triple::ArchType::sparcv9:1943 return std::make_unique<TargetSparcV9>(1944 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1945 case llvm::Triple::ArchType::riscv64:1946 return std::make_unique<TargetRISCV64>(1947 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1948 case llvm::Triple::ArchType::amdgcn:1949 return std::make_unique<TargetAMDGPU>(1950 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1951 case llvm::Triple::ArchType::nvptx64:1952 return std::make_unique<TargetNVPTX>(1953 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1954 case llvm::Triple::ArchType::loongarch64:1955 return std::make_unique<TargetLoongArch64>(1956 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1957 }1958 TODO(mlir::UnknownLoc::get(ctx), "target not implemented");1959}1960 1961std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(1962 mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap,1963 llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures,1964 const mlir::DataLayout &dl, llvm::StringRef tuneCPU) {1965 std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(1966 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);1967 1968 CGS->tuneCPU = tuneCPU;1969 return CGS;1970}1971