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