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 "flang/Optimizer/CodeGen/Target.h" |
14 | #include "flang/Optimizer/Builder/Todo.h" |
15 | #include "flang/Optimizer/Dialect/FIRType.h" |
16 | #include "flang/Optimizer/Dialect/Support/KindMapping.h" |
17 | #include "flang/Optimizer/Support/FatalError.h" |
18 | #include "flang/Optimizer/Support/Utils.h" |
19 | #include "mlir/IR/BuiltinTypes.h" |
20 | #include "mlir/IR/TypeRange.h" |
21 | #include "llvm/ADT/TypeSwitch.h" |
22 | |
23 | #define DEBUG_TYPE "flang-codegen-target" |
24 | |
25 | using namespace fir; |
26 | |
27 | namespace fir::details { |
28 | llvm::StringRef Attributes::getIntExtensionAttrName() const { |
29 | // The attribute names are available via LLVM dialect interfaces |
30 | // like getZExtAttrName(), getByValAttrName(), etc., so we'd better |
31 | // use them than literals. |
32 | if (isZeroExt()) |
33 | return "llvm.zeroext" ; |
34 | else if (isSignExt()) |
35 | return "llvm.signext" ; |
36 | return {}; |
37 | } |
38 | } // namespace fir::details |
39 | |
40 | // Reduce a REAL/float type to the floating point semantics. |
41 | static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, |
42 | mlir::Type type) { |
43 | assert(isa_real(type)); |
44 | if (auto ty = type.dyn_cast<fir::RealType>()) |
45 | return kindMap.getFloatSemantics(ty.getFKind()); |
46 | return type.cast<mlir::FloatType>().getFloatSemantics(); |
47 | } |
48 | |
49 | static void typeTodo(const llvm::fltSemantics *sem, mlir::Location loc, |
50 | const std::string &context) { |
51 | if (sem == &llvm::APFloat::IEEEhalf()) { |
52 | TODO(loc, "COMPLEX(KIND=2): for " + context + " type" ); |
53 | } else if (sem == &llvm::APFloat::BFloat()) { |
54 | TODO(loc, "COMPLEX(KIND=3): " + context + " type" ); |
55 | } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
56 | TODO(loc, "COMPLEX(KIND=10): " + context + " type" ); |
57 | } else { |
58 | TODO(loc, "complex for this precision for " + context + " type" ); |
59 | } |
60 | } |
61 | |
62 | namespace { |
63 | template <typename S> |
64 | struct GenericTarget : public CodeGenSpecifics { |
65 | using CodeGenSpecifics::CodeGenSpecifics; |
66 | using AT = CodeGenSpecifics::Attributes; |
67 | |
68 | mlir::Type complexMemoryType(mlir::Type eleTy) const override { |
69 | assert(fir::isa_real(eleTy)); |
70 | // Use a type that will be translated into LLVM as: |
71 | // { t, t } struct of 2 eleTy |
72 | return mlir::TupleType::get(eleTy.getContext(), |
73 | mlir::TypeRange{eleTy, eleTy}); |
74 | } |
75 | |
76 | mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { |
77 | auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); |
78 | auto ptrTy = fir::ReferenceType::get(eleTy); |
79 | // Use a type that will be translated into LLVM as: |
80 | // { t*, index } |
81 | return mlir::TupleType::get(eleTy.getContext(), |
82 | mlir::TypeRange{ptrTy, idxTy}); |
83 | } |
84 | |
85 | Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { |
86 | CodeGenSpecifics::Marshalling marshal; |
87 | auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); |
88 | auto ptrTy = fir::ReferenceType::get(eleTy); |
89 | marshal.emplace_back(ptrTy, AT{}); |
90 | // Return value arguments are grouped as a pair. Others are passed in a |
91 | // split format with all pointers first (in the declared position) and all |
92 | // LEN arguments appended after all of the dummy arguments. |
93 | // NB: Other conventions/ABIs can/should be supported via options. |
94 | marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, |
95 | /*sret=*/sret, /*append=*/!sret}); |
96 | return marshal; |
97 | } |
98 | |
99 | CodeGenSpecifics::Marshalling |
100 | structArgumentType(mlir::Location loc, fir::RecordType, |
101 | const Marshalling &) const override { |
102 | TODO(loc, "passing VALUE BIND(C) derived type for this target" ); |
103 | } |
104 | |
105 | CodeGenSpecifics::Marshalling |
106 | integerArgumentType(mlir::Location loc, |
107 | mlir::IntegerType argTy) const override { |
108 | CodeGenSpecifics::Marshalling marshal; |
109 | AT::IntegerExtension intExt = AT::IntegerExtension::None; |
110 | if (argTy.getWidth() < getCIntTypeWidth()) { |
111 | // isSigned() and isUnsigned() branches below are dead code currently. |
112 | // If needed, we can generate calls with signed/unsigned argument types |
113 | // to more precisely match C side (e.g. for Fortran runtime functions |
114 | // with 'unsigned short' arguments). |
115 | if (argTy.isSigned()) |
116 | intExt = AT::IntegerExtension::Sign; |
117 | else if (argTy.isUnsigned()) |
118 | intExt = AT::IntegerExtension::Zero; |
119 | else if (argTy.isSignless()) { |
120 | // Zero extend for 'i1' and sign extend for other types. |
121 | if (argTy.getWidth() == 1) |
122 | intExt = AT::IntegerExtension::Zero; |
123 | else |
124 | intExt = AT::IntegerExtension::Sign; |
125 | } |
126 | } |
127 | |
128 | marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, |
129 | /*sret=*/false, /*append=*/false, |
130 | /*intExt=*/intExt}); |
131 | return marshal; |
132 | } |
133 | |
134 | CodeGenSpecifics::Marshalling |
135 | integerReturnType(mlir::Location loc, |
136 | mlir::IntegerType argTy) const override { |
137 | return integerArgumentType(loc, argTy); |
138 | } |
139 | |
140 | // Width of 'int' type is 32-bits for almost all targets, except |
141 | // for AVR and MSP430 (see TargetInfo initializations |
142 | // in clang/lib/Basic/Targets). |
143 | unsigned char getCIntTypeWidth() const override { return 32; } |
144 | }; |
145 | } // namespace |
146 | |
147 | //===----------------------------------------------------------------------===// |
148 | // i386 (x86 32 bit) linux target specifics. |
149 | //===----------------------------------------------------------------------===// |
150 | |
151 | namespace { |
152 | struct TargetI386 : public GenericTarget<TargetI386> { |
153 | using GenericTarget::GenericTarget; |
154 | |
155 | static constexpr int defaultWidth = 32; |
156 | |
157 | CodeGenSpecifics::Marshalling |
158 | complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
159 | assert(fir::isa_real(eleTy)); |
160 | CodeGenSpecifics::Marshalling marshal; |
161 | // Use a type that will be translated into LLVM as: |
162 | // { t, t } struct of 2 eleTy, byval, align 4 |
163 | auto structTy = |
164 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
165 | marshal.emplace_back(fir::ReferenceType::get(structTy), |
166 | AT{/*alignment=*/4, /*byval=*/true}); |
167 | return marshal; |
168 | } |
169 | |
170 | CodeGenSpecifics::Marshalling |
171 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
172 | assert(fir::isa_real(eleTy)); |
173 | CodeGenSpecifics::Marshalling marshal; |
174 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
175 | if (sem == &llvm::APFloat::IEEEsingle()) { |
176 | // i64 pack both floats in a 64-bit GPR |
177 | marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
178 | AT{}); |
179 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
180 | // Use a type that will be translated into LLVM as: |
181 | // { t, t } struct of 2 eleTy, sret, align 4 |
182 | auto structTy = mlir::TupleType::get(eleTy.getContext(), |
183 | mlir::TypeRange{eleTy, eleTy}); |
184 | marshal.emplace_back(fir::ReferenceType::get(structTy), |
185 | AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); |
186 | } else { |
187 | typeTodo(sem, loc, "return" ); |
188 | } |
189 | return marshal; |
190 | } |
191 | }; |
192 | } // namespace |
193 | |
194 | //===----------------------------------------------------------------------===// |
195 | // i386 (x86 32 bit) Windows target specifics. |
196 | //===----------------------------------------------------------------------===// |
197 | |
198 | namespace { |
199 | struct TargetI386Win : public GenericTarget<TargetI386Win> { |
200 | using GenericTarget::GenericTarget; |
201 | |
202 | static constexpr int defaultWidth = 32; |
203 | |
204 | CodeGenSpecifics::Marshalling |
205 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
206 | CodeGenSpecifics::Marshalling marshal; |
207 | // Use a type that will be translated into LLVM as: |
208 | // { t, t } struct of 2 eleTy, byval, align 4 |
209 | auto structTy = |
210 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
211 | marshal.emplace_back(fir::ReferenceType::get(structTy), |
212 | AT{/*align=*/4, /*byval=*/true}); |
213 | return marshal; |
214 | } |
215 | |
216 | CodeGenSpecifics::Marshalling |
217 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
218 | CodeGenSpecifics::Marshalling marshal; |
219 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
220 | if (sem == &llvm::APFloat::IEEEsingle()) { |
221 | // i64 pack both floats in a 64-bit GPR |
222 | marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
223 | AT{}); |
224 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
225 | // Use a type that will be translated into LLVM as: |
226 | // { double, double } struct of 2 double, sret, align 8 |
227 | marshal.emplace_back( |
228 | fir::ReferenceType::get(mlir::TupleType::get( |
229 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
230 | AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); |
231 | } else if (sem == &llvm::APFloat::IEEEquad()) { |
232 | // Use a type that will be translated into LLVM as: |
233 | // { fp128, fp128 } struct of 2 fp128, sret, align 16 |
234 | marshal.emplace_back( |
235 | fir::ReferenceType::get(mlir::TupleType::get( |
236 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
237 | AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
238 | } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
239 | // Use a type that will be translated into LLVM as: |
240 | // { x86_fp80, x86_fp80 } struct of 2 x86_fp80, sret, align 4 |
241 | marshal.emplace_back( |
242 | fir::ReferenceType::get(mlir::TupleType::get( |
243 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
244 | AT{/*align=*/4, /*byval=*/false, /*sret=*/true}); |
245 | } else { |
246 | typeTodo(sem, loc, "return" ); |
247 | } |
248 | return marshal; |
249 | } |
250 | }; |
251 | } // namespace |
252 | |
253 | //===----------------------------------------------------------------------===// |
254 | // x86_64 (x86 64 bit) linux target specifics. |
255 | //===----------------------------------------------------------------------===// |
256 | |
257 | namespace { |
258 | struct TargetX86_64 : public GenericTarget<TargetX86_64> { |
259 | using GenericTarget::GenericTarget; |
260 | |
261 | static constexpr int defaultWidth = 64; |
262 | |
263 | CodeGenSpecifics::Marshalling |
264 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
265 | CodeGenSpecifics::Marshalling marshal; |
266 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
267 | if (sem == &llvm::APFloat::IEEEsingle()) { |
268 | // <2 x t> vector of 2 eleTy |
269 | marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); |
270 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
271 | // FIXME: In case of SSE register exhaustion, the ABI here may be |
272 | // incorrect since LLVM may pass the real via register and the imaginary |
273 | // part via the stack while the ABI it should be all in register or all |
274 | // in memory. Register occupancy must be analyzed here. |
275 | // two distinct double arguments |
276 | marshal.emplace_back(eleTy, AT{}); |
277 | marshal.emplace_back(eleTy, AT{}); |
278 | } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
279 | // Use a type that will be translated into LLVM as: |
280 | // { x86_fp80, x86_fp80 } struct of 2 fp128, byval, align 16 |
281 | marshal.emplace_back( |
282 | fir::ReferenceType::get(mlir::TupleType::get( |
283 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
284 | AT{/*align=*/16, /*byval=*/true}); |
285 | } else if (sem == &llvm::APFloat::IEEEquad()) { |
286 | // Use a type that will be translated into LLVM as: |
287 | // { fp128, fp128 } struct of 2 fp128, byval, align 16 |
288 | marshal.emplace_back( |
289 | fir::ReferenceType::get(mlir::TupleType::get( |
290 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
291 | AT{/*align=*/16, /*byval=*/true}); |
292 | } else { |
293 | typeTodo(sem, loc, "argument" ); |
294 | } |
295 | return marshal; |
296 | } |
297 | |
298 | CodeGenSpecifics::Marshalling |
299 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
300 | CodeGenSpecifics::Marshalling marshal; |
301 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
302 | if (sem == &llvm::APFloat::IEEEsingle()) { |
303 | // <2 x t> vector of 2 eleTy |
304 | marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); |
305 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
306 | // Use a type that will be translated into LLVM as: |
307 | // { double, double } struct of 2 double |
308 | marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
309 | mlir::TypeRange{eleTy, eleTy}), |
310 | AT{}); |
311 | } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
312 | // { x86_fp80, x86_fp80 } |
313 | marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
314 | mlir::TypeRange{eleTy, eleTy}), |
315 | AT{}); |
316 | } else if (sem == &llvm::APFloat::IEEEquad()) { |
317 | // Use a type that will be translated into LLVM as: |
318 | // { fp128, fp128 } struct of 2 fp128, sret, align 16 |
319 | marshal.emplace_back( |
320 | fir::ReferenceType::get(mlir::TupleType::get( |
321 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
322 | AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
323 | } else { |
324 | typeTodo(sem, loc, "return" ); |
325 | } |
326 | return marshal; |
327 | } |
328 | |
329 | /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3. |
330 | enum ArgClass { |
331 | Integer = 0, |
332 | SSE, |
333 | SSEUp, |
334 | X87, |
335 | X87Up, |
336 | ComplexX87, |
337 | NoClass, |
338 | Memory |
339 | }; |
340 | |
341 | /// Classify an argument type or a field of an aggregate type argument. |
342 | /// See System V ABI version 1.0 section 3.2.3. |
343 | /// The Lo and Hi class are set to the class of the lower eight eightbytes |
344 | /// and upper eight eightbytes on return. |
345 | /// If this is called for an aggregate field, the caller is responsible to |
346 | /// do the post-merge. |
347 | void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset, |
348 | ArgClass &Lo, ArgClass &Hi) const { |
349 | Hi = Lo = ArgClass::NoClass; |
350 | ArgClass ¤t = byteOffset < 8 ? Lo : Hi; |
351 | // System V AMD64 ABI 3.2.3. version 1.0 |
352 | llvm::TypeSwitch<mlir::Type>(type) |
353 | .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { |
354 | if (intTy.getWidth() == 128) |
355 | Hi = Lo = ArgClass::Integer; |
356 | else |
357 | current = ArgClass::Integer; |
358 | }) |
359 | .template Case<mlir::FloatType, fir::RealType>([&](mlir::Type floatTy) { |
360 | const auto *sem = &floatToSemantics(kindMap, floatTy); |
361 | if (sem == &llvm::APFloat::x87DoubleExtended()) { |
362 | Lo = ArgClass::X87; |
363 | Hi = ArgClass::X87Up; |
364 | } else if (sem == &llvm::APFloat::IEEEquad()) { |
365 | Lo = ArgClass::SSE; |
366 | Hi = ArgClass::SSEUp; |
367 | } else { |
368 | current = ArgClass::SSE; |
369 | } |
370 | }) |
371 | .template Case<fir::ComplexType>([&](fir::ComplexType cmplx) { |
372 | const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); |
373 | if (sem == &llvm::APFloat::x87DoubleExtended()) { |
374 | current = ArgClass::ComplexX87; |
375 | } else { |
376 | fir::SequenceType::Shape shape{2}; |
377 | classifyArray(loc, |
378 | fir::SequenceType::get(shape, cmplx.getElementType()), |
379 | byteOffset, Lo, Hi); |
380 | } |
381 | }) |
382 | .template Case<fir::LogicalType>([&](fir::LogicalType logical) { |
383 | if (kindMap.getLogicalBitsize(logical.getFKind()) == 128) |
384 | Hi = Lo = ArgClass::Integer; |
385 | else |
386 | current = ArgClass::Integer; |
387 | }) |
388 | .template Case<fir::CharacterType>( |
389 | [&](fir::CharacterType character) { current = ArgClass::Integer; }) |
390 | .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { |
391 | // Array component. |
392 | classifyArray(loc, seqTy, byteOffset, Lo, Hi); |
393 | }) |
394 | .template Case<fir::RecordType>([&](fir::RecordType recTy) { |
395 | // Component that is a derived type. |
396 | classifyStruct(loc, recTy, byteOffset, Lo, Hi); |
397 | }) |
398 | .template Case<fir::VectorType>([&](fir::VectorType vecTy) { |
399 | // Previously marshalled SSE eight byte for a previous struct |
400 | // argument. |
401 | auto *sem = fir::isa_real(vecTy.getEleTy()) |
402 | ? &floatToSemantics(kindMap, vecTy.getEleTy()) |
403 | : nullptr; |
404 | // Not expecting to hit this todo in standard code (it would |
405 | // require some vector type extension). |
406 | if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) && |
407 | !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4)) |
408 | TODO(loc, "passing vector argument to C by value" ); |
409 | current = SSE; |
410 | }) |
411 | .Default([&](mlir::Type ty) { |
412 | if (fir::conformsWithPassByRef(ty)) |
413 | current = ArgClass::Integer; // Pointers. |
414 | else |
415 | TODO(loc, "unsupported component type for BIND(C), VALUE derived " |
416 | "type argument" ); |
417 | }); |
418 | } |
419 | |
420 | // Classify fields of a derived type starting at \p offset. Returns the new |
421 | // offset. Post-merge is left to the caller. |
422 | std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy, |
423 | std::uint64_t byteOffset, ArgClass &Lo, |
424 | ArgClass &Hi) const { |
425 | for (auto component : recTy.getTypeList()) { |
426 | if (byteOffset > 16) { |
427 | // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger |
428 | // than 16 bytes here, it is not a single _m256 and or _m512 entity |
429 | // that could fit in AVX registers. |
430 | Lo = Hi = ArgClass::Memory; |
431 | return byteOffset; |
432 | } |
433 | mlir::Type compType = component.second; |
434 | auto [compSize, compAlign] = |
435 | fir::getTypeSizeAndAlignment(loc, compType, getDataLayout(), kindMap); |
436 | byteOffset = llvm::alignTo(byteOffset, compAlign); |
437 | ArgClass LoComp, HiComp; |
438 | classify(loc, compType, byteOffset, LoComp, HiComp); |
439 | Lo = mergeClass(Lo, LoComp); |
440 | Hi = mergeClass(Hi, HiComp); |
441 | byteOffset = byteOffset + llvm::alignTo(compSize, compAlign); |
442 | if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) |
443 | return byteOffset; |
444 | } |
445 | return byteOffset; |
446 | } |
447 | |
448 | // Classify fields of a constant size array type starting at \p offset. |
449 | // Returns the new offset. Post-merge is left to the caller. |
450 | void classifyArray(mlir::Location loc, fir::SequenceType seqTy, |
451 | std::uint64_t byteOffset, ArgClass &Lo, |
452 | ArgClass &Hi) const { |
453 | mlir::Type eleTy = seqTy.getEleTy(); |
454 | const std::uint64_t arraySize = seqTy.getConstantArraySize(); |
455 | auto [eleSize, eleAlign] = |
456 | fir::getTypeSizeAndAlignment(loc, eleTy, getDataLayout(), kindMap); |
457 | std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign); |
458 | for (std::uint64_t i = 0; i < arraySize; ++i) { |
459 | byteOffset = llvm::alignTo(byteOffset, eleAlign); |
460 | if (byteOffset > 16) { |
461 | // See 3.2.3 p. 1 and note 15. Same as in classifyStruct. |
462 | Lo = Hi = ArgClass::Memory; |
463 | return; |
464 | } |
465 | ArgClass LoComp, HiComp; |
466 | classify(loc, eleTy, byteOffset, LoComp, HiComp); |
467 | Lo = mergeClass(accum: Lo, field: LoComp); |
468 | Hi = mergeClass(accum: Hi, field: HiComp); |
469 | byteOffset = byteOffset + eleStorageSize; |
470 | if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) |
471 | return; |
472 | } |
473 | } |
474 | |
475 | // Goes through the previously marshalled arguments and count the |
476 | // register occupancy to check if there are enough registers left. |
477 | bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters, |
478 | int neededSSERegisters, |
479 | const Marshalling &previousArguments) const { |
480 | int availIntRegisters = 6; |
481 | int availSSERegisters = 8; |
482 | for (auto typeAndAttr : previousArguments) { |
483 | const auto &attr = std::get<Attributes>(typeAndAttr); |
484 | if (attr.isByVal()) |
485 | continue; // Previous argument passed on the stack. |
486 | ArgClass Lo, Hi; |
487 | Lo = Hi = ArgClass::NoClass; |
488 | classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi); |
489 | // post merge is not needed here since previous aggregate arguments |
490 | // were marshalled into simpler arguments. |
491 | if (Lo == ArgClass::Integer) |
492 | --availIntRegisters; |
493 | else if (Lo == SSE) |
494 | --availSSERegisters; |
495 | if (Hi == ArgClass::Integer) |
496 | --availIntRegisters; |
497 | else if (Hi == ArgClass::SSE) |
498 | --availSSERegisters; |
499 | } |
500 | return availSSERegisters >= neededSSERegisters && |
501 | availIntRegisters >= neededIntRegisters; |
502 | } |
503 | |
504 | /// Argument class merging as described in System V ABI 3.2.3 point 4. |
505 | ArgClass mergeClass(ArgClass accum, ArgClass field) const { |
506 | assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) && |
507 | "Invalid accumulated classification during merge." ); |
508 | if (accum == field || field == NoClass) |
509 | return accum; |
510 | if (field == ArgClass::Memory) |
511 | return ArgClass::Memory; |
512 | if (accum == NoClass) |
513 | return field; |
514 | if (accum == Integer || field == Integer) |
515 | return ArgClass::Integer; |
516 | if (field == ArgClass::X87 || field == ArgClass::X87Up || |
517 | field == ArgClass::ComplexX87 || accum == ArgClass::X87 || |
518 | accum == ArgClass::X87Up) |
519 | return Memory; |
520 | return SSE; |
521 | } |
522 | |
523 | /// Argument class post merging as described in System V ABI 3.2.3 point 5. |
524 | void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const { |
525 | if (Hi == ArgClass::Memory) |
526 | Lo = ArgClass::Memory; |
527 | if (Hi == ArgClass::X87Up && Lo != ArgClass::X87) |
528 | Lo = ArgClass::Memory; |
529 | if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp)) |
530 | Lo = ArgClass::Memory; |
531 | if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE) |
532 | Hi = SSE; |
533 | } |
534 | |
535 | /// When \p recTy is a one field record type that can be passed |
536 | /// like the field on its own, returns the field type. Returns |
537 | /// a null type otherwise. |
538 | mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy) const { |
539 | auto typeList = recTy.getTypeList(); |
540 | if (typeList.size() != 1) |
541 | return {}; |
542 | mlir::Type fieldType = typeList[0].second; |
543 | if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::RealType, |
544 | fir::CharacterType, fir::LogicalType>(fieldType)) |
545 | return fieldType; |
546 | // Complex field that needs to be split, or array. |
547 | return {}; |
548 | } |
549 | |
550 | mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context, |
551 | ArgClass argClass, |
552 | std::uint64_t partByteSize) const { |
553 | if (argClass == ArgClass::SSE) { |
554 | if (partByteSize > 16) |
555 | TODO(loc, "passing struct as a real > 128 bits in register" ); |
556 | // Clang uses vector type when several fp fields are marshalled |
557 | // into a single SSE register (like <n x smallest fp field> ). |
558 | // It should make no difference from an ABI point of view to just |
559 | // select an fp type of the right size, and it makes things simpler |
560 | // here. |
561 | if (partByteSize > 8) |
562 | return mlir::FloatType::getF128(context); |
563 | if (partByteSize > 4) |
564 | return mlir::FloatType::getF64(context); |
565 | if (partByteSize > 2) |
566 | return mlir::FloatType::getF32(context); |
567 | return mlir::FloatType::getF16(context); |
568 | } |
569 | assert(partByteSize <= 8 && |
570 | "expect integer part of aggregate argument to fit into eight bytes" ); |
571 | if (partByteSize > 4) |
572 | return mlir::IntegerType::get(context, 64); |
573 | if (partByteSize > 2) |
574 | return mlir::IntegerType::get(context, 32); |
575 | if (partByteSize > 1) |
576 | return mlir::IntegerType::get(context, 16); |
577 | return mlir::IntegerType::get(context, 8); |
578 | } |
579 | |
580 | /// Marshal a derived type passed by value like a C struct. |
581 | CodeGenSpecifics::Marshalling |
582 | structArgumentType(mlir::Location loc, fir::RecordType recTy, |
583 | const Marshalling &previousArguments) const override { |
584 | std::uint64_t byteOffset = 0; |
585 | ArgClass Lo, Hi; |
586 | Lo = Hi = ArgClass::NoClass; |
587 | byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi); |
588 | postMerge(byteSize: byteOffset, Lo, Hi); |
589 | if (Lo == ArgClass::Memory || Lo == ArgClass::X87 || |
590 | Lo == ArgClass::ComplexX87) |
591 | return passOnTheStack(loc, recTy); |
592 | int neededIntRegisters = 0; |
593 | int neededSSERegisters = 0; |
594 | if (Lo == ArgClass::SSE) |
595 | ++neededSSERegisters; |
596 | else if (Lo == ArgClass::Integer) |
597 | ++neededIntRegisters; |
598 | if (Hi == ArgClass::SSE) |
599 | ++neededSSERegisters; |
600 | else if (Hi == ArgClass::Integer) |
601 | ++neededIntRegisters; |
602 | // C struct should not be split into LLVM registers if LLVM codegen is not |
603 | // able to later assign actual registers to all of them (struct passing is |
604 | // all in registers or all on the stack). |
605 | if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters, |
606 | previousArguments)) |
607 | return passOnTheStack(loc, recTy); |
608 | |
609 | if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) { |
610 | CodeGenSpecifics::Marshalling marshal; |
611 | marshal.emplace_back(fieldType, AT{}); |
612 | return marshal; |
613 | } |
614 | if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) { |
615 | // Pass a single integer or floating point argument. |
616 | mlir::Type lowType = |
617 | pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset); |
618 | CodeGenSpecifics::Marshalling marshal; |
619 | marshal.emplace_back(lowType, AT{}); |
620 | return marshal; |
621 | } |
622 | // Split into two integer or floating point arguments. |
623 | // Note that for the first argument, this will always pick i64 or f64 which |
624 | // may be bigger than needed if some struct padding ends the first eight |
625 | // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and |
626 | // semantic point of view, but it may not match the LLVM IR interface clang |
627 | // would produce for the equivalent C code (the assembly will still be |
628 | // compatible). This allows keeping the logic simpler here since it |
629 | // avoids computing the "data" size of the Lo part. |
630 | mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u); |
631 | mlir::Type hiType = |
632 | pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u); |
633 | CodeGenSpecifics::Marshalling marshal; |
634 | marshal.emplace_back(lowType, AT{}); |
635 | marshal.emplace_back(hiType, AT{}); |
636 | return marshal; |
637 | } |
638 | |
639 | /// Marshal an argument that must be passed on the stack. |
640 | CodeGenSpecifics::Marshalling passOnTheStack(mlir::Location loc, |
641 | mlir::Type ty) const { |
642 | CodeGenSpecifics::Marshalling marshal; |
643 | auto sizeAndAlign = |
644 | fir::getTypeSizeAndAlignment(loc, ty, getDataLayout(), kindMap); |
645 | // The stack is always 8 byte aligned (note 14 in 3.2.3). |
646 | unsigned short align = |
647 | std::max(sizeAndAlign.second, static_cast<unsigned short>(8)); |
648 | marshal.emplace_back(fir::ReferenceType::get(ty), |
649 | AT{align, /*byval=*/true, /*sret=*/false}); |
650 | return marshal; |
651 | } |
652 | }; |
653 | } // namespace |
654 | |
655 | //===----------------------------------------------------------------------===// |
656 | // x86_64 (x86 64 bit) Windows target specifics. |
657 | //===----------------------------------------------------------------------===// |
658 | |
659 | namespace { |
660 | struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> { |
661 | using GenericTarget::GenericTarget; |
662 | |
663 | static constexpr int defaultWidth = 64; |
664 | |
665 | CodeGenSpecifics::Marshalling |
666 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
667 | CodeGenSpecifics::Marshalling marshal; |
668 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
669 | if (sem == &llvm::APFloat::IEEEsingle()) { |
670 | // i64 pack both floats in a 64-bit GPR |
671 | marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
672 | AT{}); |
673 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
674 | // Use a type that will be translated into LLVM as: |
675 | // { double, double } struct of 2 double, byval, align 8 |
676 | marshal.emplace_back( |
677 | fir::ReferenceType::get(mlir::TupleType::get( |
678 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
679 | AT{/*align=*/8, /*byval=*/true}); |
680 | } else if (sem == &llvm::APFloat::IEEEquad() || |
681 | sem == &llvm::APFloat::x87DoubleExtended()) { |
682 | // Use a type that will be translated into LLVM as: |
683 | // { t, t } struct of 2 eleTy, byval, align 16 |
684 | marshal.emplace_back( |
685 | fir::ReferenceType::get(mlir::TupleType::get( |
686 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
687 | AT{/*align=*/16, /*byval=*/true}); |
688 | } else { |
689 | typeTodo(sem, loc, "argument" ); |
690 | } |
691 | return marshal; |
692 | } |
693 | |
694 | CodeGenSpecifics::Marshalling |
695 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
696 | CodeGenSpecifics::Marshalling marshal; |
697 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
698 | if (sem == &llvm::APFloat::IEEEsingle()) { |
699 | // i64 pack both floats in a 64-bit GPR |
700 | marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
701 | AT{}); |
702 | } else if (sem == &llvm::APFloat::IEEEdouble()) { |
703 | // Use a type that will be translated into LLVM as: |
704 | // { double, double } struct of 2 double, sret, align 8 |
705 | marshal.emplace_back( |
706 | fir::ReferenceType::get(mlir::TupleType::get( |
707 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
708 | AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); |
709 | } else if (sem == &llvm::APFloat::IEEEquad() || |
710 | sem == &llvm::APFloat::x87DoubleExtended()) { |
711 | // Use a type that will be translated into LLVM as: |
712 | // { t, t } struct of 2 eleTy, sret, align 16 |
713 | marshal.emplace_back( |
714 | fir::ReferenceType::get(mlir::TupleType::get( |
715 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
716 | AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
717 | } else { |
718 | typeTodo(sem, loc, "return" ); |
719 | } |
720 | return marshal; |
721 | } |
722 | }; |
723 | } // namespace |
724 | |
725 | //===----------------------------------------------------------------------===// |
726 | // AArch64 linux target specifics. |
727 | //===----------------------------------------------------------------------===// |
728 | |
729 | namespace { |
730 | struct TargetAArch64 : public GenericTarget<TargetAArch64> { |
731 | using GenericTarget::GenericTarget; |
732 | |
733 | static constexpr int defaultWidth = 64; |
734 | |
735 | CodeGenSpecifics::Marshalling |
736 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
737 | CodeGenSpecifics::Marshalling marshal; |
738 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
739 | if (sem == &llvm::APFloat::IEEEsingle() || |
740 | sem == &llvm::APFloat::IEEEdouble() || |
741 | sem == &llvm::APFloat::IEEEquad()) { |
742 | // [2 x t] array of 2 eleTy |
743 | marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); |
744 | } else { |
745 | typeTodo(sem, loc, "argument" ); |
746 | } |
747 | return marshal; |
748 | } |
749 | |
750 | CodeGenSpecifics::Marshalling |
751 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
752 | CodeGenSpecifics::Marshalling marshal; |
753 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
754 | if (sem == &llvm::APFloat::IEEEsingle() || |
755 | sem == &llvm::APFloat::IEEEdouble() || |
756 | sem == &llvm::APFloat::IEEEquad()) { |
757 | // Use a type that will be translated into LLVM as: |
758 | // { t, t } struct of 2 eleTy |
759 | marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
760 | mlir::TypeRange{eleTy, eleTy}), |
761 | AT{}); |
762 | } else { |
763 | typeTodo(sem, loc, "return" ); |
764 | } |
765 | return marshal; |
766 | } |
767 | }; |
768 | } // namespace |
769 | |
770 | //===----------------------------------------------------------------------===// |
771 | // PPC64 (AIX 64 bit) target specifics. |
772 | //===----------------------------------------------------------------------===// |
773 | |
774 | namespace { |
775 | struct TargetPPC64 : public GenericTarget<TargetPPC64> { |
776 | using GenericTarget::GenericTarget; |
777 | |
778 | static constexpr int defaultWidth = 64; |
779 | |
780 | CodeGenSpecifics::Marshalling |
781 | complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
782 | CodeGenSpecifics::Marshalling marshal; |
783 | // two distinct element type arguments (re, im) |
784 | marshal.emplace_back(eleTy, AT{}); |
785 | marshal.emplace_back(eleTy, AT{}); |
786 | return marshal; |
787 | } |
788 | |
789 | CodeGenSpecifics::Marshalling |
790 | complexReturnType(mlir::Location, mlir::Type eleTy) const override { |
791 | CodeGenSpecifics::Marshalling marshal; |
792 | // Use a type that will be translated into LLVM as: |
793 | // { t, t } struct of 2 element type |
794 | marshal.emplace_back( |
795 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
796 | AT{}); |
797 | return marshal; |
798 | } |
799 | }; |
800 | } // namespace |
801 | |
802 | //===----------------------------------------------------------------------===// |
803 | // PPC64le linux target specifics. |
804 | //===----------------------------------------------------------------------===// |
805 | |
806 | namespace { |
807 | struct TargetPPC64le : public GenericTarget<TargetPPC64le> { |
808 | using GenericTarget::GenericTarget; |
809 | |
810 | static constexpr int defaultWidth = 64; |
811 | |
812 | CodeGenSpecifics::Marshalling |
813 | complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
814 | CodeGenSpecifics::Marshalling marshal; |
815 | // two distinct element type arguments (re, im) |
816 | marshal.emplace_back(eleTy, AT{}); |
817 | marshal.emplace_back(eleTy, AT{}); |
818 | return marshal; |
819 | } |
820 | |
821 | CodeGenSpecifics::Marshalling |
822 | complexReturnType(mlir::Location, mlir::Type eleTy) const override { |
823 | CodeGenSpecifics::Marshalling marshal; |
824 | // Use a type that will be translated into LLVM as: |
825 | // { t, t } struct of 2 element type |
826 | marshal.emplace_back( |
827 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
828 | AT{}); |
829 | return marshal; |
830 | } |
831 | }; |
832 | } // namespace |
833 | |
834 | //===----------------------------------------------------------------------===// |
835 | // sparc (sparc 32 bit) target specifics. |
836 | //===----------------------------------------------------------------------===// |
837 | |
838 | namespace { |
839 | struct TargetSparc : public GenericTarget<TargetSparc> { |
840 | using GenericTarget::GenericTarget; |
841 | |
842 | static constexpr int defaultWidth = 32; |
843 | |
844 | CodeGenSpecifics::Marshalling |
845 | complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
846 | assert(fir::isa_real(eleTy)); |
847 | CodeGenSpecifics::Marshalling marshal; |
848 | // Use a type that will be translated into LLVM as: |
849 | // { t, t } struct of 2 eleTy |
850 | auto structTy = |
851 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
852 | marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); |
853 | return marshal; |
854 | } |
855 | |
856 | CodeGenSpecifics::Marshalling |
857 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
858 | assert(fir::isa_real(eleTy)); |
859 | CodeGenSpecifics::Marshalling marshal; |
860 | // Use a type that will be translated into LLVM as: |
861 | // { t, t } struct of 2 eleTy, byval |
862 | auto structTy = |
863 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
864 | marshal.emplace_back(fir::ReferenceType::get(structTy), |
865 | AT{/*alignment=*/0, /*byval=*/true}); |
866 | return marshal; |
867 | } |
868 | }; |
869 | } // namespace |
870 | |
871 | //===----------------------------------------------------------------------===// |
872 | // sparcv9 (sparc 64 bit) target specifics. |
873 | //===----------------------------------------------------------------------===// |
874 | |
875 | namespace { |
876 | struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { |
877 | using GenericTarget::GenericTarget; |
878 | |
879 | static constexpr int defaultWidth = 64; |
880 | |
881 | CodeGenSpecifics::Marshalling |
882 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
883 | CodeGenSpecifics::Marshalling marshal; |
884 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
885 | if (sem == &llvm::APFloat::IEEEsingle() || |
886 | sem == &llvm::APFloat::IEEEdouble()) { |
887 | // two distinct float, double arguments |
888 | marshal.emplace_back(eleTy, AT{}); |
889 | marshal.emplace_back(eleTy, AT{}); |
890 | } else if (sem == &llvm::APFloat::IEEEquad()) { |
891 | // Use a type that will be translated into LLVM as: |
892 | // { fp128, fp128 } struct of 2 fp128, byval, align 16 |
893 | marshal.emplace_back( |
894 | fir::ReferenceType::get(mlir::TupleType::get( |
895 | eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
896 | AT{/*align=*/16, /*byval=*/true}); |
897 | } else { |
898 | typeTodo(sem, loc, "argument" ); |
899 | } |
900 | return marshal; |
901 | } |
902 | |
903 | CodeGenSpecifics::Marshalling |
904 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
905 | CodeGenSpecifics::Marshalling marshal; |
906 | // Use a type that will be translated into LLVM as: |
907 | // { eleTy, eleTy } struct of 2 eleTy |
908 | marshal.emplace_back( |
909 | mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
910 | AT{}); |
911 | return marshal; |
912 | } |
913 | }; |
914 | } // namespace |
915 | |
916 | //===----------------------------------------------------------------------===// |
917 | // RISCV64 linux target specifics. |
918 | //===----------------------------------------------------------------------===// |
919 | |
920 | namespace { |
921 | struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { |
922 | using GenericTarget::GenericTarget; |
923 | |
924 | static constexpr int defaultWidth = 64; |
925 | |
926 | CodeGenSpecifics::Marshalling |
927 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
928 | CodeGenSpecifics::Marshalling marshal; |
929 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
930 | if (sem == &llvm::APFloat::IEEEsingle() || |
931 | sem == &llvm::APFloat::IEEEdouble()) { |
932 | // Two distinct element type arguments (re, im) |
933 | marshal.emplace_back(eleTy, AT{}); |
934 | marshal.emplace_back(eleTy, AT{}); |
935 | } else { |
936 | typeTodo(sem, loc, "argument" ); |
937 | } |
938 | return marshal; |
939 | } |
940 | |
941 | CodeGenSpecifics::Marshalling |
942 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
943 | CodeGenSpecifics::Marshalling marshal; |
944 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
945 | if (sem == &llvm::APFloat::IEEEsingle() || |
946 | sem == &llvm::APFloat::IEEEdouble()) { |
947 | // Use a type that will be translated into LLVM as: |
948 | // { t, t } struct of 2 eleTy, byVal |
949 | marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
950 | mlir::TypeRange{eleTy, eleTy}), |
951 | AT{/*alignment=*/0, /*byval=*/true}); |
952 | } else { |
953 | typeTodo(sem, loc, "return" ); |
954 | } |
955 | return marshal; |
956 | } |
957 | }; |
958 | } // namespace |
959 | |
960 | //===----------------------------------------------------------------------===// |
961 | // AMDGPU linux target specifics. |
962 | //===----------------------------------------------------------------------===// |
963 | |
964 | namespace { |
965 | struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> { |
966 | using GenericTarget::GenericTarget; |
967 | |
968 | // Default size (in bits) of the index type for strings. |
969 | static constexpr int defaultWidth = 64; |
970 | |
971 | CodeGenSpecifics::Marshalling |
972 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
973 | CodeGenSpecifics::Marshalling marshal; |
974 | TODO(loc, "handle complex argument types" ); |
975 | return marshal; |
976 | } |
977 | |
978 | CodeGenSpecifics::Marshalling |
979 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
980 | CodeGenSpecifics::Marshalling marshal; |
981 | TODO(loc, "handle complex return types" ); |
982 | return marshal; |
983 | } |
984 | }; |
985 | } // namespace |
986 | |
987 | //===----------------------------------------------------------------------===// |
988 | // NVPTX linux target specifics. |
989 | //===----------------------------------------------------------------------===// |
990 | |
991 | namespace { |
992 | struct TargetNVPTX : public GenericTarget<TargetNVPTX> { |
993 | using GenericTarget::GenericTarget; |
994 | |
995 | // Default size (in bits) of the index type for strings. |
996 | static constexpr int defaultWidth = 64; |
997 | |
998 | CodeGenSpecifics::Marshalling |
999 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
1000 | CodeGenSpecifics::Marshalling marshal; |
1001 | TODO(loc, "handle complex argument types" ); |
1002 | return marshal; |
1003 | } |
1004 | |
1005 | CodeGenSpecifics::Marshalling |
1006 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
1007 | CodeGenSpecifics::Marshalling marshal; |
1008 | TODO(loc, "handle complex return types" ); |
1009 | return marshal; |
1010 | } |
1011 | }; |
1012 | } // namespace |
1013 | |
1014 | //===----------------------------------------------------------------------===// |
1015 | // LoongArch64 linux target specifics. |
1016 | //===----------------------------------------------------------------------===// |
1017 | |
1018 | namespace { |
1019 | struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> { |
1020 | using GenericTarget::GenericTarget; |
1021 | |
1022 | static constexpr int defaultWidth = 64; |
1023 | |
1024 | CodeGenSpecifics::Marshalling |
1025 | complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
1026 | CodeGenSpecifics::Marshalling marshal; |
1027 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
1028 | if (sem == &llvm::APFloat::IEEEsingle() || |
1029 | sem == &llvm::APFloat::IEEEdouble()) { |
1030 | // Two distinct element type arguments (re, im) |
1031 | marshal.emplace_back(eleTy, AT{}); |
1032 | marshal.emplace_back(eleTy, AT{}); |
1033 | } else { |
1034 | typeTodo(sem, loc, "argument" ); |
1035 | } |
1036 | return marshal; |
1037 | } |
1038 | |
1039 | CodeGenSpecifics::Marshalling |
1040 | complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
1041 | CodeGenSpecifics::Marshalling marshal; |
1042 | const auto *sem = &floatToSemantics(kindMap, eleTy); |
1043 | if (sem == &llvm::APFloat::IEEEsingle() || |
1044 | sem == &llvm::APFloat::IEEEdouble()) { |
1045 | // Use a type that will be translated into LLVM as: |
1046 | // { t, t } struct of 2 eleTy, byVal |
1047 | marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
1048 | mlir::TypeRange{eleTy, eleTy}), |
1049 | AT{/*alignment=*/0, /*byval=*/true}); |
1050 | } else { |
1051 | typeTodo(sem, loc, "return" ); |
1052 | } |
1053 | return marshal; |
1054 | } |
1055 | }; |
1056 | } // namespace |
1057 | |
1058 | // Instantiate the overloaded target instance based on the triple value. |
1059 | // TODO: Add other targets to this file as needed. |
1060 | std::unique_ptr<fir::CodeGenSpecifics> |
1061 | fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, |
1062 | KindMapping &&kindMap, llvm::StringRef targetCPU, |
1063 | mlir::LLVM::TargetFeaturesAttr targetFeatures, |
1064 | const mlir::DataLayout &dl) { |
1065 | switch (trp.getArch()) { |
1066 | default: |
1067 | break; |
1068 | case llvm::Triple::ArchType::x86: |
1069 | if (trp.isOSWindows()) |
1070 | return std::make_unique<TargetI386Win>(ctx, std::move(trp), |
1071 | std::move(kindMap), targetCPU, |
1072 | targetFeatures, dl); |
1073 | else |
1074 | return std::make_unique<TargetI386>(ctx, std::move(trp), |
1075 | std::move(kindMap), targetCPU, |
1076 | targetFeatures, dl); |
1077 | case llvm::Triple::ArchType::x86_64: |
1078 | if (trp.isOSWindows()) |
1079 | return std::make_unique<TargetX86_64Win>(ctx, std::move(trp), |
1080 | std::move(kindMap), targetCPU, |
1081 | targetFeatures, dl); |
1082 | else |
1083 | return std::make_unique<TargetX86_64>(ctx, std::move(trp), |
1084 | std::move(kindMap), targetCPU, |
1085 | targetFeatures, dl); |
1086 | case llvm::Triple::ArchType::aarch64: |
1087 | return std::make_unique<TargetAArch64>( |
1088 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1089 | case llvm::Triple::ArchType::ppc64: |
1090 | return std::make_unique<TargetPPC64>( |
1091 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1092 | case llvm::Triple::ArchType::ppc64le: |
1093 | return std::make_unique<TargetPPC64le>( |
1094 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1095 | case llvm::Triple::ArchType::sparc: |
1096 | return std::make_unique<TargetSparc>( |
1097 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1098 | case llvm::Triple::ArchType::sparcv9: |
1099 | return std::make_unique<TargetSparcV9>( |
1100 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1101 | case llvm::Triple::ArchType::riscv64: |
1102 | return std::make_unique<TargetRISCV64>( |
1103 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1104 | case llvm::Triple::ArchType::amdgcn: |
1105 | return std::make_unique<TargetAMDGPU>( |
1106 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1107 | case llvm::Triple::ArchType::nvptx64: |
1108 | return std::make_unique<TargetNVPTX>( |
1109 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1110 | case llvm::Triple::ArchType::loongarch64: |
1111 | return std::make_unique<TargetLoongArch64>( |
1112 | ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
1113 | } |
1114 | TODO(mlir::UnknownLoc::get(ctx), "target not implemented" ); |
1115 | } |
1116 | |