[clang][bytecode] Destroy local variables in reverse order (#125727)
[llvm-project.git] / flang / lib / Optimizer / CodeGen / Target.cpp
blob1bc673bb34e32c8b9f8b3fdf93e974a7849ae1f4
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/
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"
25 using namespace fir;
27 namespace fir::details {
28 llvm::StringRef Attributes::getIntExtensionAttrName() const {
29 // The attribute names are available via LLVM dialect interfaces
30 // like getZExtAttrName(), getByValAttrName(), etc., so we'd better
31 // use them than literals.
32 if (isZeroExt())
33 return "llvm.zeroext";
34 else if (isSignExt())
35 return "llvm.signext";
36 return {};
38 } // namespace fir::details
40 // Reduce a REAL/float type to the floating point semantics.
41 static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
42 mlir::Type type) {
43 assert(isa_real(type));
44 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");
55 } else {
56 TODO(loc, "complex for this precision for " + context + " type");
60 namespace {
61 template <typename S>
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:
78 // { t*, index }
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
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;
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;
126 else
127 intExt = AT::IntegerExtension::Sign;
131 marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
132 /*sret=*/false, /*append=*/false,
133 /*intExt=*/intExt});
134 return marshal;
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; }
148 } // namespace
150 //===----------------------------------------------------------------------===//
151 // i386 (x86 32 bit) linux target specifics.
152 //===----------------------------------------------------------------------===//
154 namespace {
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
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;
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");
192 return marshal;
195 } // namespace
197 //===----------------------------------------------------------------------===//
198 // i386 (x86 32 bit) Windows target specifics.
199 //===----------------------------------------------------------------------===//
201 namespace {
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
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;
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");
251 return marshal;
254 } // namespace
256 //===----------------------------------------------------------------------===//
257 // x86_64 (x86 64 bit) linux target specifics.
258 //===----------------------------------------------------------------------===//
260 namespace {
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});
295 } else {
296 typeTodo(sem, loc, "argument");
298 return marshal;
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");
329 return marshal;
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
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;
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;
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);
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;
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);
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
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;
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");
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;
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;
448 return byteOffset;
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;
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)
474 return;
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;
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)
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;
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;
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;
559 // Complex field that needs to be split, or array.
560 return {};
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);
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;
598 ArgClass Lo, Hi;
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,
619 previousArguments))
620 return passOnTheStack(loc, recTy, /*isResult=*/false);
622 if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
623 CodeGenSpecifics::Marshalling marshal;
624 marshal.emplace_back(fieldType, AT{});
625 return marshal;
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;
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;
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(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
670 // funny effect of
671 // {_Complex long double} return to be dealt with differently than
672 // _Complex long double.
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;
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;
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;
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;
714 } // namespace
716 //===----------------------------------------------------------------------===//
717 // x86_64 (x86 64 bit) Windows target specifics.
718 //===----------------------------------------------------------------------===//
720 namespace {
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),
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");
752 return marshal;
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");
781 return marshal;
784 } // namespace
786 //===----------------------------------------------------------------------===//
787 // AArch64 linux target specifics.
788 //===----------------------------------------------------------------------===//
790 namespace {
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{});
807 } else {
808 typeTodo(sem, loc, "argument");
810 return marshal;
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}),
824 AT{});
825 } else {
826 typeTodo(sem, loc, "return");
828 return marshal;
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());
841 if (!subTypeList)
842 return std::nullopt;
843 llvm::copy(*subTypeList, std::back_inserter(flatTypes));
844 continue;
847 // Flatten array type
848 if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {
849 if (seqTy.hasDynamicExtents())
850 return std::nullopt;
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());
856 if (!subTypeList)
857 return std::nullopt;
858 for (std::size_t i = 0; i < n; ++i)
859 llvm::copy(*subTypeList, std::back_inserter(flatTypes));
860 } else {
861 std::fill_n(std::back_inserter(flatTypes),
862 seqTy.getConstantArraySize(), eleTy);
864 continue;
867 // Other types are already flat
868 flatTypes.push_back(type);
870 return flatTypes;
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)
878 return std::nullopt;
880 std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);
881 if (!flatTypes || flatTypes->size() > 4) {
882 return std::nullopt;
885 if (!isa_real(flatTypes->front())) {
886 return std::nullopt;
889 return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()}
890 : std::nullopt;
893 struct NRegs {
894 int n{0};
895 bool isSimd{false};
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);
905 if (size <= 16)
906 return {static_cast<int>((size + 7) / 8), false};
908 // Pass on the stack, i.e. no registers used
909 return {};
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];
926 return nregs;
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");
932 return NRegs{};
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)
944 break;
946 if (attr.isByVal())
947 continue; // Previous argument passed on the stack
949 NRegs nregs = usedRegsForType(loc, type);
950 if (nregs.isSimd)
951 availSIMDRegisters -= nregs.n;
952 else
953 availIntRegisters -= nregs.n;
956 NRegs nregs = usedRegsForRecordType(loc, type);
958 if (nregs.isSimd)
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;
967 auto sizeAndAlign =
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});
974 return marshal;
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
982 if (nregs.n == 0)
983 return passOnTheStack(loc, type, isResult);
985 CodeGenSpecifics::Marshalling marshal;
987 mlir::Type pcsType;
988 if (nregs.isSimd) {
989 pcsType = type;
990 } else {
991 pcsType = fir::SequenceType::get(
992 nregs.n, mlir::IntegerType::get(type.getContext(), 64));
995 marshal.emplace_back(pcsType, AT{});
996 return marshal;
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);
1014 } // namespace
1016 //===----------------------------------------------------------------------===//
1017 // PPC64 (AIX 64 bit) target specifics.
1018 //===----------------------------------------------------------------------===//
1020 namespace {
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{});
1032 return marshal;
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}),
1042 AT{});
1043 return marshal;
1046 } // namespace
1048 //===----------------------------------------------------------------------===//
1049 // PPC64le linux target specifics.
1050 //===----------------------------------------------------------------------===//
1052 namespace {
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{});
1064 return marshal;
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}),
1074 AT{});
1075 return marshal;
1078 } // namespace
1080 //===----------------------------------------------------------------------===//
1081 // sparc (sparc 32 bit) target specifics.
1082 //===----------------------------------------------------------------------===//
1084 namespace {
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
1096 auto structTy =
1097 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1098 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
1099 return marshal;
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
1108 auto structTy =
1109 mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
1110 marshal.emplace_back(fir::ReferenceType::get(structTy),
1111 AT{/*alignment=*/0, /*byval=*/true});
1112 return marshal;
1115 } // namespace
1117 //===----------------------------------------------------------------------===//
1118 // sparcv9 (sparc 64 bit) target specifics.
1119 //===----------------------------------------------------------------------===//
1121 namespace {
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});
1143 } else {
1144 typeTodo(sem, loc, "argument");
1146 return marshal;
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}),
1156 AT{});
1157 return marshal;
1160 } // namespace
1162 //===----------------------------------------------------------------------===//
1163 // RISCV64 linux target specifics.
1164 //===----------------------------------------------------------------------===//
1166 namespace {
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{});
1181 } else {
1182 typeTodo(sem, loc, "argument");
1184 return marshal;
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});
1198 } else {
1199 typeTodo(sem, loc, "return");
1201 return marshal;
1204 } // namespace
1206 //===----------------------------------------------------------------------===//
1207 // AMDGPU linux target specifics.
1208 //===----------------------------------------------------------------------===//
1210 namespace {
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");
1221 return marshal;
1224 CodeGenSpecifics::Marshalling
1225 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1226 CodeGenSpecifics::Marshalling marshal;
1227 TODO(loc, "handle complex return types");
1228 return marshal;
1231 } // namespace
1233 //===----------------------------------------------------------------------===//
1234 // NVPTX linux target specifics.
1235 //===----------------------------------------------------------------------===//
1237 namespace {
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");
1248 return marshal;
1251 CodeGenSpecifics::Marshalling
1252 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
1253 CodeGenSpecifics::Marshalling marshal;
1254 TODO(loc, "handle complex return types");
1255 return marshal;
1258 } // namespace
1260 //===----------------------------------------------------------------------===//
1261 // LoongArch64 linux target specifics.
1262 //===----------------------------------------------------------------------===//
1264 namespace {
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});
1289 } else {
1290 typeTodo(sem, loc, "argument");
1292 return marshal;
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});
1313 } else {
1314 typeTodo(sem, loc, "return");
1316 return marshal;
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});
1337 return marshal;
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());
1365 else
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());
1373 if (width != 0)
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 "
1380 "not exceed 8");
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));
1394 } else {
1395 std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
1397 } else
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));
1417 else
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));
1424 else
1425 TODO(loc, "unsupported component type for BIND(C), VALUE derived "
1426 "type argument and type return");
1429 return flatTypes;
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
1443 // greater than 2.
1444 if (flatSize == 0 || flatSize > 2)
1445 return false;
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)
1457 return false;
1458 isFirstAvaliableFloat = true;
1459 field1Ty = floatTy;
1460 } else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
1461 if (intTy.getWidth() > GRLen)
1462 return false;
1463 field1Ty = intTy;
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)
1473 return false;
1474 field2Ty = floatTy;
1475 return true;
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)
1480 return false;
1481 if (intTy.getWidth() > GRLen)
1482 return false;
1483 field2Ty = intTy;
1484 return true;
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 {
1494 if (!type)
1495 return true;
1497 llvm::TypeSwitch<mlir::Type>(type)
1498 .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
1499 const unsigned width = intTy.getWidth();
1500 if (width > 128)
1501 TODO(loc,
1502 "integerType with width exceeding 128 bits is unsupported");
1503 if (width == 0)
1504 return;
1505 if (width <= GRLen)
1506 --GARsLeft;
1507 else if (width <= 2 * GRLen)
1508 GARsLeft = GARsLeft - 2;
1510 .template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
1511 const unsigned width = floatTy.getWidth();
1512 if (width > 128)
1513 TODO(loc, "floatType with width exceeding 128 bits is unsupported");
1514 if (width == 0)
1515 return;
1516 if (width == 32 || width == 64)
1517 --FARsLeft;
1518 else if (width <= GRLen)
1519 --GARsLeft;
1520 else if (width <= 2 * GRLen)
1521 GARsLeft = GARsLeft - 2;
1523 .Default([&](mlir::Type ty) {
1524 if (fir::conformsWithPassByRef(ty))
1525 --GARsLeft; // Pointers.
1526 else
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
1542 // GAR.
1543 --GARsLeft;
1544 continue;
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))
1553 return false;
1557 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
1558 return false;
1559 if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
1560 return false;
1561 return true;
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();
1576 if (recSize == 0) {
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});
1585 return marshal;
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,
1592 field2Ty)) {
1593 if (!isResult) {
1594 if (field1Ty)
1595 marshal.emplace_back(field1Ty, AT{});
1596 if (field2Ty)
1597 marshal.emplace_back(field2Ty, AT{});
1598 } else {
1599 // field1Ty is always preferred over field2Ty for assignment, so there
1600 // will never be a case where field1Ty == nullptr and field2Ty !=
1601 // nullptr.
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});
1610 return marshal;
1613 if (recSize <= GRLenInChar) {
1614 marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});
1615 return marshal;
1618 if (recAlign == 2 * GRLenInChar) {
1619 marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});
1620 return marshal;
1623 // recSize > GRLenInChar && recSize <= 2 * GRLenInChar
1624 marshal.emplace_back(
1625 fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
1626 AT{});
1627 return marshal;
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 {
1634 int GARsLeft = 8;
1635 int FARsLeft = FRLen ? 8 : 0;
1637 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,
1638 previousArguments);
1641 CodeGenSpecifics::Marshalling
1642 structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
1643 // The rules for return and argument types are the same.
1644 int GARsLeft = 2;
1645 int FARsLeft = FRLen ? 2 : 0;
1646 return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,
1647 {});
1650 } // namespace
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()) {
1660 default:
1661 break;
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);
1667 else
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);
1676 else
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;
1719 return CGS;