xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision bcb2740f415b0f825402f656dda3271414121a0e)
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 if (sem == &llvm::APFloat::IEEEquad()) {
142       // Use a type that will be translated into LLVM as:
143       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
144       mlir::TypeRange range = {eleTy, eleTy};
145       marshal.emplace_back(fir::ReferenceType::get(
146                                mlir::TupleType::get(eleTy.getContext(), range)),
147                            AT{/*align=*/16, /*byval=*/true});
148     } else {
149       TODO(loc, "complex for this precision");
150     }
151     return marshal;
152   }
153 
154   CodeGenSpecifics::Marshalling
155   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
156     CodeGenSpecifics::Marshalling marshal;
157     const auto *sem = &floatToSemantics(kindMap, eleTy);
158     if (sem == &llvm::APFloat::IEEEsingle()) {
159       // <2 x t>   vector of 2 eleTy
160       marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
161     } else if (sem == &llvm::APFloat::IEEEdouble()) {
162       // Use a type that will be translated into LLVM as:
163       // { double, double }   struct of 2 double
164       mlir::TypeRange range = {eleTy, eleTy};
165       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
166                            AT{});
167     } else if (sem == &llvm::APFloat::IEEEquad()) {
168       // Use a type that will be translated into LLVM as:
169       // { fp128, fp128 }   struct of 2 fp128, sret, align 16
170       mlir::TypeRange range = {eleTy, eleTy};
171       marshal.emplace_back(fir::ReferenceType::get(
172                                mlir::TupleType::get(eleTy.getContext(), range)),
173                            AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
174     } else {
175       TODO(loc, "complex for this precision");
176     }
177     return marshal;
178   }
179 };
180 } // namespace
181 
182 //===----------------------------------------------------------------------===//
183 // AArch64 linux target specifics.
184 //===----------------------------------------------------------------------===//
185 
186 namespace {
187 struct TargetAArch64 : public GenericTarget<TargetAArch64> {
188   using GenericTarget::GenericTarget;
189 
190   static constexpr int defaultWidth = 64;
191 
192   CodeGenSpecifics::Marshalling
193   complexArgumentType(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       // [2 x t]   array of 2 eleTy
199       marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
200     } else {
201       TODO(loc, "complex for this precision");
202     }
203     return marshal;
204   }
205 
206   CodeGenSpecifics::Marshalling
207   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
208     CodeGenSpecifics::Marshalling marshal;
209     const auto *sem = &floatToSemantics(kindMap, eleTy);
210     if (sem == &llvm::APFloat::IEEEsingle() ||
211         sem == &llvm::APFloat::IEEEdouble()) {
212       // Use a type that will be translated into LLVM as:
213       // { t, t }   struct of 2 eleTy
214       mlir::TypeRange range = {eleTy, eleTy};
215       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
216                            AT{});
217     } else {
218       TODO(loc, "complex for this precision");
219     }
220     return marshal;
221   }
222 };
223 } // namespace
224 
225 //===----------------------------------------------------------------------===//
226 // PPC64le linux target specifics.
227 //===----------------------------------------------------------------------===//
228 
229 namespace {
230 struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
231   using GenericTarget::GenericTarget;
232 
233   static constexpr int defaultWidth = 64;
234 
235   CodeGenSpecifics::Marshalling
236   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
237     CodeGenSpecifics::Marshalling marshal;
238     // two distinct element type arguments (re, im)
239     marshal.emplace_back(eleTy, AT{});
240     marshal.emplace_back(eleTy, AT{});
241     return marshal;
242   }
243 
244   CodeGenSpecifics::Marshalling
245   complexReturnType(mlir::Location, mlir::Type eleTy) const override {
246     CodeGenSpecifics::Marshalling marshal;
247     // Use a type that will be translated into LLVM as:
248     // { t, t }   struct of 2 element type
249     mlir::TypeRange range = {eleTy, eleTy};
250     marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
251     return marshal;
252   }
253 };
254 } // namespace
255 
256 // Instantiate the overloaded target instance based on the triple value.
257 // TODO: Add other targets to this file as needed.
258 std::unique_ptr<fir::CodeGenSpecifics>
259 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
260                            KindMapping &&kindMap) {
261   switch (trp.getArch()) {
262   default:
263     break;
264   case llvm::Triple::ArchType::x86:
265     switch (trp.getOS()) {
266     default:
267       break;
268     case llvm::Triple::OSType::Linux:
269     case llvm::Triple::OSType::Darwin:
270     case llvm::Triple::OSType::MacOSX:
271     case llvm::Triple::OSType::Solaris:
272     case llvm::Triple::OSType::Win32:
273       return std::make_unique<TargetI386>(ctx, std::move(trp),
274                                           std::move(kindMap));
275     }
276     break;
277   case llvm::Triple::ArchType::x86_64:
278     switch (trp.getOS()) {
279     default:
280       break;
281     case llvm::Triple::OSType::Linux:
282     case llvm::Triple::OSType::Darwin:
283     case llvm::Triple::OSType::MacOSX:
284     case llvm::Triple::OSType::Solaris:
285     case llvm::Triple::OSType::Win32:
286       return std::make_unique<TargetX86_64>(ctx, std::move(trp),
287                                             std::move(kindMap));
288     }
289     break;
290   case llvm::Triple::ArchType::aarch64:
291     switch (trp.getOS()) {
292     default:
293       break;
294     case llvm::Triple::OSType::Linux:
295     case llvm::Triple::OSType::Darwin:
296     case llvm::Triple::OSType::MacOSX:
297     case llvm::Triple::OSType::Win32:
298       return std::make_unique<TargetAArch64>(ctx, std::move(trp),
299                                              std::move(kindMap));
300     }
301     break;
302   case llvm::Triple::ArchType::ppc64le:
303     switch (trp.getOS()) {
304     default:
305       break;
306     case llvm::Triple::OSType::Linux:
307       return std::make_unique<TargetPPC64le>(ctx, std::move(trp),
308                                              std::move(kindMap));
309     }
310     break;
311   }
312   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
313 }
314