brintos

brintos / llvm-project-archived public Read only

0
0
Text · 77.0 KiB · b60a72e Raw
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 &current = 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