xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision 4ddc756bccb34f3d07e30c9ca96bba32cb0cf4f9)
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   integerArgumentType(mlir::Location loc,
105                       mlir::IntegerType argTy) const override {
106     CodeGenSpecifics::Marshalling marshal;
107     AT::IntegerExtension intExt = AT::IntegerExtension::None;
108     if (argTy.getWidth() < getCIntTypeWidth()) {
109       // isSigned() and isUnsigned() branches below are dead code currently.
110       // If needed, we can generate calls with signed/unsigned argument types
111       // to more precisely match C side (e.g. for Fortran runtime functions
112       // with 'unsigned short' arguments).
113       if (argTy.isSigned())
114         intExt = AT::IntegerExtension::Sign;
115       else if (argTy.isUnsigned())
116         intExt = AT::IntegerExtension::Zero;
117       else if (argTy.isSignless()) {
118         // Zero extend for 'i1' and sign extend for other types.
119         if (argTy.getWidth() == 1)
120           intExt = AT::IntegerExtension::Zero;
121         else
122           intExt = AT::IntegerExtension::Sign;
123       }
124     }
125 
126     marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
127                                    /*sret=*/false, /*append=*/false,
128                                    /*intExt=*/intExt});
129     return marshal;
130   }
131 
132   CodeGenSpecifics::Marshalling
133   integerReturnType(mlir::Location loc,
134                     mlir::IntegerType argTy) const override {
135     return integerArgumentType(loc, argTy);
136   }
137 
138   // Width of 'int' type is 32-bits for almost all targets, except
139   // for AVR and MSP430 (see TargetInfo initializations
140   // in clang/lib/Basic/Targets).
141   unsigned char getCIntTypeWidth() const override { return 32; }
142 };
143 } // namespace
144 
145 //===----------------------------------------------------------------------===//
146 // i386 (x86 32 bit) linux target specifics.
147 //===----------------------------------------------------------------------===//
148 
149 namespace {
150 struct TargetI386 : public GenericTarget<TargetI386> {
151   using GenericTarget::GenericTarget;
152 
153   static constexpr int defaultWidth = 32;
154 
155   CodeGenSpecifics::Marshalling
156   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
157     assert(fir::isa_real(eleTy));
158     CodeGenSpecifics::Marshalling marshal;
159     // Use a type that will be translated into LLVM as:
160     // { t, t }   struct of 2 eleTy, byval, align 4
161     auto structTy =
162         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
163     marshal.emplace_back(fir::ReferenceType::get(structTy),
164                          AT{/*alignment=*/4, /*byval=*/true});
165     return marshal;
166   }
167 
168   CodeGenSpecifics::Marshalling
169   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
170     assert(fir::isa_real(eleTy));
171     CodeGenSpecifics::Marshalling marshal;
172     const auto *sem = &floatToSemantics(kindMap, eleTy);
173     if (sem == &llvm::APFloat::IEEEsingle()) {
174       // i64   pack both floats in a 64-bit GPR
175       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
176                            AT{});
177     } else if (sem == &llvm::APFloat::IEEEdouble()) {
178       // Use a type that will be translated into LLVM as:
179       // { t, t }   struct of 2 eleTy, sret, align 4
180       auto structTy = mlir::TupleType::get(eleTy.getContext(),
181                                            mlir::TypeRange{eleTy, eleTy});
182       marshal.emplace_back(fir::ReferenceType::get(structTy),
183                            AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
184     } else {
185       typeTodo(sem, loc, "return");
186     }
187     return marshal;
188   }
189 };
190 } // namespace
191 
192 //===----------------------------------------------------------------------===//
193 // i386 (x86 32 bit) Windows target specifics.
194 //===----------------------------------------------------------------------===//
195 
196 namespace {
197 struct TargetI386Win : public GenericTarget<TargetI386Win> {
198   using GenericTarget::GenericTarget;
199 
200   static constexpr int defaultWidth = 32;
201 
202   CodeGenSpecifics::Marshalling
203   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
204     CodeGenSpecifics::Marshalling marshal;
205     // Use a type that will be translated into LLVM as:
206     // { t, t }   struct of 2 eleTy, byval, align 4
207     auto structTy =
208         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
209     marshal.emplace_back(fir::ReferenceType::get(structTy),
210                          AT{/*align=*/4, /*byval=*/true});
211     return marshal;
212   }
213 
214   CodeGenSpecifics::Marshalling
215   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
216     CodeGenSpecifics::Marshalling marshal;
217     const auto *sem = &floatToSemantics(kindMap, eleTy);
218     if (sem == &llvm::APFloat::IEEEsingle()) {
219       // i64   pack both floats in a 64-bit GPR
220       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
221                            AT{});
222     } else if (sem == &llvm::APFloat::IEEEdouble()) {
223       // Use a type that will be translated into LLVM as:
224       // { double, double }   struct of 2 double, sret, align 8
225       marshal.emplace_back(
226           fir::ReferenceType::get(mlir::TupleType::get(
227               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
228           AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
229     } else if (sem == &llvm::APFloat::IEEEquad()) {
230       // Use a type that will be translated into LLVM as:
231       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
232       marshal.emplace_back(
233           fir::ReferenceType::get(mlir::TupleType::get(
234               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
235           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
236     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
237       // Use a type that will be translated into LLVM as:
238       // { x86_fp80, x86_fp80 }   struct of 2 x86_fp80, sret, align 4
239       marshal.emplace_back(
240           fir::ReferenceType::get(mlir::TupleType::get(
241               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
242           AT{/*align=*/4, /*byval=*/false, /*sret=*/true});
243     } else {
244       typeTodo(sem, loc, "return");
245     }
246     return marshal;
247   }
248 };
249 } // namespace
250 
251 //===----------------------------------------------------------------------===//
252 // x86_64 (x86 64 bit) linux target specifics.
253 //===----------------------------------------------------------------------===//
254 
255 namespace {
256 struct TargetX86_64 : public GenericTarget<TargetX86_64> {
257   using GenericTarget::GenericTarget;
258 
259   static constexpr int defaultWidth = 64;
260 
261   CodeGenSpecifics::Marshalling
262   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
263     CodeGenSpecifics::Marshalling marshal;
264     const auto *sem = &floatToSemantics(kindMap, eleTy);
265     if (sem == &llvm::APFloat::IEEEsingle()) {
266       // <2 x t>   vector of 2 eleTy
267       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
268     } else if (sem == &llvm::APFloat::IEEEdouble()) {
269       // FIXME: In case of SSE register exhaustion, the ABI here may be
270       // incorrect since LLVM may pass the real via register and the imaginary
271       // part via the stack while the ABI it should be all in register or all
272       // in memory. Register occupancy must be analyzed here.
273       // two distinct double arguments
274       marshal.emplace_back(eleTy, AT{});
275       marshal.emplace_back(eleTy, AT{});
276     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
277       // Use a type that will be translated into LLVM as:
278       // { x86_fp80, x86_fp80 }  struct of 2 fp128, byval, align 16
279       marshal.emplace_back(
280           fir::ReferenceType::get(mlir::TupleType::get(
281               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
282           AT{/*align=*/16, /*byval=*/true});
283     } else if (sem == &llvm::APFloat::IEEEquad()) {
284       // Use a type that will be translated into LLVM as:
285       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
286       marshal.emplace_back(
287           fir::ReferenceType::get(mlir::TupleType::get(
288               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
289           AT{/*align=*/16, /*byval=*/true});
290     } else {
291       typeTodo(sem, loc, "argument");
292     }
293     return marshal;
294   }
295 
296   CodeGenSpecifics::Marshalling
297   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
298     CodeGenSpecifics::Marshalling marshal;
299     const auto *sem = &floatToSemantics(kindMap, eleTy);
300     if (sem == &llvm::APFloat::IEEEsingle()) {
301       // <2 x t>   vector of 2 eleTy
302       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
303     } else if (sem == &llvm::APFloat::IEEEdouble()) {
304       // Use a type that will be translated into LLVM as:
305       // { double, double }   struct of 2 double
306       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
307                                                 mlir::TypeRange{eleTy, eleTy}),
308                            AT{});
309     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
310       // { x86_fp80, x86_fp80 }
311       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
312                                                 mlir::TypeRange{eleTy, eleTy}),
313                            AT{});
314     } else if (sem == &llvm::APFloat::IEEEquad()) {
315       // Use a type that will be translated into LLVM as:
316       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
317       marshal.emplace_back(
318           fir::ReferenceType::get(mlir::TupleType::get(
319               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
320           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
321     } else {
322       typeTodo(sem, loc, "return");
323     }
324     return marshal;
325   }
326 
327   /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3.
328   enum ArgClass {
329     Integer = 0,
330     SSE,
331     SSEUp,
332     X87,
333     X87Up,
334     ComplexX87,
335     NoClass,
336     Memory
337   };
338 
339   /// Classify an argument type or a field of an aggregate type argument.
340   /// See System V ABI version 1.0 section 3.2.3.
341   /// The Lo and Hi class are set to the class of the lower eight eightbytes
342   /// and upper eight eightbytes on return.
343   /// If this is called for an aggregate field, the caller is responsible to
344   /// do the post-merge.
345   void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset,
346                 ArgClass &Lo, ArgClass &Hi) const {
347     Hi = Lo = ArgClass::NoClass;
348     ArgClass &current = byteOffset < 8 ? Lo : Hi;
349     // System V AMD64 ABI 3.2.3. version 1.0
350     llvm::TypeSwitch<mlir::Type>(type)
351         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
352           if (intTy.getWidth() == 128)
353             Hi = Lo = ArgClass::Integer;
354           else
355             current = ArgClass::Integer;
356         })
357         .template Case<mlir::FloatType>([&](mlir::Type floatTy) {
358           const auto *sem = &floatToSemantics(kindMap, floatTy);
359           if (sem == &llvm::APFloat::x87DoubleExtended()) {
360             Lo = ArgClass::X87;
361             Hi = ArgClass::X87Up;
362           } else if (sem == &llvm::APFloat::IEEEquad()) {
363             Lo = ArgClass::SSE;
364             Hi = ArgClass::SSEUp;
365           } else {
366             current = ArgClass::SSE;
367           }
368         })
369         .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
370           const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
371           if (sem == &llvm::APFloat::x87DoubleExtended()) {
372             current = ArgClass::ComplexX87;
373           } else {
374             fir::SequenceType::Shape shape{2};
375             classifyArray(loc,
376                           fir::SequenceType::get(shape, cmplx.getElementType()),
377                           byteOffset, Lo, Hi);
378           }
379         })
380         .template Case<fir::LogicalType>([&](fir::LogicalType logical) {
381           if (kindMap.getLogicalBitsize(logical.getFKind()) == 128)
382             Hi = Lo = ArgClass::Integer;
383           else
384             current = ArgClass::Integer;
385         })
386         .template Case<fir::CharacterType>(
387             [&](fir::CharacterType character) { current = ArgClass::Integer; })
388         .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
389           // Array component.
390           classifyArray(loc, seqTy, byteOffset, Lo, Hi);
391         })
392         .template Case<fir::RecordType>([&](fir::RecordType recTy) {
393           // Component that is a derived type.
394           classifyStruct(loc, recTy, byteOffset, Lo, Hi);
395         })
396         .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
397           // Previously marshalled SSE eight byte for a previous struct
398           // argument.
399           auto *sem = fir::isa_real(vecTy.getEleTy())
400                           ? &floatToSemantics(kindMap, vecTy.getEleTy())
401                           : nullptr;
402           // Not expecting to hit this todo in standard code (it would
403           // require some vector type extension).
404           if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) &&
405               !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4))
406             TODO(loc, "passing vector argument to C by value");
407           current = SSE;
408         })
409         .Default([&](mlir::Type ty) {
410           if (fir::conformsWithPassByRef(ty))
411             current = ArgClass::Integer; // Pointers.
412           else
413             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
414                       "type argument");
415         });
416   }
417 
418   // Classify fields of a derived type starting at \p offset. Returns the new
419   // offset. Post-merge is left to the caller.
420   std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy,
421                                std::uint64_t byteOffset, ArgClass &Lo,
422                                ArgClass &Hi) const {
423     for (auto component : recTy.getTypeList()) {
424       if (byteOffset > 16) {
425         // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger
426         // than 16 bytes here, it is not a single _m256 and or _m512 entity
427         // that could fit in AVX registers.
428         Lo = Hi = ArgClass::Memory;
429         return byteOffset;
430       }
431       mlir::Type compType = component.second;
432       auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash(
433           loc, compType, getDataLayout(), kindMap);
434       byteOffset = llvm::alignTo(byteOffset, compAlign);
435       ArgClass LoComp, HiComp;
436       classify(loc, compType, byteOffset, LoComp, HiComp);
437       Lo = mergeClass(Lo, LoComp);
438       Hi = mergeClass(Hi, HiComp);
439       byteOffset = byteOffset + llvm::alignTo(compSize, compAlign);
440       if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
441         return byteOffset;
442     }
443     return byteOffset;
444   }
445 
446   // Classify fields of a constant size array type starting at \p offset.
447   // Returns the new offset. Post-merge is left to the caller.
448   void classifyArray(mlir::Location loc, fir::SequenceType seqTy,
449                      std::uint64_t byteOffset, ArgClass &Lo,
450                      ArgClass &Hi) const {
451     mlir::Type eleTy = seqTy.getEleTy();
452     const std::uint64_t arraySize = seqTy.getConstantArraySize();
453     auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash(
454         loc, eleTy, getDataLayout(), kindMap);
455     std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign);
456     for (std::uint64_t i = 0; i < arraySize; ++i) {
457       byteOffset = llvm::alignTo(byteOffset, eleAlign);
458       if (byteOffset > 16) {
459         // See 3.2.3 p. 1 and note 15. Same as in classifyStruct.
460         Lo = Hi = ArgClass::Memory;
461         return;
462       }
463       ArgClass LoComp, HiComp;
464       classify(loc, eleTy, byteOffset, LoComp, HiComp);
465       Lo = mergeClass(Lo, LoComp);
466       Hi = mergeClass(Hi, HiComp);
467       byteOffset = byteOffset + eleStorageSize;
468       if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
469         return;
470     }
471   }
472 
473   // Goes through the previously marshalled arguments and count the
474   // register occupancy to check if there are enough registers left.
475   bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters,
476                           int neededSSERegisters,
477                           const Marshalling &previousArguments) const {
478     int availIntRegisters = 6;
479     int availSSERegisters = 8;
480     for (auto typeAndAttr : previousArguments) {
481       const auto &attr = std::get<Attributes>(typeAndAttr);
482       if (attr.isByVal())
483         continue; // Previous argument passed on the stack.
484       ArgClass Lo, Hi;
485       Lo = Hi = ArgClass::NoClass;
486       classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi);
487       // post merge is not needed here since previous aggregate arguments
488       // were marshalled into simpler arguments.
489       if (Lo == ArgClass::Integer)
490         --availIntRegisters;
491       else if (Lo == SSE)
492         --availSSERegisters;
493       if (Hi == ArgClass::Integer)
494         --availIntRegisters;
495       else if (Hi == ArgClass::SSE)
496         --availSSERegisters;
497     }
498     return availSSERegisters >= neededSSERegisters &&
499            availIntRegisters >= neededIntRegisters;
500   }
501 
502   /// Argument class merging as described in System V ABI 3.2.3 point 4.
503   ArgClass mergeClass(ArgClass accum, ArgClass field) const {
504     assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) &&
505            "Invalid accumulated classification during merge.");
506     if (accum == field || field == NoClass)
507       return accum;
508     if (field == ArgClass::Memory)
509       return ArgClass::Memory;
510     if (accum == NoClass)
511       return field;
512     if (accum == Integer || field == Integer)
513       return ArgClass::Integer;
514     if (field == ArgClass::X87 || field == ArgClass::X87Up ||
515         field == ArgClass::ComplexX87 || accum == ArgClass::X87 ||
516         accum == ArgClass::X87Up)
517       return Memory;
518     return SSE;
519   }
520 
521   /// Argument class post merging as described in System V ABI 3.2.3 point 5.
522   void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const {
523     if (Hi == ArgClass::Memory)
524       Lo = ArgClass::Memory;
525     if (Hi == ArgClass::X87Up && Lo != ArgClass::X87)
526       Lo = ArgClass::Memory;
527     if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp))
528       Lo = ArgClass::Memory;
529     if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE)
530       Hi = SSE;
531   }
532 
533   /// When \p recTy is a one field record type that can be passed
534   /// like the field on its own, returns the field type. Returns
535   /// a null type otherwise.
536   mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy) const {
537     auto typeList = recTy.getTypeList();
538     if (typeList.size() != 1)
539       return {};
540     mlir::Type fieldType = typeList[0].second;
541     if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::LogicalType>(
542             fieldType))
543       return fieldType;
544     if (mlir::isa<fir::CharacterType>(fieldType)) {
545       // Only CHARACTER(1) are expected in BIND(C) contexts, which is the only
546       // contexts where derived type may be passed in registers.
547       assert(mlir::cast<fir::CharacterType>(fieldType).getLen() == 1 &&
548              "fir.type value arg character components must have length 1");
549       return fieldType;
550     }
551     // Complex field that needs to be split, or array.
552     return {};
553   }
554 
555   mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context,
556                              ArgClass argClass,
557                              std::uint64_t partByteSize) const {
558     if (argClass == ArgClass::SSE) {
559       if (partByteSize > 16)
560         TODO(loc, "passing struct as a real > 128 bits in register");
561       // Clang uses vector type when several fp fields are marshalled
562       // into a single SSE register (like  <n x smallest fp field> ).
563       // It should make no difference from an ABI point of view to just
564       // select an fp type of the right size, and it makes things simpler
565       // here.
566       if (partByteSize > 8)
567         return mlir::FloatType::getF128(context);
568       if (partByteSize > 4)
569         return mlir::FloatType::getF64(context);
570       if (partByteSize > 2)
571         return mlir::FloatType::getF32(context);
572       return mlir::FloatType::getF16(context);
573     }
574     assert(partByteSize <= 8 &&
575            "expect integer part of aggregate argument to fit into eight bytes");
576     if (partByteSize > 4)
577       return mlir::IntegerType::get(context, 64);
578     if (partByteSize > 2)
579       return mlir::IntegerType::get(context, 32);
580     if (partByteSize > 1)
581       return mlir::IntegerType::get(context, 16);
582     return mlir::IntegerType::get(context, 8);
583   }
584 
585   /// Marshal a derived type passed by value like a C struct.
586   CodeGenSpecifics::Marshalling
587   structArgumentType(mlir::Location loc, fir::RecordType recTy,
588                      const Marshalling &previousArguments) const override {
589     std::uint64_t byteOffset = 0;
590     ArgClass Lo, Hi;
591     Lo = Hi = ArgClass::NoClass;
592     byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
593     postMerge(byteOffset, Lo, Hi);
594     if (Lo == ArgClass::Memory || Lo == ArgClass::X87 ||
595         Lo == ArgClass::ComplexX87)
596       return passOnTheStack(loc, recTy);
597     int neededIntRegisters = 0;
598     int neededSSERegisters = 0;
599     if (Lo == ArgClass::SSE)
600       ++neededSSERegisters;
601     else if (Lo == ArgClass::Integer)
602       ++neededIntRegisters;
603     if (Hi == ArgClass::SSE)
604       ++neededSSERegisters;
605     else if (Hi == ArgClass::Integer)
606       ++neededIntRegisters;
607     // C struct should not be split into LLVM registers if LLVM codegen is not
608     // able to later assign actual registers to all of them (struct passing is
609     // all in registers or all on the stack).
610     if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters,
611                             previousArguments))
612       return passOnTheStack(loc, recTy);
613 
614     if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
615       CodeGenSpecifics::Marshalling marshal;
616       marshal.emplace_back(fieldType, AT{});
617       return marshal;
618     }
619     if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
620       // Pass a single integer or floating point argument.
621       mlir::Type lowType =
622           pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset);
623       CodeGenSpecifics::Marshalling marshal;
624       marshal.emplace_back(lowType, AT{});
625       return marshal;
626     }
627     // Split into two integer or floating point arguments.
628     // Note that for the first argument, this will always pick i64 or f64 which
629     // may be bigger than needed if some struct padding ends the first eight
630     // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and
631     // semantic point of view, but it may not match the LLVM IR interface clang
632     // would produce for the equivalent C code (the assembly will still be
633     // compatible).  This allows keeping the logic simpler here since it
634     // avoids computing the "data" size of the Lo part.
635     mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u);
636     mlir::Type hiType =
637         pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u);
638     CodeGenSpecifics::Marshalling marshal;
639     marshal.emplace_back(lowType, AT{});
640     marshal.emplace_back(hiType, AT{});
641     return marshal;
642   }
643 
644   /// Marshal an argument that must be passed on the stack.
645   CodeGenSpecifics::Marshalling passOnTheStack(mlir::Location loc,
646                                                mlir::Type ty) const {
647     CodeGenSpecifics::Marshalling marshal;
648     auto sizeAndAlign =
649         fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
650     // The stack is always 8 byte aligned (note 14 in 3.2.3).
651     unsigned short align =
652         std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
653     marshal.emplace_back(fir::ReferenceType::get(ty),
654                          AT{align, /*byval=*/true, /*sret=*/false});
655     return marshal;
656   }
657 };
658 } // namespace
659 
660 //===----------------------------------------------------------------------===//
661 // x86_64 (x86 64 bit) Windows target specifics.
662 //===----------------------------------------------------------------------===//
663 
664 namespace {
665 struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
666   using GenericTarget::GenericTarget;
667 
668   static constexpr int defaultWidth = 64;
669 
670   CodeGenSpecifics::Marshalling
671   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
672     CodeGenSpecifics::Marshalling marshal;
673     const auto *sem = &floatToSemantics(kindMap, eleTy);
674     if (sem == &llvm::APFloat::IEEEsingle()) {
675       // i64   pack both floats in a 64-bit GPR
676       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
677                            AT{});
678     } else if (sem == &llvm::APFloat::IEEEdouble()) {
679       // Use a type that will be translated into LLVM as:
680       // { double, double }   struct of 2 double, byval, align 8
681       marshal.emplace_back(
682           fir::ReferenceType::get(mlir::TupleType::get(
683               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
684           AT{/*align=*/8, /*byval=*/true});
685     } else if (sem == &llvm::APFloat::IEEEquad() ||
686                sem == &llvm::APFloat::x87DoubleExtended()) {
687       // Use a type that will be translated into LLVM as:
688       // { t, t }   struct of 2 eleTy, byval, align 16
689       marshal.emplace_back(
690           fir::ReferenceType::get(mlir::TupleType::get(
691               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
692           AT{/*align=*/16, /*byval=*/true});
693     } else {
694       typeTodo(sem, loc, "argument");
695     }
696     return marshal;
697   }
698 
699   CodeGenSpecifics::Marshalling
700   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
701     CodeGenSpecifics::Marshalling marshal;
702     const auto *sem = &floatToSemantics(kindMap, eleTy);
703     if (sem == &llvm::APFloat::IEEEsingle()) {
704       // i64   pack both floats in a 64-bit GPR
705       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
706                            AT{});
707     } else if (sem == &llvm::APFloat::IEEEdouble()) {
708       // Use a type that will be translated into LLVM as:
709       // { double, double }   struct of 2 double, sret, align 8
710       marshal.emplace_back(
711           fir::ReferenceType::get(mlir::TupleType::get(
712               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
713           AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
714     } else if (sem == &llvm::APFloat::IEEEquad() ||
715                sem == &llvm::APFloat::x87DoubleExtended()) {
716       // Use a type that will be translated into LLVM as:
717       // { t, t }   struct of 2 eleTy, sret, align 16
718       marshal.emplace_back(
719           fir::ReferenceType::get(mlir::TupleType::get(
720               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
721           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
722     } else {
723       typeTodo(sem, loc, "return");
724     }
725     return marshal;
726   }
727 };
728 } // namespace
729 
730 //===----------------------------------------------------------------------===//
731 // AArch64 linux target specifics.
732 //===----------------------------------------------------------------------===//
733 
734 namespace {
735 struct TargetAArch64 : public GenericTarget<TargetAArch64> {
736   using GenericTarget::GenericTarget;
737 
738   static constexpr int defaultWidth = 64;
739 
740   CodeGenSpecifics::Marshalling
741   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
742     CodeGenSpecifics::Marshalling marshal;
743     const auto *sem = &floatToSemantics(kindMap, eleTy);
744     if (sem == &llvm::APFloat::IEEEsingle() ||
745         sem == &llvm::APFloat::IEEEdouble() ||
746         sem == &llvm::APFloat::IEEEquad()) {
747       // [2 x t]   array of 2 eleTy
748       marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
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         sem == &llvm::APFloat::IEEEdouble() ||
761         sem == &llvm::APFloat::IEEEquad()) {
762       // Use a type that will be translated into LLVM as:
763       // { t, t }   struct of 2 eleTy
764       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
765                                                 mlir::TypeRange{eleTy, eleTy}),
766                            AT{});
767     } else {
768       typeTodo(sem, loc, "return");
769     }
770     return marshal;
771   }
772 };
773 } // namespace
774 
775 //===----------------------------------------------------------------------===//
776 // PPC64 (AIX 64 bit) target specifics.
777 //===----------------------------------------------------------------------===//
778 
779 namespace {
780 struct TargetPPC64 : public GenericTarget<TargetPPC64> {
781   using GenericTarget::GenericTarget;
782 
783   static constexpr int defaultWidth = 64;
784 
785   CodeGenSpecifics::Marshalling
786   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
787     CodeGenSpecifics::Marshalling marshal;
788     // two distinct element type arguments (re, im)
789     marshal.emplace_back(eleTy, AT{});
790     marshal.emplace_back(eleTy, AT{});
791     return marshal;
792   }
793 
794   CodeGenSpecifics::Marshalling
795   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
796     CodeGenSpecifics::Marshalling marshal;
797     // Use a type that will be translated into LLVM as:
798     // { t, t }   struct of 2 element type
799     marshal.emplace_back(
800         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
801         AT{});
802     return marshal;
803   }
804 };
805 } // namespace
806 
807 //===----------------------------------------------------------------------===//
808 // PPC64le linux target specifics.
809 //===----------------------------------------------------------------------===//
810 
811 namespace {
812 struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
813   using GenericTarget::GenericTarget;
814 
815   static constexpr int defaultWidth = 64;
816 
817   CodeGenSpecifics::Marshalling
818   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
819     CodeGenSpecifics::Marshalling marshal;
820     // two distinct element type arguments (re, im)
821     marshal.emplace_back(eleTy, AT{});
822     marshal.emplace_back(eleTy, AT{});
823     return marshal;
824   }
825 
826   CodeGenSpecifics::Marshalling
827   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
828     CodeGenSpecifics::Marshalling marshal;
829     // Use a type that will be translated into LLVM as:
830     // { t, t }   struct of 2 element type
831     marshal.emplace_back(
832         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
833         AT{});
834     return marshal;
835   }
836 };
837 } // namespace
838 
839 //===----------------------------------------------------------------------===//
840 // sparc (sparc 32 bit) target specifics.
841 //===----------------------------------------------------------------------===//
842 
843 namespace {
844 struct TargetSparc : public GenericTarget<TargetSparc> {
845   using GenericTarget::GenericTarget;
846 
847   static constexpr int defaultWidth = 32;
848 
849   CodeGenSpecifics::Marshalling
850   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
851     assert(fir::isa_real(eleTy));
852     CodeGenSpecifics::Marshalling marshal;
853     // Use a type that will be translated into LLVM as:
854     // { t, t }   struct of 2 eleTy
855     auto structTy =
856         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
857     marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
858     return marshal;
859   }
860 
861   CodeGenSpecifics::Marshalling
862   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
863     assert(fir::isa_real(eleTy));
864     CodeGenSpecifics::Marshalling marshal;
865     // Use a type that will be translated into LLVM as:
866     // { t, t }   struct of 2 eleTy, byval
867     auto structTy =
868         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
869     marshal.emplace_back(fir::ReferenceType::get(structTy),
870                          AT{/*alignment=*/0, /*byval=*/true});
871     return marshal;
872   }
873 };
874 } // namespace
875 
876 //===----------------------------------------------------------------------===//
877 // sparcv9 (sparc 64 bit) target specifics.
878 //===----------------------------------------------------------------------===//
879 
880 namespace {
881 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
882   using GenericTarget::GenericTarget;
883 
884   static constexpr int defaultWidth = 64;
885 
886   CodeGenSpecifics::Marshalling
887   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
888     CodeGenSpecifics::Marshalling marshal;
889     const auto *sem = &floatToSemantics(kindMap, eleTy);
890     if (sem == &llvm::APFloat::IEEEsingle() ||
891         sem == &llvm::APFloat::IEEEdouble()) {
892       // two distinct float, double arguments
893       marshal.emplace_back(eleTy, AT{});
894       marshal.emplace_back(eleTy, AT{});
895     } else if (sem == &llvm::APFloat::IEEEquad()) {
896       // Use a type that will be translated into LLVM as:
897       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
898       marshal.emplace_back(
899           fir::ReferenceType::get(mlir::TupleType::get(
900               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
901           AT{/*align=*/16, /*byval=*/true});
902     } else {
903       typeTodo(sem, loc, "argument");
904     }
905     return marshal;
906   }
907 
908   CodeGenSpecifics::Marshalling
909   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
910     CodeGenSpecifics::Marshalling marshal;
911     // Use a type that will be translated into LLVM as:
912     // { eleTy, eleTy }   struct of 2 eleTy
913     marshal.emplace_back(
914         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
915         AT{});
916     return marshal;
917   }
918 };
919 } // namespace
920 
921 //===----------------------------------------------------------------------===//
922 // RISCV64 linux target specifics.
923 //===----------------------------------------------------------------------===//
924 
925 namespace {
926 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
927   using GenericTarget::GenericTarget;
928 
929   static constexpr int defaultWidth = 64;
930 
931   CodeGenSpecifics::Marshalling
932   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
933     CodeGenSpecifics::Marshalling marshal;
934     const auto *sem = &floatToSemantics(kindMap, eleTy);
935     if (sem == &llvm::APFloat::IEEEsingle() ||
936         sem == &llvm::APFloat::IEEEdouble()) {
937       // Two distinct element type arguments (re, im)
938       marshal.emplace_back(eleTy, AT{});
939       marshal.emplace_back(eleTy, AT{});
940     } else {
941       typeTodo(sem, loc, "argument");
942     }
943     return marshal;
944   }
945 
946   CodeGenSpecifics::Marshalling
947   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
948     CodeGenSpecifics::Marshalling marshal;
949     const auto *sem = &floatToSemantics(kindMap, eleTy);
950     if (sem == &llvm::APFloat::IEEEsingle() ||
951         sem == &llvm::APFloat::IEEEdouble()) {
952       // Use a type that will be translated into LLVM as:
953       // { t, t }   struct of 2 eleTy, byVal
954       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
955                                                 mlir::TypeRange{eleTy, eleTy}),
956                            AT{/*alignment=*/0, /*byval=*/true});
957     } else {
958       typeTodo(sem, loc, "return");
959     }
960     return marshal;
961   }
962 };
963 } // namespace
964 
965 //===----------------------------------------------------------------------===//
966 // AMDGPU linux target specifics.
967 //===----------------------------------------------------------------------===//
968 
969 namespace {
970 struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {
971   using GenericTarget::GenericTarget;
972 
973   // Default size (in bits) of the index type for strings.
974   static constexpr int defaultWidth = 64;
975 
976   CodeGenSpecifics::Marshalling
977   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
978     CodeGenSpecifics::Marshalling marshal;
979     TODO(loc, "handle complex argument types");
980     return marshal;
981   }
982 
983   CodeGenSpecifics::Marshalling
984   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
985     CodeGenSpecifics::Marshalling marshal;
986     TODO(loc, "handle complex return types");
987     return marshal;
988   }
989 };
990 } // namespace
991 
992 //===----------------------------------------------------------------------===//
993 // NVPTX linux target specifics.
994 //===----------------------------------------------------------------------===//
995 
996 namespace {
997 struct TargetNVPTX : public GenericTarget<TargetNVPTX> {
998   using GenericTarget::GenericTarget;
999 
1000   // Default size (in bits) of the index type for strings.
1001   static constexpr int defaultWidth = 64;
1002 
1003   CodeGenSpecifics::Marshalling
1004   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1005     CodeGenSpecifics::Marshalling marshal;
1006     TODO(loc, "handle complex argument types");
1007     return marshal;
1008   }
1009 
1010   CodeGenSpecifics::Marshalling
1011   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1012     CodeGenSpecifics::Marshalling marshal;
1013     TODO(loc, "handle complex return types");
1014     return marshal;
1015   }
1016 };
1017 } // namespace
1018 
1019 //===----------------------------------------------------------------------===//
1020 // LoongArch64 linux target specifics.
1021 //===----------------------------------------------------------------------===//
1022 
1023 namespace {
1024 struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
1025   using GenericTarget::GenericTarget;
1026 
1027   static constexpr int defaultWidth = 64;
1028 
1029   CodeGenSpecifics::Marshalling
1030   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1031     CodeGenSpecifics::Marshalling marshal;
1032     const auto *sem = &floatToSemantics(kindMap, eleTy);
1033     if (sem == &llvm::APFloat::IEEEsingle() ||
1034         sem == &llvm::APFloat::IEEEdouble()) {
1035       // Two distinct element type arguments (re, im)
1036       marshal.emplace_back(eleTy, AT{});
1037       marshal.emplace_back(eleTy, AT{});
1038     } else {
1039       typeTodo(sem, loc, "argument");
1040     }
1041     return marshal;
1042   }
1043 
1044   CodeGenSpecifics::Marshalling
1045   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1046     CodeGenSpecifics::Marshalling marshal;
1047     const auto *sem = &floatToSemantics(kindMap, eleTy);
1048     if (sem == &llvm::APFloat::IEEEsingle() ||
1049         sem == &llvm::APFloat::IEEEdouble()) {
1050       // Use a type that will be translated into LLVM as:
1051       // { t, t }   struct of 2 eleTy, byVal
1052       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1053                                                 mlir::TypeRange{eleTy, eleTy}),
1054                            AT{/*alignment=*/0, /*byval=*/true});
1055     } else {
1056       typeTodo(sem, loc, "return");
1057     }
1058     return marshal;
1059   }
1060 };
1061 } // namespace
1062 
1063 // Instantiate the overloaded target instance based on the triple value.
1064 // TODO: Add other targets to this file as needed.
1065 std::unique_ptr<fir::CodeGenSpecifics>
1066 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
1067                            KindMapping &&kindMap, llvm::StringRef targetCPU,
1068                            mlir::LLVM::TargetFeaturesAttr targetFeatures,
1069                            const mlir::DataLayout &dl) {
1070   switch (trp.getArch()) {
1071   default:
1072     break;
1073   case llvm::Triple::ArchType::x86:
1074     if (trp.isOSWindows())
1075       return std::make_unique<TargetI386Win>(ctx, std::move(trp),
1076                                              std::move(kindMap), targetCPU,
1077                                              targetFeatures, dl);
1078     else
1079       return std::make_unique<TargetI386>(ctx, std::move(trp),
1080                                           std::move(kindMap), targetCPU,
1081                                           targetFeatures, dl);
1082   case llvm::Triple::ArchType::x86_64:
1083     if (trp.isOSWindows())
1084       return std::make_unique<TargetX86_64Win>(ctx, std::move(trp),
1085                                                std::move(kindMap), targetCPU,
1086                                                targetFeatures, dl);
1087     else
1088       return std::make_unique<TargetX86_64>(ctx, std::move(trp),
1089                                             std::move(kindMap), targetCPU,
1090                                             targetFeatures, dl);
1091   case llvm::Triple::ArchType::aarch64:
1092     return std::make_unique<TargetAArch64>(
1093         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1094   case llvm::Triple::ArchType::ppc64:
1095     return std::make_unique<TargetPPC64>(
1096         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1097   case llvm::Triple::ArchType::ppc64le:
1098     return std::make_unique<TargetPPC64le>(
1099         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1100   case llvm::Triple::ArchType::sparc:
1101     return std::make_unique<TargetSparc>(
1102         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1103   case llvm::Triple::ArchType::sparcv9:
1104     return std::make_unique<TargetSparcV9>(
1105         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1106   case llvm::Triple::ArchType::riscv64:
1107     return std::make_unique<TargetRISCV64>(
1108         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1109   case llvm::Triple::ArchType::amdgcn:
1110     return std::make_unique<TargetAMDGPU>(
1111         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1112   case llvm::Triple::ArchType::nvptx64:
1113     return std::make_unique<TargetNVPTX>(
1114         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1115   case llvm::Triple::ArchType::loongarch64:
1116     return std::make_unique<TargetLoongArch64>(
1117         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1118   }
1119   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
1120 }
1121 
1122 std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
1123     mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap,
1124     llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures,
1125     const mlir::DataLayout &dl, llvm::StringRef tuneCPU) {
1126   std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(
1127       ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1128 
1129   CGS->tuneCPU = tuneCPU;
1130   return CGS;
1131 }
1132