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
25using namespace fir;
26
27namespace fir::details {
28llvm::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.
41static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
42 mlir::Type type) {
43 assert(isa_real(type));
44 return mlir::cast<mlir::FloatType>(type).getFloatSemantics();
45}
46
47static void typeTodo(const llvm::fltSemantics *sem, mlir::Location loc,
48 const std::string &context) {
49 if (sem == &llvm::APFloat::IEEEhalf()) {
50 TODO(loc, "COMPLEX(KIND=2): for " + context + " type");
51 } else if (sem == &llvm::APFloat::BFloat()) {
52 TODO(loc, "COMPLEX(KIND=3): " + context + " type");
53 } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
54 TODO(loc, "COMPLEX(KIND=10): " + context + " type");
55 } else {
56 TODO(loc, "complex for this precision for " + context + " type");
57 }
58}
59
60namespace {
61template <typename S>
62struct GenericTarget : public CodeGenSpecifics {
63 using CodeGenSpecifics::CodeGenSpecifics;
64 using AT = CodeGenSpecifics::Attributes;
65
66 mlir::Type complexMemoryType(mlir::Type eleTy) const override {
67 assert(fir::isa_real(eleTy));
68 // Use a type that will be translated into LLVM as:
69 // { t, t } struct of 2 eleTy
70 return mlir::TupleType::get(eleTy.getContext(),
71 mlir::TypeRange{eleTy, eleTy});
72 }
73
74 mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
75 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
76 auto ptrTy = fir::ReferenceType::get(eleTy);
77 // Use a type that will be translated into LLVM as:
78 // { t*, index }
79 return mlir::TupleType::get(eleTy.getContext(),
80 mlir::TypeRange{ptrTy, idxTy});
81 }
82
83 Marshalling boxcharArgumentType(mlir::Type eleTy) const override {
84 CodeGenSpecifics::Marshalling marshal;
85 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
86 auto ptrTy = fir::ReferenceType::get(eleTy);
87 marshal.emplace_back(ptrTy, AT{});
88 // Characters are passed in a split format with all pointers first (in the
89 // declared position) and all LEN arguments appended after all of the dummy
90 // arguments.
91 // NB: Other conventions/ABIs can/should be supported via options.
92 marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
93 /*sret=*/false, /*append=*/true});
94 return marshal;
95 }
96
97 CodeGenSpecifics::Marshalling
98 structArgumentType(mlir::Location loc, fir::RecordType,
99 const Marshalling &) const override {
100 TODO(loc, "passing VALUE BIND(C) derived type for this target");
101 }
102
103 CodeGenSpecifics::Marshalling
104 structReturnType(mlir::Location loc, fir::RecordType ty) const override {
105 TODO(loc, "returning BIND(C) derived type for this target");
106 }
107
108 CodeGenSpecifics::Marshalling
109 integerArgumentType(mlir::Location loc,
110 mlir::IntegerType argTy) const override {
111 CodeGenSpecifics::Marshalling marshal;
112 AT::IntegerExtension intExt = AT::IntegerExtension::None;
113 if (argTy.getWidth() < getCIntTypeWidth()) {
114 // isSigned() and isUnsigned() branches below are dead code currently.
115 // If needed, we can generate calls with signed/unsigned argument types
116 // to more precisely match C side (e.g. for Fortran runtime functions
117 // with 'unsigned short' arguments).
118 if (argTy.isSigned())
119 intExt = AT::IntegerExtension::Sign;
120 else if (argTy.isUnsigned())
121 intExt = AT::IntegerExtension::Zero;
122 else if (argTy.isSignless()) {
123 // Zero extend for 'i1' and sign extend for other types.
124 if (argTy.getWidth() == 1)
125 intExt = AT::IntegerExtension::Zero;
126 else
127 intExt = AT::IntegerExtension::Sign;
128 }
129 }
130
131 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
132 /*sret=*/false, /*append=*/false,
133 /*intExt=*/intExt});
134 return marshal;
135 }
136
137 CodeGenSpecifics::Marshalling
138 integerReturnType(mlir::Location loc,
139 mlir::IntegerType argTy) const override {
140 return integerArgumentType(loc, argTy);
141 }
142
143 // Width of 'int' type is 32-bits for almost all targets, except
144 // for AVR and MSP430 (see TargetInfo initializations
145 // in clang/lib/Basic/Targets).
146 unsigned char getCIntTypeWidth() const override { return 32; }
147};
148} // namespace
149
150//===----------------------------------------------------------------------===//
151// i386 (x86 32 bit) linux target specifics.
152//===----------------------------------------------------------------------===//
153
154namespace {
155struct TargetI386 : public GenericTarget<TargetI386> {
156 using GenericTarget::GenericTarget;
157
158 static constexpr int defaultWidth = 32;
159
160 CodeGenSpecifics::Marshalling
161 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
162 assert(fir::isa_real(eleTy));
163 CodeGenSpecifics::Marshalling marshal;
164 // Use a type that will be translated into LLVM as:
165 // { t, t } struct of 2 eleTy, byval, align 4
166 auto structTy =
167 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
168 marshal.emplace_back(fir::ReferenceType::get(structTy),
169 AT{/*alignment=*/4, /*byval=*/true});
170 return marshal;
171 }
172
173 CodeGenSpecifics::Marshalling
174 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
175 assert(fir::isa_real(eleTy));
176 CodeGenSpecifics::Marshalling marshal;
177 const auto *sem = &floatToSemantics(kindMap, eleTy);
178 if (sem == &llvm::APFloat::IEEEsingle()) {
179 // i64 pack both floats in a 64-bit GPR
180 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
181 AT{});
182 } else if (sem == &llvm::APFloat::IEEEdouble()) {
183 // Use a type that will be translated into LLVM as:
184 // { t, t } struct of 2 eleTy, sret, align 4
185 auto structTy = mlir::TupleType::get(eleTy.getContext(),
186 mlir::TypeRange{eleTy, eleTy});
187 marshal.emplace_back(fir::ReferenceType::get(structTy),
188 AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
189 } else {
190 typeTodo(sem, loc, "return");
191 }
192 return marshal;
193 }
194};
195} // namespace
196
197//===----------------------------------------------------------------------===//
198// i386 (x86 32 bit) Windows target specifics.
199//===----------------------------------------------------------------------===//
200
201namespace {
202struct TargetI386Win : public GenericTarget<TargetI386Win> {
203 using GenericTarget::GenericTarget;
204
205 static constexpr int defaultWidth = 32;
206
207 CodeGenSpecifics::Marshalling
208 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
209 CodeGenSpecifics::Marshalling marshal;
210 // Use a type that will be translated into LLVM as:
211 // { t, t } struct of 2 eleTy, byval, align 4
212 auto structTy =
213 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
214 marshal.emplace_back(fir::ReferenceType::get(structTy),
215 AT{/*align=*/4, /*byval=*/true});
216 return marshal;
217 }
218
219 CodeGenSpecifics::Marshalling
220 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
221 CodeGenSpecifics::Marshalling marshal;
222 const auto *sem = &floatToSemantics(kindMap, eleTy);
223 if (sem == &llvm::APFloat::IEEEsingle()) {
224 // i64 pack both floats in a 64-bit GPR
225 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
226 AT{});
227 } else if (sem == &llvm::APFloat::IEEEdouble()) {
228 // Use a type that will be translated into LLVM as:
229 // { double, double } struct of 2 double, sret, align 8
230 marshal.emplace_back(
231 fir::ReferenceType::get(mlir::TupleType::get(
232 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
233 AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
234 } else if (sem == &llvm::APFloat::IEEEquad()) {
235 // Use a type that will be translated into LLVM as:
236 // { fp128, fp128 } struct of 2 fp128, sret, align 16
237 marshal.emplace_back(
238 fir::ReferenceType::get(mlir::TupleType::get(
239 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
240 AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
241 } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
242 // Use a type that will be translated into LLVM as:
243 // { x86_fp80, x86_fp80 } struct of 2 x86_fp80, sret, align 4
244 marshal.emplace_back(
245 fir::ReferenceType::get(mlir::TupleType::get(
246 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
247 AT{/*align=*/4, /*byval=*/false, /*sret=*/true});
248 } else {
249 typeTodo(sem, loc, "return");
250 }
251 return marshal;
252 }
253};
254} // namespace
255
256//===----------------------------------------------------------------------===//
257// x86_64 (x86 64 bit) linux target specifics.
258//===----------------------------------------------------------------------===//
259
260namespace {
261struct TargetX86_64 : public GenericTarget<TargetX86_64> {
262 using GenericTarget::GenericTarget;
263
264 static constexpr int defaultWidth = 64;
265
266 CodeGenSpecifics::Marshalling
267 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
268 CodeGenSpecifics::Marshalling marshal;
269 const auto *sem = &floatToSemantics(kindMap, eleTy);
270 if (sem == &llvm::APFloat::IEEEsingle()) {
271 // <2 x t> vector of 2 eleTy
272 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
273 } else if (sem == &llvm::APFloat::IEEEdouble()) {
274 // FIXME: In case of SSE register exhaustion, the ABI here may be
275 // incorrect since LLVM may pass the real via register and the imaginary
276 // part via the stack while the ABI it should be all in register or all
277 // in memory. Register occupancy must be analyzed here.
278 // two distinct double arguments
279 marshal.emplace_back(eleTy, AT{});
280 marshal.emplace_back(eleTy, AT{});
281 } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
282 // Use a type that will be translated into LLVM as:
283 // { x86_fp80, x86_fp80 } struct of 2 fp128, byval, align 16
284 marshal.emplace_back(
285 fir::ReferenceType::get(mlir::TupleType::get(
286 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
287 AT{/*align=*/16, /*byval=*/true});
288 } else if (sem == &llvm::APFloat::IEEEquad()) {
289 // Use a type that will be translated into LLVM as:
290 // { fp128, fp128 } struct of 2 fp128, byval, align 16
291 marshal.emplace_back(
292 fir::ReferenceType::get(mlir::TupleType::get(
293 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
294 AT{/*align=*/16, /*byval=*/true});
295 } else {
296 typeTodo(sem, loc, "argument");
297 }
298 return marshal;
299 }
300
301 CodeGenSpecifics::Marshalling
302 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
303 CodeGenSpecifics::Marshalling marshal;
304 const auto *sem = &floatToSemantics(kindMap, eleTy);
305 if (sem == &llvm::APFloat::IEEEsingle()) {
306 // <2 x t> vector of 2 eleTy
307 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
308 } else if (sem == &llvm::APFloat::IEEEdouble()) {
309 // Use a type that will be translated into LLVM as:
310 // { double, double } struct of 2 double
311 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
312 mlir::TypeRange{eleTy, eleTy}),
313 AT{});
314 } else if (sem == &llvm::APFloat::x87DoubleExtended()) {
315 // { x86_fp80, x86_fp80 }
316 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
317 mlir::TypeRange{eleTy, eleTy}),
318 AT{});
319 } else if (sem == &llvm::APFloat::IEEEquad()) {
320 // Use a type that will be translated into LLVM as:
321 // { fp128, fp128 } struct of 2 fp128, sret, align 16
322 marshal.emplace_back(
323 fir::ReferenceType::get(mlir::TupleType::get(
324 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
325 AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
326 } else {
327 typeTodo(sem, loc, "return");
328 }
329 return marshal;
330 }
331
332 /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3.
333 enum ArgClass {
334 Integer = 0,
335 SSE,
336 SSEUp,
337 X87,
338 X87Up,
339 ComplexX87,
340 NoClass,
341 Memory
342 };
343
344 /// Classify an argument type or a field of an aggregate type argument.
345 /// See System V ABI version 1.0 section 3.2.3.
346 /// The Lo and Hi class are set to the class of the lower eight eightbytes
347 /// and upper eight eightbytes on return.
348 /// If this is called for an aggregate field, the caller is responsible to
349 /// do the post-merge.
350 void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset,
351 ArgClass &Lo, ArgClass &Hi) const {
352 Hi = Lo = ArgClass::NoClass;
353 ArgClass &current = byteOffset < 8 ? Lo : Hi;
354 // System V AMD64 ABI 3.2.3. version 1.0
355 llvm::TypeSwitch<mlir::Type>(type)
356 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
357 if (intTy.getWidth() == 128)
358 Hi = Lo = ArgClass::Integer;
359 else
360 current = ArgClass::Integer;
361 })
362 .template Case<mlir::FloatType>([&](mlir::Type floatTy) {
363 const auto *sem = &floatToSemantics(kindMap, floatTy);
364 if (sem == &llvm::APFloat::x87DoubleExtended()) {
365 Lo = ArgClass::X87;
366 Hi = ArgClass::X87Up;
367 } else if (sem == &llvm::APFloat::IEEEquad()) {
368 Lo = ArgClass::SSE;
369 Hi = ArgClass::SSEUp;
370 } else {
371 current = ArgClass::SSE;
372 }
373 })
374 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
375 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
376 if (sem == &llvm::APFloat::x87DoubleExtended()) {
377 current = ArgClass::ComplexX87;
378 } else {
379 fir::SequenceType::Shape shape{2};
380 classifyArray(loc,
381 fir::SequenceType::get(shape, cmplx.getElementType()),
382 byteOffset, Lo, Hi);
383 }
384 })
385 .template Case<fir::LogicalType>([&](fir::LogicalType logical) {
386 if (kindMap.getLogicalBitsize(logical.getFKind()) == 128)
387 Hi = Lo = ArgClass::Integer;
388 else
389 current = ArgClass::Integer;
390 })
391 .template Case<fir::CharacterType>(
392 [&](fir::CharacterType character) { current = ArgClass::Integer; })
393 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
394 // Array component.
395 classifyArray(loc, seqTy, byteOffset, Lo, Hi);
396 })
397 .template Case<fir::RecordType>([&](fir::RecordType recTy) {
398 // Component that is a derived type.
399 classifyStruct(loc, recTy, byteOffset, Lo, Hi);
400 })
401 .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
402 // Previously marshalled SSE eight byte for a previous struct
403 // argument.
404 auto *sem = fir::isa_real(vecTy.getEleTy())
405 ? &floatToSemantics(kindMap, vecTy.getEleTy())
406 : nullptr;
407 // Not expecting to hit this todo in standard code (it would
408 // require some vector type extension).
409 if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) &&
410 !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4))
411 TODO(loc, "passing vector argument to C by value");
412 current = SSE;
413 })
414 .Default([&](mlir::Type ty) {
415 if (fir::conformsWithPassByRef(ty))
416 current = ArgClass::Integer; // Pointers.
417 else
418 TODO(loc, "unsupported component type for BIND(C), VALUE derived "
419 "type argument");
420 });
421 }
422
423 // Classify fields of a derived type starting at \p offset. Returns the new
424 // offset. Post-merge is left to the caller.
425 std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy,
426 std::uint64_t byteOffset, ArgClass &Lo,
427 ArgClass &Hi) const {
428 for (auto component : recTy.getTypeList()) {
429 if (byteOffset > 16) {
430 // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger
431 // than 16 bytes here, it is not a single _m256 and or _m512 entity
432 // that could fit in AVX registers.
433 Lo = Hi = ArgClass::Memory;
434 return byteOffset;
435 }
436 mlir::Type compType = component.second;
437 auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash(
438 loc, compType, getDataLayout(), kindMap);
439 byteOffset = llvm::alignTo(byteOffset, compAlign);
440 ArgClass LoComp, HiComp;
441 classify(loc, compType, byteOffset, LoComp, HiComp);
442 Lo = mergeClass(Lo, LoComp);
443 Hi = mergeClass(Hi, HiComp);
444 byteOffset = byteOffset + llvm::alignTo(compSize, compAlign);
445 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
446 return byteOffset;
447 }
448 return byteOffset;
449 }
450
451 // Classify fields of a constant size array type starting at \p offset.
452 // Returns the new offset. Post-merge is left to the caller.
453 void classifyArray(mlir::Location loc, fir::SequenceType seqTy,
454 std::uint64_t byteOffset, ArgClass &Lo,
455 ArgClass &Hi) const {
456 mlir::Type eleTy = seqTy.getEleTy();
457 const std::uint64_t arraySize = seqTy.getConstantArraySize();
458 auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash(
459 loc, eleTy, getDataLayout(), kindMap);
460 std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign);
461 for (std::uint64_t i = 0; i < arraySize; ++i) {
462 byteOffset = llvm::alignTo(byteOffset, eleAlign);
463 if (byteOffset > 16) {
464 // See 3.2.3 p. 1 and note 15. Same as in classifyStruct.
465 Lo = Hi = ArgClass::Memory;
466 return;
467 }
468 ArgClass LoComp, HiComp;
469 classify(loc, eleTy, byteOffset, LoComp, HiComp);
470 Lo = mergeClass(accum: Lo, field: LoComp);
471 Hi = mergeClass(accum: Hi, field: HiComp);
472 byteOffset = byteOffset + eleStorageSize;
473 if (Lo == ArgClass::Memory || Hi == ArgClass::Memory)
474 return;
475 }
476 }
477
478 // Goes through the previously marshalled arguments and count the
479 // register occupancy to check if there are enough registers left.
480 bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters,
481 int neededSSERegisters,
482 const Marshalling &previousArguments) const {
483 int availIntRegisters = 6;
484 int availSSERegisters = 8;
485 for (auto typeAndAttr : previousArguments) {
486 const auto &attr = std::get<Attributes>(typeAndAttr);
487 if (attr.isByVal())
488 continue; // Previous argument passed on the stack.
489 ArgClass Lo, Hi;
490 Lo = Hi = ArgClass::NoClass;
491 classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi);
492 // post merge is not needed here since previous aggregate arguments
493 // were marshalled into simpler arguments.
494 if (Lo == ArgClass::Integer)
495 --availIntRegisters;
496 else if (Lo == SSE)
497 --availSSERegisters;
498 if (Hi == ArgClass::Integer)
499 --availIntRegisters;
500 else if (Hi == ArgClass::SSE)
501 --availSSERegisters;
502 }
503 return availSSERegisters >= neededSSERegisters &&
504 availIntRegisters >= neededIntRegisters;
505 }
506
507 /// Argument class merging as described in System V ABI 3.2.3 point 4.
508 ArgClass mergeClass(ArgClass accum, ArgClass field) const {
509 assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) &&
510 "Invalid accumulated classification during merge.");
511 if (accum == field || field == NoClass)
512 return accum;
513 if (field == ArgClass::Memory)
514 return ArgClass::Memory;
515 if (accum == NoClass)
516 return field;
517 if (accum == Integer || field == Integer)
518 return ArgClass::Integer;
519 if (field == ArgClass::X87 || field == ArgClass::X87Up ||
520 field == ArgClass::ComplexX87 || accum == ArgClass::X87 ||
521 accum == ArgClass::X87Up)
522 return Memory;
523 return SSE;
524 }
525
526 /// Argument class post merging as described in System V ABI 3.2.3 point 5.
527 void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const {
528 if (Hi == ArgClass::Memory)
529 Lo = ArgClass::Memory;
530 if (Hi == ArgClass::X87Up && Lo != ArgClass::X87)
531 Lo = ArgClass::Memory;
532 if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp))
533 Lo = ArgClass::Memory;
534 if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE)
535 Hi = SSE;
536 }
537
538 /// When \p recTy is a one field record type that can be passed
539 /// like the field on its own, returns the field type. Returns
540 /// a null type otherwise.
541 mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy,
542 bool allowComplex = false) const {
543 auto typeList = recTy.getTypeList();
544 if (typeList.size() != 1)
545 return {};
546 mlir::Type fieldType = typeList[0].second;
547 if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::LogicalType>(
548 fieldType))
549 return fieldType;
550 if (allowComplex && mlir::isa<mlir::ComplexType>(fieldType))
551 return fieldType;
552 if (mlir::isa<fir::CharacterType>(fieldType)) {
553 // Only CHARACTER(1) are expected in BIND(C) contexts, which is the only
554 // contexts where derived type may be passed in registers.
555 assert(mlir::cast<fir::CharacterType>(fieldType).getLen() == 1 &&
556 "fir.type value arg character components must have length 1");
557 return fieldType;
558 }
559 // Complex field that needs to be split, or array.
560 return {};
561 }
562
563 mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context,
564 ArgClass argClass,
565 std::uint64_t partByteSize) const {
566 if (argClass == ArgClass::SSE) {
567 if (partByteSize > 16)
568 TODO(loc, "passing struct as a real > 128 bits in register");
569 // Clang uses vector type when several fp fields are marshalled
570 // into a single SSE register (like <n x smallest fp field> ).
571 // It should make no difference from an ABI point of view to just
572 // select an fp type of the right size, and it makes things simpler
573 // here.
574 if (partByteSize > 8)
575 return mlir::Float128Type::get(context);
576 if (partByteSize > 4)
577 return mlir::Float64Type::get(context);
578 if (partByteSize > 2)
579 return mlir::Float32Type::get(context);
580 return mlir::Float16Type::get(context);
581 }
582 assert(partByteSize <= 8 &&
583 "expect integer part of aggregate argument to fit into eight bytes");
584 if (partByteSize > 4)
585 return mlir::IntegerType::get(context, 64);
586 if (partByteSize > 2)
587 return mlir::IntegerType::get(context, 32);
588 if (partByteSize > 1)
589 return mlir::IntegerType::get(context, 16);
590 return mlir::IntegerType::get(context, 8);
591 }
592
593 /// Marshal a derived type passed by value like a C struct.
594 CodeGenSpecifics::Marshalling
595 structArgumentType(mlir::Location loc, fir::RecordType recTy,
596 const Marshalling &previousArguments) const override {
597 std::uint64_t byteOffset = 0;
598 ArgClass Lo, Hi;
599 Lo = Hi = ArgClass::NoClass;
600 byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
601 postMerge(byteSize: byteOffset, Lo, Hi);
602 if (Lo == ArgClass::Memory || Lo == ArgClass::X87 ||
603 Lo == ArgClass::ComplexX87)
604 return passOnTheStack(loc, recTy, /*isResult=*/false);
605 int neededIntRegisters = 0;
606 int neededSSERegisters = 0;
607 if (Lo == ArgClass::SSE)
608 ++neededSSERegisters;
609 else if (Lo == ArgClass::Integer)
610 ++neededIntRegisters;
611 if (Hi == ArgClass::SSE)
612 ++neededSSERegisters;
613 else if (Hi == ArgClass::Integer)
614 ++neededIntRegisters;
615 // C struct should not be split into LLVM registers if LLVM codegen is not
616 // able to later assign actual registers to all of them (struct passing is
617 // all in registers or all on the stack).
618 if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters,
619 previousArguments))
620 return passOnTheStack(loc, recTy, /*isResult=*/false);
621
622 if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
623 CodeGenSpecifics::Marshalling marshal;
624 marshal.emplace_back(fieldType, AT{});
625 return marshal;
626 }
627 if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
628 // Pass a single integer or floating point argument.
629 mlir::Type lowType =
630 pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset);
631 CodeGenSpecifics::Marshalling marshal;
632 marshal.emplace_back(lowType, AT{});
633 return marshal;
634 }
635 // Split into two integer or floating point arguments.
636 // Note that for the first argument, this will always pick i64 or f64 which
637 // may be bigger than needed if some struct padding ends the first eight
638 // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and
639 // semantic point of view, but it may not match the LLVM IR interface clang
640 // would produce for the equivalent C code (the assembly will still be
641 // compatible). This allows keeping the logic simpler here since it
642 // avoids computing the "data" size of the Lo part.
643 mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u);
644 mlir::Type hiType =
645 pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u);
646 CodeGenSpecifics::Marshalling marshal;
647 marshal.emplace_back(lowType, AT{});
648 marshal.emplace_back(hiType, AT{});
649 return marshal;
650 }
651
652 CodeGenSpecifics::Marshalling
653 structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
654 std::uint64_t byteOffset = 0;
655 ArgClass Lo, Hi;
656 Lo = Hi = ArgClass::NoClass;
657 byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
658 mlir::MLIRContext *context = recTy.getContext();
659 postMerge(byteSize: byteOffset, Lo, Hi);
660 if (Lo == ArgClass::Memory)
661 return passOnTheStack(loc, recTy, /*isResult=*/true);
662
663 // Note that X87/ComplexX87 are passed in memory, but returned via %st0
664 // %st1 registers. Here, they are returned as fp80 or {fp80, fp80} by
665 // passAsFieldIfOneFieldStruct, and LLVM will use the expected registers.
666
667 // Note that {_Complex long double} is not 100% clear from an ABI
668 // perspective because the aggregate post merger rules say it should be
669 // passed in memory because it is bigger than 2 eight bytes. This has the
670 // funny effect of
671 // {_Complex long double} return to be dealt with differently than
672 // _Complex long double.
673
674 if (auto fieldType =
675 passAsFieldIfOneFieldStruct(recTy, /*allowComplex=*/true)) {
676 if (auto complexType = mlir::dyn_cast<mlir::ComplexType>(fieldType))
677 return complexReturnType(loc, complexType.getElementType());
678 CodeGenSpecifics::Marshalling marshal;
679 marshal.emplace_back(fieldType, AT{});
680 return marshal;
681 }
682
683 if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
684 // Return a single integer or floating point argument.
685 mlir::Type lowType = pickLLVMArgType(loc, context, Lo, byteOffset);
686 CodeGenSpecifics::Marshalling marshal;
687 marshal.emplace_back(lowType, AT{});
688 return marshal;
689 }
690 // Will be returned in two different registers. Generate {lowTy, HiTy} for
691 // the LLVM IR result type.
692 CodeGenSpecifics::Marshalling marshal;
693 mlir::Type lowType = pickLLVMArgType(loc, context, Lo, 8u);
694 mlir::Type hiType = pickLLVMArgType(loc, context, Hi, byteOffset - 8u);
695 marshal.emplace_back(mlir::TupleType::get(context, {lowType, hiType}),
696 AT{});
697 return marshal;
698 }
699
700 /// Marshal an argument that must be passed on the stack.
701 CodeGenSpecifics::Marshalling
702 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
703 CodeGenSpecifics::Marshalling marshal;
704 auto sizeAndAlign =
705 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
706 // The stack is always 8 byte aligned (note 14 in 3.2.3).
707 unsigned short align =
708 std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
709 marshal.emplace_back(fir::ReferenceType::get(ty),
710 AT{align, /*byval=*/!isResult, /*sret=*/isResult});
711 return marshal;
712 }
713};
714} // namespace
715
716//===----------------------------------------------------------------------===//
717// x86_64 (x86 64 bit) Windows target specifics.
718//===----------------------------------------------------------------------===//
719
720namespace {
721struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> {
722 using GenericTarget::GenericTarget;
723
724 static constexpr int defaultWidth = 64;
725
726 CodeGenSpecifics::Marshalling
727 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
728 CodeGenSpecifics::Marshalling marshal;
729 const auto *sem = &floatToSemantics(kindMap, eleTy);
730 if (sem == &llvm::APFloat::IEEEsingle()) {
731 // i64 pack both floats in a 64-bit GPR
732 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
733 AT{});
734 } else if (sem == &llvm::APFloat::IEEEdouble()) {
735 // Use a type that will be translated into LLVM as:
736 // { double, double } struct of 2 double, byval, align 8
737 marshal.emplace_back(
738 fir::ReferenceType::get(mlir::TupleType::get(
739 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
740 AT{/*align=*/8, /*byval=*/true});
741 } else if (sem == &llvm::APFloat::IEEEquad() ||
742 sem == &llvm::APFloat::x87DoubleExtended()) {
743 // Use a type that will be translated into LLVM as:
744 // { t, t } struct of 2 eleTy, byval, align 16
745 marshal.emplace_back(
746 fir::ReferenceType::get(mlir::TupleType::get(
747 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
748 AT{/*align=*/16, /*byval=*/true});
749 } else {
750 typeTodo(sem, loc, "argument");
751 }
752 return marshal;
753 }
754
755 CodeGenSpecifics::Marshalling
756 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
757 CodeGenSpecifics::Marshalling marshal;
758 const auto *sem = &floatToSemantics(kindMap, eleTy);
759 if (sem == &llvm::APFloat::IEEEsingle()) {
760 // i64 pack both floats in a 64-bit GPR
761 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
762 AT{});
763 } else if (sem == &llvm::APFloat::IEEEdouble()) {
764 // Use a type that will be translated into LLVM as:
765 // { double, double } struct of 2 double, sret, align 8
766 marshal.emplace_back(
767 fir::ReferenceType::get(mlir::TupleType::get(
768 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
769 AT{/*align=*/8, /*byval=*/false, /*sret=*/true});
770 } else if (sem == &llvm::APFloat::IEEEquad() ||
771 sem == &llvm::APFloat::x87DoubleExtended()) {
772 // Use a type that will be translated into LLVM as:
773 // { t, t } struct of 2 eleTy, sret, align 16
774 marshal.emplace_back(
775 fir::ReferenceType::get(mlir::TupleType::get(
776 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
777 AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
778 } else {
779 typeTodo(sem, loc, "return");
780 }
781 return marshal;
782 }
783};
784} // namespace
785
786//===----------------------------------------------------------------------===//
787// AArch64 target specifics.
788//===----------------------------------------------------------------------===//
789
790namespace {
791// AArch64 procedure call standard:
792// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing
793struct TargetAArch64 : public GenericTarget<TargetAArch64> {
794 using GenericTarget::GenericTarget;
795
796 static constexpr int defaultWidth = 64;
797
798 CodeGenSpecifics::Marshalling
799 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
800 CodeGenSpecifics::Marshalling marshal;
801 const auto *sem = &floatToSemantics(kindMap, eleTy);
802 if (sem == &llvm::APFloat::IEEEsingle() ||
803 sem == &llvm::APFloat::IEEEdouble() ||
804 sem == &llvm::APFloat::IEEEquad()) {
805 // [2 x t] array of 2 eleTy
806 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
807 } else {
808 typeTodo(sem, loc, "argument");
809 }
810 return marshal;
811 }
812
813 CodeGenSpecifics::Marshalling
814 integerArgumentType(mlir::Location loc,
815 mlir::IntegerType argTy) const override {
816 if (argTy.getWidth() < getCIntTypeWidth() && argTy.isSignless()) {
817 AT::IntegerExtension intExt;
818 if (argTy.getWidth() == 1) {
819 // Zero extend for 'i1'.
820 intExt = AT::IntegerExtension::Zero;
821 } else {
822 if (triple.isOSDarwin()) {
823 // On Darwin, sign extend. The apple developer guide specifies this as
824 // a divergence from the AArch64PCS:
825 // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-arguments-to-functions-correctly
826 intExt = AT::IntegerExtension::Sign;
827 } else {
828 // On linux, pass directly and do not extend.
829 intExt = AT::IntegerExtension::None;
830 }
831 }
832 CodeGenSpecifics::Marshalling marshal;
833 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
834 /*sret=*/false, /*append=*/false,
835 /*intExt=*/intExt});
836 return marshal;
837 }
838 return GenericTarget::integerArgumentType(loc, argTy);
839 }
840
841 CodeGenSpecifics::Marshalling
842 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
843 CodeGenSpecifics::Marshalling marshal;
844 const auto *sem = &floatToSemantics(kindMap, eleTy);
845 if (sem == &llvm::APFloat::IEEEsingle() ||
846 sem == &llvm::APFloat::IEEEdouble() ||
847 sem == &llvm::APFloat::IEEEquad()) {
848 // Use a type that will be translated into LLVM as:
849 // { t, t } struct of 2 eleTy
850 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
851 mlir::TypeRange{eleTy, eleTy}),
852 AT{});
853 } else {
854 typeTodo(sem, loc, "return");
855 }
856 return marshal;
857 }
858
859 // Flatten a RecordType::TypeList containing more record types or array type
860 static std::optional<std::vector<mlir::Type>>
861 flattenTypeList(const RecordType::TypeList &types) {
862 std::vector<mlir::Type> flatTypes;
863 // The flat list will be at least the same size as the non-flat list.
864 flatTypes.reserve(types.size());
865 for (auto [c, type] : types) {
866 // Flatten record type
867 if (auto recTy = mlir::dyn_cast<RecordType>(type)) {
868 auto subTypeList = flattenTypeList(recTy.getTypeList());
869 if (!subTypeList)
870 return std::nullopt;
871 llvm::copy(*subTypeList, std::back_inserter(flatTypes));
872 continue;
873 }
874
875 // Flatten array type
876 if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {
877 if (seqTy.hasDynamicExtents())
878 return std::nullopt;
879 std::size_t n = seqTy.getConstantArraySize();
880 auto eleTy = seqTy.getElementType();
881 // Flatten array of record types
882 if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) {
883 auto subTypeList = flattenTypeList(recTy.getTypeList());
884 if (!subTypeList)
885 return std::nullopt;
886 for (std::size_t i = 0; i < n; ++i)
887 llvm::copy(*subTypeList, std::back_inserter(flatTypes));
888 } else {
889 std::fill_n(std::back_inserter(flatTypes),
890 seqTy.getConstantArraySize(), eleTy);
891 }
892 continue;
893 }
894
895 // Other types are already flat
896 flatTypes.push_back(type);
897 }
898 return flatTypes;
899 }
900
901 // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An
902 // HFA is a record type with up to 4 floating-point members of the same type.
903 static std::optional<int> usedRegsForHFA(fir::RecordType ty) {
904 RecordType::TypeList types = ty.getTypeList();
905 if (types.empty() || types.size() > 4)
906 return std::nullopt;
907
908 std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);
909 if (!flatTypes || flatTypes->size() > 4) {
910 return std::nullopt;
911 }
912
913 if (!isa_real(flatTypes->front())) {
914 return std::nullopt;
915 }
916
917 return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()}
918 : std::nullopt;
919 }
920
921 struct NRegs {
922 int n{0};
923 bool isSimd{false};
924 };
925
926 NRegs usedRegsForRecordType(mlir::Location loc, fir::RecordType type) const {
927 if (std::optional<int> size = usedRegsForHFA(type))
928 return {.n: *size, .isSimd: true};
929
930 auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash(
931 loc, type, getDataLayout(), kindMap);
932
933 if (size <= 16)
934 return {static_cast<int>((size + 7) / 8), false};
935
936 // Pass on the stack, i.e. no registers used
937 return {};
938 }
939
940 NRegs usedRegsForType(mlir::Location loc, mlir::Type type) const {
941 return llvm::TypeSwitch<mlir::Type, NRegs>(type)
942 .Case<mlir::IntegerType>([&](auto intTy) {
943 return intTy.getWidth() == 128 ? NRegs{2, false} : NRegs{1, false};
944 })
945 .Case<mlir::FloatType>([&](auto) { return NRegs{1, true}; })
946 .Case<mlir::ComplexType>([&](auto) { return NRegs{2, true}; })
947 .Case<fir::LogicalType>([&](auto) { return NRegs{1, false}; })
948 .Case<fir::CharacterType>([&](auto) { return NRegs{1, false}; })
949 .Case<fir::SequenceType>([&](auto ty) {
950 assert(ty.getShape().size() == 1 &&
951 "invalid array dimensions in BIND(C)");
952 NRegs nregs = usedRegsForType(loc, ty.getEleTy());
953 nregs.n *= ty.getShape()[0];
954 return nregs;
955 })
956 .Case<fir::RecordType>(
957 [&](auto ty) { return usedRegsForRecordType(loc, ty); })
958 .Case<fir::VectorType>([&](auto) {
959 TODO(loc, "passing vector argument to C by value is not supported");
960 return NRegs{};
961 })
962 .Default([&](auto ty) {
963 if (fir::conformsWithPassByRef(ty))
964 return NRegs{1, false}; // Pointers take 1 integer register
965 TODO(loc, "unsupported component type for BIND(C), VALUE derived "
966 "type argument");
967 return NRegs{};
968 });
969 }
970
971 bool hasEnoughRegisters(mlir::Location loc, fir::RecordType type,
972 const Marshalling &previousArguments) const {
973 int availIntRegisters = 8;
974 int availSIMDRegisters = 8;
975
976 // Check previous arguments to see how many registers are used already
977 for (auto [type, attr] : previousArguments) {
978 if (availIntRegisters <= 0 || availSIMDRegisters <= 0)
979 break;
980
981 if (attr.isByVal())
982 continue; // Previous argument passed on the stack
983
984 NRegs nregs = usedRegsForType(loc, type);
985 if (nregs.isSimd)
986 availSIMDRegisters -= nregs.n;
987 else
988 availIntRegisters -= nregs.n;
989 }
990
991 NRegs nregs = usedRegsForRecordType(loc, type);
992
993 if (nregs.isSimd)
994 return nregs.n <= availSIMDRegisters;
995
996 return nregs.n <= availIntRegisters;
997 }
998
999 CodeGenSpecifics::Marshalling
1000 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
1001 CodeGenSpecifics::Marshalling marshal;
1002 auto sizeAndAlign =
1003 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
1004 // The stack is always 8 byte aligned
1005 unsigned short align =
1006 std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
1007 marshal.emplace_back(fir::ReferenceType::get(ty),
1008 AT{align, /*byval=*/!isResult, /*sret=*/isResult});
1009 return marshal;
1010 }
1011
1012 CodeGenSpecifics::Marshalling
1013 structType(mlir::Location loc, fir::RecordType type, bool isResult) const {
1014 NRegs nregs = usedRegsForRecordType(loc, type);
1015
1016 // If the type needs no registers it must need to be passed on the stack
1017 if (nregs.n == 0)
1018 return passOnTheStack(loc, type, isResult);
1019
1020 CodeGenSpecifics::Marshalling marshal;
1021
1022 mlir::Type pcsType;
1023 if (nregs.isSimd) {
1024 pcsType = type;
1025 } else {
1026 pcsType = fir::SequenceType::get(
1027 nregs.n, mlir::IntegerType::get(type.getContext(), 64));
1028 }
1029
1030 marshal.emplace_back(pcsType, AT{});
1031 return marshal;
1032 }
1033
1034 CodeGenSpecifics::Marshalling
1035 structArgumentType(mlir::Location loc, fir::RecordType ty,
1036 const Marshalling &previousArguments) const override {
1037 if (!hasEnoughRegisters(loc, ty, previousArguments)) {
1038 return passOnTheStack(loc, ty, /*isResult=*/false);
1039 }
1040
1041 return structType(loc, ty, /*isResult=*/false);
1042 }
1043
1044 CodeGenSpecifics::Marshalling
1045 structReturnType(mlir::Location loc, fir::RecordType ty) const override {
1046 return structType(loc, ty, /*isResult=*/true);
1047 }
1048};
1049} // namespace
1050
1051//===----------------------------------------------------------------------===//
1052// PPC (AIX 32 bit) target specifics.
1053//===----------------------------------------------------------------------===//
1054namespace {
1055struct TargetPPC : public GenericTarget<TargetPPC> {
1056 using GenericTarget::GenericTarget;
1057
1058 static constexpr int defaultWidth = 32;
1059
1060 CodeGenSpecifics::Marshalling
1061 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
1062 CodeGenSpecifics::Marshalling marshal;
1063 // two distinct element type arguments (re, im)
1064 marshal.emplace_back(eleTy, AT{});
1065 marshal.emplace_back(eleTy, AT{});
1066 return marshal;
1067 }
1068
1069 CodeGenSpecifics::Marshalling
1070 complexReturnType(mlir::Location, mlir::Type eleTy) const override {
1071 CodeGenSpecifics::Marshalling marshal;
1072 // Use a type that will be translated into LLVM as:
1073 // { t, t } struct of 2 element type
1074 marshal.emplace_back(
1075 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1076 AT{});
1077 return marshal;
1078 }
1079};
1080} // namespace
1081
1082//===----------------------------------------------------------------------===//
1083// PPC64 (AIX 64 bit) target specifics.
1084//===----------------------------------------------------------------------===//
1085
1086namespace {
1087struct TargetPPC64 : public GenericTarget<TargetPPC64> {
1088 using GenericTarget::GenericTarget;
1089
1090 static constexpr int defaultWidth = 64;
1091
1092 CodeGenSpecifics::Marshalling
1093 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
1094 CodeGenSpecifics::Marshalling marshal;
1095 // two distinct element type arguments (re, im)
1096 marshal.emplace_back(eleTy, AT{});
1097 marshal.emplace_back(eleTy, AT{});
1098 return marshal;
1099 }
1100
1101 CodeGenSpecifics::Marshalling
1102 complexReturnType(mlir::Location, mlir::Type eleTy) const override {
1103 CodeGenSpecifics::Marshalling marshal;
1104 // Use a type that will be translated into LLVM as:
1105 // { t, t } struct of 2 element type
1106 marshal.emplace_back(
1107 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1108 AT{});
1109 return marshal;
1110 }
1111
1112 CodeGenSpecifics::Marshalling
1113 structType(mlir::Location loc, fir::RecordType ty, bool isResult) const {
1114 CodeGenSpecifics::Marshalling marshal;
1115 auto sizeAndAlign{
1116 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
1117 unsigned short align{
1118 std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
1119 marshal.emplace_back(fir::ReferenceType::get(ty),
1120 AT{align, /*byval*/ !isResult, /*sret*/ isResult});
1121 return marshal;
1122 }
1123
1124 CodeGenSpecifics::Marshalling
1125 structArgumentType(mlir::Location loc, fir::RecordType ty,
1126 const Marshalling &previousArguments) const override {
1127 return structType(loc, ty, false);
1128 }
1129
1130 CodeGenSpecifics::Marshalling
1131 structReturnType(mlir::Location loc, fir::RecordType ty) const override {
1132 return structType(loc, ty, true);
1133 }
1134};
1135} // namespace
1136
1137//===----------------------------------------------------------------------===//
1138// PPC64le linux target specifics.
1139//===----------------------------------------------------------------------===//
1140
1141namespace {
1142struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
1143 using GenericTarget::GenericTarget;
1144
1145 static constexpr int defaultWidth{64};
1146
1147 CodeGenSpecifics::Marshalling
1148 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
1149 CodeGenSpecifics::Marshalling marshal;
1150 // two distinct element type arguments (re, im)
1151 marshal.emplace_back(eleTy, AT{});
1152 marshal.emplace_back(eleTy, AT{});
1153 return marshal;
1154 }
1155
1156 CodeGenSpecifics::Marshalling
1157 complexReturnType(mlir::Location, mlir::Type eleTy) const override {
1158 CodeGenSpecifics::Marshalling marshal;
1159 // Use a type that will be translated into LLVM as:
1160 // { t, t } struct of 2 element type
1161 marshal.emplace_back(
1162 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1163 AT{});
1164 return marshal;
1165 }
1166
1167 unsigned getElemWidth(mlir::Type ty) const {
1168 unsigned width{};
1169 llvm::TypeSwitch<mlir::Type>(ty)
1170 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
1171 auto elemType{
1172 mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
1173 width = elemType.getWidth();
1174 })
1175 .template Case<mlir::FloatType>(
1176 [&](mlir::FloatType real) { width = real.getWidth(); });
1177 return width;
1178 }
1179
1180 // Determine if all derived types components are of the same float type with
1181 // the same width. Complex(4) is considered 2 floats and complex(8) 2 doubles.
1182 bool hasSameFloatAndWidth(
1183 fir::RecordType recTy,
1184 std::pair<mlir::Type, unsigned> &firstTypeAndWidth) const {
1185 for (auto comp : recTy.getTypeList()) {
1186 mlir::Type compType{comp.second};
1187 if (mlir::isa<fir::RecordType>(compType)) {
1188 auto rc{hasSameFloatAndWidth(mlir::cast<fir::RecordType>(compType),
1189 firstTypeAndWidth)};
1190 if (!rc)
1191 return false;
1192 } else {
1193 mlir::Type ty;
1194 bool isFloatType{false};
1195 if (mlir::isa<mlir::FloatType, mlir::ComplexType>(compType)) {
1196 ty = compType;
1197 isFloatType = true;
1198 } else if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(compType)) {
1199 ty = seqTy.getEleTy();
1200 isFloatType = mlir::isa<mlir::FloatType, mlir::ComplexType>(ty);
1201 }
1202
1203 if (!isFloatType) {
1204 return false;
1205 }
1206 auto width{getElemWidth(ty)};
1207 if (firstTypeAndWidth.first == nullptr) {
1208 firstTypeAndWidth.first = ty;
1209 firstTypeAndWidth.second = width;
1210 } else if (width != firstTypeAndWidth.second) {
1211 return false;
1212 }
1213 }
1214 }
1215 return true;
1216 }
1217
1218 CodeGenSpecifics::Marshalling
1219 passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
1220 CodeGenSpecifics::Marshalling marshal;
1221 auto sizeAndAlign{
1222 fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
1223 unsigned short align{
1224 std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
1225 marshal.emplace_back(fir::ReferenceType::get(ty),
1226 AT{align, /*byval=*/!isResult, /*sret=*/isResult});
1227 return marshal;
1228 }
1229
1230 CodeGenSpecifics::Marshalling
1231 structType(mlir::Location loc, fir::RecordType recTy, bool isResult) const {
1232 CodeGenSpecifics::Marshalling marshal;
1233 auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
1234 loc, recTy, getDataLayout(), kindMap)};
1235 auto recordTypeSize{sizeAndAlign.first};
1236 mlir::Type seqTy;
1237 std::pair<mlir::Type, unsigned> firstTyAndWidth{nullptr, 0};
1238
1239 // If there are less than or equal to 8 floats, the structure is flatten as
1240 // an array of floats.
1241 constexpr uint64_t maxNoOfFloats{8};
1242
1243 // i64 type
1244 mlir::Type elemTy{mlir::IntegerType::get(recTy.getContext(), defaultWidth)};
1245 uint64_t nElem{static_cast<uint64_t>(
1246 std::ceil(static_cast<float>(recordTypeSize * 8) / defaultWidth))};
1247
1248 // If the derived type components contains are all floats with the same
1249 // width, the argument is passed as an array of floats.
1250 if (hasSameFloatAndWidth(recTy, firstTyAndWidth)) {
1251 uint64_t n{};
1252 auto firstType{firstTyAndWidth.first};
1253
1254 // Type is either float or complex
1255 if (auto cmplx = mlir::dyn_cast<mlir::ComplexType>(firstType)) {
1256 auto fltType{mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
1257 n = static_cast<uint64_t>(8 * recordTypeSize / fltType.getWidth());
1258 if (n <= maxNoOfFloats) {
1259 nElem = n;
1260 elemTy = fltType;
1261 }
1262 } else if (mlir::isa<mlir::FloatType>(firstType)) {
1263 auto elemSizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
1264 loc, firstType, getDataLayout(), kindMap)};
1265 n = static_cast<uint64_t>(recordTypeSize / elemSizeAndAlign.first);
1266 if (n <= maxNoOfFloats) {
1267 nElem = n;
1268 elemTy = firstType;
1269 }
1270 }
1271 // Neither float nor complex
1272 assert(n > 0 && "unexpected type");
1273 }
1274
1275 // For function returns, only flattened if there are less than 8
1276 // floats in total.
1277 if (isResult &&
1278 ((mlir::isa<mlir::FloatType>(elemTy) && nElem > maxNoOfFloats) ||
1279 !mlir::isa<mlir::FloatType>(elemTy))) {
1280 return passOnTheStack(loc, recTy, isResult);
1281 }
1282
1283 seqTy = fir::SequenceType::get(nElem, elemTy);
1284 marshal.emplace_back(seqTy, AT{});
1285 return marshal;
1286 }
1287
1288 CodeGenSpecifics::Marshalling
1289 structArgumentType(mlir::Location loc, fir::RecordType recType,
1290 const Marshalling &previousArguments) const override {
1291 auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
1292 loc, recType, getDataLayout(), kindMap)};
1293 if (sizeAndAlign.first > 64) {
1294 return passOnTheStack(loc, recType, false);
1295 }
1296 return structType(loc, recType, false);
1297 }
1298
1299 CodeGenSpecifics::Marshalling
1300 structReturnType(mlir::Location loc, fir::RecordType recType) const override {
1301 return structType(loc, recType, true);
1302 }
1303};
1304} // namespace
1305
1306//===----------------------------------------------------------------------===//
1307// sparc (sparc 32 bit) target specifics.
1308//===----------------------------------------------------------------------===//
1309
1310namespace {
1311struct TargetSparc : public GenericTarget<TargetSparc> {
1312 using GenericTarget::GenericTarget;
1313
1314 static constexpr int defaultWidth = 32;
1315
1316 CodeGenSpecifics::Marshalling
1317 complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
1318 assert(fir::isa_real(eleTy));
1319 CodeGenSpecifics::Marshalling marshal;
1320 // Use a type that will be translated into LLVM as:
1321 // { t, t } struct of 2 eleTy
1322 auto structTy =
1323 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1324 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
1325 return marshal;
1326 }
1327
1328 CodeGenSpecifics::Marshalling
1329 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1330 assert(fir::isa_real(eleTy));
1331 CodeGenSpecifics::Marshalling marshal;
1332 // Use a type that will be translated into LLVM as:
1333 // { t, t } struct of 2 eleTy, byval
1334 auto structTy =
1335 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1336 marshal.emplace_back(fir::ReferenceType::get(structTy),
1337 AT{/*alignment=*/0, /*byval=*/true});
1338 return marshal;
1339 }
1340};
1341} // namespace
1342
1343//===----------------------------------------------------------------------===//
1344// sparcv9 (sparc 64 bit) target specifics.
1345//===----------------------------------------------------------------------===//
1346
1347namespace {
1348struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
1349 using GenericTarget::GenericTarget;
1350
1351 static constexpr int defaultWidth = 64;
1352
1353 CodeGenSpecifics::Marshalling
1354 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1355 CodeGenSpecifics::Marshalling marshal;
1356 const auto *sem = &floatToSemantics(kindMap, eleTy);
1357 if (sem == &llvm::APFloat::IEEEsingle() ||
1358 sem == &llvm::APFloat::IEEEdouble()) {
1359 // two distinct float, double arguments
1360 marshal.emplace_back(eleTy, AT{});
1361 marshal.emplace_back(eleTy, AT{});
1362 } else if (sem == &llvm::APFloat::IEEEquad()) {
1363 // Use a type that will be translated into LLVM as:
1364 // { fp128, fp128 } struct of 2 fp128, byval, align 16
1365 marshal.emplace_back(
1366 fir::ReferenceType::get(mlir::TupleType::get(
1367 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1368 AT{/*align=*/16, /*byval=*/true});
1369 } else {
1370 typeTodo(sem, loc, "argument");
1371 }
1372 return marshal;
1373 }
1374
1375 CodeGenSpecifics::Marshalling
1376 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1377 CodeGenSpecifics::Marshalling marshal;
1378 // Use a type that will be translated into LLVM as:
1379 // { eleTy, eleTy } struct of 2 eleTy
1380 marshal.emplace_back(
1381 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
1382 AT{});
1383 return marshal;
1384 }
1385};
1386} // namespace
1387
1388//===----------------------------------------------------------------------===//
1389// RISCV64 linux target specifics.
1390//===----------------------------------------------------------------------===//
1391
1392namespace {
1393struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
1394 using GenericTarget::GenericTarget;
1395
1396 static constexpr int defaultWidth = 64;
1397
1398 CodeGenSpecifics::Marshalling
1399 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1400 CodeGenSpecifics::Marshalling marshal;
1401 const auto *sem = &floatToSemantics(kindMap, eleTy);
1402 if (sem == &llvm::APFloat::IEEEsingle() ||
1403 sem == &llvm::APFloat::IEEEdouble()) {
1404 // Two distinct element type arguments (re, im)
1405 marshal.emplace_back(eleTy, AT{});
1406 marshal.emplace_back(eleTy, AT{});
1407 } else {
1408 typeTodo(sem, loc, "argument");
1409 }
1410 return marshal;
1411 }
1412
1413 CodeGenSpecifics::Marshalling
1414 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1415 CodeGenSpecifics::Marshalling marshal;
1416 const auto *sem = &floatToSemantics(kindMap, eleTy);
1417 if (sem == &llvm::APFloat::IEEEsingle() ||
1418 sem == &llvm::APFloat::IEEEdouble()) {
1419 // Use a type that will be translated into LLVM as:
1420 // { t, t } struct of 2 eleTy, byVal
1421 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1422 mlir::TypeRange{eleTy, eleTy}),
1423 AT{/*alignment=*/0, /*byval=*/true});
1424 } else {
1425 typeTodo(sem, loc, "return");
1426 }
1427 return marshal;
1428 }
1429};
1430} // namespace
1431
1432//===----------------------------------------------------------------------===//
1433// AMDGPU linux target specifics.
1434//===----------------------------------------------------------------------===//
1435
1436namespace {
1437struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {
1438 using GenericTarget::GenericTarget;
1439
1440 // Default size (in bits) of the index type for strings.
1441 static constexpr int defaultWidth = 64;
1442
1443 CodeGenSpecifics::Marshalling
1444 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1445 CodeGenSpecifics::Marshalling marshal;
1446 TODO(loc, "handle complex argument types");
1447 return marshal;
1448 }
1449
1450 CodeGenSpecifics::Marshalling
1451 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1452 CodeGenSpecifics::Marshalling marshal;
1453 TODO(loc, "handle complex return types");
1454 return marshal;
1455 }
1456};
1457} // namespace
1458
1459//===----------------------------------------------------------------------===//
1460// NVPTX linux target specifics.
1461//===----------------------------------------------------------------------===//
1462
1463namespace {
1464struct TargetNVPTX : public GenericTarget<TargetNVPTX> {
1465 using GenericTarget::GenericTarget;
1466
1467 // Default size (in bits) of the index type for strings.
1468 static constexpr int defaultWidth = 64;
1469
1470 CodeGenSpecifics::Marshalling
1471 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1472 CodeGenSpecifics::Marshalling marshal;
1473 TODO(loc, "handle complex argument types");
1474 return marshal;
1475 }
1476
1477 CodeGenSpecifics::Marshalling
1478 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1479 CodeGenSpecifics::Marshalling marshal;
1480 TODO(loc, "handle complex return types");
1481 return marshal;
1482 }
1483};
1484} // namespace
1485
1486//===----------------------------------------------------------------------===//
1487// LoongArch64 linux target specifics.
1488//===----------------------------------------------------------------------===//
1489
1490namespace {
1491struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
1492 using GenericTarget::GenericTarget;
1493
1494 static constexpr int defaultWidth = 64;
1495 static constexpr int GRLen = defaultWidth; /* eight bytes */
1496 static constexpr int GRLenInChar = GRLen / 8;
1497 static constexpr int FRLen = defaultWidth; /* eight bytes */
1498
1499 CodeGenSpecifics::Marshalling
1500 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
1501 CodeGenSpecifics::Marshalling marshal;
1502 const auto *sem = &floatToSemantics(kindMap, eleTy);
1503 if (sem == &llvm::APFloat::IEEEsingle() ||
1504 sem == &llvm::APFloat::IEEEdouble()) {
1505 // Two distinct element type arguments (re, im)
1506 marshal.emplace_back(eleTy, AT{});
1507 marshal.emplace_back(eleTy, AT{});
1508 } else if (sem == &llvm::APFloat::IEEEquad()) {
1509 // Use a type that will be translated into LLVM as:
1510 // { fp128, fp128 } struct of 2 fp128, byval
1511 marshal.emplace_back(
1512 fir::ReferenceType::get(mlir::TupleType::get(
1513 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1514 AT{/*align=*/16, /*byval=*/true});
1515 } else {
1516 typeTodo(sem, loc, "argument");
1517 }
1518 return marshal;
1519 }
1520
1521 CodeGenSpecifics::Marshalling
1522 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1523 CodeGenSpecifics::Marshalling marshal;
1524 const auto *sem = &floatToSemantics(kindMap, eleTy);
1525 if (sem == &llvm::APFloat::IEEEsingle() ||
1526 sem == &llvm::APFloat::IEEEdouble()) {
1527 // Use a type that will be translated into LLVM as:
1528 // { t, t } struct of 2 eleTy, byVal
1529 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
1530 mlir::TypeRange{eleTy, eleTy}),
1531 AT{/*alignment=*/0, /*byval=*/true});
1532 } else if (sem == &llvm::APFloat::IEEEquad()) {
1533 // Use a type that will be translated into LLVM as:
1534 // { fp128, fp128 } struct of 2 fp128, sret, align 16
1535 marshal.emplace_back(
1536 fir::ReferenceType::get(mlir::TupleType::get(
1537 eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
1538 AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
1539 } else {
1540 typeTodo(sem, loc, "return");
1541 }
1542 return marshal;
1543 }
1544
1545 CodeGenSpecifics::Marshalling
1546 integerArgumentType(mlir::Location loc,
1547 mlir::IntegerType argTy) const override {
1548 if (argTy.getWidth() == 32) {
1549 // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended.
1550 // Therefore, Flang also follows it if a function needs to be
1551 // interoperable with C.
1552 //
1553 // Currently, it only adds `signext` attribute to the dummy arguments and
1554 // return values in the function signatures, but it does not add the
1555 // corresponding attribute to the actual arguments and return values in
1556 // `fir.call` instruction. Thanks to LLVM's integration of all these
1557 // attributes, the modification is still effective.
1558 CodeGenSpecifics::Marshalling marshal;
1559 AT::IntegerExtension intExt = AT::IntegerExtension::Sign;
1560 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
1561 /*sret=*/false, /*append=*/false,
1562 /*intExt=*/intExt});
1563 return marshal;
1564 }
1565
1566 return GenericTarget::integerArgumentType(loc, argTy);
1567 }
1568
1569 /// Flatten non-basic types, resulting in an array of types containing only
1570 /// `IntegerType` and `FloatType`.
1571 llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc,
1572 const mlir::Type type) const {
1573 llvm::SmallVector<mlir::Type> flatTypes;
1574
1575 llvm::TypeSwitch<mlir::Type>(type)
1576 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1577 if (intTy.getWidth() != 0)
1578 flatTypes.push_back(intTy);
1579 })
1580 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1581 if (floatTy.getWidth() != 0)
1582 flatTypes.push_back(floatTy);
1583 })
1584 .template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
1585 const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
1586 if (sem == &llvm::APFloat::IEEEsingle() ||
1587 sem == &llvm::APFloat::IEEEdouble() ||
1588 sem == &llvm::APFloat::IEEEquad())
1589 std::fill_n(std::back_inserter(flatTypes), 2,
1590 cmplx.getElementType());
1591 else
1592 TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "
1593 "IEEEquad) as a structure component for BIND(C), "
1594 "VALUE derived type argument and type return");
1595 })
1596 .template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {
1597 const unsigned width =
1598 kindMap.getLogicalBitsize(logicalTy.getFKind());
1599 if (width != 0)
1600 flatTypes.push_back(
1601 mlir::IntegerType::get(type.getContext(), width));
1602 })
1603 .template Case<fir::CharacterType>([&](fir::CharacterType charTy) {
1604 assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 &&
1605 "the bit size of characterType as an interoperable type must "
1606 "not exceed 8");
1607 for (unsigned i = 0; i < charTy.getLen(); ++i)
1608 flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
1609 })
1610 .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
1611 if (!seqTy.hasDynamicExtents()) {
1612 const std::uint64_t numOfEle = seqTy.getConstantArraySize();
1613 mlir::Type eleTy = seqTy.getEleTy();
1614 if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
1615 llvm::SmallVector<mlir::Type> subTypeList =
1616 flattenTypeList(loc, eleTy);
1617 if (subTypeList.size() != 0)
1618 for (std::uint64_t i = 0; i < numOfEle; ++i)
1619 llvm::copy(subTypeList, std::back_inserter(flatTypes));
1620 } else {
1621 std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
1622 }
1623 } else
1624 TODO(loc, "unsupported dynamic extent sequence type as a structure "
1625 "component for BIND(C), "
1626 "VALUE derived type argument and type return");
1627 })
1628 .template Case<fir::RecordType>([&](fir::RecordType recTy) {
1629 for (auto &component : recTy.getTypeList()) {
1630 mlir::Type eleTy = component.second;
1631 llvm::SmallVector<mlir::Type> subTypeList =
1632 flattenTypeList(loc, eleTy);
1633 if (subTypeList.size() != 0)
1634 llvm::copy(subTypeList, std::back_inserter(flatTypes));
1635 }
1636 })
1637 .template Case<fir::VectorType>([&](fir::VectorType vecTy) {
1638 auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash(
1639 loc, vecTy, getDataLayout(), kindMap);
1640 if (sizeAndAlign.first == 2 * GRLenInChar)
1641 flatTypes.push_back(
1642 mlir::IntegerType::get(type.getContext(), 2 * GRLen));
1643 else
1644 TODO(loc, "unsupported vector width(must be 128 bits)");
1645 })
1646 .Default([&](mlir::Type ty) {
1647 if (fir::conformsWithPassByRef(ty))
1648 flatTypes.push_back(
1649 mlir::IntegerType::get(type.getContext(), GRLen));
1650 else
1651 TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1652 "type argument and type return");
1653 });
1654
1655 return flatTypes;
1656 }
1657
1658 /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,
1659 /// when flattened it contains a single fp value, fp+fp, or int+fp of
1660 /// appropriate size).
1661 bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
1662 mlir::Type &field1Ty,
1663 mlir::Type &field2Ty) const {
1664 field1Ty = field2Ty = nullptr;
1665 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);
1666 size_t flatSize = flatTypes.size();
1667
1668 // Cannot be eligible if the number of flattened types is equal to 0 or
1669 // greater than 2.
1670 if (flatSize == 0 || flatSize > 2)
1671 return false;
1672
1673 bool isFirstAvaliableFloat = false;
1674
1675 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
1676 "Type must be integerType or floatType after flattening");
1677 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
1678 const unsigned Size = floatTy.getWidth();
1679 // Can't be eligible if larger than the FP registers. Half precision isn't
1680 // currently supported on LoongArch and the ABI hasn't been confirmed, so
1681 // default to the integer ABI in that case.
1682 if (Size > FRLen || Size < 32)
1683 return false;
1684 isFirstAvaliableFloat = true;
1685 field1Ty = floatTy;
1686 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
1687 if (intTy.getWidth() > GRLen)
1688 return false;
1689 field1Ty = intTy;
1690 }
1691
1692 // flatTypes has two elements
1693 if (flatSize == 2) {
1694 assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&
1695 "Type must be integerType or floatType after flattening");
1696 if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
1697 const unsigned Size = floatTy.getWidth();
1698 if (Size > FRLen || Size < 32)
1699 return false;
1700 field2Ty = floatTy;
1701 return true;
1702 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) {
1703 // Can't be eligible if an integer type was already found (int+int pairs
1704 // are not eligible).
1705 if (!isFirstAvaliableFloat)
1706 return false;
1707 if (intTy.getWidth() > GRLen)
1708 return false;
1709 field2Ty = intTy;
1710 return true;
1711 }
1712 }
1713
1714 // return isFirstAvaliableFloat if flatTypes only has one element
1715 return isFirstAvaliableFloat;
1716 }
1717
1718 bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,
1719 const mlir::Type type) const {
1720 if (!type)
1721 return true;
1722
1723 llvm::TypeSwitch<mlir::Type>(type)
1724 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1725 const unsigned width = intTy.getWidth();
1726 if (width > 128)
1727 TODO(loc,
1728 "integerType with width exceeding 128 bits is unsupported");
1729 if (width == 0)
1730 return;
1731 if (width <= GRLen)
1732 --GARsLeft;
1733 else if (width <= 2 * GRLen)
1734 GARsLeft = GARsLeft - 2;
1735 })
1736 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1737 const unsigned width = floatTy.getWidth();
1738 if (width > 128)
1739 TODO(loc, "floatType with width exceeding 128 bits is unsupported");
1740 if (width == 0)
1741 return;
1742 if (width == 32 || width == 64)
1743 --FARsLeft;
1744 else if (width <= GRLen)
1745 --GARsLeft;
1746 else if (width <= 2 * GRLen)
1747 GARsLeft = GARsLeft - 2;
1748 })
1749 .Default([&](mlir::Type ty) {
1750 if (fir::conformsWithPassByRef(ty))
1751 --GARsLeft; // Pointers.
1752 else
1753 TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1754 "type argument and type return");
1755 });
1756
1757 return GARsLeft >= 0 && FARsLeft >= 0;
1758 }
1759
1760 bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,
1761 const Marshalling &previousArguments,
1762 const mlir::Type &field1Ty,
1763 const mlir::Type &field2Ty) const {
1764 for (auto &typeAndAttr : previousArguments) {
1765 const auto &attr = std::get<Attributes>(typeAndAttr);
1766 if (attr.isByVal()) {
1767 // Previous argument passed on the stack, and its address is passed in
1768 // GAR.
1769 --GARsLeft;
1770 continue;
1771 }
1772
1773 // Previous aggregate arguments were marshalled into simpler arguments.
1774 const auto &type = std::get<mlir::Type>(typeAndAttr);
1775 llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);
1776
1777 for (auto &flatTy : flatTypes) {
1778 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))
1779 return false;
1780 }
1781 }
1782
1783 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
1784 return false;
1785 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
1786 return false;
1787 return true;
1788 }
1789
1790 /// LoongArch64 subroutine calling sequence ABI in:
1791 /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
1792 CodeGenSpecifics::Marshalling
1793 classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft,
1794 int FARsLeft, bool isResult,
1795 const Marshalling &previousArguments) const {
1796 CodeGenSpecifics::Marshalling marshal;
1797
1798 auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(
1799 loc, recTy, getDataLayout(), kindMap);
1800 mlir::MLIRContext *context = recTy.getContext();
1801
1802 if (recSize == 0) {
1803 TODO(loc, "unsupported empty struct type for BIND(C), "
1804 "VALUE derived type argument and type return");
1805 }
1806
1807 if (recSize > 2 * GRLenInChar) {
1808 marshal.emplace_back(
1809 fir::ReferenceType::get(recTy),
1810 AT{recAlign, /*byval=*/!isResult, /*sret=*/isResult});
1811 return marshal;
1812 }
1813
1814 // Pass by FARs(and GARs)
1815 mlir::Type field1Ty = nullptr, field2Ty = nullptr;
1816 if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) &&
1817 hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty,
1818 field2Ty)) {
1819 if (!isResult) {
1820 if (field1Ty)
1821 marshal.emplace_back(field1Ty, AT{});
1822 if (field2Ty)
1823 marshal.emplace_back(field2Ty, AT{});
1824 } else {
1825 // field1Ty is always preferred over field2Ty for assignment, so there
1826 // will never be a case where field1Ty == nullptr and field2Ty !=
1827 // nullptr.
1828 if (field1Ty && !field2Ty)
1829 marshal.emplace_back(field1Ty, AT{});
1830 else if (field1Ty && field2Ty)
1831 marshal.emplace_back(
1832 mlir::TupleType::get(context,
1833 mlir::TypeRange{field1Ty, field2Ty}),
1834 AT{/*alignment=*/0, /*byval=*/true});
1835 }
1836 return marshal;
1837 }
1838
1839 if (recSize <= GRLenInChar) {
1840 marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});
1841 return marshal;
1842 }
1843
1844 if (recAlign == 2 * GRLenInChar) {
1845 marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});
1846 return marshal;
1847 }
1848
1849 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar
1850 marshal.emplace_back(
1851 fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
1852 AT{});
1853 return marshal;
1854 }
1855
1856 /// Marshal a derived type passed by value like a C struct.
1857 CodeGenSpecifics::Marshalling
1858 structArgumentType(mlir::Location loc, fir::RecordType recTy,
1859 const Marshalling &previousArguments) const override {
1860 int GARsLeft = 8;
1861 int FARsLeft = FRLen ? 8 : 0;
1862
1863 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,
1864 previousArguments);
1865 }
1866
1867 CodeGenSpecifics::Marshalling
1868 structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
1869 // The rules for return and argument types are the same.
1870 int GARsLeft = 2;
1871 int FARsLeft = FRLen ? 2 : 0;
1872 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,
1873 {});
1874 }
1875};
1876} // namespace
1877
1878// Instantiate the overloaded target instance based on the triple value.
1879// TODO: Add other targets to this file as needed.
1880std::unique_ptr<fir::CodeGenSpecifics>
1881fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
1882 KindMapping &&kindMap, llvm::StringRef targetCPU,
1883 mlir::LLVM::TargetFeaturesAttr targetFeatures,
1884 const mlir::DataLayout &dl) {
1885 switch (trp.getArch()) {
1886 default:
1887 break;
1888 case llvm::Triple::ArchType::x86:
1889 if (trp.isOSWindows())
1890 return std::make_unique<TargetI386Win>(ctx, std::move(trp),
1891 std::move(kindMap), targetCPU,
1892 targetFeatures, dl);
1893 else
1894 return std::make_unique<TargetI386>(ctx, std::move(trp),
1895 std::move(kindMap), targetCPU,
1896 targetFeatures, dl);
1897 case llvm::Triple::ArchType::x86_64:
1898 if (trp.isOSWindows())
1899 return std::make_unique<TargetX86_64Win>(ctx, std::move(trp),
1900 std::move(kindMap), targetCPU,
1901 targetFeatures, dl);
1902 else
1903 return std::make_unique<TargetX86_64>(ctx, std::move(trp),
1904 std::move(kindMap), targetCPU,
1905 targetFeatures, dl);
1906 case llvm::Triple::ArchType::aarch64:
1907 return std::make_unique<TargetAArch64>(
1908 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1909 case llvm::Triple::ArchType::ppc:
1910 return std::make_unique<TargetPPC>(ctx, std::move(trp), std::move(kindMap),
1911 targetCPU, targetFeatures, dl);
1912 case llvm::Triple::ArchType::ppc64:
1913 return std::make_unique<TargetPPC64>(
1914 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1915 case llvm::Triple::ArchType::ppc64le:
1916 return std::make_unique<TargetPPC64le>(
1917 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1918 case llvm::Triple::ArchType::sparc:
1919 return std::make_unique<TargetSparc>(
1920 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1921 case llvm::Triple::ArchType::sparcv9:
1922 return std::make_unique<TargetSparcV9>(
1923 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1924 case llvm::Triple::ArchType::riscv64:
1925 return std::make_unique<TargetRISCV64>(
1926 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1927 case llvm::Triple::ArchType::amdgcn:
1928 return std::make_unique<TargetAMDGPU>(
1929 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1930 case llvm::Triple::ArchType::nvptx64:
1931 return std::make_unique<TargetNVPTX>(
1932 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1933 case llvm::Triple::ArchType::loongarch64:
1934 return std::make_unique<TargetLoongArch64>(
1935 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1936 }
1937 TODO(mlir::UnknownLoc::get(ctx), "target not implemented");
1938}
1939
1940std::unique_ptr<fir::CodeGenSpecifics> fir::CodeGenSpecifics::get(
1941 mlir::MLIRContext *ctx, llvm::Triple &&trp, KindMapping &&kindMap,
1942 llvm::StringRef targetCPU, mlir::LLVM::TargetFeaturesAttr targetFeatures,
1943 const mlir::DataLayout &dl, llvm::StringRef tuneCPU) {
1944 std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(
1945 ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
1946
1947 CGS->tuneCPU = tuneCPU;
1948 return CGS;
1949}
1950

source code of flang/lib/Optimizer/CodeGen/Target.cpp