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