xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision dab9fa2d7f3b3092d4ab0c815868ec68a968a31a)
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, bool sret) 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     // Return value arguments are grouped as a pair. Others are passed in a
89     // split format with all pointers first (in the declared position) and all
90     // LEN arguments appended after all of the dummy arguments.
91     // NB: Other conventions/ABIs can/should be supported via options.
92     marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
93                                    /*sret=*/sret, /*append=*/!sret});
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::FloatType::getF128(context);
576       if (partByteSize > 4)
577         return mlir::FloatType::getF64(context);
578       if (partByteSize > 2)
579         return mlir::FloatType::getF32(context);
580       return mlir::FloatType::getF16(context);
581     }
582     assert(partByteSize <= 8 &&
583            "expect integer part of aggregate argument to fit into eight bytes");
584     if (partByteSize > 4)
585       return mlir::IntegerType::get(context, 64);
586     if (partByteSize > 2)
587       return mlir::IntegerType::get(context, 32);
588     if (partByteSize > 1)
589       return mlir::IntegerType::get(context, 16);
590     return mlir::IntegerType::get(context, 8);
591   }
592 
593   /// Marshal a derived type passed by value like a C struct.
594   CodeGenSpecifics::Marshalling
595   structArgumentType(mlir::Location loc, fir::RecordType recTy,
596                      const Marshalling &previousArguments) const override {
597     std::uint64_t byteOffset = 0;
598     ArgClass Lo, Hi;
599     Lo = Hi = ArgClass::NoClass;
600     byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
601     postMerge(byteOffset, Lo, Hi);
602     if (Lo == ArgClass::Memory || Lo == ArgClass::X87 ||
603         Lo == ArgClass::ComplexX87)
604       return passOnTheStack(loc, recTy, /*isResult=*/false);
605     int neededIntRegisters = 0;
606     int neededSSERegisters = 0;
607     if (Lo == ArgClass::SSE)
608       ++neededSSERegisters;
609     else if (Lo == ArgClass::Integer)
610       ++neededIntRegisters;
611     if (Hi == ArgClass::SSE)
612       ++neededSSERegisters;
613     else if (Hi == ArgClass::Integer)
614       ++neededIntRegisters;
615     // C struct should not be split into LLVM registers if LLVM codegen is not
616     // able to later assign actual registers to all of them (struct passing is
617     // all in registers or all on the stack).
618     if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters,
619                             previousArguments))
620       return passOnTheStack(loc, recTy, /*isResult=*/false);
621 
622     if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
623       CodeGenSpecifics::Marshalling marshal;
624       marshal.emplace_back(fieldType, AT{});
625       return marshal;
626     }
627     if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
628       // Pass a single integer or floating point argument.
629       mlir::Type lowType =
630           pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset);
631       CodeGenSpecifics::Marshalling marshal;
632       marshal.emplace_back(lowType, AT{});
633       return marshal;
634     }
635     // Split into two integer or floating point arguments.
636     // Note that for the first argument, this will always pick i64 or f64 which
637     // may be bigger than needed if some struct padding ends the first eight
638     // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and
639     // semantic point of view, but it may not match the LLVM IR interface clang
640     // would produce for the equivalent C code (the assembly will still be
641     // compatible).  This allows keeping the logic simpler here since it
642     // avoids computing the "data" size of the Lo part.
643     mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u);
644     mlir::Type hiType =
645         pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u);
646     CodeGenSpecifics::Marshalling marshal;
647     marshal.emplace_back(lowType, AT{});
648     marshal.emplace_back(hiType, AT{});
649     return marshal;
650   }
651 
652   CodeGenSpecifics::Marshalling
653   structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
654     std::uint64_t byteOffset = 0;
655     ArgClass Lo, Hi;
656     Lo = Hi = ArgClass::NoClass;
657     byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
658     mlir::MLIRContext *context = recTy.getContext();
659     postMerge(byteOffset, Lo, Hi);
660     if (Lo == ArgClass::Memory)
661       return passOnTheStack(loc, recTy, /*isResult=*/true);
662 
663     // Note that X87/ComplexX87 are passed in memory, but returned via %st0
664     // %st1 registers. Here, they are returned as fp80 or {fp80, fp80} by
665     // passAsFieldIfOneFieldStruct, and LLVM will use the expected registers.
666 
667     // Note that {_Complex long double} is not 100% clear from an ABI
668     // perspective because the aggregate post merger rules say it should be
669     // passed in memory because it is bigger than 2 eight bytes. This has the
670     // funny effect of
671     // {_Complex long double} return to be dealt with differently than
672     // _Complex long double.
673 
674     if (auto fieldType =
675             passAsFieldIfOneFieldStruct(recTy, /*allowComplex=*/true)) {
676       if (auto complexType = mlir::dyn_cast<mlir::ComplexType>(fieldType))
677         return complexReturnType(loc, complexType.getElementType());
678       CodeGenSpecifics::Marshalling marshal;
679       marshal.emplace_back(fieldType, AT{});
680       return marshal;
681     }
682 
683     if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
684       // Return a single integer or floating point argument.
685       mlir::Type lowType = pickLLVMArgType(loc, context, Lo, byteOffset);
686       CodeGenSpecifics::Marshalling marshal;
687       marshal.emplace_back(lowType, AT{});
688       return marshal;
689     }
690     // Will be returned in two different registers. Generate {lowTy, HiTy} for
691     // the LLVM IR result type.
692     CodeGenSpecifics::Marshalling marshal;
693     mlir::Type lowType = pickLLVMArgType(loc, context, Lo, 8u);
694     mlir::Type hiType = pickLLVMArgType(loc, context, Hi, byteOffset - 8u);
695     marshal.emplace_back(mlir::TupleType::get(context, {lowType, hiType}),
696                          AT{});
697     return marshal;
698   }
699 
700   /// Marshal an argument that must be passed on the stack.
701   CodeGenSpecifics::Marshalling
702   passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
703     CodeGenSpecifics::Marshalling marshal;
704     auto sizeAndAlign =
705         fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
706     // The stack is always 8 byte aligned (note 14 in 3.2.3).
707     unsigned short align =
708         std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
709     marshal.emplace_back(fir::ReferenceType::get(ty),
710                          AT{align, /*byval=*/!isResult, /*sret=*/isResult});
711     return marshal;
712   }
713 };
714 } // namespace
715 
716 //===----------------------------------------------------------------------===//
717 // x86_64 (x86 64 bit) Windows target specifics.
718 //===----------------------------------------------------------------------===//
719 
720 namespace {
721 struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
722   using GenericTarget::GenericTarget;
723 
724   static constexpr int defaultWidth = 64;
725 
726   CodeGenSpecifics::Marshalling
727   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
728     CodeGenSpecifics::Marshalling marshal;
729     const auto *sem = &floatToSemantics(kindMap, eleTy);
730     if (sem == &llvm::APFloat::IEEEsingle()) {
731       // i64   pack both floats in a 64-bit GPR
732       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
733                            AT{});
734     } else if (sem == &llvm::APFloat::IEEEdouble()) {
735       // Use a type that will be translated into LLVM as:
736       // { double, double }   struct of 2 double, byval, align 8
737       marshal.emplace_back(
738           fir::ReferenceType::get(mlir::TupleType::get(
739               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
740           AT{/*align=*/8, /*byval=*/true});
741     } else if (sem == &llvm::APFloat::IEEEquad() ||
742                sem == &llvm::APFloat::x87DoubleExtended()) {
743       // Use a type that will be translated into LLVM as:
744       // { t, t }   struct of 2 eleTy, byval, align 16
745       marshal.emplace_back(
746           fir::ReferenceType::get(mlir::TupleType::get(
747               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
748           AT{/*align=*/16, /*byval=*/true});
749     } else {
750       typeTodo(sem, loc, "argument");
751     }
752     return marshal;
753   }
754 
755   CodeGenSpecifics::Marshalling
756   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
757     CodeGenSpecifics::Marshalling marshal;
758     const auto *sem = &floatToSemantics(kindMap, eleTy);
759     if (sem == &llvm::APFloat::IEEEsingle()) {
760       // i64   pack both floats in a 64-bit GPR
761       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
762                            AT{});
763     } else if (sem == &llvm::APFloat::IEEEdouble()) {
764       // Use a type that will be translated into LLVM as:
765       // { double, double }   struct of 2 double, sret, align 8
766       marshal.emplace_back(
767           fir::ReferenceType::get(mlir::TupleType::get(
768               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
769           AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
770     } else if (sem == &llvm::APFloat::IEEEquad() ||
771                sem == &llvm::APFloat::x87DoubleExtended()) {
772       // Use a type that will be translated into LLVM as:
773       // { t, t }   struct of 2 eleTy, sret, align 16
774       marshal.emplace_back(
775           fir::ReferenceType::get(mlir::TupleType::get(
776               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
777           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
778     } else {
779       typeTodo(sem, loc, "return");
780     }
781     return marshal;
782   }
783 };
784 } // namespace
785 
786 //===----------------------------------------------------------------------===//
787 // AArch64 linux target specifics.
788 //===----------------------------------------------------------------------===//
789 
790 namespace {
791 struct TargetAArch64 : public GenericTarget<TargetAArch64> {
792   using GenericTarget::GenericTarget;
793 
794   static constexpr int defaultWidth = 64;
795 
796   CodeGenSpecifics::Marshalling
797   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
798     CodeGenSpecifics::Marshalling marshal;
799     const auto *sem = &floatToSemantics(kindMap, eleTy);
800     if (sem == &llvm::APFloat::IEEEsingle() ||
801         sem == &llvm::APFloat::IEEEdouble() ||
802         sem == &llvm::APFloat::IEEEquad()) {
803       // [2 x t]   array of 2 eleTy
804       marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
805     } else {
806       typeTodo(sem, loc, "argument");
807     }
808     return marshal;
809   }
810 
811   CodeGenSpecifics::Marshalling
812   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
813     CodeGenSpecifics::Marshalling marshal;
814     const auto *sem = &floatToSemantics(kindMap, eleTy);
815     if (sem == &llvm::APFloat::IEEEsingle() ||
816         sem == &llvm::APFloat::IEEEdouble() ||
817         sem == &llvm::APFloat::IEEEquad()) {
818       // Use a type that will be translated into LLVM as:
819       // { t, t }   struct of 2 eleTy
820       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
821                                                 mlir::TypeRange{eleTy, eleTy}),
822                            AT{});
823     } else {
824       typeTodo(sem, loc, "return");
825     }
826     return marshal;
827   }
828 
829   // Flatten a RecordType::TypeList containing more record types or array types
830   static std::optional<std::vector<mlir::Type>>
831   flattenTypeList(const RecordType::TypeList &types) {
832     std::vector<mlir::Type> flatTypes;
833     // The flat list will be at least the same size as the non-flat list.
834     flatTypes.reserve(types.size());
835     for (auto [c, type] : types) {
836       // Flatten record type
837       if (auto recTy = mlir::dyn_cast<RecordType>(type)) {
838         auto subTypeList = flattenTypeList(recTy.getTypeList());
839         if (!subTypeList)
840           return std::nullopt;
841         llvm::copy(*subTypeList, std::back_inserter(flatTypes));
842         continue;
843       }
844 
845       // Flatten array type
846       if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {
847         if (seqTy.hasDynamicExtents())
848           return std::nullopt;
849         std::size_t n = seqTy.getConstantArraySize();
850         auto eleTy = seqTy.getElementType();
851         // Flatten array of record types
852         if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) {
853           auto subTypeList = flattenTypeList(recTy.getTypeList());
854           if (!subTypeList)
855             return std::nullopt;
856           for (std::size_t i = 0; i < n; ++i)
857             llvm::copy(*subTypeList, std::back_inserter(flatTypes));
858         } else {
859           std::fill_n(std::back_inserter(flatTypes),
860                       seqTy.getConstantArraySize(), eleTy);
861         }
862         continue;
863       }
864 
865       // Other types are already flat
866       flatTypes.push_back(type);
867     }
868     return flatTypes;
869   }
870 
871   // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An
872   // HFA is a record type with up to 4 floating-point members of the same type.
873   static bool isHFA(fir::RecordType ty) {
874     RecordType::TypeList types = ty.getTypeList();
875     if (types.empty() || types.size() > 4)
876       return false;
877 
878     std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);
879     if (!flatTypes || flatTypes->size() > 4) {
880       return false;
881     }
882 
883     if (!isa_real(flatTypes->front())) {
884       return false;
885     }
886 
887     return llvm::all_equal(*flatTypes);
888   }
889 
890   // AArch64 procedure call ABI:
891   // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing
892   CodeGenSpecifics::Marshalling
893   structReturnType(mlir::Location loc, fir::RecordType ty) const override {
894     CodeGenSpecifics::Marshalling marshal;
895 
896     if (isHFA(ty)) {
897       // Just return the existing record type
898       marshal.emplace_back(ty, AT{});
899       return marshal;
900     }
901 
902     auto [size, align] =
903         fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
904 
905     // return in registers if size <= 16 bytes
906     if (size <= 16) {
907       std::size_t dwordSize = (size + 7) / 8;
908       auto newTy = fir::SequenceType::get(
909           dwordSize, mlir::IntegerType::get(ty.getContext(), 64));
910       marshal.emplace_back(newTy, AT{});
911       return marshal;
912     }
913 
914     unsigned short stackAlign = std::max<unsigned short>(align, 8u);
915     marshal.emplace_back(fir::ReferenceType::get(ty),
916                          AT{stackAlign, false, true});
917     return marshal;
918   }
919 };
920 } // namespace
921 
922 //===----------------------------------------------------------------------===//
923 // PPC64 (AIX 64 bit) target specifics.
924 //===----------------------------------------------------------------------===//
925 
926 namespace {
927 struct TargetPPC64 : public GenericTarget<TargetPPC64> {
928   using GenericTarget::GenericTarget;
929 
930   static constexpr int defaultWidth = 64;
931 
932   CodeGenSpecifics::Marshalling
933   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
934     CodeGenSpecifics::Marshalling marshal;
935     // two distinct element type arguments (re, im)
936     marshal.emplace_back(eleTy, AT{});
937     marshal.emplace_back(eleTy, AT{});
938     return marshal;
939   }
940 
941   CodeGenSpecifics::Marshalling
942   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
943     CodeGenSpecifics::Marshalling marshal;
944     // Use a type that will be translated into LLVM as:
945     // { t, t }   struct of 2 element type
946     marshal.emplace_back(
947         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
948         AT{});
949     return marshal;
950   }
951 };
952 } // namespace
953 
954 //===----------------------------------------------------------------------===//
955 // PPC64le linux target specifics.
956 //===----------------------------------------------------------------------===//
957 
958 namespace {
959 struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
960   using GenericTarget::GenericTarget;
961 
962   static constexpr int defaultWidth = 64;
963 
964   CodeGenSpecifics::Marshalling
965   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
966     CodeGenSpecifics::Marshalling marshal;
967     // two distinct element type arguments (re, im)
968     marshal.emplace_back(eleTy, AT{});
969     marshal.emplace_back(eleTy, AT{});
970     return marshal;
971   }
972 
973   CodeGenSpecifics::Marshalling
974   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
975     CodeGenSpecifics::Marshalling marshal;
976     // Use a type that will be translated into LLVM as:
977     // { t, t }   struct of 2 element type
978     marshal.emplace_back(
979         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
980         AT{});
981     return marshal;
982   }
983 };
984 } // namespace
985 
986 //===----------------------------------------------------------------------===//
987 // sparc (sparc 32 bit) target specifics.
988 //===----------------------------------------------------------------------===//
989 
990 namespace {
991 struct TargetSparc : public GenericTarget<TargetSparc> {
992   using GenericTarget::GenericTarget;
993 
994   static constexpr int defaultWidth = 32;
995 
996   CodeGenSpecifics::Marshalling
997   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
998     assert(fir::isa_real(eleTy));
999     CodeGenSpecifics::Marshalling marshal;
1000     // Use a type that will be translated into LLVM as:
1001     // { t, t }   struct of 2 eleTy
1002     auto structTy =
1003         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1004     marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
1005     return marshal;
1006   }
1007 
1008   CodeGenSpecifics::Marshalling
1009   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1010     assert(fir::isa_real(eleTy));
1011     CodeGenSpecifics::Marshalling marshal;
1012     // Use a type that will be translated into LLVM as:
1013     // { t, t }   struct of 2 eleTy, byval
1014     auto structTy =
1015         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1016     marshal.emplace_back(fir::ReferenceType::get(structTy),
1017                          AT{/*alignment=*/0, /*byval=*/true});
1018     return marshal;
1019   }
1020 };
1021 } // namespace
1022 
1023 //===----------------------------------------------------------------------===//
1024 // sparcv9 (sparc 64 bit) target specifics.
1025 //===----------------------------------------------------------------------===//
1026 
1027 namespace {
1028 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
1029   using GenericTarget::GenericTarget;
1030 
1031   static constexpr int defaultWidth = 64;
1032 
1033   CodeGenSpecifics::Marshalling
1034   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1035     CodeGenSpecifics::Marshalling marshal;
1036     const auto *sem = &floatToSemantics(kindMap, eleTy);
1037     if (sem == &llvm::APFloat::IEEEsingle() ||
1038         sem == &llvm::APFloat::IEEEdouble()) {
1039       // two distinct float, double arguments
1040       marshal.emplace_back(eleTy, AT{});
1041       marshal.emplace_back(eleTy, AT{});
1042     } else if (sem == &llvm::APFloat::IEEEquad()) {
1043       // Use a type that will be translated into LLVM as:
1044       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
1045       marshal.emplace_back(
1046           fir::ReferenceType::get(mlir::TupleType::get(
1047               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1048           AT{/*align=*/16, /*byval=*/true});
1049     } else {
1050       typeTodo(sem, loc, "argument");
1051     }
1052     return marshal;
1053   }
1054 
1055   CodeGenSpecifics::Marshalling
1056   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1057     CodeGenSpecifics::Marshalling marshal;
1058     // Use a type that will be translated into LLVM as:
1059     // { eleTy, eleTy }   struct of 2 eleTy
1060     marshal.emplace_back(
1061         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1062         AT{});
1063     return marshal;
1064   }
1065 };
1066 } // namespace
1067 
1068 //===----------------------------------------------------------------------===//
1069 // RISCV64 linux target specifics.
1070 //===----------------------------------------------------------------------===//
1071 
1072 namespace {
1073 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
1074   using GenericTarget::GenericTarget;
1075 
1076   static constexpr int defaultWidth = 64;
1077 
1078   CodeGenSpecifics::Marshalling
1079   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1080     CodeGenSpecifics::Marshalling marshal;
1081     const auto *sem = &floatToSemantics(kindMap, eleTy);
1082     if (sem == &llvm::APFloat::IEEEsingle() ||
1083         sem == &llvm::APFloat::IEEEdouble()) {
1084       // Two distinct element type arguments (re, im)
1085       marshal.emplace_back(eleTy, AT{});
1086       marshal.emplace_back(eleTy, AT{});
1087     } else {
1088       typeTodo(sem, loc, "argument");
1089     }
1090     return marshal;
1091   }
1092 
1093   CodeGenSpecifics::Marshalling
1094   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1095     CodeGenSpecifics::Marshalling marshal;
1096     const auto *sem = &floatToSemantics(kindMap, eleTy);
1097     if (sem == &llvm::APFloat::IEEEsingle() ||
1098         sem == &llvm::APFloat::IEEEdouble()) {
1099       // Use a type that will be translated into LLVM as:
1100       // { t, t }   struct of 2 eleTy, byVal
1101       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1102                                                 mlir::TypeRange{eleTy, eleTy}),
1103                            AT{/*alignment=*/0, /*byval=*/true});
1104     } else {
1105       typeTodo(sem, loc, "return");
1106     }
1107     return marshal;
1108   }
1109 };
1110 } // namespace
1111 
1112 //===----------------------------------------------------------------------===//
1113 // AMDGPU linux target specifics.
1114 //===----------------------------------------------------------------------===//
1115 
1116 namespace {
1117 struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {
1118   using GenericTarget::GenericTarget;
1119 
1120   // Default size (in bits) of the index type for strings.
1121   static constexpr int defaultWidth = 64;
1122 
1123   CodeGenSpecifics::Marshalling
1124   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1125     CodeGenSpecifics::Marshalling marshal;
1126     TODO(loc, "handle complex argument types");
1127     return marshal;
1128   }
1129 
1130   CodeGenSpecifics::Marshalling
1131   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1132     CodeGenSpecifics::Marshalling marshal;
1133     TODO(loc, "handle complex return types");
1134     return marshal;
1135   }
1136 };
1137 } // namespace
1138 
1139 //===----------------------------------------------------------------------===//
1140 // NVPTX linux target specifics.
1141 //===----------------------------------------------------------------------===//
1142 
1143 namespace {
1144 struct TargetNVPTX : public GenericTarget<TargetNVPTX> {
1145   using GenericTarget::GenericTarget;
1146 
1147   // Default size (in bits) of the index type for strings.
1148   static constexpr int defaultWidth = 64;
1149 
1150   CodeGenSpecifics::Marshalling
1151   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1152     CodeGenSpecifics::Marshalling marshal;
1153     TODO(loc, "handle complex argument types");
1154     return marshal;
1155   }
1156 
1157   CodeGenSpecifics::Marshalling
1158   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1159     CodeGenSpecifics::Marshalling marshal;
1160     TODO(loc, "handle complex return types");
1161     return marshal;
1162   }
1163 };
1164 } // namespace
1165 
1166 //===----------------------------------------------------------------------===//
1167 // LoongArch64 linux target specifics.
1168 //===----------------------------------------------------------------------===//
1169 
1170 namespace {
1171 struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
1172   using GenericTarget::GenericTarget;
1173 
1174   static constexpr int defaultWidth = 64;
1175   static constexpr int GRLen = defaultWidth; /* eight bytes */
1176   static constexpr int GRLenInChar = GRLen / 8;
1177   static constexpr int FRLen = defaultWidth; /* eight bytes */
1178 
1179   CodeGenSpecifics::Marshalling
1180   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1181     CodeGenSpecifics::Marshalling marshal;
1182     const auto *sem = &floatToSemantics(kindMap, eleTy);
1183     if (sem == &llvm::APFloat::IEEEsingle() ||
1184         sem == &llvm::APFloat::IEEEdouble()) {
1185       // Two distinct element type arguments (re, im)
1186       marshal.emplace_back(eleTy, AT{});
1187       marshal.emplace_back(eleTy, AT{});
1188     } else if (sem == &llvm::APFloat::IEEEquad()) {
1189       // Use a type that will be translated into LLVM as:
1190       // { fp128, fp128 }   struct of 2 fp128, byval
1191       marshal.emplace_back(
1192           fir::ReferenceType::get(mlir::TupleType::get(
1193               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1194           AT{/*align=*/16, /*byval=*/true});
1195     } else {
1196       typeTodo(sem, loc, "argument");
1197     }
1198     return marshal;
1199   }
1200 
1201   CodeGenSpecifics::Marshalling
1202   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1203     CodeGenSpecifics::Marshalling marshal;
1204     const auto *sem = &floatToSemantics(kindMap, eleTy);
1205     if (sem == &llvm::APFloat::IEEEsingle() ||
1206         sem == &llvm::APFloat::IEEEdouble()) {
1207       // Use a type that will be translated into LLVM as:
1208       // { t, t }   struct of 2 eleTy, byVal
1209       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1210                                                 mlir::TypeRange{eleTy, eleTy}),
1211                            AT{/*alignment=*/0, /*byval=*/true});
1212     } else if (sem == &llvm::APFloat::IEEEquad()) {
1213       // Use a type that will be translated into LLVM as:
1214       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
1215       marshal.emplace_back(
1216           fir::ReferenceType::get(mlir::TupleType::get(
1217               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1218           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
1219     } else {
1220       typeTodo(sem, loc, "return");
1221     }
1222     return marshal;
1223   }
1224 
1225   CodeGenSpecifics::Marshalling
1226   integerArgumentType(mlir::Location loc,
1227                       mlir::IntegerType argTy) const override {
1228     if (argTy.getWidth() == 32) {
1229       // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended.
1230       // Therefore, Flang also follows it if a function needs to be
1231       // interoperable with C.
1232       //
1233       // Currently, it only adds `signext` attribute to the dummy arguments and
1234       // return values in the function signatures, but it does not add the
1235       // corresponding attribute to the actual arguments and return values in
1236       // `fir.call` instruction. Thanks to LLVM's integration of all these
1237       // attributes, the modification is still effective.
1238       CodeGenSpecifics::Marshalling marshal;
1239       AT::IntegerExtension intExt = AT::IntegerExtension::Sign;
1240       marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
1241                                      /*sret=*/false, /*append=*/false,
1242                                      /*intExt=*/intExt});
1243       return marshal;
1244     }
1245 
1246     return GenericTarget::integerArgumentType(loc, argTy);
1247   }
1248 
1249   /// Flatten non-basic types, resulting in an array of types containing only
1250   /// `IntegerType` and `FloatType`.
1251   llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc,
1252                                                 const mlir::Type type) const {
1253     llvm::SmallVector<mlir::Type> flatTypes;
1254 
1255     llvm::TypeSwitch<mlir::Type>(type)
1256         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1257           if (intTy.getWidth() != 0)
1258             flatTypes.push_back(intTy);
1259         })
1260         .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1261           if (floatTy.getWidth() != 0)
1262             flatTypes.push_back(floatTy);
1263         })
1264         .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
1265           const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
1266           if (sem == &llvm::APFloat::IEEEsingle() ||
1267               sem == &llvm::APFloat::IEEEdouble() ||
1268               sem == &llvm::APFloat::IEEEquad())
1269             std::fill_n(std::back_inserter(flatTypes), 2,
1270                         cmplx.getElementType());
1271           else
1272             TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "
1273                       "IEEEquad) as a structure component for BIND(C), "
1274                       "VALUE derived type argument and type return");
1275         })
1276         .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {
1277           const unsigned width =
1278               kindMap.getLogicalBitsize(logicalTy.getFKind());
1279           if (width != 0)
1280             flatTypes.push_back(
1281                 mlir::IntegerType::get(type.getContext(), width));
1282         })
1283         .template Case<fir::CharacterType>([&](fir::CharacterType charTy) {
1284           assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 &&
1285                  "the bit size of characterType as an interoperable type must "
1286                  "not exceed 8");
1287           for (unsigned i = 0; i < charTy.getLen(); ++i)
1288             flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
1289         })
1290         .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
1291           if (!seqTy.hasDynamicExtents()) {
1292             const std::uint64_t numOfEle = seqTy.getConstantArraySize();
1293             mlir::Type eleTy = seqTy.getEleTy();
1294             if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
1295               llvm::SmallVector<mlir::Type> subTypeList =
1296                   flattenTypeList(loc, eleTy);
1297               if (subTypeList.size() != 0)
1298                 for (std::uint64_t i = 0; i < numOfEle; ++i)
1299                   llvm::copy(subTypeList, std::back_inserter(flatTypes));
1300             } else {
1301               std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
1302             }
1303           } else
1304             TODO(loc, "unsupported dynamic extent sequence type as a structure "
1305                       "component for BIND(C), "
1306                       "VALUE derived type argument and type return");
1307         })
1308         .template Case<fir::RecordType>([&](fir::RecordType recTy) {
1309           for (auto &component : recTy.getTypeList()) {
1310             mlir::Type eleTy = component.second;
1311             llvm::SmallVector<mlir::Type> subTypeList =
1312                 flattenTypeList(loc, eleTy);
1313             if (subTypeList.size() != 0)
1314               llvm::copy(subTypeList, std::back_inserter(flatTypes));
1315           }
1316         })
1317         .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
1318           auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash(
1319               loc, vecTy, getDataLayout(), kindMap);
1320           if (sizeAndAlign.first == 2 * GRLenInChar)
1321             flatTypes.push_back(
1322                 mlir::IntegerType::get(type.getContext(), 2 * GRLen));
1323           else
1324             TODO(loc, "unsupported vector width(must be 128 bits)");
1325         })
1326         .Default([&](mlir::Type ty) {
1327           if (fir::conformsWithPassByRef(ty))
1328             flatTypes.push_back(
1329                 mlir::IntegerType::get(type.getContext(), GRLen));
1330           else
1331             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1332                       "type argument and type return");
1333         });
1334 
1335     return flatTypes;
1336   }
1337 
1338   /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,
1339   /// when flattened it contains a single fp value, fp+fp, or int+fp of
1340   /// appropriate size).
1341   bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
1342                                 mlir::Type &field1Ty,
1343                                 mlir::Type &field2Ty) const {
1344     field1Ty = field2Ty = nullptr;
1345     llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);
1346     size_t flatSize = flatTypes.size();
1347 
1348     // Cannot be eligible if the number of flattened types is equal to 0 or
1349     // greater than 2.
1350     if (flatSize == 0 || flatSize > 2)
1351       return false;
1352 
1353     bool isFirstAvaliableFloat = false;
1354 
1355     assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
1356            "Type must be integerType or floatType after flattening");
1357     if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
1358       const unsigned Size = floatTy.getWidth();
1359       // Can't be eligible if larger than the FP registers. Half precision isn't
1360       // currently supported on LoongArch and the ABI hasn't been confirmed, so
1361       // default to the integer ABI in that case.
1362       if (Size > FRLen || Size < 32)
1363         return false;
1364       isFirstAvaliableFloat = true;
1365       field1Ty = floatTy;
1366     } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
1367       if (intTy.getWidth() > GRLen)
1368         return false;
1369       field1Ty = intTy;
1370     }
1371 
1372     // flatTypes has two elements
1373     if (flatSize == 2) {
1374       assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&
1375              "Type must be integerType or floatType after flattening");
1376       if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
1377         const unsigned Size = floatTy.getWidth();
1378         if (Size > FRLen || Size < 32)
1379           return false;
1380         field2Ty = floatTy;
1381         return true;
1382       } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) {
1383         // Can't be eligible if an integer type was already found (int+int pairs
1384         // are not eligible).
1385         if (!isFirstAvaliableFloat)
1386           return false;
1387         if (intTy.getWidth() > GRLen)
1388           return false;
1389         field2Ty = intTy;
1390         return true;
1391       }
1392     }
1393 
1394     // return isFirstAvaliableFloat if flatTypes only has one element
1395     return isFirstAvaliableFloat;
1396   }
1397 
1398   bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,
1399                               const mlir::Type type) const {
1400     if (!type)
1401       return true;
1402 
1403     llvm::TypeSwitch<mlir::Type>(type)
1404         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1405           const unsigned width = intTy.getWidth();
1406           if (width > 128)
1407             TODO(loc,
1408                  "integerType with width exceeding 128 bits is unsupported");
1409           if (width == 0)
1410             return;
1411           if (width <= GRLen)
1412             --GARsLeft;
1413           else if (width <= 2 * GRLen)
1414             GARsLeft = GARsLeft - 2;
1415         })
1416         .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1417           const unsigned width = floatTy.getWidth();
1418           if (width > 128)
1419             TODO(loc, "floatType with width exceeding 128 bits is unsupported");
1420           if (width == 0)
1421             return;
1422           if (width == 32 || width == 64)
1423             --FARsLeft;
1424           else if (width <= GRLen)
1425             --GARsLeft;
1426           else if (width <= 2 * GRLen)
1427             GARsLeft = GARsLeft - 2;
1428         })
1429         .Default([&](mlir::Type ty) {
1430           if (fir::conformsWithPassByRef(ty))
1431             --GARsLeft; // Pointers.
1432           else
1433             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1434                       "type argument and type return");
1435         });
1436 
1437     return GARsLeft >= 0 && FARsLeft >= 0;
1438   }
1439 
1440   bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,
1441                           const Marshalling &previousArguments,
1442                           const mlir::Type &field1Ty,
1443                           const mlir::Type &field2Ty) const {
1444     for (auto &typeAndAttr : previousArguments) {
1445       const auto &attr = std::get<Attributes>(typeAndAttr);
1446       if (attr.isByVal()) {
1447         // Previous argument passed on the stack, and its address is passed in
1448         // GAR.
1449         --GARsLeft;
1450         continue;
1451       }
1452 
1453       // Previous aggregate arguments were marshalled into simpler arguments.
1454       const auto &type = std::get<mlir::Type>(typeAndAttr);
1455       llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);
1456 
1457       for (auto &flatTy : flatTypes) {
1458         if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))
1459           return false;
1460       }
1461     }
1462 
1463     if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
1464       return false;
1465     if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
1466       return false;
1467     return true;
1468   }
1469 
1470   /// LoongArch64 subroutine calling sequence ABI in:
1471   /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
1472   CodeGenSpecifics::Marshalling
1473   classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft,
1474                  int FARsLeft, bool isResult,
1475                  const Marshalling &previousArguments) const {
1476     CodeGenSpecifics::Marshalling marshal;
1477 
1478     auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(
1479         loc, recTy, getDataLayout(), kindMap);
1480     mlir::MLIRContext *context = recTy.getContext();
1481 
1482     if (recSize == 0) {
1483       TODO(loc, "unsupported empty struct type for BIND(C), "
1484                 "VALUE derived type argument and type return");
1485     }
1486 
1487     if (recSize > 2 * GRLenInChar) {
1488       marshal.emplace_back(
1489           fir::ReferenceType::get(recTy),
1490           AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult});
1491       return marshal;
1492     }
1493 
1494     // Pass by FARs(and GARs)
1495     mlir::Type field1Ty = nullptr, field2Ty = nullptr;
1496     if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) &&
1497         hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty,
1498                            field2Ty)) {
1499       if (!isResult) {
1500         if (field1Ty)
1501           marshal.emplace_back(field1Ty, AT{});
1502         if (field2Ty)
1503           marshal.emplace_back(field2Ty, AT{});
1504       } else {
1505         // field1Ty is always preferred over field2Ty for assignment, so there
1506         // will never be a case where field1Ty == nullptr and field2Ty !=
1507         // nullptr.
1508         if (field1Ty && !field2Ty)
1509           marshal.emplace_back(field1Ty, AT{});
1510         else if (field1Ty && field2Ty)
1511           marshal.emplace_back(
1512               mlir::TupleType::get(context,
1513                                    mlir::TypeRange{field1Ty, field2Ty}),
1514               AT{/*alignment=*/0, /*byval=*/true});
1515       }
1516       return marshal;
1517     }
1518 
1519     if (recSize <= GRLenInChar) {
1520       marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});
1521       return marshal;
1522     }
1523 
1524     if (recAlign == 2 * GRLenInChar) {
1525       marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});
1526       return marshal;
1527     }
1528 
1529     // recSize > GRLenInChar && recSize <= 2 * GRLenInChar
1530     marshal.emplace_back(
1531         fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
1532         AT{});
1533     return marshal;
1534   }
1535 
1536   /// Marshal a derived type passed by value like a C struct.
1537   CodeGenSpecifics::Marshalling
1538   structArgumentType(mlir::Location loc, fir::RecordType recTy,
1539                      const Marshalling &previousArguments) const override {
1540     int GARsLeft = 8;
1541     int FARsLeft = FRLen ? 8 : 0;
1542 
1543     return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,
1544                           previousArguments);
1545   }
1546 
1547   CodeGenSpecifics::Marshalling
1548   structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
1549     // The rules for return and argument types are the same.
1550     int GARsLeft = 2;
1551     int FARsLeft = FRLen ? 2 : 0;
1552     return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,
1553                           {});
1554   }
1555 };
1556 } // namespace
1557 
1558 // Instantiate the overloaded target instance based on the triple value.
1559 // TODO: Add other targets to this file as needed.
1560 std::unique_ptr<fir::CodeGenSpecifics>
1561 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
1562                            KindMapping &&kindMap, llvm::StringRef targetCPU,
1563                            mlir::LLVM::TargetFeaturesAttr targetFeatures,
1564                            const mlir::DataLayout &dl) {
1565   switch (trp.getArch()) {
1566   default:
1567     break;
1568   case llvm::Triple::ArchType::x86:
1569     if (trp.isOSWindows())
1570       return std::make_unique<TargetI386Win>(ctx, std::move(trp),
1571                                              std::move(kindMap), targetCPU,
1572                                              targetFeatures, dl);
1573     else
1574       return std::make_unique<TargetI386>(ctx, std::move(trp),
1575                                           std::move(kindMap), targetCPU,
1576                                           targetFeatures, dl);
1577   case llvm::Triple::ArchType::x86_64:
1578     if (trp.isOSWindows())
1579       return std::make_unique<TargetX86_64Win>(ctx, std::move(trp),
1580                                                std::move(kindMap), targetCPU,
1581                                                targetFeatures, dl);
1582     else
1583       return std::make_unique<TargetX86_64>(ctx, std::move(trp),
1584                                             std::move(kindMap), targetCPU,
1585                                             targetFeatures, dl);
1586   case llvm::Triple::ArchType::aarch64:
1587     return std::make_unique<TargetAArch64>(
1588         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1589   case llvm::Triple::ArchType::ppc64:
1590     return std::make_unique<TargetPPC64>(
1591         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1592   case llvm::Triple::ArchType::ppc64le:
1593     return std::make_unique<TargetPPC64le>(
1594         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1595   case llvm::Triple::ArchType::sparc:
1596     return std::make_unique<TargetSparc>(
1597         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1598   case llvm::Triple::ArchType::sparcv9:
1599     return std::make_unique<TargetSparcV9>(
1600         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1601   case llvm::Triple::ArchType::riscv64:
1602     return std::make_unique<TargetRISCV64>(
1603         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1604   case llvm::Triple::ArchType::amdgcn:
1605     return std::make_unique<TargetAMDGPU>(
1606         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1607   case llvm::Triple::ArchType::nvptx64:
1608     return std::make_unique<TargetNVPTX>(
1609         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1610   case llvm::Triple::ArchType::loongarch64:
1611     return std::make_unique<TargetLoongArch64>(
1612         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1613   }
1614   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
1615 }
1616 
1617 std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
1618     mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap,
1619     llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures,
1620     const mlir::DataLayout &dl, llvm::StringRef tuneCPU) {
1621   std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(
1622       ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1623 
1624   CGS->tuneCPU = tuneCPU;
1625   return CGS;
1626 }
1627