xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision f023da12d12635f5fba436e825cbfc999e28e623)
14c263edeSDiana Picus //===-- Target.cpp --------------------------------------------------------===//
24c263edeSDiana Picus //
34c263edeSDiana Picus // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44c263edeSDiana Picus // See https://llvm.org/LICENSE.txt for license information.
54c263edeSDiana Picus // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64c263edeSDiana Picus //
74c263edeSDiana Picus //===----------------------------------------------------------------------===//
84c263edeSDiana Picus //
94c263edeSDiana Picus // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
104c263edeSDiana Picus //
114c263edeSDiana Picus //===----------------------------------------------------------------------===//
124c263edeSDiana Picus 
1330408f5cSValentin Clement #include "flang/Optimizer/CodeGen/Target.h"
140c1cf585SEric Schweitz #include "flang/Optimizer/Builder/Todo.h"
154c263edeSDiana Picus #include "flang/Optimizer/Dialect/FIRType.h"
16b07ef9e7SRenaud-K #include "flang/Optimizer/Dialect/Support/KindMapping.h"
170c1cf585SEric Schweitz #include "flang/Optimizer/Support/FatalError.h"
1804b18530SPete Steinfeld #include "flang/Optimizer/Support/Utils.h"
194c263edeSDiana Picus #include "mlir/IR/BuiltinTypes.h"
204c263edeSDiana Picus #include "mlir/IR/TypeRange.h"
2127d9a479SjeanPerier #include "llvm/ADT/TypeSwitch.h"
224c263edeSDiana Picus 
234c263edeSDiana Picus #define DEBUG_TYPE "flang-codegen-target"
244c263edeSDiana Picus 
254c263edeSDiana Picus using namespace fir;
264c263edeSDiana Picus 
27ff794116SSlava Zakharin namespace fir::details {
28ff794116SSlava Zakharin llvm::StringRef Attributes::getIntExtensionAttrName() const {
29ff794116SSlava Zakharin   // The attribute names are available via LLVM dialect interfaces
30ff794116SSlava Zakharin   // like getZExtAttrName(), getByValAttrName(), etc., so we'd better
31ff794116SSlava Zakharin   // use them than literals.
32ff794116SSlava Zakharin   if (isZeroExt())
33ff794116SSlava Zakharin     return "llvm.zeroext";
34ff794116SSlava Zakharin   else if (isSignExt())
35ff794116SSlava Zakharin     return "llvm.signext";
36ff794116SSlava Zakharin   return {};
37ff794116SSlava Zakharin }
38ff794116SSlava Zakharin } // namespace fir::details
39ff794116SSlava Zakharin 
4065431d3aSDiana Picus // Reduce a REAL/float type to the floating point semantics.
4165431d3aSDiana Picus static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
4265431d3aSDiana Picus                                                   mlir::Type type) {
4365431d3aSDiana Picus   assert(isa_real(type));
44fac349a1SChristian Sigg   return mlir::cast<mlir::FloatType>(type).getFloatSemantics();
4565431d3aSDiana Picus }
4665431d3aSDiana Picus 
4704b18530SPete Steinfeld static void typeTodo(const llvm::fltSemantics *sem, mlir::Location loc,
485a20a208SPeter Klausler                      const std::string &context) {
4904b18530SPete Steinfeld   if (sem == &llvm::APFloat::IEEEhalf()) {
5004b18530SPete Steinfeld     TODO(loc, "COMPLEX(KIND=2): for " + context + " type");
5104b18530SPete Steinfeld   } else if (sem == &llvm::APFloat::BFloat()) {
5204b18530SPete Steinfeld     TODO(loc, "COMPLEX(KIND=3): " + context + " type");
5304b18530SPete Steinfeld   } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
5404b18530SPete Steinfeld     TODO(loc, "COMPLEX(KIND=10): " + context + " type");
5504b18530SPete Steinfeld   } else {
5604b18530SPete Steinfeld     TODO(loc, "complex for this precision for " + context + " type");
5704b18530SPete Steinfeld   }
5804b18530SPete Steinfeld }
5904b18530SPete Steinfeld 
604c263edeSDiana Picus namespace {
614c263edeSDiana Picus template <typename S>
624c263edeSDiana Picus struct GenericTarget : public CodeGenSpecifics {
634c263edeSDiana Picus   using CodeGenSpecifics::CodeGenSpecifics;
644c263edeSDiana Picus   using AT = CodeGenSpecifics::Attributes;
654c263edeSDiana Picus 
667b5132daSValentin Clement   mlir::Type complexMemoryType(mlir::Type eleTy) const override {
677b5132daSValentin Clement     assert(fir::isa_real(eleTy));
68faf869dcSDiana Picus     // Use a type that will be translated into LLVM as:
697b5132daSValentin Clement     // { t, t }   struct of 2 eleTy
70ff794116SSlava Zakharin     return mlir::TupleType::get(eleTy.getContext(),
71ff794116SSlava Zakharin                                 mlir::TypeRange{eleTy, eleTy});
727b5132daSValentin Clement   }
737b5132daSValentin Clement 
74bb201826SAndrzej Warzynski   mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
75bb201826SAndrzej Warzynski     auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
76bb201826SAndrzej Warzynski     auto ptrTy = fir::ReferenceType::get(eleTy);
77faf869dcSDiana Picus     // Use a type that will be translated into LLVM as:
78bb201826SAndrzej Warzynski     // { t*, index }
79ff794116SSlava Zakharin     return mlir::TupleType::get(eleTy.getContext(),
80ff794116SSlava Zakharin                                 mlir::TypeRange{ptrTy, idxTy});
81bb201826SAndrzej Warzynski   }
82bb201826SAndrzej Warzynski 
83cbb49d4bSjeanPerier   Marshalling boxcharArgumentType(mlir::Type eleTy) const override {
844c263edeSDiana Picus     CodeGenSpecifics::Marshalling marshal;
854c263edeSDiana Picus     auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
864c263edeSDiana Picus     auto ptrTy = fir::ReferenceType::get(eleTy);
874c263edeSDiana Picus     marshal.emplace_back(ptrTy, AT{});
88cbb49d4bSjeanPerier     // Characters are passed in a split format with all pointers first (in the
89cbb49d4bSjeanPerier     // declared position) and all LEN arguments appended after all of the dummy
90cbb49d4bSjeanPerier     // arguments.
914c263edeSDiana Picus     // NB: Other conventions/ABIs can/should be supported via options.
9265431d3aSDiana Picus     marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
93cbb49d4bSjeanPerier                                    /*sret=*/false, /*append=*/true});
944c263edeSDiana Picus     return marshal;
954c263edeSDiana Picus   }
96ff794116SSlava Zakharin 
97ff794116SSlava Zakharin   CodeGenSpecifics::Marshalling
9827d9a479SjeanPerier   structArgumentType(mlir::Location loc, fir::RecordType,
9927d9a479SjeanPerier                      const Marshalling &) const override {
10027d9a479SjeanPerier     TODO(loc, "passing VALUE BIND(C) derived type for this target");
10127d9a479SjeanPerier   }
10227d9a479SjeanPerier 
10327d9a479SjeanPerier   CodeGenSpecifics::Marshalling
104367c3c96SjeanPerier   structReturnType(mlir::Location loc, fir::RecordType ty) const override {
105367c3c96SjeanPerier     TODO(loc, "returning BIND(C) derived type for this target");
106367c3c96SjeanPerier   }
107367c3c96SjeanPerier 
108367c3c96SjeanPerier   CodeGenSpecifics::Marshalling
109ff794116SSlava Zakharin   integerArgumentType(mlir::Location loc,
110ff794116SSlava Zakharin                       mlir::IntegerType argTy) const override {
111ff794116SSlava Zakharin     CodeGenSpecifics::Marshalling marshal;
112ff794116SSlava Zakharin     AT::IntegerExtension intExt = AT::IntegerExtension::None;
113ff794116SSlava Zakharin     if (argTy.getWidth() < getCIntTypeWidth()) {
114ff794116SSlava Zakharin       // isSigned() and isUnsigned() branches below are dead code currently.
115ff794116SSlava Zakharin       // If needed, we can generate calls with signed/unsigned argument types
116ff794116SSlava Zakharin       // to more precisely match C side (e.g. for Fortran runtime functions
117ff794116SSlava Zakharin       // with 'unsigned short' arguments).
118ff794116SSlava Zakharin       if (argTy.isSigned())
119ff794116SSlava Zakharin         intExt = AT::IntegerExtension::Sign;
120ff794116SSlava Zakharin       else if (argTy.isUnsigned())
121ff794116SSlava Zakharin         intExt = AT::IntegerExtension::Zero;
122ff794116SSlava Zakharin       else if (argTy.isSignless()) {
123ff794116SSlava Zakharin         // Zero extend for 'i1' and sign extend for other types.
124ff794116SSlava Zakharin         if (argTy.getWidth() == 1)
125ff794116SSlava Zakharin           intExt = AT::IntegerExtension::Zero;
126ff794116SSlava Zakharin         else
127ff794116SSlava Zakharin           intExt = AT::IntegerExtension::Sign;
128ff794116SSlava Zakharin       }
129ff794116SSlava Zakharin     }
130ff794116SSlava Zakharin 
131ff794116SSlava Zakharin     marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
132ff794116SSlava Zakharin                                    /*sret=*/false, /*append=*/false,
133ff794116SSlava Zakharin                                    /*intExt=*/intExt});
134ff794116SSlava Zakharin     return marshal;
135ff794116SSlava Zakharin   }
136ff794116SSlava Zakharin 
137ff794116SSlava Zakharin   CodeGenSpecifics::Marshalling
138ff794116SSlava Zakharin   integerReturnType(mlir::Location loc,
139ff794116SSlava Zakharin                     mlir::IntegerType argTy) const override {
140ff794116SSlava Zakharin     return integerArgumentType(loc, argTy);
141ff794116SSlava Zakharin   }
142ff794116SSlava Zakharin 
143ff794116SSlava Zakharin   // Width of 'int' type is 32-bits for almost all targets, except
144ff794116SSlava Zakharin   // for AVR and MSP430 (see TargetInfo initializations
145ff794116SSlava Zakharin   // in clang/lib/Basic/Targets).
146ff794116SSlava Zakharin   unsigned char getCIntTypeWidth() const override { return 32; }
1474c263edeSDiana Picus };
1484c263edeSDiana Picus } // namespace
1494c263edeSDiana Picus 
1504c263edeSDiana Picus //===----------------------------------------------------------------------===//
1514c263edeSDiana Picus // i386 (x86 32 bit) linux target specifics.
1524c263edeSDiana Picus //===----------------------------------------------------------------------===//
1534c263edeSDiana Picus 
1544c263edeSDiana Picus namespace {
1554c263edeSDiana Picus struct TargetI386 : public GenericTarget<TargetI386> {
1564c263edeSDiana Picus   using GenericTarget::GenericTarget;
1574c263edeSDiana Picus 
1584c263edeSDiana Picus   static constexpr int defaultWidth = 32;
15965431d3aSDiana Picus 
16065431d3aSDiana Picus   CodeGenSpecifics::Marshalling
1610c1cf585SEric Schweitz   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
16265431d3aSDiana Picus     assert(fir::isa_real(eleTy));
16365431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
164faf869dcSDiana Picus     // Use a type that will be translated into LLVM as:
16565431d3aSDiana Picus     // { t, t }   struct of 2 eleTy, byval, align 4
166ff794116SSlava Zakharin     auto structTy =
167ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
16865431d3aSDiana Picus     marshal.emplace_back(fir::ReferenceType::get(structTy),
16965431d3aSDiana Picus                          AT{/*alignment=*/4, /*byval=*/true});
17065431d3aSDiana Picus     return marshal;
17165431d3aSDiana Picus   }
17265431d3aSDiana Picus 
17365431d3aSDiana Picus   CodeGenSpecifics::Marshalling
1740c1cf585SEric Schweitz   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
17565431d3aSDiana Picus     assert(fir::isa_real(eleTy));
17665431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
17765431d3aSDiana Picus     const auto *sem = &floatToSemantics(kindMap, eleTy);
17865431d3aSDiana Picus     if (sem == &llvm::APFloat::IEEEsingle()) {
17965431d3aSDiana Picus       // i64   pack both floats in a 64-bit GPR
18065431d3aSDiana Picus       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
18165431d3aSDiana Picus                            AT{});
18265431d3aSDiana Picus     } else if (sem == &llvm::APFloat::IEEEdouble()) {
183faf869dcSDiana Picus       // Use a type that will be translated into LLVM as:
18465431d3aSDiana Picus       // { t, t }   struct of 2 eleTy, sret, align 4
185ff794116SSlava Zakharin       auto structTy = mlir::TupleType::get(eleTy.getContext(),
186ff794116SSlava Zakharin                                            mlir::TypeRange{eleTy, eleTy});
18765431d3aSDiana Picus       marshal.emplace_back(fir::ReferenceType::get(structTy),
18865431d3aSDiana Picus                            AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
18965431d3aSDiana Picus     } else {
19004b18530SPete Steinfeld       typeTodo(sem, loc, "return");
19165431d3aSDiana Picus     }
19265431d3aSDiana Picus     return marshal;
19365431d3aSDiana Picus   }
1944c263edeSDiana Picus };
1954c263edeSDiana Picus } // namespace
1964c263edeSDiana Picus 
1974c263edeSDiana Picus //===----------------------------------------------------------------------===//
198774703ecSMarkus Mützel // i386 (x86 32 bit) Windows target specifics.
199774703ecSMarkus Mützel //===----------------------------------------------------------------------===//
200774703ecSMarkus Mützel 
201774703ecSMarkus Mützel namespace {
202774703ecSMarkus Mützel struct TargetI386Win : public GenericTarget<TargetI386Win> {
203774703ecSMarkus Mützel   using GenericTarget::GenericTarget;
204774703ecSMarkus Mützel 
205774703ecSMarkus Mützel   static constexpr int defaultWidth = 32;
206774703ecSMarkus Mützel 
207774703ecSMarkus Mützel   CodeGenSpecifics::Marshalling
208774703ecSMarkus Mützel   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
209774703ecSMarkus Mützel     CodeGenSpecifics::Marshalling marshal;
210774703ecSMarkus Mützel     // Use a type that will be translated into LLVM as:
211774703ecSMarkus Mützel     // { t, t }   struct of 2 eleTy, byval, align 4
212774703ecSMarkus Mützel     auto structTy =
213774703ecSMarkus Mützel         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
214774703ecSMarkus Mützel     marshal.emplace_back(fir::ReferenceType::get(structTy),
215774703ecSMarkus Mützel                          AT{/*align=*/4, /*byval=*/true});
216774703ecSMarkus Mützel     return marshal;
217774703ecSMarkus Mützel   }
218774703ecSMarkus Mützel 
219774703ecSMarkus Mützel   CodeGenSpecifics::Marshalling
220774703ecSMarkus Mützel   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
221774703ecSMarkus Mützel     CodeGenSpecifics::Marshalling marshal;
222774703ecSMarkus Mützel     const auto *sem = &floatToSemantics(kindMap, eleTy);
223774703ecSMarkus Mützel     if (sem == &llvm::APFloat::IEEEsingle()) {
224774703ecSMarkus Mützel       // i64   pack both floats in a 64-bit GPR
225774703ecSMarkus Mützel       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
226774703ecSMarkus Mützel                            AT{});
227774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEdouble()) {
228774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
229774703ecSMarkus Mützel       // { double, double }   struct of 2 double, sret, align 8
230774703ecSMarkus Mützel       marshal.emplace_back(
231774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
232774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
233774703ecSMarkus Mützel           AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
234774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEquad()) {
235774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
236774703ecSMarkus Mützel       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
237774703ecSMarkus Mützel       marshal.emplace_back(
238774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
239774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
240774703ecSMarkus Mützel           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
241774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
242774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
243774703ecSMarkus Mützel       // { x86_fp80, x86_fp80 }   struct of 2 x86_fp80, sret, align 4
244774703ecSMarkus Mützel       marshal.emplace_back(
245774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
246774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
247774703ecSMarkus Mützel           AT{/*align=*/4, /*byval=*/false, /*sret=*/true});
248774703ecSMarkus Mützel     } else {
24904b18530SPete Steinfeld       typeTodo(sem, loc, "return");
250774703ecSMarkus Mützel     }
251774703ecSMarkus Mützel     return marshal;
252774703ecSMarkus Mützel   }
253774703ecSMarkus Mützel };
254774703ecSMarkus Mützel } // namespace
255774703ecSMarkus Mützel 
256774703ecSMarkus Mützel //===----------------------------------------------------------------------===//
2574c263edeSDiana Picus // x86_64 (x86 64 bit) linux target specifics.
2584c263edeSDiana Picus //===----------------------------------------------------------------------===//
2594c263edeSDiana Picus 
2604c263edeSDiana Picus namespace {
2614c263edeSDiana Picus struct TargetX86_64 : public GenericTarget<TargetX86_64> {
2624c263edeSDiana Picus   using GenericTarget::GenericTarget;
2634c263edeSDiana Picus 
2644c263edeSDiana Picus   static constexpr int defaultWidth = 64;
26565431d3aSDiana Picus 
26665431d3aSDiana Picus   CodeGenSpecifics::Marshalling
2670c1cf585SEric Schweitz   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
26865431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
26965431d3aSDiana Picus     const auto *sem = &floatToSemantics(kindMap, eleTy);
27065431d3aSDiana Picus     if (sem == &llvm::APFloat::IEEEsingle()) {
27165431d3aSDiana Picus       // <2 x t>   vector of 2 eleTy
27265431d3aSDiana Picus       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
27365431d3aSDiana Picus     } else if (sem == &llvm::APFloat::IEEEdouble()) {
274f65e3af7SjeanPerier       // FIXME: In case of SSE register exhaustion, the ABI here may be
275f65e3af7SjeanPerier       // incorrect since LLVM may pass the real via register and the imaginary
276f65e3af7SjeanPerier       // part via the stack while the ABI it should be all in register or all
277f65e3af7SjeanPerier       // in memory. Register occupancy must be analyzed here.
27865431d3aSDiana Picus       // two distinct double arguments
27965431d3aSDiana Picus       marshal.emplace_back(eleTy, AT{});
28065431d3aSDiana Picus       marshal.emplace_back(eleTy, AT{});
281f65e3af7SjeanPerier     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
282f65e3af7SjeanPerier       // Use a type that will be translated into LLVM as:
283f65e3af7SjeanPerier       // { x86_fp80, x86_fp80 }  struct of 2 fp128, byval, align 16
284f65e3af7SjeanPerier       marshal.emplace_back(
285f65e3af7SjeanPerier           fir::ReferenceType::get(mlir::TupleType::get(
286f65e3af7SjeanPerier               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
287f65e3af7SjeanPerier           AT{/*align=*/16, /*byval=*/true});
2881906188fSValentin Clement     } else if (sem == &llvm::APFloat::IEEEquad()) {
2891906188fSValentin Clement       // Use a type that will be translated into LLVM as:
2901906188fSValentin Clement       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
291ff794116SSlava Zakharin       marshal.emplace_back(
292ff794116SSlava Zakharin           fir::ReferenceType::get(mlir::TupleType::get(
293ff794116SSlava Zakharin               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
2941906188fSValentin Clement           AT{/*align=*/16, /*byval=*/true});
29565431d3aSDiana Picus     } else {
29604b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
29765431d3aSDiana Picus     }
29865431d3aSDiana Picus     return marshal;
29965431d3aSDiana Picus   }
30065431d3aSDiana Picus 
30165431d3aSDiana Picus   CodeGenSpecifics::Marshalling
3020c1cf585SEric Schweitz   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
30365431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
30465431d3aSDiana Picus     const auto *sem = &floatToSemantics(kindMap, eleTy);
30565431d3aSDiana Picus     if (sem == &llvm::APFloat::IEEEsingle()) {
30665431d3aSDiana Picus       // <2 x t>   vector of 2 eleTy
30765431d3aSDiana Picus       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
30865431d3aSDiana Picus     } else if (sem == &llvm::APFloat::IEEEdouble()) {
309faf869dcSDiana Picus       // Use a type that will be translated into LLVM as:
310faf869dcSDiana Picus       // { double, double }   struct of 2 double
311ff794116SSlava Zakharin       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
312ff794116SSlava Zakharin                                                 mlir::TypeRange{eleTy, eleTy}),
31365431d3aSDiana Picus                            AT{});
314f65e3af7SjeanPerier     } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
315f65e3af7SjeanPerier       // { x86_fp80, x86_fp80 }
316f65e3af7SjeanPerier       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
317f65e3af7SjeanPerier                                                 mlir::TypeRange{eleTy, eleTy}),
318f65e3af7SjeanPerier                            AT{});
3191906188fSValentin Clement     } else if (sem == &llvm::APFloat::IEEEquad()) {
3201906188fSValentin Clement       // Use a type that will be translated into LLVM as:
3211906188fSValentin Clement       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
322ff794116SSlava Zakharin       marshal.emplace_back(
323ff794116SSlava Zakharin           fir::ReferenceType::get(mlir::TupleType::get(
324ff794116SSlava Zakharin               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
3251906188fSValentin Clement           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
32665431d3aSDiana Picus     } else {
32704b18530SPete Steinfeld       typeTodo(sem, loc, "return");
32865431d3aSDiana Picus     }
32965431d3aSDiana Picus     return marshal;
33065431d3aSDiana Picus   }
33127d9a479SjeanPerier 
33227d9a479SjeanPerier   /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3.
33327d9a479SjeanPerier   enum ArgClass {
33427d9a479SjeanPerier     Integer = 0,
33527d9a479SjeanPerier     SSE,
33627d9a479SjeanPerier     SSEUp,
33727d9a479SjeanPerier     X87,
33827d9a479SjeanPerier     X87Up,
33927d9a479SjeanPerier     ComplexX87,
34027d9a479SjeanPerier     NoClass,
34127d9a479SjeanPerier     Memory
34227d9a479SjeanPerier   };
34327d9a479SjeanPerier 
34427d9a479SjeanPerier   /// Classify an argument type or a field of an aggregate type argument.
34527d9a479SjeanPerier   /// See System V ABI version 1.0 section 3.2.3.
34627d9a479SjeanPerier   /// The Lo and Hi class are set to the class of the lower eight eightbytes
34727d9a479SjeanPerier   /// and upper eight eightbytes on return.
34827d9a479SjeanPerier   /// If this is called for an aggregate field, the caller is responsible to
34927d9a479SjeanPerier   /// do the post-merge.
35027d9a479SjeanPerier   void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset,
35127d9a479SjeanPerier                 ArgClass &Lo, ArgClass &Hi) const {
35227d9a479SjeanPerier     Hi = Lo = ArgClass::NoClass;
35327d9a479SjeanPerier     ArgClass &current = byteOffset < 8 ? Lo : Hi;
35427d9a479SjeanPerier     // System V AMD64 ABI 3.2.3. version 1.0
35527d9a479SjeanPerier     llvm::TypeSwitch<mlir::Type>(type)
35627d9a479SjeanPerier         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
35727d9a479SjeanPerier           if (intTy.getWidth() == 128)
35827d9a479SjeanPerier             Hi = Lo = ArgClass::Integer;
35927d9a479SjeanPerier           else
36027d9a479SjeanPerier             current = ArgClass::Integer;
36127d9a479SjeanPerier         })
3621753de2dSjeanPerier         .template Case<mlir::FloatType>([&](mlir::Type floatTy) {
36327d9a479SjeanPerier           const auto *sem = &floatToSemantics(kindMap, floatTy);
36427d9a479SjeanPerier           if (sem == &llvm::APFloat::x87DoubleExtended()) {
36527d9a479SjeanPerier             Lo = ArgClass::X87;
36627d9a479SjeanPerier             Hi = ArgClass::X87Up;
36727d9a479SjeanPerier           } else if (sem == &llvm::APFloat::IEEEquad()) {
36827d9a479SjeanPerier             Lo = ArgClass::SSE;
36927d9a479SjeanPerier             Hi = ArgClass::SSEUp;
37027d9a479SjeanPerier           } else {
37127d9a479SjeanPerier             current = ArgClass::SSE;
37227d9a479SjeanPerier           }
37327d9a479SjeanPerier         })
374c4204c0bSjeanPerier         .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
37527d9a479SjeanPerier           const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
37627d9a479SjeanPerier           if (sem == &llvm::APFloat::x87DoubleExtended()) {
37727d9a479SjeanPerier             current = ArgClass::ComplexX87;
37827d9a479SjeanPerier           } else {
37927d9a479SjeanPerier             fir::SequenceType::Shape shape{2};
38027d9a479SjeanPerier             classifyArray(loc,
38127d9a479SjeanPerier                           fir::SequenceType::get(shape, cmplx.getElementType()),
38227d9a479SjeanPerier                           byteOffset, Lo, Hi);
38327d9a479SjeanPerier           }
38427d9a479SjeanPerier         })
38527d9a479SjeanPerier         .template Case<fir::LogicalType>([&](fir::LogicalType logical) {
38627d9a479SjeanPerier           if (kindMap.getLogicalBitsize(logical.getFKind()) == 128)
38727d9a479SjeanPerier             Hi = Lo = ArgClass::Integer;
38827d9a479SjeanPerier           else
38927d9a479SjeanPerier             current = ArgClass::Integer;
39027d9a479SjeanPerier         })
39127d9a479SjeanPerier         .template Case<fir::CharacterType>(
39227d9a479SjeanPerier             [&](fir::CharacterType character) { current = ArgClass::Integer; })
39327d9a479SjeanPerier         .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
39427d9a479SjeanPerier           // Array component.
39527d9a479SjeanPerier           classifyArray(loc, seqTy, byteOffset, Lo, Hi);
39627d9a479SjeanPerier         })
39727d9a479SjeanPerier         .template Case<fir::RecordType>([&](fir::RecordType recTy) {
39827d9a479SjeanPerier           // Component that is a derived type.
39927d9a479SjeanPerier           classifyStruct(loc, recTy, byteOffset, Lo, Hi);
40027d9a479SjeanPerier         })
40127d9a479SjeanPerier         .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
40227d9a479SjeanPerier           // Previously marshalled SSE eight byte for a previous struct
40327d9a479SjeanPerier           // argument.
40427d9a479SjeanPerier           auto *sem = fir::isa_real(vecTy.getEleTy())
40527d9a479SjeanPerier                           ? &floatToSemantics(kindMap, vecTy.getEleTy())
40627d9a479SjeanPerier                           : nullptr;
40727d9a479SjeanPerier           // Not expecting to hit this todo in standard code (it would
40827d9a479SjeanPerier           // require some vector type extension).
40927d9a479SjeanPerier           if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) &&
41027d9a479SjeanPerier               !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4))
41127d9a479SjeanPerier             TODO(loc, "passing vector argument to C by value");
41227d9a479SjeanPerier           current = SSE;
41327d9a479SjeanPerier         })
41427d9a479SjeanPerier         .Default([&](mlir::Type ty) {
41527d9a479SjeanPerier           if (fir::conformsWithPassByRef(ty))
41627d9a479SjeanPerier             current = ArgClass::Integer; // Pointers.
41727d9a479SjeanPerier           else
41827d9a479SjeanPerier             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
41927d9a479SjeanPerier                       "type argument");
42027d9a479SjeanPerier         });
42127d9a479SjeanPerier   }
42227d9a479SjeanPerier 
42327d9a479SjeanPerier   // Classify fields of a derived type starting at \p offset. Returns the new
42427d9a479SjeanPerier   // offset. Post-merge is left to the caller.
42527d9a479SjeanPerier   std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy,
42627d9a479SjeanPerier                                std::uint64_t byteOffset, ArgClass &Lo,
42727d9a479SjeanPerier                                ArgClass &Hi) const {
42827d9a479SjeanPerier     for (auto component : recTy.getTypeList()) {
42927d9a479SjeanPerier       if (byteOffset > 16) {
43027d9a479SjeanPerier         // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger
43127d9a479SjeanPerier         // than 16 bytes here, it is not a single _m256 and or _m512 entity
43227d9a479SjeanPerier         // that could fit in AVX registers.
43327d9a479SjeanPerier         Lo = Hi = ArgClass::Memory;
43427d9a479SjeanPerier         return byteOffset;
43527d9a479SjeanPerier       }
43627d9a479SjeanPerier       mlir::Type compType = component.second;
437d07dc73bSAbid Qadeer       auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash(
438d07dc73bSAbid Qadeer           loc, compType, getDataLayout(), kindMap);
43927d9a479SjeanPerier       byteOffset = llvm::alignTo(byteOffset, compAlign);
44027d9a479SjeanPerier       ArgClass LoComp, HiComp;
44127d9a479SjeanPerier       classify(loc, compType, byteOffset, LoComp, HiComp);
44227d9a479SjeanPerier       Lo = mergeClass(Lo, LoComp);
44327d9a479SjeanPerier       Hi = mergeClass(Hi, HiComp);
44427d9a479SjeanPerier       byteOffset = byteOffset + llvm::alignTo(compSize, compAlign);
44527d9a479SjeanPerier       if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
44627d9a479SjeanPerier         return byteOffset;
44727d9a479SjeanPerier     }
44827d9a479SjeanPerier     return byteOffset;
44927d9a479SjeanPerier   }
45027d9a479SjeanPerier 
45127d9a479SjeanPerier   // Classify fields of a constant size array type starting at \p offset.
45227d9a479SjeanPerier   // Returns the new offset. Post-merge is left to the caller.
45327d9a479SjeanPerier   void classifyArray(mlir::Location loc, fir::SequenceType seqTy,
45427d9a479SjeanPerier                      std::uint64_t byteOffset, ArgClass &Lo,
45527d9a479SjeanPerier                      ArgClass &Hi) const {
45627d9a479SjeanPerier     mlir::Type eleTy = seqTy.getEleTy();
45727d9a479SjeanPerier     const std::uint64_t arraySize = seqTy.getConstantArraySize();
458d07dc73bSAbid Qadeer     auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash(
459d07dc73bSAbid Qadeer         loc, eleTy, getDataLayout(), kindMap);
46027d9a479SjeanPerier     std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign);
46127d9a479SjeanPerier     for (std::uint64_t i = 0; i < arraySize; ++i) {
46227d9a479SjeanPerier       byteOffset = llvm::alignTo(byteOffset, eleAlign);
46327d9a479SjeanPerier       if (byteOffset > 16) {
46427d9a479SjeanPerier         // See 3.2.3 p. 1 and note 15. Same as in classifyStruct.
46527d9a479SjeanPerier         Lo = Hi = ArgClass::Memory;
46627d9a479SjeanPerier         return;
46727d9a479SjeanPerier       }
46827d9a479SjeanPerier       ArgClass LoComp, HiComp;
46927d9a479SjeanPerier       classify(loc, eleTy, byteOffset, LoComp, HiComp);
47027d9a479SjeanPerier       Lo = mergeClass(Lo, LoComp);
47127d9a479SjeanPerier       Hi = mergeClass(Hi, HiComp);
47227d9a479SjeanPerier       byteOffset = byteOffset + eleStorageSize;
47327d9a479SjeanPerier       if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
47427d9a479SjeanPerier         return;
47527d9a479SjeanPerier     }
47627d9a479SjeanPerier   }
47727d9a479SjeanPerier 
47827d9a479SjeanPerier   // Goes through the previously marshalled arguments and count the
47927d9a479SjeanPerier   // register occupancy to check if there are enough registers left.
48027d9a479SjeanPerier   bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters,
48127d9a479SjeanPerier                           int neededSSERegisters,
48227d9a479SjeanPerier                           const Marshalling &previousArguments) const {
48327d9a479SjeanPerier     int availIntRegisters = 6;
48427d9a479SjeanPerier     int availSSERegisters = 8;
48527d9a479SjeanPerier     for (auto typeAndAttr : previousArguments) {
48627d9a479SjeanPerier       const auto &attr = std::get<Attributes>(typeAndAttr);
48727d9a479SjeanPerier       if (attr.isByVal())
48827d9a479SjeanPerier         continue; // Previous argument passed on the stack.
48927d9a479SjeanPerier       ArgClass Lo, Hi;
49027d9a479SjeanPerier       Lo = Hi = ArgClass::NoClass;
49127d9a479SjeanPerier       classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi);
49227d9a479SjeanPerier       // post merge is not needed here since previous aggregate arguments
49327d9a479SjeanPerier       // were marshalled into simpler arguments.
49427d9a479SjeanPerier       if (Lo == ArgClass::Integer)
49527d9a479SjeanPerier         --availIntRegisters;
49627d9a479SjeanPerier       else if (Lo == SSE)
49727d9a479SjeanPerier         --availSSERegisters;
49827d9a479SjeanPerier       if (Hi == ArgClass::Integer)
49927d9a479SjeanPerier         --availIntRegisters;
50027d9a479SjeanPerier       else if (Hi == ArgClass::SSE)
50127d9a479SjeanPerier         --availSSERegisters;
50227d9a479SjeanPerier     }
50327d9a479SjeanPerier     return availSSERegisters >= neededSSERegisters &&
50427d9a479SjeanPerier            availIntRegisters >= neededIntRegisters;
50527d9a479SjeanPerier   }
50627d9a479SjeanPerier 
50727d9a479SjeanPerier   /// Argument class merging as described in System V ABI 3.2.3 point 4.
50827d9a479SjeanPerier   ArgClass mergeClass(ArgClass accum, ArgClass field) const {
50927d9a479SjeanPerier     assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) &&
51027d9a479SjeanPerier            "Invalid accumulated classification during merge.");
51127d9a479SjeanPerier     if (accum == field || field == NoClass)
51227d9a479SjeanPerier       return accum;
51327d9a479SjeanPerier     if (field == ArgClass::Memory)
51427d9a479SjeanPerier       return ArgClass::Memory;
51527d9a479SjeanPerier     if (accum == NoClass)
51627d9a479SjeanPerier       return field;
51727d9a479SjeanPerier     if (accum == Integer || field == Integer)
51827d9a479SjeanPerier       return ArgClass::Integer;
51927d9a479SjeanPerier     if (field == ArgClass::X87 || field == ArgClass::X87Up ||
52027d9a479SjeanPerier         field == ArgClass::ComplexX87 || accum == ArgClass::X87 ||
52127d9a479SjeanPerier         accum == ArgClass::X87Up)
52227d9a479SjeanPerier       return Memory;
52327d9a479SjeanPerier     return SSE;
52427d9a479SjeanPerier   }
52527d9a479SjeanPerier 
52627d9a479SjeanPerier   /// Argument class post merging as described in System V ABI 3.2.3 point 5.
52727d9a479SjeanPerier   void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const {
52827d9a479SjeanPerier     if (Hi == ArgClass::Memory)
52927d9a479SjeanPerier       Lo = ArgClass::Memory;
53027d9a479SjeanPerier     if (Hi == ArgClass::X87Up && Lo != ArgClass::X87)
53127d9a479SjeanPerier       Lo = ArgClass::Memory;
53227d9a479SjeanPerier     if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp))
53327d9a479SjeanPerier       Lo = ArgClass::Memory;
53427d9a479SjeanPerier     if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE)
53527d9a479SjeanPerier       Hi = SSE;
53627d9a479SjeanPerier   }
53727d9a479SjeanPerier 
5381d57b9a5SjeanPerier   /// When \p recTy is a one field record type that can be passed
5391d57b9a5SjeanPerier   /// like the field on its own, returns the field type. Returns
5401d57b9a5SjeanPerier   /// a null type otherwise.
541367c3c96SjeanPerier   mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy,
542367c3c96SjeanPerier                                          bool allowComplex = false) const {
5431d57b9a5SjeanPerier     auto typeList = recTy.getTypeList();
5441d57b9a5SjeanPerier     if (typeList.size() != 1)
5451d57b9a5SjeanPerier       return {};
5461d57b9a5SjeanPerier     mlir::Type fieldType = typeList[0].second;
5471753de2dSjeanPerier     if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::LogicalType>(
5481753de2dSjeanPerier             fieldType))
5491d57b9a5SjeanPerier       return fieldType;
550367c3c96SjeanPerier     if (allowComplex && mlir::isa<mlir::ComplexType>(fieldType))
551367c3c96SjeanPerier       return fieldType;
5521753de2dSjeanPerier     if (mlir::isa<fir::CharacterType>(fieldType)) {
5531753de2dSjeanPerier       // Only CHARACTER(1) are expected in BIND(C) contexts, which is the only
5541753de2dSjeanPerier       // contexts where derived type may be passed in registers.
5551753de2dSjeanPerier       assert(mlir::cast<fir::CharacterType>(fieldType).getLen() == 1 &&
5561753de2dSjeanPerier              "fir.type value arg character components must have length 1");
5571753de2dSjeanPerier       return fieldType;
5581753de2dSjeanPerier     }
5591d57b9a5SjeanPerier     // Complex field that needs to be split, or array.
5601d57b9a5SjeanPerier     return {};
5611d57b9a5SjeanPerier   }
5621d57b9a5SjeanPerier 
563011ba725SjeanPerier   mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context,
564011ba725SjeanPerier                              ArgClass argClass,
565011ba725SjeanPerier                              std::uint64_t partByteSize) const {
566011ba725SjeanPerier     if (argClass == ArgClass::SSE) {
567011ba725SjeanPerier       if (partByteSize > 16)
568011ba725SjeanPerier         TODO(loc, "passing struct as a real > 128 bits in register");
569011ba725SjeanPerier       // Clang uses vector type when several fp fields are marshalled
570011ba725SjeanPerier       // into a single SSE register (like  <n x smallest fp field> ).
571011ba725SjeanPerier       // It should make no difference from an ABI point of view to just
572011ba725SjeanPerier       // select an fp type of the right size, and it makes things simpler
573011ba725SjeanPerier       // here.
574011ba725SjeanPerier       if (partByteSize > 8)
575*f023da12SMatthias Springer         return mlir::Float128Type::get(context);
576011ba725SjeanPerier       if (partByteSize > 4)
577*f023da12SMatthias Springer         return mlir::Float64Type::get(context);
578011ba725SjeanPerier       if (partByteSize > 2)
579*f023da12SMatthias Springer         return mlir::Float32Type::get(context);
580*f023da12SMatthias Springer       return mlir::Float16Type::get(context);
581011ba725SjeanPerier     }
582011ba725SjeanPerier     assert(partByteSize <= 8 &&
583011ba725SjeanPerier            "expect integer part of aggregate argument to fit into eight bytes");
584011ba725SjeanPerier     if (partByteSize > 4)
585011ba725SjeanPerier       return mlir::IntegerType::get(context, 64);
586011ba725SjeanPerier     if (partByteSize > 2)
587011ba725SjeanPerier       return mlir::IntegerType::get(context, 32);
588011ba725SjeanPerier     if (partByteSize > 1)
589011ba725SjeanPerier       return mlir::IntegerType::get(context, 16);
590011ba725SjeanPerier     return mlir::IntegerType::get(context, 8);
591011ba725SjeanPerier   }
592011ba725SjeanPerier 
59327d9a479SjeanPerier   /// Marshal a derived type passed by value like a C struct.
59427d9a479SjeanPerier   CodeGenSpecifics::Marshalling
59527d9a479SjeanPerier   structArgumentType(mlir::Location loc, fir::RecordType recTy,
59627d9a479SjeanPerier                      const Marshalling &previousArguments) const override {
59727d9a479SjeanPerier     std::uint64_t byteOffset = 0;
59827d9a479SjeanPerier     ArgClass Lo, Hi;
59927d9a479SjeanPerier     Lo = Hi = ArgClass::NoClass;
60027d9a479SjeanPerier     byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
60127d9a479SjeanPerier     postMerge(byteOffset, Lo, Hi);
60227d9a479SjeanPerier     if (Lo == ArgClass::Memory || Lo == ArgClass::X87 ||
60327d9a479SjeanPerier         Lo == ArgClass::ComplexX87)
604367c3c96SjeanPerier       return passOnTheStack(loc, recTy, /*isResult=*/false);
60527d9a479SjeanPerier     int neededIntRegisters = 0;
60627d9a479SjeanPerier     int neededSSERegisters = 0;
60727d9a479SjeanPerier     if (Lo == ArgClass::SSE)
60827d9a479SjeanPerier       ++neededSSERegisters;
60927d9a479SjeanPerier     else if (Lo == ArgClass::Integer)
61027d9a479SjeanPerier       ++neededIntRegisters;
61127d9a479SjeanPerier     if (Hi == ArgClass::SSE)
61227d9a479SjeanPerier       ++neededSSERegisters;
61327d9a479SjeanPerier     else if (Hi == ArgClass::Integer)
61427d9a479SjeanPerier       ++neededIntRegisters;
61527d9a479SjeanPerier     // C struct should not be split into LLVM registers if LLVM codegen is not
61627d9a479SjeanPerier     // able to later assign actual registers to all of them (struct passing is
61727d9a479SjeanPerier     // all in registers or all on the stack).
61827d9a479SjeanPerier     if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters,
61927d9a479SjeanPerier                             previousArguments))
620367c3c96SjeanPerier       return passOnTheStack(loc, recTy, /*isResult=*/false);
6211d57b9a5SjeanPerier 
6221d57b9a5SjeanPerier     if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
6231d57b9a5SjeanPerier       CodeGenSpecifics::Marshalling marshal;
6241d57b9a5SjeanPerier       marshal.emplace_back(fieldType, AT{});
6251d57b9a5SjeanPerier       return marshal;
6261d57b9a5SjeanPerier     }
627011ba725SjeanPerier     if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
628011ba725SjeanPerier       // Pass a single integer or floating point argument.
629011ba725SjeanPerier       mlir::Type lowType =
630011ba725SjeanPerier           pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset);
631011ba725SjeanPerier       CodeGenSpecifics::Marshalling marshal;
632011ba725SjeanPerier       marshal.emplace_back(lowType, AT{});
633011ba725SjeanPerier       return marshal;
634011ba725SjeanPerier     }
635011ba725SjeanPerier     // Split into two integer or floating point arguments.
636011ba725SjeanPerier     // Note that for the first argument, this will always pick i64 or f64 which
637011ba725SjeanPerier     // may be bigger than needed if some struct padding ends the first eight
638011ba725SjeanPerier     // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and
639011ba725SjeanPerier     // semantic point of view, but it may not match the LLVM IR interface clang
640011ba725SjeanPerier     // would produce for the equivalent C code (the assembly will still be
641011ba725SjeanPerier     // compatible).  This allows keeping the logic simpler here since it
642011ba725SjeanPerier     // avoids computing the "data" size of the Lo part.
643011ba725SjeanPerier     mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u);
644011ba725SjeanPerier     mlir::Type hiType =
645011ba725SjeanPerier         pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u);
646011ba725SjeanPerier     CodeGenSpecifics::Marshalling marshal;
647011ba725SjeanPerier     marshal.emplace_back(lowType, AT{});
648011ba725SjeanPerier     marshal.emplace_back(hiType, AT{});
649011ba725SjeanPerier     return marshal;
65027d9a479SjeanPerier   }
65127d9a479SjeanPerier 
652367c3c96SjeanPerier   CodeGenSpecifics::Marshalling
653367c3c96SjeanPerier   structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
654367c3c96SjeanPerier     std::uint64_t byteOffset = 0;
655367c3c96SjeanPerier     ArgClass Lo, Hi;
656367c3c96SjeanPerier     Lo = Hi = ArgClass::NoClass;
657367c3c96SjeanPerier     byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
658367c3c96SjeanPerier     mlir::MLIRContext *context = recTy.getContext();
659367c3c96SjeanPerier     postMerge(byteOffset, Lo, Hi);
660367c3c96SjeanPerier     if (Lo == ArgClass::Memory)
661367c3c96SjeanPerier       return passOnTheStack(loc, recTy, /*isResult=*/true);
662367c3c96SjeanPerier 
663367c3c96SjeanPerier     // Note that X87/ComplexX87 are passed in memory, but returned via %st0
664367c3c96SjeanPerier     // %st1 registers. Here, they are returned as fp80 or {fp80, fp80} by
665367c3c96SjeanPerier     // passAsFieldIfOneFieldStruct, and LLVM will use the expected registers.
666367c3c96SjeanPerier 
667367c3c96SjeanPerier     // Note that {_Complex long double} is not 100% clear from an ABI
668367c3c96SjeanPerier     // perspective because the aggregate post merger rules say it should be
669367c3c96SjeanPerier     // passed in memory because it is bigger than 2 eight bytes. This has the
670367c3c96SjeanPerier     // funny effect of
671367c3c96SjeanPerier     // {_Complex long double} return to be dealt with differently than
672367c3c96SjeanPerier     // _Complex long double.
673367c3c96SjeanPerier 
674367c3c96SjeanPerier     if (auto fieldType =
675367c3c96SjeanPerier             passAsFieldIfOneFieldStruct(recTy, /*allowComplex=*/true)) {
676367c3c96SjeanPerier       if (auto complexType = mlir::dyn_cast<mlir::ComplexType>(fieldType))
677367c3c96SjeanPerier         return complexReturnType(loc, complexType.getElementType());
678367c3c96SjeanPerier       CodeGenSpecifics::Marshalling marshal;
679367c3c96SjeanPerier       marshal.emplace_back(fieldType, AT{});
680367c3c96SjeanPerier       return marshal;
681367c3c96SjeanPerier     }
682367c3c96SjeanPerier 
683367c3c96SjeanPerier     if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
684367c3c96SjeanPerier       // Return a single integer or floating point argument.
685367c3c96SjeanPerier       mlir::Type lowType = pickLLVMArgType(loc, context, Lo, byteOffset);
686367c3c96SjeanPerier       CodeGenSpecifics::Marshalling marshal;
687367c3c96SjeanPerier       marshal.emplace_back(lowType, AT{});
688367c3c96SjeanPerier       return marshal;
689367c3c96SjeanPerier     }
690367c3c96SjeanPerier     // Will be returned in two different registers. Generate {lowTy, HiTy} for
691367c3c96SjeanPerier     // the LLVM IR result type.
692367c3c96SjeanPerier     CodeGenSpecifics::Marshalling marshal;
693367c3c96SjeanPerier     mlir::Type lowType = pickLLVMArgType(loc, context, Lo, 8u);
694367c3c96SjeanPerier     mlir::Type hiType = pickLLVMArgType(loc, context, Hi, byteOffset - 8u);
695367c3c96SjeanPerier     marshal.emplace_back(mlir::TupleType::get(context, {lowType, hiType}),
696367c3c96SjeanPerier                          AT{});
697367c3c96SjeanPerier     return marshal;
698367c3c96SjeanPerier   }
699367c3c96SjeanPerier 
70027d9a479SjeanPerier   /// Marshal an argument that must be passed on the stack.
701367c3c96SjeanPerier   CodeGenSpecifics::Marshalling
702367c3c96SjeanPerier   passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
70327d9a479SjeanPerier     CodeGenSpecifics::Marshalling marshal;
7043e47e75fSSlava Zakharin     auto sizeAndAlign =
705d07dc73bSAbid Qadeer         fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
70627d9a479SjeanPerier     // The stack is always 8 byte aligned (note 14 in 3.2.3).
70727d9a479SjeanPerier     unsigned short align =
70827d9a479SjeanPerier         std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
70927d9a479SjeanPerier     marshal.emplace_back(fir::ReferenceType::get(ty),
710367c3c96SjeanPerier                          AT{align, /*byval=*/!isResult, /*sret=*/isResult});
71127d9a479SjeanPerier     return marshal;
71227d9a479SjeanPerier   }
7134c263edeSDiana Picus };
7144c263edeSDiana Picus } // namespace
7154c263edeSDiana Picus 
7164c263edeSDiana Picus //===----------------------------------------------------------------------===//
717774703ecSMarkus Mützel // x86_64 (x86 64 bit) Windows target specifics.
718774703ecSMarkus Mützel //===----------------------------------------------------------------------===//
719774703ecSMarkus Mützel 
720774703ecSMarkus Mützel namespace {
721774703ecSMarkus Mützel struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
722774703ecSMarkus Mützel   using GenericTarget::GenericTarget;
723774703ecSMarkus Mützel 
724774703ecSMarkus Mützel   static constexpr int defaultWidth = 64;
725774703ecSMarkus Mützel 
726774703ecSMarkus Mützel   CodeGenSpecifics::Marshalling
727774703ecSMarkus Mützel   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
728774703ecSMarkus Mützel     CodeGenSpecifics::Marshalling marshal;
729774703ecSMarkus Mützel     const auto *sem = &floatToSemantics(kindMap, eleTy);
730774703ecSMarkus Mützel     if (sem == &llvm::APFloat::IEEEsingle()) {
731774703ecSMarkus Mützel       // i64   pack both floats in a 64-bit GPR
732774703ecSMarkus Mützel       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
733774703ecSMarkus Mützel                            AT{});
734774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEdouble()) {
735774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
736774703ecSMarkus Mützel       // { double, double }   struct of 2 double, byval, align 8
737774703ecSMarkus Mützel       marshal.emplace_back(
738774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
739774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
740774703ecSMarkus Mützel           AT{/*align=*/8, /*byval=*/true});
741774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEquad() ||
742774703ecSMarkus Mützel                sem == &llvm::APFloat::x87DoubleExtended()) {
743774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
744774703ecSMarkus Mützel       // { t, t }   struct of 2 eleTy, byval, align 16
745774703ecSMarkus Mützel       marshal.emplace_back(
746774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
747774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
748774703ecSMarkus Mützel           AT{/*align=*/16, /*byval=*/true});
749774703ecSMarkus Mützel     } else {
75004b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
751774703ecSMarkus Mützel     }
752774703ecSMarkus Mützel     return marshal;
753774703ecSMarkus Mützel   }
754774703ecSMarkus Mützel 
755774703ecSMarkus Mützel   CodeGenSpecifics::Marshalling
756774703ecSMarkus Mützel   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
757774703ecSMarkus Mützel     CodeGenSpecifics::Marshalling marshal;
758774703ecSMarkus Mützel     const auto *sem = &floatToSemantics(kindMap, eleTy);
759774703ecSMarkus Mützel     if (sem == &llvm::APFloat::IEEEsingle()) {
760774703ecSMarkus Mützel       // i64   pack both floats in a 64-bit GPR
761774703ecSMarkus Mützel       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
762774703ecSMarkus Mützel                            AT{});
763774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEdouble()) {
764774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
765774703ecSMarkus Mützel       // { double, double }   struct of 2 double, sret, align 8
766774703ecSMarkus Mützel       marshal.emplace_back(
767774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
768774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
769774703ecSMarkus Mützel           AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
770774703ecSMarkus Mützel     } else if (sem == &llvm::APFloat::IEEEquad() ||
771774703ecSMarkus Mützel                sem == &llvm::APFloat::x87DoubleExtended()) {
772774703ecSMarkus Mützel       // Use a type that will be translated into LLVM as:
773774703ecSMarkus Mützel       // { t, t }   struct of 2 eleTy, sret, align 16
774774703ecSMarkus Mützel       marshal.emplace_back(
775774703ecSMarkus Mützel           fir::ReferenceType::get(mlir::TupleType::get(
776774703ecSMarkus Mützel               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
777774703ecSMarkus Mützel           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
778774703ecSMarkus Mützel     } else {
77904b18530SPete Steinfeld       typeTodo(sem, loc, "return");
780774703ecSMarkus Mützel     }
781774703ecSMarkus Mützel     return marshal;
782774703ecSMarkus Mützel   }
783774703ecSMarkus Mützel };
784774703ecSMarkus Mützel } // namespace
785774703ecSMarkus Mützel 
786774703ecSMarkus Mützel //===----------------------------------------------------------------------===//
7874c263edeSDiana Picus // AArch64 linux target specifics.
7884c263edeSDiana Picus //===----------------------------------------------------------------------===//
7894c263edeSDiana Picus 
7904c263edeSDiana Picus namespace {
79144aa476aSDavid Truby // AArch64 procedure call standard:
79244aa476aSDavid Truby // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing
7934c263edeSDiana Picus struct TargetAArch64 : public GenericTarget<TargetAArch64> {
7944c263edeSDiana Picus   using GenericTarget::GenericTarget;
7954c263edeSDiana Picus 
7964c263edeSDiana Picus   static constexpr int defaultWidth = 64;
79765431d3aSDiana Picus 
79865431d3aSDiana Picus   CodeGenSpecifics::Marshalling
7990c1cf585SEric Schweitz   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
80065431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
80165431d3aSDiana Picus     const auto *sem = &floatToSemantics(kindMap, eleTy);
80265431d3aSDiana Picus     if (sem == &llvm::APFloat::IEEEsingle() ||
803d7428722SKiran Chandramohan         sem == &llvm::APFloat::IEEEdouble() ||
804d7428722SKiran Chandramohan         sem == &llvm::APFloat::IEEEquad()) {
80565431d3aSDiana Picus       // [2 x t]   array of 2 eleTy
80665431d3aSDiana Picus       marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
80765431d3aSDiana Picus     } else {
80804b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
80965431d3aSDiana Picus     }
81065431d3aSDiana Picus     return marshal;
81165431d3aSDiana Picus   }
81265431d3aSDiana Picus 
81365431d3aSDiana Picus   CodeGenSpecifics::Marshalling
8140c1cf585SEric Schweitz   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
81565431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
81665431d3aSDiana Picus     const auto *sem = &floatToSemantics(kindMap, eleTy);
81765431d3aSDiana Picus     if (sem == &llvm::APFloat::IEEEsingle() ||
818d7428722SKiran Chandramohan         sem == &llvm::APFloat::IEEEdouble() ||
819d7428722SKiran Chandramohan         sem == &llvm::APFloat::IEEEquad()) {
820faf869dcSDiana Picus       // Use a type that will be translated into LLVM as:
821faf869dcSDiana Picus       // { t, t }   struct of 2 eleTy
822ff794116SSlava Zakharin       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
823ff794116SSlava Zakharin                                                 mlir::TypeRange{eleTy, eleTy}),
82465431d3aSDiana Picus                            AT{});
82565431d3aSDiana Picus     } else {
82604b18530SPete Steinfeld       typeTodo(sem, loc, "return");
82765431d3aSDiana Picus     }
82865431d3aSDiana Picus     return marshal;
82965431d3aSDiana Picus   }
8302d62daabSDavid Truby 
83144aa476aSDavid Truby   // Flatten a RecordType::TypeList containing more record types or array type
8322d62daabSDavid Truby   static std::optional<std::vector<mlir::Type>>
8332d62daabSDavid Truby   flattenTypeList(const RecordType::TypeList &types) {
8342d62daabSDavid Truby     std::vector<mlir::Type> flatTypes;
8352d62daabSDavid Truby     // The flat list will be at least the same size as the non-flat list.
8362d62daabSDavid Truby     flatTypes.reserve(types.size());
8372d62daabSDavid Truby     for (auto [c, type] : types) {
8382d62daabSDavid Truby       // Flatten record type
8392d62daabSDavid Truby       if (auto recTy = mlir::dyn_cast<RecordType>(type)) {
8402d62daabSDavid Truby         auto subTypeList = flattenTypeList(recTy.getTypeList());
8412d62daabSDavid Truby         if (!subTypeList)
8422d62daabSDavid Truby           return std::nullopt;
8432d62daabSDavid Truby         llvm::copy(*subTypeList, std::back_inserter(flatTypes));
8442d62daabSDavid Truby         continue;
8452d62daabSDavid Truby       }
8462d62daabSDavid Truby 
8472d62daabSDavid Truby       // Flatten array type
8482d62daabSDavid Truby       if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {
8492d62daabSDavid Truby         if (seqTy.hasDynamicExtents())
8502d62daabSDavid Truby           return std::nullopt;
8512d62daabSDavid Truby         std::size_t n = seqTy.getConstantArraySize();
8522d62daabSDavid Truby         auto eleTy = seqTy.getElementType();
8532d62daabSDavid Truby         // Flatten array of record types
8542d62daabSDavid Truby         if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) {
8552d62daabSDavid Truby           auto subTypeList = flattenTypeList(recTy.getTypeList());
8562d62daabSDavid Truby           if (!subTypeList)
8572d62daabSDavid Truby             return std::nullopt;
8582d62daabSDavid Truby           for (std::size_t i = 0; i < n; ++i)
8592d62daabSDavid Truby             llvm::copy(*subTypeList, std::back_inserter(flatTypes));
8602d62daabSDavid Truby         } else {
8612d62daabSDavid Truby           std::fill_n(std::back_inserter(flatTypes),
8622d62daabSDavid Truby                       seqTy.getConstantArraySize(), eleTy);
8632d62daabSDavid Truby         }
8642d62daabSDavid Truby         continue;
8652d62daabSDavid Truby       }
8662d62daabSDavid Truby 
8672d62daabSDavid Truby       // Other types are already flat
8682d62daabSDavid Truby       flatTypes.push_back(type);
8692d62daabSDavid Truby     }
8702d62daabSDavid Truby     return flatTypes;
8712d62daabSDavid Truby   }
8722d62daabSDavid Truby 
8732d62daabSDavid Truby   // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An
8742d62daabSDavid Truby   // HFA is a record type with up to 4 floating-point members of the same type.
87544aa476aSDavid Truby   static std::optional<int> usedRegsForHFA(fir::RecordType ty) {
8762d62daabSDavid Truby     RecordType::TypeList types = ty.getTypeList();
8772d62daabSDavid Truby     if (types.empty() || types.size() > 4)
87844aa476aSDavid Truby       return std::nullopt;
8792d62daabSDavid Truby 
8802d62daabSDavid Truby     std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);
8812d62daabSDavid Truby     if (!flatTypes || flatTypes->size() > 4) {
88244aa476aSDavid Truby       return std::nullopt;
8832d62daabSDavid Truby     }
8842d62daabSDavid Truby 
8852d62daabSDavid Truby     if (!isa_real(flatTypes->front())) {
88644aa476aSDavid Truby       return std::nullopt;
8872d62daabSDavid Truby     }
8882d62daabSDavid Truby 
88944aa476aSDavid Truby     return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()}
89044aa476aSDavid Truby                                        : std::nullopt;
8912d62daabSDavid Truby   }
8922d62daabSDavid Truby 
89344aa476aSDavid Truby   struct NRegs {
89444aa476aSDavid Truby     int n{0};
89544aa476aSDavid Truby     bool isSimd{false};
89644aa476aSDavid Truby   };
89744aa476aSDavid Truby 
89844aa476aSDavid Truby   NRegs usedRegsForRecordType(mlir::Location loc, fir::RecordType type) const {
89944aa476aSDavid Truby     if (std::optional<int> size = usedRegsForHFA(type))
90044aa476aSDavid Truby       return {*size, true};
90144aa476aSDavid Truby 
90244aa476aSDavid Truby     auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash(
90344aa476aSDavid Truby         loc, type, getDataLayout(), kindMap);
90444aa476aSDavid Truby 
90544aa476aSDavid Truby     if (size <= 16)
90644aa476aSDavid Truby       return {static_cast<int>((size + 7) / 8), false};
90744aa476aSDavid Truby 
90844aa476aSDavid Truby     // Pass on the stack, i.e. no registers used
90944aa476aSDavid Truby     return {};
91044aa476aSDavid Truby   }
91144aa476aSDavid Truby 
91244aa476aSDavid Truby   NRegs usedRegsForType(mlir::Location loc, mlir::Type type) const {
91344aa476aSDavid Truby     return llvm::TypeSwitch<mlir::Type, NRegs>(type)
91444aa476aSDavid Truby         .Case<mlir::IntegerType>([&](auto intTy) {
91544aa476aSDavid Truby           return intTy.getWidth() == 128 ? NRegs{2, false} : NRegs{1, false};
91644aa476aSDavid Truby         })
91744aa476aSDavid Truby         .Case<mlir::FloatType>([&](auto) { return NRegs{1, true}; })
91844aa476aSDavid Truby         .Case<mlir::ComplexType>([&](auto) { return NRegs{2, true}; })
91944aa476aSDavid Truby         .Case<fir::LogicalType>([&](auto) { return NRegs{1, false}; })
92044aa476aSDavid Truby         .Case<fir::CharacterType>([&](auto) { return NRegs{1, false}; })
92144aa476aSDavid Truby         .Case<fir::SequenceType>([&](auto ty) {
92244aa476aSDavid Truby           assert(ty.getShape().size() == 1 &&
92344aa476aSDavid Truby                  "invalid array dimensions in BIND(C)");
92444aa476aSDavid Truby           NRegs nregs = usedRegsForType(loc, ty.getEleTy());
92544aa476aSDavid Truby           nregs.n *= ty.getShape()[0];
92644aa476aSDavid Truby           return nregs;
92744aa476aSDavid Truby         })
92844aa476aSDavid Truby         .Case<fir::RecordType>(
92944aa476aSDavid Truby             [&](auto ty) { return usedRegsForRecordType(loc, ty); })
93044aa476aSDavid Truby         .Case<fir::VectorType>([&](auto) {
93144aa476aSDavid Truby           TODO(loc, "passing vector argument to C by value is not supported");
93244aa476aSDavid Truby           return NRegs{};
93344aa476aSDavid Truby         });
93444aa476aSDavid Truby   }
93544aa476aSDavid Truby 
93644aa476aSDavid Truby   bool hasEnoughRegisters(mlir::Location loc, fir::RecordType type,
93744aa476aSDavid Truby                           const Marshalling &previousArguments) const {
93844aa476aSDavid Truby     int availIntRegisters = 8;
93944aa476aSDavid Truby     int availSIMDRegisters = 8;
94044aa476aSDavid Truby 
94144aa476aSDavid Truby     // Check previous arguments to see how many registers are used already
94244aa476aSDavid Truby     for (auto [type, attr] : previousArguments) {
94344aa476aSDavid Truby       if (availIntRegisters <= 0 || availSIMDRegisters <= 0)
94444aa476aSDavid Truby         break;
94544aa476aSDavid Truby 
94644aa476aSDavid Truby       if (attr.isByVal())
94744aa476aSDavid Truby         continue; // Previous argument passed on the stack
94844aa476aSDavid Truby 
94944aa476aSDavid Truby       NRegs nregs = usedRegsForType(loc, type);
95044aa476aSDavid Truby       if (nregs.isSimd)
95144aa476aSDavid Truby         availSIMDRegisters -= nregs.n;
95244aa476aSDavid Truby       else
95344aa476aSDavid Truby         availIntRegisters -= nregs.n;
95444aa476aSDavid Truby     }
95544aa476aSDavid Truby 
95644aa476aSDavid Truby     NRegs nregs = usedRegsForRecordType(loc, type);
95744aa476aSDavid Truby 
95844aa476aSDavid Truby     if (nregs.isSimd)
95944aa476aSDavid Truby       return nregs.n <= availSIMDRegisters;
96044aa476aSDavid Truby 
96144aa476aSDavid Truby     return nregs.n <= availIntRegisters;
96244aa476aSDavid Truby   }
96344aa476aSDavid Truby 
9642d62daabSDavid Truby   CodeGenSpecifics::Marshalling
96544aa476aSDavid Truby   passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
96644aa476aSDavid Truby     CodeGenSpecifics::Marshalling marshal;
96744aa476aSDavid Truby     auto sizeAndAlign =
96844aa476aSDavid Truby         fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
96944aa476aSDavid Truby     // The stack is always 8 byte aligned
97044aa476aSDavid Truby     unsigned short align =
97144aa476aSDavid Truby         std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
97244aa476aSDavid Truby     marshal.emplace_back(fir::ReferenceType::get(ty),
97344aa476aSDavid Truby                          AT{align, /*byval=*/!isResult, /*sret=*/isResult});
97444aa476aSDavid Truby     return marshal;
97544aa476aSDavid Truby   }
97644aa476aSDavid Truby 
97744aa476aSDavid Truby   CodeGenSpecifics::Marshalling
97844aa476aSDavid Truby   structType(mlir::Location loc, fir::RecordType type, bool isResult) const {
97944aa476aSDavid Truby     NRegs nregs = usedRegsForRecordType(loc, type);
98044aa476aSDavid Truby 
98144aa476aSDavid Truby     // If the type needs no registers it must need to be passed on the stack
98244aa476aSDavid Truby     if (nregs.n == 0)
98344aa476aSDavid Truby       return passOnTheStack(loc, type, isResult);
98444aa476aSDavid Truby 
9852d62daabSDavid Truby     CodeGenSpecifics::Marshalling marshal;
9862d62daabSDavid Truby 
98744aa476aSDavid Truby     mlir::Type pcsType;
98844aa476aSDavid Truby     if (nregs.isSimd) {
98944aa476aSDavid Truby       pcsType = type;
99044aa476aSDavid Truby     } else {
99144aa476aSDavid Truby       pcsType = fir::SequenceType::get(
99244aa476aSDavid Truby           nregs.n, mlir::IntegerType::get(type.getContext(), 64));
99344aa476aSDavid Truby     }
99444aa476aSDavid Truby 
99544aa476aSDavid Truby     marshal.emplace_back(pcsType, AT{});
9962d62daabSDavid Truby     return marshal;
9972d62daabSDavid Truby   }
9982d62daabSDavid Truby 
99944aa476aSDavid Truby   CodeGenSpecifics::Marshalling
100044aa476aSDavid Truby   structArgumentType(mlir::Location loc, fir::RecordType ty,
100144aa476aSDavid Truby                      const Marshalling &previousArguments) const override {
100244aa476aSDavid Truby     if (!hasEnoughRegisters(loc, ty, previousArguments)) {
100344aa476aSDavid Truby       return passOnTheStack(loc, ty, /*isResult=*/false);
10042d62daabSDavid Truby     }
10052d62daabSDavid Truby 
100644aa476aSDavid Truby     return structType(loc, ty, /*isResult=*/false);
100744aa476aSDavid Truby   }
100844aa476aSDavid Truby 
100944aa476aSDavid Truby   CodeGenSpecifics::Marshalling
101044aa476aSDavid Truby   structReturnType(mlir::Location loc, fir::RecordType ty) const override {
101144aa476aSDavid Truby     return structType(loc, ty, /*isResult=*/true);
10122d62daabSDavid Truby   }
10134c263edeSDiana Picus };
10144c263edeSDiana Picus } // namespace
10154c263edeSDiana Picus 
10164c263edeSDiana Picus //===----------------------------------------------------------------------===//
1017320fbff4SMark Danial // PPC64 (AIX 64 bit) target specifics.
1018320fbff4SMark Danial //===----------------------------------------------------------------------===//
1019320fbff4SMark Danial 
1020320fbff4SMark Danial namespace {
1021320fbff4SMark Danial struct TargetPPC64 : public GenericTarget<TargetPPC64> {
1022320fbff4SMark Danial   using GenericTarget::GenericTarget;
1023320fbff4SMark Danial 
1024320fbff4SMark Danial   static constexpr int defaultWidth = 64;
1025320fbff4SMark Danial 
1026320fbff4SMark Danial   CodeGenSpecifics::Marshalling
1027320fbff4SMark Danial   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
1028320fbff4SMark Danial     CodeGenSpecifics::Marshalling marshal;
1029320fbff4SMark Danial     // two distinct element type arguments (re, im)
1030320fbff4SMark Danial     marshal.emplace_back(eleTy, AT{});
1031320fbff4SMark Danial     marshal.emplace_back(eleTy, AT{});
1032320fbff4SMark Danial     return marshal;
1033320fbff4SMark Danial   }
1034320fbff4SMark Danial 
1035320fbff4SMark Danial   CodeGenSpecifics::Marshalling
1036320fbff4SMark Danial   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
1037320fbff4SMark Danial     CodeGenSpecifics::Marshalling marshal;
1038320fbff4SMark Danial     // Use a type that will be translated into LLVM as:
1039320fbff4SMark Danial     // { t, t }   struct of 2 element type
1040ff794116SSlava Zakharin     marshal.emplace_back(
1041ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1042ff794116SSlava Zakharin         AT{});
1043320fbff4SMark Danial     return marshal;
1044320fbff4SMark Danial   }
1045320fbff4SMark Danial };
1046320fbff4SMark Danial } // namespace
1047320fbff4SMark Danial 
1048320fbff4SMark Danial //===----------------------------------------------------------------------===//
10494c263edeSDiana Picus // PPC64le linux target specifics.
10504c263edeSDiana Picus //===----------------------------------------------------------------------===//
10514c263edeSDiana Picus 
10524c263edeSDiana Picus namespace {
10534c263edeSDiana Picus struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
10544c263edeSDiana Picus   using GenericTarget::GenericTarget;
10554c263edeSDiana Picus 
10564c263edeSDiana Picus   static constexpr int defaultWidth = 64;
105765431d3aSDiana Picus 
105865431d3aSDiana Picus   CodeGenSpecifics::Marshalling
10590c1cf585SEric Schweitz   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
106065431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
106165431d3aSDiana Picus     // two distinct element type arguments (re, im)
106265431d3aSDiana Picus     marshal.emplace_back(eleTy, AT{});
106365431d3aSDiana Picus     marshal.emplace_back(eleTy, AT{});
106465431d3aSDiana Picus     return marshal;
106565431d3aSDiana Picus   }
106665431d3aSDiana Picus 
106765431d3aSDiana Picus   CodeGenSpecifics::Marshalling
10680c1cf585SEric Schweitz   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
106965431d3aSDiana Picus     CodeGenSpecifics::Marshalling marshal;
1070faf869dcSDiana Picus     // Use a type that will be translated into LLVM as:
1071faf869dcSDiana Picus     // { t, t }   struct of 2 element type
1072ff794116SSlava Zakharin     marshal.emplace_back(
1073ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1074ff794116SSlava Zakharin         AT{});
107565431d3aSDiana Picus     return marshal;
107665431d3aSDiana Picus   }
10774c263edeSDiana Picus };
10784c263edeSDiana Picus } // namespace
10794c263edeSDiana Picus 
10801774a8a7SRainer Orth //===----------------------------------------------------------------------===//
10811774a8a7SRainer Orth // sparc (sparc 32 bit) target specifics.
10821774a8a7SRainer Orth //===----------------------------------------------------------------------===//
10831774a8a7SRainer Orth 
10841774a8a7SRainer Orth namespace {
10851774a8a7SRainer Orth struct TargetSparc : public GenericTarget<TargetSparc> {
10861774a8a7SRainer Orth   using GenericTarget::GenericTarget;
10871774a8a7SRainer Orth 
10881774a8a7SRainer Orth   static constexpr int defaultWidth = 32;
10891774a8a7SRainer Orth 
10901774a8a7SRainer Orth   CodeGenSpecifics::Marshalling
10911774a8a7SRainer Orth   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
10921774a8a7SRainer Orth     assert(fir::isa_real(eleTy));
10931774a8a7SRainer Orth     CodeGenSpecifics::Marshalling marshal;
10941774a8a7SRainer Orth     // Use a type that will be translated into LLVM as:
10951774a8a7SRainer Orth     // { t, t }   struct of 2 eleTy
1096ff794116SSlava Zakharin     auto structTy =
1097ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
10981774a8a7SRainer Orth     marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
10991774a8a7SRainer Orth     return marshal;
11001774a8a7SRainer Orth   }
11011774a8a7SRainer Orth 
11021774a8a7SRainer Orth   CodeGenSpecifics::Marshalling
11031774a8a7SRainer Orth   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
11041774a8a7SRainer Orth     assert(fir::isa_real(eleTy));
11051774a8a7SRainer Orth     CodeGenSpecifics::Marshalling marshal;
11061774a8a7SRainer Orth     // Use a type that will be translated into LLVM as:
11071774a8a7SRainer Orth     // { t, t }   struct of 2 eleTy, byval
1108ff794116SSlava Zakharin     auto structTy =
1109ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
11101774a8a7SRainer Orth     marshal.emplace_back(fir::ReferenceType::get(structTy),
11111774a8a7SRainer Orth                          AT{/*alignment=*/0, /*byval=*/true});
11121774a8a7SRainer Orth     return marshal;
11131774a8a7SRainer Orth   }
11141774a8a7SRainer Orth };
11151774a8a7SRainer Orth } // namespace
11161774a8a7SRainer Orth 
11171774a8a7SRainer Orth //===----------------------------------------------------------------------===//
11181774a8a7SRainer Orth // sparcv9 (sparc 64 bit) target specifics.
11191774a8a7SRainer Orth //===----------------------------------------------------------------------===//
11201774a8a7SRainer Orth 
11211774a8a7SRainer Orth namespace {
11221774a8a7SRainer Orth struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
11231774a8a7SRainer Orth   using GenericTarget::GenericTarget;
11241774a8a7SRainer Orth 
11251774a8a7SRainer Orth   static constexpr int defaultWidth = 64;
11261774a8a7SRainer Orth 
11271774a8a7SRainer Orth   CodeGenSpecifics::Marshalling
11281774a8a7SRainer Orth   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
11291774a8a7SRainer Orth     CodeGenSpecifics::Marshalling marshal;
11301774a8a7SRainer Orth     const auto *sem = &floatToSemantics(kindMap, eleTy);
11311774a8a7SRainer Orth     if (sem == &llvm::APFloat::IEEEsingle() ||
11321774a8a7SRainer Orth         sem == &llvm::APFloat::IEEEdouble()) {
11331774a8a7SRainer Orth       // two distinct float, double arguments
11341774a8a7SRainer Orth       marshal.emplace_back(eleTy, AT{});
11351774a8a7SRainer Orth       marshal.emplace_back(eleTy, AT{});
11361774a8a7SRainer Orth     } else if (sem == &llvm::APFloat::IEEEquad()) {
11371774a8a7SRainer Orth       // Use a type that will be translated into LLVM as:
11381774a8a7SRainer Orth       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
1139ff794116SSlava Zakharin       marshal.emplace_back(
1140ff794116SSlava Zakharin           fir::ReferenceType::get(mlir::TupleType::get(
1141ff794116SSlava Zakharin               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
11421774a8a7SRainer Orth           AT{/*align=*/16, /*byval=*/true});
11431774a8a7SRainer Orth     } else {
114404b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
11451774a8a7SRainer Orth     }
11461774a8a7SRainer Orth     return marshal;
11471774a8a7SRainer Orth   }
11481774a8a7SRainer Orth 
11491774a8a7SRainer Orth   CodeGenSpecifics::Marshalling
11501774a8a7SRainer Orth   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
11511774a8a7SRainer Orth     CodeGenSpecifics::Marshalling marshal;
11521774a8a7SRainer Orth     // Use a type that will be translated into LLVM as:
11531774a8a7SRainer Orth     // { eleTy, eleTy }   struct of 2 eleTy
1154ff794116SSlava Zakharin     marshal.emplace_back(
1155ff794116SSlava Zakharin         mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1156ff794116SSlava Zakharin         AT{});
11571774a8a7SRainer Orth     return marshal;
11581774a8a7SRainer Orth   }
11591774a8a7SRainer Orth };
11601774a8a7SRainer Orth } // namespace
11611774a8a7SRainer Orth 
1162bac88e89SQihan Cai //===----------------------------------------------------------------------===//
1163bac88e89SQihan Cai // RISCV64 linux target specifics.
1164bac88e89SQihan Cai //===----------------------------------------------------------------------===//
1165bac88e89SQihan Cai 
1166bac88e89SQihan Cai namespace {
1167bac88e89SQihan Cai struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
1168bac88e89SQihan Cai   using GenericTarget::GenericTarget;
1169bac88e89SQihan Cai 
1170bac88e89SQihan Cai   static constexpr int defaultWidth = 64;
1171bac88e89SQihan Cai 
1172bac88e89SQihan Cai   CodeGenSpecifics::Marshalling
1173bac88e89SQihan Cai   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1174bac88e89SQihan Cai     CodeGenSpecifics::Marshalling marshal;
1175bac88e89SQihan Cai     const auto *sem = &floatToSemantics(kindMap, eleTy);
1176bac88e89SQihan Cai     if (sem == &llvm::APFloat::IEEEsingle() ||
1177bac88e89SQihan Cai         sem == &llvm::APFloat::IEEEdouble()) {
1178bac88e89SQihan Cai       // Two distinct element type arguments (re, im)
1179bac88e89SQihan Cai       marshal.emplace_back(eleTy, AT{});
1180bac88e89SQihan Cai       marshal.emplace_back(eleTy, AT{});
1181bac88e89SQihan Cai     } else {
118204b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
1183bac88e89SQihan Cai     }
1184bac88e89SQihan Cai     return marshal;
1185bac88e89SQihan Cai   }
1186bac88e89SQihan Cai 
1187bac88e89SQihan Cai   CodeGenSpecifics::Marshalling
1188bac88e89SQihan Cai   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1189bac88e89SQihan Cai     CodeGenSpecifics::Marshalling marshal;
1190bac88e89SQihan Cai     const auto *sem = &floatToSemantics(kindMap, eleTy);
1191bac88e89SQihan Cai     if (sem == &llvm::APFloat::IEEEsingle() ||
1192bac88e89SQihan Cai         sem == &llvm::APFloat::IEEEdouble()) {
1193bac88e89SQihan Cai       // Use a type that will be translated into LLVM as:
1194bac88e89SQihan Cai       // { t, t }   struct of 2 eleTy, byVal
1195ff794116SSlava Zakharin       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1196ff794116SSlava Zakharin                                                 mlir::TypeRange{eleTy, eleTy}),
1197bac88e89SQihan Cai                            AT{/*alignment=*/0, /*byval=*/true});
1198bac88e89SQihan Cai     } else {
119904b18530SPete Steinfeld       typeTodo(sem, loc, "return");
1200bac88e89SQihan Cai     }
1201bac88e89SQihan Cai     return marshal;
1202bac88e89SQihan Cai   }
1203bac88e89SQihan Cai };
1204bac88e89SQihan Cai } // namespace
1205bac88e89SQihan Cai 
120608749a91SJan Sjodin //===----------------------------------------------------------------------===//
120708749a91SJan Sjodin // AMDGPU linux target specifics.
120808749a91SJan Sjodin //===----------------------------------------------------------------------===//
120908749a91SJan Sjodin 
121008749a91SJan Sjodin namespace {
121108749a91SJan Sjodin struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {
121208749a91SJan Sjodin   using GenericTarget::GenericTarget;
121308749a91SJan Sjodin 
121408749a91SJan Sjodin   // Default size (in bits) of the index type for strings.
121508749a91SJan Sjodin   static constexpr int defaultWidth = 64;
121608749a91SJan Sjodin 
121708749a91SJan Sjodin   CodeGenSpecifics::Marshalling
121808749a91SJan Sjodin   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
121908749a91SJan Sjodin     CodeGenSpecifics::Marshalling marshal;
122008749a91SJan Sjodin     TODO(loc, "handle complex argument types");
122108749a91SJan Sjodin     return marshal;
122208749a91SJan Sjodin   }
122308749a91SJan Sjodin 
122408749a91SJan Sjodin   CodeGenSpecifics::Marshalling
122508749a91SJan Sjodin   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
122608749a91SJan Sjodin     CodeGenSpecifics::Marshalling marshal;
122708749a91SJan Sjodin     TODO(loc, "handle complex return types");
122808749a91SJan Sjodin     return marshal;
122908749a91SJan Sjodin   }
123008749a91SJan Sjodin };
123108749a91SJan Sjodin } // namespace
123208749a91SJan Sjodin 
1233720f728dSWeining Lu //===----------------------------------------------------------------------===//
1234be9fa9deSFabian Mora // NVPTX linux target specifics.
1235be9fa9deSFabian Mora //===----------------------------------------------------------------------===//
1236be9fa9deSFabian Mora 
1237be9fa9deSFabian Mora namespace {
1238be9fa9deSFabian Mora struct TargetNVPTX : public GenericTarget<TargetNVPTX> {
1239be9fa9deSFabian Mora   using GenericTarget::GenericTarget;
1240be9fa9deSFabian Mora 
1241be9fa9deSFabian Mora   // Default size (in bits) of the index type for strings.
1242be9fa9deSFabian Mora   static constexpr int defaultWidth = 64;
1243be9fa9deSFabian Mora 
1244be9fa9deSFabian Mora   CodeGenSpecifics::Marshalling
1245be9fa9deSFabian Mora   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1246be9fa9deSFabian Mora     CodeGenSpecifics::Marshalling marshal;
1247be9fa9deSFabian Mora     TODO(loc, "handle complex argument types");
1248be9fa9deSFabian Mora     return marshal;
1249be9fa9deSFabian Mora   }
1250be9fa9deSFabian Mora 
1251be9fa9deSFabian Mora   CodeGenSpecifics::Marshalling
1252be9fa9deSFabian Mora   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1253be9fa9deSFabian Mora     CodeGenSpecifics::Marshalling marshal;
1254be9fa9deSFabian Mora     TODO(loc, "handle complex return types");
1255be9fa9deSFabian Mora     return marshal;
1256be9fa9deSFabian Mora   }
1257be9fa9deSFabian Mora };
1258be9fa9deSFabian Mora } // namespace
1259be9fa9deSFabian Mora 
1260be9fa9deSFabian Mora //===----------------------------------------------------------------------===//
1261720f728dSWeining Lu // LoongArch64 linux target specifics.
1262720f728dSWeining Lu //===----------------------------------------------------------------------===//
1263720f728dSWeining Lu 
1264720f728dSWeining Lu namespace {
1265720f728dSWeining Lu struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
1266720f728dSWeining Lu   using GenericTarget::GenericTarget;
1267720f728dSWeining Lu 
1268720f728dSWeining Lu   static constexpr int defaultWidth = 64;
1269dab9fa2dSZhaoxin Yang   static constexpr int GRLen = defaultWidth; /* eight bytes */
1270dab9fa2dSZhaoxin Yang   static constexpr int GRLenInChar = GRLen / 8;
1271dab9fa2dSZhaoxin Yang   static constexpr int FRLen = defaultWidth; /* eight bytes */
1272720f728dSWeining Lu 
1273720f728dSWeining Lu   CodeGenSpecifics::Marshalling
1274720f728dSWeining Lu   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1275720f728dSWeining Lu     CodeGenSpecifics::Marshalling marshal;
1276720f728dSWeining Lu     const auto *sem = &floatToSemantics(kindMap, eleTy);
1277720f728dSWeining Lu     if (sem == &llvm::APFloat::IEEEsingle() ||
1278720f728dSWeining Lu         sem == &llvm::APFloat::IEEEdouble()) {
1279720f728dSWeining Lu       // Two distinct element type arguments (re, im)
1280720f728dSWeining Lu       marshal.emplace_back(eleTy, AT{});
1281720f728dSWeining Lu       marshal.emplace_back(eleTy, AT{});
128220b442a2SZhaoxin Yang     } else if (sem == &llvm::APFloat::IEEEquad()) {
128320b442a2SZhaoxin Yang       // Use a type that will be translated into LLVM as:
128420b442a2SZhaoxin Yang       // { fp128, fp128 }   struct of 2 fp128, byval
128520b442a2SZhaoxin Yang       marshal.emplace_back(
128620b442a2SZhaoxin Yang           fir::ReferenceType::get(mlir::TupleType::get(
128720b442a2SZhaoxin Yang               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
128820b442a2SZhaoxin Yang           AT{/*align=*/16, /*byval=*/true});
1289720f728dSWeining Lu     } else {
129004b18530SPete Steinfeld       typeTodo(sem, loc, "argument");
1291720f728dSWeining Lu     }
1292720f728dSWeining Lu     return marshal;
1293720f728dSWeining Lu   }
1294720f728dSWeining Lu 
1295720f728dSWeining Lu   CodeGenSpecifics::Marshalling
1296720f728dSWeining Lu   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1297720f728dSWeining Lu     CodeGenSpecifics::Marshalling marshal;
1298720f728dSWeining Lu     const auto *sem = &floatToSemantics(kindMap, eleTy);
1299720f728dSWeining Lu     if (sem == &llvm::APFloat::IEEEsingle() ||
1300720f728dSWeining Lu         sem == &llvm::APFloat::IEEEdouble()) {
1301720f728dSWeining Lu       // Use a type that will be translated into LLVM as:
1302720f728dSWeining Lu       // { t, t }   struct of 2 eleTy, byVal
1303720f728dSWeining Lu       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1304720f728dSWeining Lu                                                 mlir::TypeRange{eleTy, eleTy}),
1305720f728dSWeining Lu                            AT{/*alignment=*/0, /*byval=*/true});
130620b442a2SZhaoxin Yang     } else if (sem == &llvm::APFloat::IEEEquad()) {
130720b442a2SZhaoxin Yang       // Use a type that will be translated into LLVM as:
130820b442a2SZhaoxin Yang       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
130920b442a2SZhaoxin Yang       marshal.emplace_back(
131020b442a2SZhaoxin Yang           fir::ReferenceType::get(mlir::TupleType::get(
131120b442a2SZhaoxin Yang               eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
131220b442a2SZhaoxin Yang           AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
1313720f728dSWeining Lu     } else {
131404b18530SPete Steinfeld       typeTodo(sem, loc, "return");
1315720f728dSWeining Lu     }
1316720f728dSWeining Lu     return marshal;
1317720f728dSWeining Lu   }
1318b24acc06SZhaoxin Yang 
1319b24acc06SZhaoxin Yang   CodeGenSpecifics::Marshalling
1320b24acc06SZhaoxin Yang   integerArgumentType(mlir::Location loc,
1321b24acc06SZhaoxin Yang                       mlir::IntegerType argTy) const override {
1322b24acc06SZhaoxin Yang     if (argTy.getWidth() == 32) {
1323b24acc06SZhaoxin Yang       // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended.
1324b24acc06SZhaoxin Yang       // Therefore, Flang also follows it if a function needs to be
1325b24acc06SZhaoxin Yang       // interoperable with C.
1326b24acc06SZhaoxin Yang       //
1327b24acc06SZhaoxin Yang       // Currently, it only adds `signext` attribute to the dummy arguments and
1328b24acc06SZhaoxin Yang       // return values in the function signatures, but it does not add the
1329b24acc06SZhaoxin Yang       // corresponding attribute to the actual arguments and return values in
1330b24acc06SZhaoxin Yang       // `fir.call` instruction. Thanks to LLVM's integration of all these
1331b24acc06SZhaoxin Yang       // attributes, the modification is still effective.
1332b24acc06SZhaoxin Yang       CodeGenSpecifics::Marshalling marshal;
1333b24acc06SZhaoxin Yang       AT::IntegerExtension intExt = AT::IntegerExtension::Sign;
1334b24acc06SZhaoxin Yang       marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
1335b24acc06SZhaoxin Yang                                      /*sret=*/false, /*append=*/false,
1336b24acc06SZhaoxin Yang                                      /*intExt=*/intExt});
1337b24acc06SZhaoxin Yang       return marshal;
1338b24acc06SZhaoxin Yang     }
1339b24acc06SZhaoxin Yang 
1340b24acc06SZhaoxin Yang     return GenericTarget::integerArgumentType(loc, argTy);
1341b24acc06SZhaoxin Yang   }
1342dab9fa2dSZhaoxin Yang 
1343dab9fa2dSZhaoxin Yang   /// Flatten non-basic types, resulting in an array of types containing only
1344dab9fa2dSZhaoxin Yang   /// `IntegerType` and `FloatType`.
1345dab9fa2dSZhaoxin Yang   llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc,
1346dab9fa2dSZhaoxin Yang                                                 const mlir::Type type) const {
1347dab9fa2dSZhaoxin Yang     llvm::SmallVector<mlir::Type> flatTypes;
1348dab9fa2dSZhaoxin Yang 
1349dab9fa2dSZhaoxin Yang     llvm::TypeSwitch<mlir::Type>(type)
1350dab9fa2dSZhaoxin Yang         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1351dab9fa2dSZhaoxin Yang           if (intTy.getWidth() != 0)
1352dab9fa2dSZhaoxin Yang             flatTypes.push_back(intTy);
1353dab9fa2dSZhaoxin Yang         })
1354dab9fa2dSZhaoxin Yang         .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1355dab9fa2dSZhaoxin Yang           if (floatTy.getWidth() != 0)
1356dab9fa2dSZhaoxin Yang             flatTypes.push_back(floatTy);
1357dab9fa2dSZhaoxin Yang         })
1358dab9fa2dSZhaoxin Yang         .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
1359dab9fa2dSZhaoxin Yang           const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
1360dab9fa2dSZhaoxin Yang           if (sem == &llvm::APFloat::IEEEsingle() ||
1361dab9fa2dSZhaoxin Yang               sem == &llvm::APFloat::IEEEdouble() ||
1362dab9fa2dSZhaoxin Yang               sem == &llvm::APFloat::IEEEquad())
1363dab9fa2dSZhaoxin Yang             std::fill_n(std::back_inserter(flatTypes), 2,
1364dab9fa2dSZhaoxin Yang                         cmplx.getElementType());
1365dab9fa2dSZhaoxin Yang           else
1366dab9fa2dSZhaoxin Yang             TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "
1367dab9fa2dSZhaoxin Yang                       "IEEEquad) as a structure component for BIND(C), "
1368dab9fa2dSZhaoxin Yang                       "VALUE derived type argument and type return");
1369dab9fa2dSZhaoxin Yang         })
1370dab9fa2dSZhaoxin Yang         .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {
1371dab9fa2dSZhaoxin Yang           const unsigned width =
1372dab9fa2dSZhaoxin Yang               kindMap.getLogicalBitsize(logicalTy.getFKind());
1373dab9fa2dSZhaoxin Yang           if (width != 0)
1374dab9fa2dSZhaoxin Yang             flatTypes.push_back(
1375dab9fa2dSZhaoxin Yang                 mlir::IntegerType::get(type.getContext(), width));
1376dab9fa2dSZhaoxin Yang         })
1377dab9fa2dSZhaoxin Yang         .template Case<fir::CharacterType>([&](fir::CharacterType charTy) {
1378dab9fa2dSZhaoxin Yang           assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 &&
1379dab9fa2dSZhaoxin Yang                  "the bit size of characterType as an interoperable type must "
1380dab9fa2dSZhaoxin Yang                  "not exceed 8");
1381dab9fa2dSZhaoxin Yang           for (unsigned i = 0; i < charTy.getLen(); ++i)
1382dab9fa2dSZhaoxin Yang             flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
1383dab9fa2dSZhaoxin Yang         })
1384dab9fa2dSZhaoxin Yang         .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
1385dab9fa2dSZhaoxin Yang           if (!seqTy.hasDynamicExtents()) {
1386dab9fa2dSZhaoxin Yang             const std::uint64_t numOfEle = seqTy.getConstantArraySize();
1387dab9fa2dSZhaoxin Yang             mlir::Type eleTy = seqTy.getEleTy();
1388dab9fa2dSZhaoxin Yang             if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
1389dab9fa2dSZhaoxin Yang               llvm::SmallVector<mlir::Type> subTypeList =
1390dab9fa2dSZhaoxin Yang                   flattenTypeList(loc, eleTy);
1391dab9fa2dSZhaoxin Yang               if (subTypeList.size() != 0)
1392dab9fa2dSZhaoxin Yang                 for (std::uint64_t i = 0; i < numOfEle; ++i)
1393dab9fa2dSZhaoxin Yang                   llvm::copy(subTypeList, std::back_inserter(flatTypes));
1394dab9fa2dSZhaoxin Yang             } else {
1395dab9fa2dSZhaoxin Yang               std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
1396dab9fa2dSZhaoxin Yang             }
1397dab9fa2dSZhaoxin Yang           } else
1398dab9fa2dSZhaoxin Yang             TODO(loc, "unsupported dynamic extent sequence type as a structure "
1399dab9fa2dSZhaoxin Yang                       "component for BIND(C), "
1400dab9fa2dSZhaoxin Yang                       "VALUE derived type argument and type return");
1401dab9fa2dSZhaoxin Yang         })
1402dab9fa2dSZhaoxin Yang         .template Case<fir::RecordType>([&](fir::RecordType recTy) {
1403dab9fa2dSZhaoxin Yang           for (auto &component : recTy.getTypeList()) {
1404dab9fa2dSZhaoxin Yang             mlir::Type eleTy = component.second;
1405dab9fa2dSZhaoxin Yang             llvm::SmallVector<mlir::Type> subTypeList =
1406dab9fa2dSZhaoxin Yang                 flattenTypeList(loc, eleTy);
1407dab9fa2dSZhaoxin Yang             if (subTypeList.size() != 0)
1408dab9fa2dSZhaoxin Yang               llvm::copy(subTypeList, std::back_inserter(flatTypes));
1409dab9fa2dSZhaoxin Yang           }
1410dab9fa2dSZhaoxin Yang         })
1411dab9fa2dSZhaoxin Yang         .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
1412dab9fa2dSZhaoxin Yang           auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash(
1413dab9fa2dSZhaoxin Yang               loc, vecTy, getDataLayout(), kindMap);
1414dab9fa2dSZhaoxin Yang           if (sizeAndAlign.first == 2 * GRLenInChar)
1415dab9fa2dSZhaoxin Yang             flatTypes.push_back(
1416dab9fa2dSZhaoxin Yang                 mlir::IntegerType::get(type.getContext(), 2 * GRLen));
1417dab9fa2dSZhaoxin Yang           else
1418dab9fa2dSZhaoxin Yang             TODO(loc, "unsupported vector width(must be 128 bits)");
1419dab9fa2dSZhaoxin Yang         })
1420dab9fa2dSZhaoxin Yang         .Default([&](mlir::Type ty) {
1421dab9fa2dSZhaoxin Yang           if (fir::conformsWithPassByRef(ty))
1422dab9fa2dSZhaoxin Yang             flatTypes.push_back(
1423dab9fa2dSZhaoxin Yang                 mlir::IntegerType::get(type.getContext(), GRLen));
1424dab9fa2dSZhaoxin Yang           else
1425dab9fa2dSZhaoxin Yang             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1426dab9fa2dSZhaoxin Yang                       "type argument and type return");
1427dab9fa2dSZhaoxin Yang         });
1428dab9fa2dSZhaoxin Yang 
1429dab9fa2dSZhaoxin Yang     return flatTypes;
1430dab9fa2dSZhaoxin Yang   }
1431dab9fa2dSZhaoxin Yang 
1432dab9fa2dSZhaoxin Yang   /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,
1433dab9fa2dSZhaoxin Yang   /// when flattened it contains a single fp value, fp+fp, or int+fp of
1434dab9fa2dSZhaoxin Yang   /// appropriate size).
1435dab9fa2dSZhaoxin Yang   bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
1436dab9fa2dSZhaoxin Yang                                 mlir::Type &field1Ty,
1437dab9fa2dSZhaoxin Yang                                 mlir::Type &field2Ty) const {
1438dab9fa2dSZhaoxin Yang     field1Ty = field2Ty = nullptr;
1439dab9fa2dSZhaoxin Yang     llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);
1440dab9fa2dSZhaoxin Yang     size_t flatSize = flatTypes.size();
1441dab9fa2dSZhaoxin Yang 
1442dab9fa2dSZhaoxin Yang     // Cannot be eligible if the number of flattened types is equal to 0 or
1443dab9fa2dSZhaoxin Yang     // greater than 2.
1444dab9fa2dSZhaoxin Yang     if (flatSize == 0 || flatSize > 2)
1445dab9fa2dSZhaoxin Yang       return false;
1446dab9fa2dSZhaoxin Yang 
1447dab9fa2dSZhaoxin Yang     bool isFirstAvaliableFloat = false;
1448dab9fa2dSZhaoxin Yang 
1449dab9fa2dSZhaoxin Yang     assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
1450dab9fa2dSZhaoxin Yang            "Type must be integerType or floatType after flattening");
1451dab9fa2dSZhaoxin Yang     if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
1452dab9fa2dSZhaoxin Yang       const unsigned Size = floatTy.getWidth();
1453dab9fa2dSZhaoxin Yang       // Can't be eligible if larger than the FP registers. Half precision isn't
1454dab9fa2dSZhaoxin Yang       // currently supported on LoongArch and the ABI hasn't been confirmed, so
1455dab9fa2dSZhaoxin Yang       // default to the integer ABI in that case.
1456dab9fa2dSZhaoxin Yang       if (Size > FRLen || Size < 32)
1457dab9fa2dSZhaoxin Yang         return false;
1458dab9fa2dSZhaoxin Yang       isFirstAvaliableFloat = true;
1459dab9fa2dSZhaoxin Yang       field1Ty = floatTy;
1460dab9fa2dSZhaoxin Yang     } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
1461dab9fa2dSZhaoxin Yang       if (intTy.getWidth() > GRLen)
1462dab9fa2dSZhaoxin Yang         return false;
1463dab9fa2dSZhaoxin Yang       field1Ty = intTy;
1464dab9fa2dSZhaoxin Yang     }
1465dab9fa2dSZhaoxin Yang 
1466dab9fa2dSZhaoxin Yang     // flatTypes has two elements
1467dab9fa2dSZhaoxin Yang     if (flatSize == 2) {
1468dab9fa2dSZhaoxin Yang       assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&
1469dab9fa2dSZhaoxin Yang              "Type must be integerType or floatType after flattening");
1470dab9fa2dSZhaoxin Yang       if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
1471dab9fa2dSZhaoxin Yang         const unsigned Size = floatTy.getWidth();
1472dab9fa2dSZhaoxin Yang         if (Size > FRLen || Size < 32)
1473dab9fa2dSZhaoxin Yang           return false;
1474dab9fa2dSZhaoxin Yang         field2Ty = floatTy;
1475dab9fa2dSZhaoxin Yang         return true;
1476dab9fa2dSZhaoxin Yang       } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) {
1477dab9fa2dSZhaoxin Yang         // Can't be eligible if an integer type was already found (int+int pairs
1478dab9fa2dSZhaoxin Yang         // are not eligible).
1479dab9fa2dSZhaoxin Yang         if (!isFirstAvaliableFloat)
1480dab9fa2dSZhaoxin Yang           return false;
1481dab9fa2dSZhaoxin Yang         if (intTy.getWidth() > GRLen)
1482dab9fa2dSZhaoxin Yang           return false;
1483dab9fa2dSZhaoxin Yang         field2Ty = intTy;
1484dab9fa2dSZhaoxin Yang         return true;
1485dab9fa2dSZhaoxin Yang       }
1486dab9fa2dSZhaoxin Yang     }
1487dab9fa2dSZhaoxin Yang 
1488dab9fa2dSZhaoxin Yang     // return isFirstAvaliableFloat if flatTypes only has one element
1489dab9fa2dSZhaoxin Yang     return isFirstAvaliableFloat;
1490dab9fa2dSZhaoxin Yang   }
1491dab9fa2dSZhaoxin Yang 
1492dab9fa2dSZhaoxin Yang   bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,
1493dab9fa2dSZhaoxin Yang                               const mlir::Type type) const {
1494dab9fa2dSZhaoxin Yang     if (!type)
1495dab9fa2dSZhaoxin Yang       return true;
1496dab9fa2dSZhaoxin Yang 
1497dab9fa2dSZhaoxin Yang     llvm::TypeSwitch<mlir::Type>(type)
1498dab9fa2dSZhaoxin Yang         .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1499dab9fa2dSZhaoxin Yang           const unsigned width = intTy.getWidth();
1500dab9fa2dSZhaoxin Yang           if (width > 128)
1501dab9fa2dSZhaoxin Yang             TODO(loc,
1502dab9fa2dSZhaoxin Yang                  "integerType with width exceeding 128 bits is unsupported");
1503dab9fa2dSZhaoxin Yang           if (width == 0)
1504dab9fa2dSZhaoxin Yang             return;
1505dab9fa2dSZhaoxin Yang           if (width <= GRLen)
1506dab9fa2dSZhaoxin Yang             --GARsLeft;
1507dab9fa2dSZhaoxin Yang           else if (width <= 2 * GRLen)
1508dab9fa2dSZhaoxin Yang             GARsLeft = GARsLeft - 2;
1509dab9fa2dSZhaoxin Yang         })
1510dab9fa2dSZhaoxin Yang         .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1511dab9fa2dSZhaoxin Yang           const unsigned width = floatTy.getWidth();
1512dab9fa2dSZhaoxin Yang           if (width > 128)
1513dab9fa2dSZhaoxin Yang             TODO(loc, "floatType with width exceeding 128 bits is unsupported");
1514dab9fa2dSZhaoxin Yang           if (width == 0)
1515dab9fa2dSZhaoxin Yang             return;
1516dab9fa2dSZhaoxin Yang           if (width == 32 || width == 64)
1517dab9fa2dSZhaoxin Yang             --FARsLeft;
1518dab9fa2dSZhaoxin Yang           else if (width <= GRLen)
1519dab9fa2dSZhaoxin Yang             --GARsLeft;
1520dab9fa2dSZhaoxin Yang           else if (width <= 2 * GRLen)
1521dab9fa2dSZhaoxin Yang             GARsLeft = GARsLeft - 2;
1522dab9fa2dSZhaoxin Yang         })
1523dab9fa2dSZhaoxin Yang         .Default([&](mlir::Type ty) {
1524dab9fa2dSZhaoxin Yang           if (fir::conformsWithPassByRef(ty))
1525dab9fa2dSZhaoxin Yang             --GARsLeft; // Pointers.
1526dab9fa2dSZhaoxin Yang           else
1527dab9fa2dSZhaoxin Yang             TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1528dab9fa2dSZhaoxin Yang                       "type argument and type return");
1529dab9fa2dSZhaoxin Yang         });
1530dab9fa2dSZhaoxin Yang 
1531dab9fa2dSZhaoxin Yang     return GARsLeft >= 0 && FARsLeft >= 0;
1532dab9fa2dSZhaoxin Yang   }
1533dab9fa2dSZhaoxin Yang 
1534dab9fa2dSZhaoxin Yang   bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,
1535dab9fa2dSZhaoxin Yang                           const Marshalling &previousArguments,
1536dab9fa2dSZhaoxin Yang                           const mlir::Type &field1Ty,
1537dab9fa2dSZhaoxin Yang                           const mlir::Type &field2Ty) const {
1538dab9fa2dSZhaoxin Yang     for (auto &typeAndAttr : previousArguments) {
1539dab9fa2dSZhaoxin Yang       const auto &attr = std::get<Attributes>(typeAndAttr);
1540dab9fa2dSZhaoxin Yang       if (attr.isByVal()) {
1541dab9fa2dSZhaoxin Yang         // Previous argument passed on the stack, and its address is passed in
1542dab9fa2dSZhaoxin Yang         // GAR.
1543dab9fa2dSZhaoxin Yang         --GARsLeft;
1544dab9fa2dSZhaoxin Yang         continue;
1545dab9fa2dSZhaoxin Yang       }
1546dab9fa2dSZhaoxin Yang 
1547dab9fa2dSZhaoxin Yang       // Previous aggregate arguments were marshalled into simpler arguments.
1548dab9fa2dSZhaoxin Yang       const auto &type = std::get<mlir::Type>(typeAndAttr);
1549dab9fa2dSZhaoxin Yang       llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);
1550dab9fa2dSZhaoxin Yang 
1551dab9fa2dSZhaoxin Yang       for (auto &flatTy : flatTypes) {
1552dab9fa2dSZhaoxin Yang         if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))
1553dab9fa2dSZhaoxin Yang           return false;
1554dab9fa2dSZhaoxin Yang       }
1555dab9fa2dSZhaoxin Yang     }
1556dab9fa2dSZhaoxin Yang 
1557dab9fa2dSZhaoxin Yang     if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
1558dab9fa2dSZhaoxin Yang       return false;
1559dab9fa2dSZhaoxin Yang     if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
1560dab9fa2dSZhaoxin Yang       return false;
1561dab9fa2dSZhaoxin Yang     return true;
1562dab9fa2dSZhaoxin Yang   }
1563dab9fa2dSZhaoxin Yang 
1564dab9fa2dSZhaoxin Yang   /// LoongArch64 subroutine calling sequence ABI in:
1565dab9fa2dSZhaoxin Yang   /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
1566dab9fa2dSZhaoxin Yang   CodeGenSpecifics::Marshalling
1567dab9fa2dSZhaoxin Yang   classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft,
1568dab9fa2dSZhaoxin Yang                  int FARsLeft, bool isResult,
1569dab9fa2dSZhaoxin Yang                  const Marshalling &previousArguments) const {
1570dab9fa2dSZhaoxin Yang     CodeGenSpecifics::Marshalling marshal;
1571dab9fa2dSZhaoxin Yang 
1572dab9fa2dSZhaoxin Yang     auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(
1573dab9fa2dSZhaoxin Yang         loc, recTy, getDataLayout(), kindMap);
1574dab9fa2dSZhaoxin Yang     mlir::MLIRContext *context = recTy.getContext();
1575dab9fa2dSZhaoxin Yang 
1576dab9fa2dSZhaoxin Yang     if (recSize == 0) {
1577dab9fa2dSZhaoxin Yang       TODO(loc, "unsupported empty struct type for BIND(C), "
1578dab9fa2dSZhaoxin Yang                 "VALUE derived type argument and type return");
1579dab9fa2dSZhaoxin Yang     }
1580dab9fa2dSZhaoxin Yang 
1581dab9fa2dSZhaoxin Yang     if (recSize > 2 * GRLenInChar) {
1582dab9fa2dSZhaoxin Yang       marshal.emplace_back(
1583dab9fa2dSZhaoxin Yang           fir::ReferenceType::get(recTy),
1584dab9fa2dSZhaoxin Yang           AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult});
1585dab9fa2dSZhaoxin Yang       return marshal;
1586dab9fa2dSZhaoxin Yang     }
1587dab9fa2dSZhaoxin Yang 
1588dab9fa2dSZhaoxin Yang     // Pass by FARs(and GARs)
1589dab9fa2dSZhaoxin Yang     mlir::Type field1Ty = nullptr, field2Ty = nullptr;
1590dab9fa2dSZhaoxin Yang     if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) &&
1591dab9fa2dSZhaoxin Yang         hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty,
1592dab9fa2dSZhaoxin Yang                            field2Ty)) {
1593dab9fa2dSZhaoxin Yang       if (!isResult) {
1594dab9fa2dSZhaoxin Yang         if (field1Ty)
1595dab9fa2dSZhaoxin Yang           marshal.emplace_back(field1Ty, AT{});
1596dab9fa2dSZhaoxin Yang         if (field2Ty)
1597dab9fa2dSZhaoxin Yang           marshal.emplace_back(field2Ty, AT{});
1598dab9fa2dSZhaoxin Yang       } else {
1599dab9fa2dSZhaoxin Yang         // field1Ty is always preferred over field2Ty for assignment, so there
1600dab9fa2dSZhaoxin Yang         // will never be a case where field1Ty == nullptr and field2Ty !=
1601dab9fa2dSZhaoxin Yang         // nullptr.
1602dab9fa2dSZhaoxin Yang         if (field1Ty && !field2Ty)
1603dab9fa2dSZhaoxin Yang           marshal.emplace_back(field1Ty, AT{});
1604dab9fa2dSZhaoxin Yang         else if (field1Ty && field2Ty)
1605dab9fa2dSZhaoxin Yang           marshal.emplace_back(
1606dab9fa2dSZhaoxin Yang               mlir::TupleType::get(context,
1607dab9fa2dSZhaoxin Yang                                    mlir::TypeRange{field1Ty, field2Ty}),
1608dab9fa2dSZhaoxin Yang               AT{/*alignment=*/0, /*byval=*/true});
1609dab9fa2dSZhaoxin Yang       }
1610dab9fa2dSZhaoxin Yang       return marshal;
1611dab9fa2dSZhaoxin Yang     }
1612dab9fa2dSZhaoxin Yang 
1613dab9fa2dSZhaoxin Yang     if (recSize <= GRLenInChar) {
1614dab9fa2dSZhaoxin Yang       marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});
1615dab9fa2dSZhaoxin Yang       return marshal;
1616dab9fa2dSZhaoxin Yang     }
1617dab9fa2dSZhaoxin Yang 
1618dab9fa2dSZhaoxin Yang     if (recAlign == 2 * GRLenInChar) {
1619dab9fa2dSZhaoxin Yang       marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});
1620dab9fa2dSZhaoxin Yang       return marshal;
1621dab9fa2dSZhaoxin Yang     }
1622dab9fa2dSZhaoxin Yang 
1623dab9fa2dSZhaoxin Yang     // recSize > GRLenInChar && recSize <= 2 * GRLenInChar
1624dab9fa2dSZhaoxin Yang     marshal.emplace_back(
1625dab9fa2dSZhaoxin Yang         fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
1626dab9fa2dSZhaoxin Yang         AT{});
1627dab9fa2dSZhaoxin Yang     return marshal;
1628dab9fa2dSZhaoxin Yang   }
1629dab9fa2dSZhaoxin Yang 
1630dab9fa2dSZhaoxin Yang   /// Marshal a derived type passed by value like a C struct.
1631dab9fa2dSZhaoxin Yang   CodeGenSpecifics::Marshalling
1632dab9fa2dSZhaoxin Yang   structArgumentType(mlir::Location loc, fir::RecordType recTy,
1633dab9fa2dSZhaoxin Yang                      const Marshalling &previousArguments) const override {
1634dab9fa2dSZhaoxin Yang     int GARsLeft = 8;
1635dab9fa2dSZhaoxin Yang     int FARsLeft = FRLen ? 8 : 0;
1636dab9fa2dSZhaoxin Yang 
1637dab9fa2dSZhaoxin Yang     return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,
1638dab9fa2dSZhaoxin Yang                           previousArguments);
1639dab9fa2dSZhaoxin Yang   }
1640dab9fa2dSZhaoxin Yang 
1641dab9fa2dSZhaoxin Yang   CodeGenSpecifics::Marshalling
1642dab9fa2dSZhaoxin Yang   structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
1643dab9fa2dSZhaoxin Yang     // The rules for return and argument types are the same.
1644dab9fa2dSZhaoxin Yang     int GARsLeft = 2;
1645dab9fa2dSZhaoxin Yang     int FARsLeft = FRLen ? 2 : 0;
1646dab9fa2dSZhaoxin Yang     return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,
1647dab9fa2dSZhaoxin Yang                           {});
1648dab9fa2dSZhaoxin Yang   }
1649720f728dSWeining Lu };
1650720f728dSWeining Lu } // namespace
1651720f728dSWeining Lu 
16524c263edeSDiana Picus // Instantiate the overloaded target instance based on the triple value.
1653b389fbd0SAndrzej Warzynski // TODO: Add other targets to this file as needed.
16544c263edeSDiana Picus std::unique_ptr<fir::CodeGenSpecifics>
16554c263edeSDiana Picus fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
1656837bff11SSergio Afonso                            KindMapping &&kindMap, llvm::StringRef targetCPU,
1657837bff11SSergio Afonso                            mlir::LLVM::TargetFeaturesAttr targetFeatures,
1658837bff11SSergio Afonso                            const mlir::DataLayout &dl) {
16594c263edeSDiana Picus   switch (trp.getArch()) {
16604c263edeSDiana Picus   default:
16614c263edeSDiana Picus     break;
16624c263edeSDiana Picus   case llvm::Triple::ArchType::x86:
1663774703ecSMarkus Mützel     if (trp.isOSWindows())
1664774703ecSMarkus Mützel       return std::make_unique<TargetI386Win>(ctx, std::move(trp),
1665837bff11SSergio Afonso                                              std::move(kindMap), targetCPU,
1666837bff11SSergio Afonso                                              targetFeatures, dl);
1667774703ecSMarkus Mützel     else
16684c263edeSDiana Picus       return std::make_unique<TargetI386>(ctx, std::move(trp),
1669837bff11SSergio Afonso                                           std::move(kindMap), targetCPU,
1670837bff11SSergio Afonso                                           targetFeatures, dl);
16714c263edeSDiana Picus   case llvm::Triple::ArchType::x86_64:
1672774703ecSMarkus Mützel     if (trp.isOSWindows())
1673774703ecSMarkus Mützel       return std::make_unique<TargetX86_64Win>(ctx, std::move(trp),
1674837bff11SSergio Afonso                                                std::move(kindMap), targetCPU,
1675837bff11SSergio Afonso                                                targetFeatures, dl);
1676774703ecSMarkus Mützel     else
16774c263edeSDiana Picus       return std::make_unique<TargetX86_64>(ctx, std::move(trp),
1678837bff11SSergio Afonso                                             std::move(kindMap), targetCPU,
1679837bff11SSergio Afonso                                             targetFeatures, dl);
16804c263edeSDiana Picus   case llvm::Triple::ArchType::aarch64:
1681837bff11SSergio Afonso     return std::make_unique<TargetAArch64>(
1682837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1683320fbff4SMark Danial   case llvm::Triple::ArchType::ppc64:
1684837bff11SSergio Afonso     return std::make_unique<TargetPPC64>(
1685837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
16864c263edeSDiana Picus   case llvm::Triple::ArchType::ppc64le:
1687837bff11SSergio Afonso     return std::make_unique<TargetPPC64le>(
1688837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
16891774a8a7SRainer Orth   case llvm::Triple::ArchType::sparc:
1690837bff11SSergio Afonso     return std::make_unique<TargetSparc>(
1691837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
16921774a8a7SRainer Orth   case llvm::Triple::ArchType::sparcv9:
1693837bff11SSergio Afonso     return std::make_unique<TargetSparcV9>(
1694837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1695bac88e89SQihan Cai   case llvm::Triple::ArchType::riscv64:
1696837bff11SSergio Afonso     return std::make_unique<TargetRISCV64>(
1697837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
169808749a91SJan Sjodin   case llvm::Triple::ArchType::amdgcn:
1699837bff11SSergio Afonso     return std::make_unique<TargetAMDGPU>(
1700837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1701be9fa9deSFabian Mora   case llvm::Triple::ArchType::nvptx64:
1702837bff11SSergio Afonso     return std::make_unique<TargetNVPTX>(
1703837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1704720f728dSWeining Lu   case llvm::Triple::ArchType::loongarch64:
1705837bff11SSergio Afonso     return std::make_unique<TargetLoongArch64>(
1706837bff11SSergio Afonso         ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
17071774a8a7SRainer Orth   }
17080c1cf585SEric Schweitz   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
17094c263edeSDiana Picus }
1710f1d3fe7aSAlexis Perry-Holby 
1711f1d3fe7aSAlexis Perry-Holby std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
1712f1d3fe7aSAlexis Perry-Holby     mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap,
1713f1d3fe7aSAlexis Perry-Holby     llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures,
1714f1d3fe7aSAlexis Perry-Holby     const mlir::DataLayout &dl, llvm::StringRef tuneCPU) {
1715f1d3fe7aSAlexis Perry-Holby   std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(
1716f1d3fe7aSAlexis Perry-Holby       ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1717f1d3fe7aSAlexis Perry-Holby 
1718f1d3fe7aSAlexis Perry-Holby   CGS->tuneCPU = tuneCPU;
1719f1d3fe7aSAlexis Perry-Holby   return CGS;
1720f1d3fe7aSAlexis Perry-Holby }
1721