xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision 0c1cf585c06dfbca5596eb11664f8445d22d128b)
1 //===-- Target.cpp --------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "Target.h"
14 #include "flang/Optimizer/Builder/Todo.h"
15 #include "flang/Optimizer/Dialect/FIRType.h"
16 #include "flang/Optimizer/Support/FatalError.h"
17 #include "flang/Optimizer/Support/KindMapping.h"
18 #include "mlir/IR/BuiltinTypes.h"
19 #include "mlir/IR/TypeRange.h"
20 
21 #define DEBUG_TYPE "flang-codegen-target"
22 
23 using namespace fir;
24 
25 // Reduce a REAL/float type to the floating point semantics.
26 static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
27                                                   mlir::Type type) {
28   assert(isa_real(type));
29   if (auto ty = type.dyn_cast<fir::RealType>())
30     return kindMap.getFloatSemantics(ty.getFKind());
31   return type.cast<mlir::FloatType>().getFloatSemantics();
32 }
33 
34 namespace {
35 template <typename S>
36 struct GenericTarget : public CodeGenSpecifics {
37   using CodeGenSpecifics::CodeGenSpecifics;
38   using AT = CodeGenSpecifics::Attributes;
39 
40   mlir::Type complexMemoryType(mlir::Type eleTy) const override {
41     assert(fir::isa_real(eleTy));
42     // Use a type that will be translated into LLVM as:
43     // { t, t }   struct of 2 eleTy
44     mlir::TypeRange range = {eleTy, eleTy};
45     return mlir::TupleType::get(eleTy.getContext(), range);
46   }
47 
48   mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
49     auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
50     auto ptrTy = fir::ReferenceType::get(eleTy);
51     // Use a type that will be translated into LLVM as:
52     // { t*, index }
53     mlir::TypeRange range = {ptrTy, idxTy};
54     return mlir::TupleType::get(eleTy.getContext(), range);
55   }
56 
57   Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
58     CodeGenSpecifics::Marshalling marshal;
59     auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
60     auto ptrTy = fir::ReferenceType::get(eleTy);
61     marshal.emplace_back(ptrTy, AT{});
62     // Return value arguments are grouped as a pair. Others are passed in a
63     // split format with all pointers first (in the declared position) and all
64     // LEN arguments appended after all of the dummy arguments.
65     // NB: Other conventions/ABIs can/should be supported via options.
66     marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
67                                    /*sret=*/sret, /*append=*/!sret});
68     return marshal;
69   }
70 };
71 } // namespace
72 
73 //===----------------------------------------------------------------------===//
74 // i386 (x86 32 bit) linux target specifics.
75 //===----------------------------------------------------------------------===//
76 
77 namespace {
78 struct TargetI386 : public GenericTarget<TargetI386> {
79   using GenericTarget::GenericTarget;
80 
81   static constexpr int defaultWidth = 32;
82 
83   CodeGenSpecifics::Marshalling
84   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
85     assert(fir::isa_real(eleTy));
86     CodeGenSpecifics::Marshalling marshal;
87     // Use a type that will be translated into LLVM as:
88     // { t, t }   struct of 2 eleTy, byval, align 4
89     mlir::TypeRange range = {eleTy, eleTy};
90     auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
91     marshal.emplace_back(fir::ReferenceType::get(structTy),
92                          AT{/*alignment=*/4, /*byval=*/true});
93     return marshal;
94   }
95 
96   CodeGenSpecifics::Marshalling
97   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
98     assert(fir::isa_real(eleTy));
99     CodeGenSpecifics::Marshalling marshal;
100     const auto *sem = &floatToSemantics(kindMap, eleTy);
101     if (sem == &llvm::APFloat::IEEEsingle()) {
102       // i64   pack both floats in a 64-bit GPR
103       marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
104                            AT{});
105     } else if (sem == &llvm::APFloat::IEEEdouble()) {
106       // Use a type that will be translated into LLVM as:
107       // { t, t }   struct of 2 eleTy, sret, align 4
108       mlir::TypeRange range = {eleTy, eleTy};
109       auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
110       marshal.emplace_back(fir::ReferenceType::get(structTy),
111                            AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
112     } else {
113       TODO(loc, "complex for this precision");
114     }
115     return marshal;
116   }
117 };
118 } // namespace
119 
120 //===----------------------------------------------------------------------===//
121 // x86_64 (x86 64 bit) linux target specifics.
122 //===----------------------------------------------------------------------===//
123 
124 namespace {
125 struct TargetX86_64 : public GenericTarget<TargetX86_64> {
126   using GenericTarget::GenericTarget;
127 
128   static constexpr int defaultWidth = 64;
129 
130   CodeGenSpecifics::Marshalling
131   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
132     CodeGenSpecifics::Marshalling marshal;
133     const auto *sem = &floatToSemantics(kindMap, eleTy);
134     if (sem == &llvm::APFloat::IEEEsingle()) {
135       // <2 x t>   vector of 2 eleTy
136       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
137     } else if (sem == &llvm::APFloat::IEEEdouble()) {
138       // two distinct double arguments
139       marshal.emplace_back(eleTy, AT{});
140       marshal.emplace_back(eleTy, AT{});
141     } else {
142       TODO(loc, "complex for this precision");
143     }
144     return marshal;
145   }
146 
147   CodeGenSpecifics::Marshalling
148   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
149     CodeGenSpecifics::Marshalling marshal;
150     const auto *sem = &floatToSemantics(kindMap, eleTy);
151     if (sem == &llvm::APFloat::IEEEsingle()) {
152       // <2 x t>   vector of 2 eleTy
153       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
154     } else if (sem == &llvm::APFloat::IEEEdouble()) {
155       // Use a type that will be translated into LLVM as:
156       // { double, double }   struct of 2 double
157       mlir::TypeRange range = {eleTy, eleTy};
158       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
159                            AT{});
160     } else {
161       TODO(loc, "complex for this precision");
162     }
163     return marshal;
164   }
165 };
166 } // namespace
167 
168 //===----------------------------------------------------------------------===//
169 // AArch64 linux target specifics.
170 //===----------------------------------------------------------------------===//
171 
172 namespace {
173 struct TargetAArch64 : public GenericTarget<TargetAArch64> {
174   using GenericTarget::GenericTarget;
175 
176   static constexpr int defaultWidth = 64;
177 
178   CodeGenSpecifics::Marshalling
179   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
180     CodeGenSpecifics::Marshalling marshal;
181     const auto *sem = &floatToSemantics(kindMap, eleTy);
182     if (sem == &llvm::APFloat::IEEEsingle() ||
183         sem == &llvm::APFloat::IEEEdouble()) {
184       // [2 x t]   array of 2 eleTy
185       marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
186     } else {
187       TODO(loc, "complex for this precision");
188     }
189     return marshal;
190   }
191 
192   CodeGenSpecifics::Marshalling
193   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
194     CodeGenSpecifics::Marshalling marshal;
195     const auto *sem = &floatToSemantics(kindMap, eleTy);
196     if (sem == &llvm::APFloat::IEEEsingle() ||
197         sem == &llvm::APFloat::IEEEdouble()) {
198       // Use a type that will be translated into LLVM as:
199       // { t, t }   struct of 2 eleTy
200       mlir::TypeRange range = {eleTy, eleTy};
201       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
202                            AT{});
203     } else {
204       TODO(loc, "complex for this precision");
205     }
206     return marshal;
207   }
208 };
209 } // namespace
210 
211 //===----------------------------------------------------------------------===//
212 // PPC64le linux target specifics.
213 //===----------------------------------------------------------------------===//
214 
215 namespace {
216 struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
217   using GenericTarget::GenericTarget;
218 
219   static constexpr int defaultWidth = 64;
220 
221   CodeGenSpecifics::Marshalling
222   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
223     CodeGenSpecifics::Marshalling marshal;
224     // two distinct element type arguments (re, im)
225     marshal.emplace_back(eleTy, AT{});
226     marshal.emplace_back(eleTy, AT{});
227     return marshal;
228   }
229 
230   CodeGenSpecifics::Marshalling
231   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
232     CodeGenSpecifics::Marshalling marshal;
233     // Use a type that will be translated into LLVM as:
234     // { t, t }   struct of 2 element type
235     mlir::TypeRange range = {eleTy, eleTy};
236     marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
237     return marshal;
238   }
239 };
240 } // namespace
241 
242 // Instantiate the overloaded target instance based on the triple value.
243 // TODO: Add other targets to this file as needed.
244 std::unique_ptr<fir::CodeGenSpecifics>
245 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
246                            KindMapping &&kindMap) {
247   switch (trp.getArch()) {
248   default:
249     break;
250   case llvm::Triple::ArchType::x86:
251     switch (trp.getOS()) {
252     default:
253       break;
254     case llvm::Triple::OSType::Linux:
255     case llvm::Triple::OSType::Darwin:
256     case llvm::Triple::OSType::MacOSX:
257     case llvm::Triple::OSType::Win32:
258       return std::make_unique<TargetI386>(ctx, std::move(trp),
259                                           std::move(kindMap));
260     }
261     break;
262   case llvm::Triple::ArchType::x86_64:
263     switch (trp.getOS()) {
264     default:
265       break;
266     case llvm::Triple::OSType::Linux:
267     case llvm::Triple::OSType::Darwin:
268     case llvm::Triple::OSType::MacOSX:
269     case llvm::Triple::OSType::Win32:
270       return std::make_unique<TargetX86_64>(ctx, std::move(trp),
271                                             std::move(kindMap));
272     }
273     break;
274   case llvm::Triple::ArchType::aarch64:
275     switch (trp.getOS()) {
276     default:
277       break;
278     case llvm::Triple::OSType::Linux:
279     case llvm::Triple::OSType::Darwin:
280     case llvm::Triple::OSType::MacOSX:
281     case llvm::Triple::OSType::Win32:
282       return std::make_unique<TargetAArch64>(ctx, std::move(trp),
283                                              std::move(kindMap));
284     }
285     break;
286   case llvm::Triple::ArchType::ppc64le:
287     switch (trp.getOS()) {
288     default:
289       break;
290     case llvm::Triple::OSType::Linux:
291       return std::make_unique<TargetPPC64le>(ctx, std::move(trp),
292                                              std::move(kindMap));
293     }
294     break;
295   }
296   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
297 }
298