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

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