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