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