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