1 //===-- Target.cpp --------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
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"
23 #define DEBUG_TYPE "flang-codegen-target"
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.
33 return "llvm.zeroext";
35 return "llvm.signext";
38 } // namespace fir::details
40 // Reduce a REAL/float type to the floating point semantics.
41 static const llvm::fltSemantics
&floatToSemantics(const KindMapping
&kindMap
,
43 assert(isa_real(type
));
44 return mlir::cast
<mlir::FloatType
>(type
).getFloatSemantics();
47 static 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");
56 TODO(loc
, "complex for this precision for " + context
+ " type");
62 struct GenericTarget
: public CodeGenSpecifics
{
63 using CodeGenSpecifics::CodeGenSpecifics
;
64 using AT
= CodeGenSpecifics::Attributes
;
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
});
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:
79 return mlir::TupleType::get(eleTy
.getContext(),
80 mlir::TypeRange
{ptrTy
, idxTy
});
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
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});
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");
103 CodeGenSpecifics::Marshalling
104 structReturnType(mlir::Location loc
, fir::RecordType ty
) const override
{
105 TODO(loc
, "returning BIND(C) derived type for this target");
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
;
127 intExt
= AT::IntegerExtension::Sign
;
131 marshal
.emplace_back(argTy
, AT
{/*alignment=*/0, /*byval=*/false,
132 /*sret=*/false, /*append=*/false,
137 CodeGenSpecifics::Marshalling
138 integerReturnType(mlir::Location loc
,
139 mlir::IntegerType argTy
) const override
{
140 return integerArgumentType(loc
, argTy
);
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; }
150 //===----------------------------------------------------------------------===//
151 // i386 (x86 32 bit) linux target specifics.
152 //===----------------------------------------------------------------------===//
155 struct TargetI386
: public GenericTarget
<TargetI386
> {
156 using GenericTarget::GenericTarget
;
158 static constexpr int defaultWidth
= 32;
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
167 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
});
168 marshal
.emplace_back(fir::ReferenceType::get(structTy
),
169 AT
{/*alignment=*/4, /*byval=*/true});
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),
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});
190 typeTodo(sem
, loc
, "return");
197 //===----------------------------------------------------------------------===//
198 // i386 (x86 32 bit) Windows target specifics.
199 //===----------------------------------------------------------------------===//
202 struct TargetI386Win
: public GenericTarget
<TargetI386Win
> {
203 using GenericTarget::GenericTarget
;
205 static constexpr int defaultWidth
= 32;
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
213 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
});
214 marshal
.emplace_back(fir::ReferenceType::get(structTy
),
215 AT
{/*align=*/4, /*byval=*/true});
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),
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});
249 typeTodo(sem
, loc
, "return");
256 //===----------------------------------------------------------------------===//
257 // x86_64 (x86 64 bit) linux target specifics.
258 //===----------------------------------------------------------------------===//
261 struct TargetX86_64
: public GenericTarget
<TargetX86_64
> {
262 using GenericTarget::GenericTarget
;
264 static constexpr int defaultWidth
= 64;
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});
296 typeTodo(sem
, loc
, "argument");
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
}),
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
}),
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});
327 typeTodo(sem
, loc
, "return");
332 /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3.
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
¤t
= 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
;
360 current
= ArgClass::Integer
;
362 .template Case
<mlir::FloatType
>([&](mlir::Type floatTy
) {
363 const auto *sem
= &floatToSemantics(kindMap
, floatTy
);
364 if (sem
== &llvm::APFloat::x87DoubleExtended()) {
366 Hi
= ArgClass::X87Up
;
367 } else if (sem
== &llvm::APFloat::IEEEquad()) {
369 Hi
= ArgClass::SSEUp
;
371 current
= ArgClass::SSE
;
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
;
379 fir::SequenceType::Shape shape
{2};
381 fir::SequenceType::get(shape
, cmplx
.getElementType()),
385 .template Case
<fir::LogicalType
>([&](fir::LogicalType logical
) {
386 if (kindMap
.getLogicalBitsize(logical
.getFKind()) == 128)
387 Hi
= Lo
= ArgClass::Integer
;
389 current
= ArgClass::Integer
;
391 .template Case
<fir::CharacterType
>(
392 [&](fir::CharacterType character
) { current
= ArgClass::Integer
; })
393 .template Case
<fir::SequenceType
>([&](fir::SequenceType seqTy
) {
395 classifyArray(loc
, seqTy
, byteOffset
, Lo
, Hi
);
397 .template Case
<fir::RecordType
>([&](fir::RecordType recTy
) {
398 // Component that is a derived type.
399 classifyStruct(loc
, recTy
, byteOffset
, Lo
, Hi
);
401 .template Case
<fir::VectorType
>([&](fir::VectorType vecTy
) {
402 // Previously marshalled SSE eight byte for a previous struct
404 auto *sem
= fir::isa_real(vecTy
.getEleTy())
405 ? &floatToSemantics(kindMap
, vecTy
.getEleTy())
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");
414 .Default([&](mlir::Type ty
) {
415 if (fir::conformsWithPassByRef(ty
))
416 current
= ArgClass::Integer
; // Pointers.
418 TODO(loc
, "unsupported component type for BIND(C), VALUE derived "
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
;
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
)
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
;
468 ArgClass LoComp
, HiComp
;
469 classify(loc
, eleTy
, byteOffset
, LoComp
, HiComp
);
470 Lo
= mergeClass(Lo
, LoComp
);
471 Hi
= mergeClass(Hi
, HiComp
);
472 byteOffset
= byteOffset
+ eleStorageSize
;
473 if (Lo
== ArgClass::Memory
|| Hi
== ArgClass::Memory
)
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
);
488 continue; // Previous argument passed on the stack.
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
)
498 if (Hi
== ArgClass::Integer
)
500 else if (Hi
== ArgClass::SSE
)
503 return availSSERegisters
>= neededSSERegisters
&&
504 availIntRegisters
>= neededIntRegisters
;
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
)
513 if (field
== ArgClass::Memory
)
514 return ArgClass::Memory
;
515 if (accum
== NoClass
)
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
)
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
)
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)
546 mlir::Type fieldType
= typeList
[0].second
;
547 if (mlir::isa
<mlir::FloatType
, mlir::IntegerType
, fir::LogicalType
>(
550 if (allowComplex
&& mlir::isa
<mlir::ComplexType
>(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");
559 // Complex field that needs to be split, or array.
563 mlir::Type
pickLLVMArgType(mlir::Location loc
, mlir::MLIRContext
*context
,
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
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
);
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);
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;
599 Lo
= Hi
= ArgClass::NoClass
;
600 byteOffset
= classifyStruct(loc
, recTy
, byteOffset
, Lo
, Hi
);
601 postMerge(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
,
620 return passOnTheStack(loc
, recTy
, /*isResult=*/false);
622 if (auto fieldType
= passAsFieldIfOneFieldStruct(recTy
)) {
623 CodeGenSpecifics::Marshalling marshal
;
624 marshal
.emplace_back(fieldType
, AT
{});
627 if (Hi
== ArgClass::NoClass
|| Hi
== ArgClass::SSEUp
) {
628 // Pass a single integer or floating point argument.
630 pickLLVMArgType(loc
, recTy
.getContext(), Lo
, byteOffset
);
631 CodeGenSpecifics::Marshalling marshal
;
632 marshal
.emplace_back(lowType
, AT
{});
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);
645 pickLLVMArgType(loc
, recTy
.getContext(), Hi
, byteOffset
- 8u);
646 CodeGenSpecifics::Marshalling marshal
;
647 marshal
.emplace_back(lowType
, AT
{});
648 marshal
.emplace_back(hiType
, AT
{});
652 CodeGenSpecifics::Marshalling
653 structReturnType(mlir::Location loc
, fir::RecordType recTy
) const override
{
654 std::uint64_t byteOffset
= 0;
656 Lo
= Hi
= ArgClass::NoClass
;
657 byteOffset
= classifyStruct(loc
, recTy
, byteOffset
, Lo
, Hi
);
658 mlir::MLIRContext
*context
= recTy
.getContext();
659 postMerge(byteOffset
, Lo
, Hi
);
660 if (Lo
== ArgClass::Memory
)
661 return passOnTheStack(loc
, recTy
, /*isResult=*/true);
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.
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
671 // {_Complex long double} return to be dealt with differently than
672 // _Complex long double.
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
{});
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
{});
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
}),
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
;
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
});
716 //===----------------------------------------------------------------------===//
717 // x86_64 (x86 64 bit) Windows target specifics.
718 //===----------------------------------------------------------------------===//
721 struct TargetX86_64Win
: public GenericTarget
<TargetX86_64Win
> {
722 using GenericTarget::GenericTarget
;
724 static constexpr int defaultWidth
= 64;
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),
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});
750 typeTodo(sem
, loc
, "argument");
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),
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});
779 typeTodo(sem
, loc
, "return");
786 //===----------------------------------------------------------------------===//
787 // AArch64 linux target specifics.
788 //===----------------------------------------------------------------------===//
791 // AArch64 procedure call standard:
792 // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#parameter-passing
793 struct TargetAArch64
: public GenericTarget
<TargetAArch64
> {
794 using GenericTarget::GenericTarget
;
796 static constexpr int defaultWidth
= 64;
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
{});
808 typeTodo(sem
, loc
, "argument");
813 CodeGenSpecifics::Marshalling
814 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
815 CodeGenSpecifics::Marshalling marshal
;
816 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
817 if (sem
== &llvm::APFloat::IEEEsingle() ||
818 sem
== &llvm::APFloat::IEEEdouble() ||
819 sem
== &llvm::APFloat::IEEEquad()) {
820 // Use a type that will be translated into LLVM as:
821 // { t, t } struct of 2 eleTy
822 marshal
.emplace_back(mlir::TupleType::get(eleTy
.getContext(),
823 mlir::TypeRange
{eleTy
, eleTy
}),
826 typeTodo(sem
, loc
, "return");
831 // Flatten a RecordType::TypeList containing more record types or array type
832 static std::optional
<std::vector
<mlir::Type
>>
833 flattenTypeList(const RecordType::TypeList
&types
) {
834 std::vector
<mlir::Type
> flatTypes
;
835 // The flat list will be at least the same size as the non-flat list.
836 flatTypes
.reserve(types
.size());
837 for (auto [c
, type
] : types
) {
838 // Flatten record type
839 if (auto recTy
= mlir::dyn_cast
<RecordType
>(type
)) {
840 auto subTypeList
= flattenTypeList(recTy
.getTypeList());
843 llvm::copy(*subTypeList
, std::back_inserter(flatTypes
));
847 // Flatten array type
848 if (auto seqTy
= mlir::dyn_cast
<SequenceType
>(type
)) {
849 if (seqTy
.hasDynamicExtents())
851 std::size_t n
= seqTy
.getConstantArraySize();
852 auto eleTy
= seqTy
.getElementType();
853 // Flatten array of record types
854 if (auto recTy
= mlir::dyn_cast
<RecordType
>(eleTy
)) {
855 auto subTypeList
= flattenTypeList(recTy
.getTypeList());
858 for (std::size_t i
= 0; i
< n
; ++i
)
859 llvm::copy(*subTypeList
, std::back_inserter(flatTypes
));
861 std::fill_n(std::back_inserter(flatTypes
),
862 seqTy
.getConstantArraySize(), eleTy
);
867 // Other types are already flat
868 flatTypes
.push_back(type
);
873 // Determine if the type is a Homogenous Floating-point Aggregate (HFA). An
874 // HFA is a record type with up to 4 floating-point members of the same type.
875 static std::optional
<int> usedRegsForHFA(fir::RecordType ty
) {
876 RecordType::TypeList types
= ty
.getTypeList();
877 if (types
.empty() || types
.size() > 4)
880 std::optional
<std::vector
<mlir::Type
>> flatTypes
= flattenTypeList(types
);
881 if (!flatTypes
|| flatTypes
->size() > 4) {
885 if (!isa_real(flatTypes
->front())) {
889 return llvm::all_equal(*flatTypes
) ? std::optional
<int>{flatTypes
->size()}
898 NRegs
usedRegsForRecordType(mlir::Location loc
, fir::RecordType type
) const {
899 if (std::optional
<int> size
= usedRegsForHFA(type
))
900 return {*size
, true};
902 auto [size
, align
] = fir::getTypeSizeAndAlignmentOrCrash(
903 loc
, type
, getDataLayout(), kindMap
);
906 return {static_cast<int>((size
+ 7) / 8), false};
908 // Pass on the stack, i.e. no registers used
912 NRegs
usedRegsForType(mlir::Location loc
, mlir::Type type
) const {
913 return llvm::TypeSwitch
<mlir::Type
, NRegs
>(type
)
914 .Case
<mlir::IntegerType
>([&](auto intTy
) {
915 return intTy
.getWidth() == 128 ? NRegs
{2, false} : NRegs
{1, false};
917 .Case
<mlir::FloatType
>([&](auto) { return NRegs
{1, true}; })
918 .Case
<mlir::ComplexType
>([&](auto) { return NRegs
{2, true}; })
919 .Case
<fir::LogicalType
>([&](auto) { return NRegs
{1, false}; })
920 .Case
<fir::CharacterType
>([&](auto) { return NRegs
{1, false}; })
921 .Case
<fir::SequenceType
>([&](auto ty
) {
922 assert(ty
.getShape().size() == 1 &&
923 "invalid array dimensions in BIND(C)");
924 NRegs nregs
= usedRegsForType(loc
, ty
.getEleTy());
925 nregs
.n
*= ty
.getShape()[0];
928 .Case
<fir::RecordType
>(
929 [&](auto ty
) { return usedRegsForRecordType(loc
, ty
); })
930 .Case
<fir::VectorType
>([&](auto) {
931 TODO(loc
, "passing vector argument to C by value is not supported");
936 bool hasEnoughRegisters(mlir::Location loc
, fir::RecordType type
,
937 const Marshalling
&previousArguments
) const {
938 int availIntRegisters
= 8;
939 int availSIMDRegisters
= 8;
941 // Check previous arguments to see how many registers are used already
942 for (auto [type
, attr
] : previousArguments
) {
943 if (availIntRegisters
<= 0 || availSIMDRegisters
<= 0)
947 continue; // Previous argument passed on the stack
949 NRegs nregs
= usedRegsForType(loc
, type
);
951 availSIMDRegisters
-= nregs
.n
;
953 availIntRegisters
-= nregs
.n
;
956 NRegs nregs
= usedRegsForRecordType(loc
, type
);
959 return nregs
.n
<= availSIMDRegisters
;
961 return nregs
.n
<= availIntRegisters
;
964 CodeGenSpecifics::Marshalling
965 passOnTheStack(mlir::Location loc
, mlir::Type ty
, bool isResult
) const {
966 CodeGenSpecifics::Marshalling marshal
;
968 fir::getTypeSizeAndAlignmentOrCrash(loc
, ty
, getDataLayout(), kindMap
);
969 // The stack is always 8 byte aligned
970 unsigned short align
=
971 std::max(sizeAndAlign
.second
, static_cast<unsigned short>(8));
972 marshal
.emplace_back(fir::ReferenceType::get(ty
),
973 AT
{align
, /*byval=*/!isResult
, /*sret=*/isResult
});
977 CodeGenSpecifics::Marshalling
978 structType(mlir::Location loc
, fir::RecordType type
, bool isResult
) const {
979 NRegs nregs
= usedRegsForRecordType(loc
, type
);
981 // If the type needs no registers it must need to be passed on the stack
983 return passOnTheStack(loc
, type
, isResult
);
985 CodeGenSpecifics::Marshalling marshal
;
991 pcsType
= fir::SequenceType::get(
992 nregs
.n
, mlir::IntegerType::get(type
.getContext(), 64));
995 marshal
.emplace_back(pcsType
, AT
{});
999 CodeGenSpecifics::Marshalling
1000 structArgumentType(mlir::Location loc
, fir::RecordType ty
,
1001 const Marshalling
&previousArguments
) const override
{
1002 if (!hasEnoughRegisters(loc
, ty
, previousArguments
)) {
1003 return passOnTheStack(loc
, ty
, /*isResult=*/false);
1006 return structType(loc
, ty
, /*isResult=*/false);
1009 CodeGenSpecifics::Marshalling
1010 structReturnType(mlir::Location loc
, fir::RecordType ty
) const override
{
1011 return structType(loc
, ty
, /*isResult=*/true);
1016 //===----------------------------------------------------------------------===//
1017 // PPC64 (AIX 64 bit) target specifics.
1018 //===----------------------------------------------------------------------===//
1021 struct TargetPPC64
: public GenericTarget
<TargetPPC64
> {
1022 using GenericTarget::GenericTarget
;
1024 static constexpr int defaultWidth
= 64;
1026 CodeGenSpecifics::Marshalling
1027 complexArgumentType(mlir::Location
, mlir::Type eleTy
) const override
{
1028 CodeGenSpecifics::Marshalling marshal
;
1029 // two distinct element type arguments (re, im)
1030 marshal
.emplace_back(eleTy
, AT
{});
1031 marshal
.emplace_back(eleTy
, AT
{});
1035 CodeGenSpecifics::Marshalling
1036 complexReturnType(mlir::Location
, mlir::Type eleTy
) const override
{
1037 CodeGenSpecifics::Marshalling marshal
;
1038 // Use a type that will be translated into LLVM as:
1039 // { t, t } struct of 2 element type
1040 marshal
.emplace_back(
1041 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
}),
1048 //===----------------------------------------------------------------------===//
1049 // PPC64le linux target specifics.
1050 //===----------------------------------------------------------------------===//
1053 struct TargetPPC64le
: public GenericTarget
<TargetPPC64le
> {
1054 using GenericTarget::GenericTarget
;
1056 static constexpr int defaultWidth
= 64;
1058 CodeGenSpecifics::Marshalling
1059 complexArgumentType(mlir::Location
, mlir::Type eleTy
) const override
{
1060 CodeGenSpecifics::Marshalling marshal
;
1061 // two distinct element type arguments (re, im)
1062 marshal
.emplace_back(eleTy
, AT
{});
1063 marshal
.emplace_back(eleTy
, AT
{});
1067 CodeGenSpecifics::Marshalling
1068 complexReturnType(mlir::Location
, mlir::Type eleTy
) const override
{
1069 CodeGenSpecifics::Marshalling marshal
;
1070 // Use a type that will be translated into LLVM as:
1071 // { t, t } struct of 2 element type
1072 marshal
.emplace_back(
1073 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
}),
1080 //===----------------------------------------------------------------------===//
1081 // sparc (sparc 32 bit) target specifics.
1082 //===----------------------------------------------------------------------===//
1085 struct TargetSparc
: public GenericTarget
<TargetSparc
> {
1086 using GenericTarget::GenericTarget
;
1088 static constexpr int defaultWidth
= 32;
1090 CodeGenSpecifics::Marshalling
1091 complexArgumentType(mlir::Location
, mlir::Type eleTy
) const override
{
1092 assert(fir::isa_real(eleTy
));
1093 CodeGenSpecifics::Marshalling marshal
;
1094 // Use a type that will be translated into LLVM as:
1095 // { t, t } struct of 2 eleTy
1097 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
});
1098 marshal
.emplace_back(fir::ReferenceType::get(structTy
), AT
{});
1102 CodeGenSpecifics::Marshalling
1103 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1104 assert(fir::isa_real(eleTy
));
1105 CodeGenSpecifics::Marshalling marshal
;
1106 // Use a type that will be translated into LLVM as:
1107 // { t, t } struct of 2 eleTy, byval
1109 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
});
1110 marshal
.emplace_back(fir::ReferenceType::get(structTy
),
1111 AT
{/*alignment=*/0, /*byval=*/true});
1117 //===----------------------------------------------------------------------===//
1118 // sparcv9 (sparc 64 bit) target specifics.
1119 //===----------------------------------------------------------------------===//
1122 struct TargetSparcV9
: public GenericTarget
<TargetSparcV9
> {
1123 using GenericTarget::GenericTarget
;
1125 static constexpr int defaultWidth
= 64;
1127 CodeGenSpecifics::Marshalling
1128 complexArgumentType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1129 CodeGenSpecifics::Marshalling marshal
;
1130 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
1131 if (sem
== &llvm::APFloat::IEEEsingle() ||
1132 sem
== &llvm::APFloat::IEEEdouble()) {
1133 // two distinct float, double arguments
1134 marshal
.emplace_back(eleTy
, AT
{});
1135 marshal
.emplace_back(eleTy
, AT
{});
1136 } else if (sem
== &llvm::APFloat::IEEEquad()) {
1137 // Use a type that will be translated into LLVM as:
1138 // { fp128, fp128 } struct of 2 fp128, byval, align 16
1139 marshal
.emplace_back(
1140 fir::ReferenceType::get(mlir::TupleType::get(
1141 eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
})),
1142 AT
{/*align=*/16, /*byval=*/true});
1144 typeTodo(sem
, loc
, "argument");
1149 CodeGenSpecifics::Marshalling
1150 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1151 CodeGenSpecifics::Marshalling marshal
;
1152 // Use a type that will be translated into LLVM as:
1153 // { eleTy, eleTy } struct of 2 eleTy
1154 marshal
.emplace_back(
1155 mlir::TupleType::get(eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
}),
1162 //===----------------------------------------------------------------------===//
1163 // RISCV64 linux target specifics.
1164 //===----------------------------------------------------------------------===//
1167 struct TargetRISCV64
: public GenericTarget
<TargetRISCV64
> {
1168 using GenericTarget::GenericTarget
;
1170 static constexpr int defaultWidth
= 64;
1172 CodeGenSpecifics::Marshalling
1173 complexArgumentType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1174 CodeGenSpecifics::Marshalling marshal
;
1175 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
1176 if (sem
== &llvm::APFloat::IEEEsingle() ||
1177 sem
== &llvm::APFloat::IEEEdouble()) {
1178 // Two distinct element type arguments (re, im)
1179 marshal
.emplace_back(eleTy
, AT
{});
1180 marshal
.emplace_back(eleTy
, AT
{});
1182 typeTodo(sem
, loc
, "argument");
1187 CodeGenSpecifics::Marshalling
1188 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1189 CodeGenSpecifics::Marshalling marshal
;
1190 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
1191 if (sem
== &llvm::APFloat::IEEEsingle() ||
1192 sem
== &llvm::APFloat::IEEEdouble()) {
1193 // Use a type that will be translated into LLVM as:
1194 // { t, t } struct of 2 eleTy, byVal
1195 marshal
.emplace_back(mlir::TupleType::get(eleTy
.getContext(),
1196 mlir::TypeRange
{eleTy
, eleTy
}),
1197 AT
{/*alignment=*/0, /*byval=*/true});
1199 typeTodo(sem
, loc
, "return");
1206 //===----------------------------------------------------------------------===//
1207 // AMDGPU linux target specifics.
1208 //===----------------------------------------------------------------------===//
1211 struct TargetAMDGPU
: public GenericTarget
<TargetAMDGPU
> {
1212 using GenericTarget::GenericTarget
;
1214 // Default size (in bits) of the index type for strings.
1215 static constexpr int defaultWidth
= 64;
1217 CodeGenSpecifics::Marshalling
1218 complexArgumentType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1219 CodeGenSpecifics::Marshalling marshal
;
1220 TODO(loc
, "handle complex argument types");
1224 CodeGenSpecifics::Marshalling
1225 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1226 CodeGenSpecifics::Marshalling marshal
;
1227 TODO(loc
, "handle complex return types");
1233 //===----------------------------------------------------------------------===//
1234 // NVPTX linux target specifics.
1235 //===----------------------------------------------------------------------===//
1238 struct TargetNVPTX
: public GenericTarget
<TargetNVPTX
> {
1239 using GenericTarget::GenericTarget
;
1241 // Default size (in bits) of the index type for strings.
1242 static constexpr int defaultWidth
= 64;
1244 CodeGenSpecifics::Marshalling
1245 complexArgumentType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1246 CodeGenSpecifics::Marshalling marshal
;
1247 TODO(loc
, "handle complex argument types");
1251 CodeGenSpecifics::Marshalling
1252 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1253 CodeGenSpecifics::Marshalling marshal
;
1254 TODO(loc
, "handle complex return types");
1260 //===----------------------------------------------------------------------===//
1261 // LoongArch64 linux target specifics.
1262 //===----------------------------------------------------------------------===//
1265 struct TargetLoongArch64
: public GenericTarget
<TargetLoongArch64
> {
1266 using GenericTarget::GenericTarget
;
1268 static constexpr int defaultWidth
= 64;
1269 static constexpr int GRLen
= defaultWidth
; /* eight bytes */
1270 static constexpr int GRLenInChar
= GRLen
/ 8;
1271 static constexpr int FRLen
= defaultWidth
; /* eight bytes */
1273 CodeGenSpecifics::Marshalling
1274 complexArgumentType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1275 CodeGenSpecifics::Marshalling marshal
;
1276 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
1277 if (sem
== &llvm::APFloat::IEEEsingle() ||
1278 sem
== &llvm::APFloat::IEEEdouble()) {
1279 // Two distinct element type arguments (re, im)
1280 marshal
.emplace_back(eleTy
, AT
{});
1281 marshal
.emplace_back(eleTy
, AT
{});
1282 } else if (sem
== &llvm::APFloat::IEEEquad()) {
1283 // Use a type that will be translated into LLVM as:
1284 // { fp128, fp128 } struct of 2 fp128, byval
1285 marshal
.emplace_back(
1286 fir::ReferenceType::get(mlir::TupleType::get(
1287 eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
})),
1288 AT
{/*align=*/16, /*byval=*/true});
1290 typeTodo(sem
, loc
, "argument");
1295 CodeGenSpecifics::Marshalling
1296 complexReturnType(mlir::Location loc
, mlir::Type eleTy
) const override
{
1297 CodeGenSpecifics::Marshalling marshal
;
1298 const auto *sem
= &floatToSemantics(kindMap
, eleTy
);
1299 if (sem
== &llvm::APFloat::IEEEsingle() ||
1300 sem
== &llvm::APFloat::IEEEdouble()) {
1301 // Use a type that will be translated into LLVM as:
1302 // { t, t } struct of 2 eleTy, byVal
1303 marshal
.emplace_back(mlir::TupleType::get(eleTy
.getContext(),
1304 mlir::TypeRange
{eleTy
, eleTy
}),
1305 AT
{/*alignment=*/0, /*byval=*/true});
1306 } else if (sem
== &llvm::APFloat::IEEEquad()) {
1307 // Use a type that will be translated into LLVM as:
1308 // { fp128, fp128 } struct of 2 fp128, sret, align 16
1309 marshal
.emplace_back(
1310 fir::ReferenceType::get(mlir::TupleType::get(
1311 eleTy
.getContext(), mlir::TypeRange
{eleTy
, eleTy
})),
1312 AT
{/*align=*/16, /*byval=*/false, /*sret=*/true});
1314 typeTodo(sem
, loc
, "return");
1319 CodeGenSpecifics::Marshalling
1320 integerArgumentType(mlir::Location loc
,
1321 mlir::IntegerType argTy
) const override
{
1322 if (argTy
.getWidth() == 32) {
1323 // LA64 LP64D ABI requires unsigned 32 bit integers to be sign extended.
1324 // Therefore, Flang also follows it if a function needs to be
1325 // interoperable with C.
1327 // Currently, it only adds `signext` attribute to the dummy arguments and
1328 // return values in the function signatures, but it does not add the
1329 // corresponding attribute to the actual arguments and return values in
1330 // `fir.call` instruction. Thanks to LLVM's integration of all these
1331 // attributes, the modification is still effective.
1332 CodeGenSpecifics::Marshalling marshal
;
1333 AT::IntegerExtension intExt
= AT::IntegerExtension::Sign
;
1334 marshal
.emplace_back(argTy
, AT
{/*alignment=*/0, /*byval=*/false,
1335 /*sret=*/false, /*append=*/false,
1336 /*intExt=*/intExt
});
1340 return GenericTarget::integerArgumentType(loc
, argTy
);
1343 /// Flatten non-basic types, resulting in an array of types containing only
1344 /// `IntegerType` and `FloatType`.
1345 llvm::SmallVector
<mlir::Type
> flattenTypeList(mlir::Location loc
,
1346 const mlir::Type type
) const {
1347 llvm::SmallVector
<mlir::Type
> flatTypes
;
1349 llvm::TypeSwitch
<mlir::Type
>(type
)
1350 .template Case
<mlir::IntegerType
>([&](mlir::IntegerType intTy
) {
1351 if (intTy
.getWidth() != 0)
1352 flatTypes
.push_back(intTy
);
1354 .template Case
<mlir::FloatType
>([&](mlir::FloatType floatTy
) {
1355 if (floatTy
.getWidth() != 0)
1356 flatTypes
.push_back(floatTy
);
1358 .template Case
<mlir::ComplexType
>([&](mlir::ComplexType cmplx
) {
1359 const auto *sem
= &floatToSemantics(kindMap
, cmplx
.getElementType());
1360 if (sem
== &llvm::APFloat::IEEEsingle() ||
1361 sem
== &llvm::APFloat::IEEEdouble() ||
1362 sem
== &llvm::APFloat::IEEEquad())
1363 std::fill_n(std::back_inserter(flatTypes
), 2,
1364 cmplx
.getElementType());
1366 TODO(loc
, "unsupported complex type(not IEEEsingle, IEEEdouble, "
1367 "IEEEquad) as a structure component for BIND(C), "
1368 "VALUE derived type argument and type return");
1370 .template Case
<fir::LogicalType
>([&](fir::LogicalType logicalTy
) {
1371 const unsigned width
=
1372 kindMap
.getLogicalBitsize(logicalTy
.getFKind());
1374 flatTypes
.push_back(
1375 mlir::IntegerType::get(type
.getContext(), width
));
1377 .template Case
<fir::CharacterType
>([&](fir::CharacterType charTy
) {
1378 assert(kindMap
.getCharacterBitsize(charTy
.getFKind()) <= 8 &&
1379 "the bit size of characterType as an interoperable type must "
1381 for (unsigned i
= 0; i
< charTy
.getLen(); ++i
)
1382 flatTypes
.push_back(mlir::IntegerType::get(type
.getContext(), 8));
1384 .template Case
<fir::SequenceType
>([&](fir::SequenceType seqTy
) {
1385 if (!seqTy
.hasDynamicExtents()) {
1386 const std::uint64_t numOfEle
= seqTy
.getConstantArraySize();
1387 mlir::Type eleTy
= seqTy
.getEleTy();
1388 if (!mlir::isa
<mlir::IntegerType
, mlir::FloatType
>(eleTy
)) {
1389 llvm::SmallVector
<mlir::Type
> subTypeList
=
1390 flattenTypeList(loc
, eleTy
);
1391 if (subTypeList
.size() != 0)
1392 for (std::uint64_t i
= 0; i
< numOfEle
; ++i
)
1393 llvm::copy(subTypeList
, std::back_inserter(flatTypes
));
1395 std::fill_n(std::back_inserter(flatTypes
), numOfEle
, eleTy
);
1398 TODO(loc
, "unsupported dynamic extent sequence type as a structure "
1399 "component for BIND(C), "
1400 "VALUE derived type argument and type return");
1402 .template Case
<fir::RecordType
>([&](fir::RecordType recTy
) {
1403 for (auto &component
: recTy
.getTypeList()) {
1404 mlir::Type eleTy
= component
.second
;
1405 llvm::SmallVector
<mlir::Type
> subTypeList
=
1406 flattenTypeList(loc
, eleTy
);
1407 if (subTypeList
.size() != 0)
1408 llvm::copy(subTypeList
, std::back_inserter(flatTypes
));
1411 .template Case
<fir::VectorType
>([&](fir::VectorType vecTy
) {
1412 auto sizeAndAlign
= fir::getTypeSizeAndAlignmentOrCrash(
1413 loc
, vecTy
, getDataLayout(), kindMap
);
1414 if (sizeAndAlign
.first
== 2 * GRLenInChar
)
1415 flatTypes
.push_back(
1416 mlir::IntegerType::get(type
.getContext(), 2 * GRLen
));
1418 TODO(loc
, "unsupported vector width(must be 128 bits)");
1420 .Default([&](mlir::Type ty
) {
1421 if (fir::conformsWithPassByRef(ty
))
1422 flatTypes
.push_back(
1423 mlir::IntegerType::get(type
.getContext(), GRLen
));
1425 TODO(loc
, "unsupported component type for BIND(C), VALUE derived "
1426 "type argument and type return");
1432 /// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,
1433 /// when flattened it contains a single fp value, fp+fp, or int+fp of
1434 /// appropriate size).
1435 bool detectFARsEligibleStruct(mlir::Location loc
, fir::RecordType recTy
,
1436 mlir::Type
&field1Ty
,
1437 mlir::Type
&field2Ty
) const {
1438 field1Ty
= field2Ty
= nullptr;
1439 llvm::SmallVector
<mlir::Type
> flatTypes
= flattenTypeList(loc
, recTy
);
1440 size_t flatSize
= flatTypes
.size();
1442 // Cannot be eligible if the number of flattened types is equal to 0 or
1444 if (flatSize
== 0 || flatSize
> 2)
1447 bool isFirstAvaliableFloat
= false;
1449 assert((mlir::isa
<mlir::IntegerType
, mlir::FloatType
>(flatTypes
[0])) &&
1450 "Type must be integerType or floatType after flattening");
1451 if (auto floatTy
= mlir::dyn_cast
<mlir::FloatType
>(flatTypes
[0])) {
1452 const unsigned Size
= floatTy
.getWidth();
1453 // Can't be eligible if larger than the FP registers. Half precision isn't
1454 // currently supported on LoongArch and the ABI hasn't been confirmed, so
1455 // default to the integer ABI in that case.
1456 if (Size
> FRLen
|| Size
< 32)
1458 isFirstAvaliableFloat
= true;
1460 } else if (auto intTy
= mlir::dyn_cast
<mlir::IntegerType
>(flatTypes
[0])) {
1461 if (intTy
.getWidth() > GRLen
)
1466 // flatTypes has two elements
1467 if (flatSize
== 2) {
1468 assert((mlir::isa
<mlir::IntegerType
, mlir::FloatType
>(flatTypes
[1])) &&
1469 "Type must be integerType or floatType after flattening");
1470 if (auto floatTy
= mlir::dyn_cast
<mlir::FloatType
>(flatTypes
[1])) {
1471 const unsigned Size
= floatTy
.getWidth();
1472 if (Size
> FRLen
|| Size
< 32)
1476 } else if (auto intTy
= mlir::dyn_cast
<mlir::IntegerType
>(flatTypes
[1])) {
1477 // Can't be eligible if an integer type was already found (int+int pairs
1478 // are not eligible).
1479 if (!isFirstAvaliableFloat
)
1481 if (intTy
.getWidth() > GRLen
)
1488 // return isFirstAvaliableFloat if flatTypes only has one element
1489 return isFirstAvaliableFloat
;
1492 bool checkTypeHasEnoughRegs(mlir::Location loc
, int &GARsLeft
, int &FARsLeft
,
1493 const mlir::Type type
) const {
1497 llvm::TypeSwitch
<mlir::Type
>(type
)
1498 .template Case
<mlir::IntegerType
>([&](mlir::IntegerType intTy
) {
1499 const unsigned width
= intTy
.getWidth();
1502 "integerType with width exceeding 128 bits is unsupported");
1507 else if (width
<= 2 * GRLen
)
1508 GARsLeft
= GARsLeft
- 2;
1510 .template Case
<mlir::FloatType
>([&](mlir::FloatType floatTy
) {
1511 const unsigned width
= floatTy
.getWidth();
1513 TODO(loc
, "floatType with width exceeding 128 bits is unsupported");
1516 if (width
== 32 || width
== 64)
1518 else if (width
<= GRLen
)
1520 else if (width
<= 2 * GRLen
)
1521 GARsLeft
= GARsLeft
- 2;
1523 .Default([&](mlir::Type ty
) {
1524 if (fir::conformsWithPassByRef(ty
))
1525 --GARsLeft
; // Pointers.
1527 TODO(loc
, "unsupported component type for BIND(C), VALUE derived "
1528 "type argument and type return");
1531 return GARsLeft
>= 0 && FARsLeft
>= 0;
1534 bool hasEnoughRegisters(mlir::Location loc
, int GARsLeft
, int FARsLeft
,
1535 const Marshalling
&previousArguments
,
1536 const mlir::Type
&field1Ty
,
1537 const mlir::Type
&field2Ty
) const {
1538 for (auto &typeAndAttr
: previousArguments
) {
1539 const auto &attr
= std::get
<Attributes
>(typeAndAttr
);
1540 if (attr
.isByVal()) {
1541 // Previous argument passed on the stack, and its address is passed in
1547 // Previous aggregate arguments were marshalled into simpler arguments.
1548 const auto &type
= std::get
<mlir::Type
>(typeAndAttr
);
1549 llvm::SmallVector
<mlir::Type
> flatTypes
= flattenTypeList(loc
, type
);
1551 for (auto &flatTy
: flatTypes
) {
1552 if (!checkTypeHasEnoughRegs(loc
, GARsLeft
, FARsLeft
, flatTy
))
1557 if (!checkTypeHasEnoughRegs(loc
, GARsLeft
, FARsLeft
, field1Ty
))
1559 if (!checkTypeHasEnoughRegs(loc
, GARsLeft
, FARsLeft
, field2Ty
))
1564 /// LoongArch64 subroutine calling sequence ABI in:
1565 /// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
1566 CodeGenSpecifics::Marshalling
1567 classifyStruct(mlir::Location loc
, fir::RecordType recTy
, int GARsLeft
,
1568 int FARsLeft
, bool isResult
,
1569 const Marshalling
&previousArguments
) const {
1570 CodeGenSpecifics::Marshalling marshal
;
1572 auto [recSize
, recAlign
] = fir::getTypeSizeAndAlignmentOrCrash(
1573 loc
, recTy
, getDataLayout(), kindMap
);
1574 mlir::MLIRContext
*context
= recTy
.getContext();
1577 TODO(loc
, "unsupported empty struct type for BIND(C), "
1578 "VALUE derived type argument and type return");
1581 if (recSize
> 2 * GRLenInChar
) {
1582 marshal
.emplace_back(
1583 fir::ReferenceType::get(recTy
),
1584 AT
{recAlign
, /*byval=*/!isResult
, /*sret=*/isResult
});
1588 // Pass by FARs(and GARs)
1589 mlir::Type field1Ty
= nullptr, field2Ty
= nullptr;
1590 if (detectFARsEligibleStruct(loc
, recTy
, field1Ty
, field2Ty
) &&
1591 hasEnoughRegisters(loc
, GARsLeft
, FARsLeft
, previousArguments
, field1Ty
,
1595 marshal
.emplace_back(field1Ty
, AT
{});
1597 marshal
.emplace_back(field2Ty
, AT
{});
1599 // field1Ty is always preferred over field2Ty for assignment, so there
1600 // will never be a case where field1Ty == nullptr and field2Ty !=
1602 if (field1Ty
&& !field2Ty
)
1603 marshal
.emplace_back(field1Ty
, AT
{});
1604 else if (field1Ty
&& field2Ty
)
1605 marshal
.emplace_back(
1606 mlir::TupleType::get(context
,
1607 mlir::TypeRange
{field1Ty
, field2Ty
}),
1608 AT
{/*alignment=*/0, /*byval=*/true});
1613 if (recSize
<= GRLenInChar
) {
1614 marshal
.emplace_back(mlir::IntegerType::get(context
, GRLen
), AT
{});
1618 if (recAlign
== 2 * GRLenInChar
) {
1619 marshal
.emplace_back(mlir::IntegerType::get(context
, 2 * GRLen
), AT
{});
1623 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar
1624 marshal
.emplace_back(
1625 fir::SequenceType::get({2}, mlir::IntegerType::get(context
, GRLen
)),
1630 /// Marshal a derived type passed by value like a C struct.
1631 CodeGenSpecifics::Marshalling
1632 structArgumentType(mlir::Location loc
, fir::RecordType recTy
,
1633 const Marshalling
&previousArguments
) const override
{
1635 int FARsLeft
= FRLen
? 8 : 0;
1637 return classifyStruct(loc
, recTy
, GARsLeft
, FARsLeft
, /*isResult=*/false,
1641 CodeGenSpecifics::Marshalling
1642 structReturnType(mlir::Location loc
, fir::RecordType recTy
) const override
{
1643 // The rules for return and argument types are the same.
1645 int FARsLeft
= FRLen
? 2 : 0;
1646 return classifyStruct(loc
, recTy
, GARsLeft
, FARsLeft
, /*isResult=*/true,
1652 // Instantiate the overloaded target instance based on the triple value.
1653 // TODO: Add other targets to this file as needed.
1654 std::unique_ptr
<fir::CodeGenSpecifics
>
1655 fir::CodeGenSpecifics::get(mlir::MLIRContext
*ctx
, llvm::Triple
&&trp
,
1656 KindMapping
&&kindMap
, llvm::StringRef targetCPU
,
1657 mlir::LLVM::TargetFeaturesAttr targetFeatures
,
1658 const mlir::DataLayout
&dl
) {
1659 switch (trp
.getArch()) {
1662 case llvm::Triple::ArchType::x86
:
1663 if (trp
.isOSWindows())
1664 return std::make_unique
<TargetI386Win
>(ctx
, std::move(trp
),
1665 std::move(kindMap
), targetCPU
,
1666 targetFeatures
, dl
);
1668 return std::make_unique
<TargetI386
>(ctx
, std::move(trp
),
1669 std::move(kindMap
), targetCPU
,
1670 targetFeatures
, dl
);
1671 case llvm::Triple::ArchType::x86_64
:
1672 if (trp
.isOSWindows())
1673 return std::make_unique
<TargetX86_64Win
>(ctx
, std::move(trp
),
1674 std::move(kindMap
), targetCPU
,
1675 targetFeatures
, dl
);
1677 return std::make_unique
<TargetX86_64
>(ctx
, std::move(trp
),
1678 std::move(kindMap
), targetCPU
,
1679 targetFeatures
, dl
);
1680 case llvm::Triple::ArchType::aarch64
:
1681 return std::make_unique
<TargetAArch64
>(
1682 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1683 case llvm::Triple::ArchType::ppc64
:
1684 return std::make_unique
<TargetPPC64
>(
1685 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1686 case llvm::Triple::ArchType::ppc64le
:
1687 return std::make_unique
<TargetPPC64le
>(
1688 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1689 case llvm::Triple::ArchType::sparc
:
1690 return std::make_unique
<TargetSparc
>(
1691 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1692 case llvm::Triple::ArchType::sparcv9
:
1693 return std::make_unique
<TargetSparcV9
>(
1694 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1695 case llvm::Triple::ArchType::riscv64
:
1696 return std::make_unique
<TargetRISCV64
>(
1697 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1698 case llvm::Triple::ArchType::amdgcn
:
1699 return std::make_unique
<TargetAMDGPU
>(
1700 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1701 case llvm::Triple::ArchType::nvptx64
:
1702 return std::make_unique
<TargetNVPTX
>(
1703 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1704 case llvm::Triple::ArchType::loongarch64
:
1705 return std::make_unique
<TargetLoongArch64
>(
1706 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1708 TODO(mlir::UnknownLoc::get(ctx
), "target not implemented");
1711 std::unique_ptr
<fir::CodeGenSpecifics
> fir::CodeGenSpecifics::get(
1712 mlir::MLIRContext
*ctx
, llvm::Triple
&&trp
, KindMapping
&&kindMap
,
1713 llvm::StringRef targetCPU
, mlir::LLVM::TargetFeaturesAttr targetFeatures
,
1714 const mlir::DataLayout
&dl
, llvm::StringRef tuneCPU
) {
1715 std::unique_ptr
<fir::CodeGenSpecifics
> CGS
= fir::CodeGenSpecifics::get(
1716 ctx
, std::move(trp
), std::move(kindMap
), targetCPU
, targetFeatures
, dl
);
1718 CGS
->tuneCPU
= tuneCPU
;