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