xref: /llvm-project/flang/lib/Optimizer/CodeGen/Target.cpp (revision bac88e898f3da5ec0469d86e4cf03aa58d2f9b2c)
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 //===----------------------------------------------------------------------===//
257 // sparc (sparc 32 bit) target specifics.
258 //===----------------------------------------------------------------------===//
259 
260 namespace {
261 struct TargetSparc : public GenericTarget<TargetSparc> {
262   using GenericTarget::GenericTarget;
263 
264   static constexpr int defaultWidth = 32;
265 
266   CodeGenSpecifics::Marshalling
267   complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
268     assert(fir::isa_real(eleTy));
269     CodeGenSpecifics::Marshalling marshal;
270     // Use a type that will be translated into LLVM as:
271     // { t, t }   struct of 2 eleTy
272     mlir::TypeRange range = {eleTy, eleTy};
273     auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
274     marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
275     return marshal;
276   }
277 
278   CodeGenSpecifics::Marshalling
279   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
280     assert(fir::isa_real(eleTy));
281     CodeGenSpecifics::Marshalling marshal;
282     // Use a type that will be translated into LLVM as:
283     // { t, t }   struct of 2 eleTy, byval
284     mlir::TypeRange range = {eleTy, eleTy};
285     auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
286     marshal.emplace_back(fir::ReferenceType::get(structTy),
287                          AT{/*alignment=*/0, /*byval=*/true});
288     return marshal;
289   }
290 };
291 } // namespace
292 
293 //===----------------------------------------------------------------------===//
294 // sparcv9 (sparc 64 bit) target specifics.
295 //===----------------------------------------------------------------------===//
296 
297 namespace {
298 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
299   using GenericTarget::GenericTarget;
300 
301   static constexpr int defaultWidth = 64;
302 
303   CodeGenSpecifics::Marshalling
304   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
305     CodeGenSpecifics::Marshalling marshal;
306     const auto *sem = &floatToSemantics(kindMap, eleTy);
307     if (sem == &llvm::APFloat::IEEEsingle() ||
308         sem == &llvm::APFloat::IEEEdouble()) {
309       // two distinct float, double arguments
310       marshal.emplace_back(eleTy, AT{});
311       marshal.emplace_back(eleTy, AT{});
312     } else if (sem == &llvm::APFloat::IEEEquad()) {
313       // Use a type that will be translated into LLVM as:
314       // { fp128, fp128 }   struct of 2 fp128, byval, align 16
315       mlir::TypeRange range = {eleTy, eleTy};
316       marshal.emplace_back(fir::ReferenceType::get(
317                                mlir::TupleType::get(eleTy.getContext(), range)),
318                            AT{/*align=*/16, /*byval=*/true});
319     } else {
320       TODO(loc, "complex for this precision");
321     }
322     return marshal;
323   }
324 
325   CodeGenSpecifics::Marshalling
326   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
327     CodeGenSpecifics::Marshalling marshal;
328     // Use a type that will be translated into LLVM as:
329     // { eleTy, eleTy }   struct of 2 eleTy
330     mlir::TypeRange range = {eleTy, eleTy};
331     marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
332     return marshal;
333   }
334 };
335 } // namespace
336 
337 //===----------------------------------------------------------------------===//
338 // RISCV64 linux target specifics.
339 //===----------------------------------------------------------------------===//
340 
341 namespace {
342 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
343   using GenericTarget::GenericTarget;
344 
345   static constexpr int defaultWidth = 64;
346 
347   CodeGenSpecifics::Marshalling
348   complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
349     CodeGenSpecifics::Marshalling marshal;
350     const auto *sem = &floatToSemantics(kindMap, eleTy);
351     if (sem == &llvm::APFloat::IEEEsingle() ||
352         sem == &llvm::APFloat::IEEEdouble()) {
353       // Two distinct element type arguments (re, im)
354       marshal.emplace_back(eleTy, AT{});
355       marshal.emplace_back(eleTy, AT{});
356     } else {
357       TODO(loc, "complex for this precision");
358     }
359     return marshal;
360   }
361 
362   CodeGenSpecifics::Marshalling
363   complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
364     CodeGenSpecifics::Marshalling marshal;
365     const auto *sem = &floatToSemantics(kindMap, eleTy);
366     if (sem == &llvm::APFloat::IEEEsingle() ||
367         sem == &llvm::APFloat::IEEEdouble()) {
368       // Use a type that will be translated into LLVM as:
369       // { t, t }   struct of 2 eleTy, byVal
370       mlir::TypeRange range = {eleTy, eleTy};
371       marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
372                            AT{/*alignment=*/0, /*byval=*/true});
373     } else {
374       TODO(loc, "complex for this precision");
375     }
376     return marshal;
377   }
378 };
379 } // namespace
380 
381 // Instantiate the overloaded target instance based on the triple value.
382 // TODO: Add other targets to this file as needed.
383 std::unique_ptr<fir::CodeGenSpecifics>
384 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
385                            KindMapping &&kindMap) {
386   switch (trp.getArch()) {
387   default:
388     break;
389   case llvm::Triple::ArchType::x86:
390     return std::make_unique<TargetI386>(ctx, std::move(trp),
391                                         std::move(kindMap));
392   case llvm::Triple::ArchType::x86_64:
393     return std::make_unique<TargetX86_64>(ctx, std::move(trp),
394                                           std::move(kindMap));
395   case llvm::Triple::ArchType::aarch64:
396     return std::make_unique<TargetAArch64>(ctx, std::move(trp),
397                                            std::move(kindMap));
398   case llvm::Triple::ArchType::ppc64le:
399     return std::make_unique<TargetPPC64le>(ctx, std::move(trp),
400                                            std::move(kindMap));
401   case llvm::Triple::ArchType::sparc:
402     return std::make_unique<TargetSparc>(ctx, std::move(trp),
403                                          std::move(kindMap));
404   case llvm::Triple::ArchType::sparcv9:
405     return std::make_unique<TargetSparcV9>(ctx, std::move(trp),
406                                            std::move(kindMap));
407   case llvm::Triple::ArchType::riscv64:
408     return std::make_unique<TargetRISCV64>(ctx, std::move(trp),
409                                            std::move(kindMap));
410   }
411   TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
412 }
413