| //===-- Target.cpp --------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Optimizer/CodeGen/Target.h" |
| #include "flang/Optimizer/Builder/Todo.h" |
| #include "flang/Optimizer/Dialect/FIRType.h" |
| #include "flang/Optimizer/Dialect/Support/KindMapping.h" |
| #include "flang/Optimizer/Support/FatalError.h" |
| #include "flang/Optimizer/Support/Utils.h" |
| #include "mlir/IR/BuiltinTypes.h" |
| #include "mlir/IR/TypeRange.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| |
| #define DEBUG_TYPE "flang-codegen-target" |
| |
| using namespace fir; |
| |
| namespace fir::details { |
| llvm::StringRef Attributes::getIntExtensionAttrName() const { |
| // The attribute names are available via LLVM dialect interfaces |
| // like getZExtAttrName(), getByValAttrName(), etc., so we'd better |
| // use them than literals. |
| if (isZeroExt()) |
| return "llvm.zeroext"; |
| else if (isSignExt()) |
| return "llvm.signext"; |
| return {}; |
| } |
| } // namespace fir::details |
| |
| // Reduce a REAL/float type to the floating point semantics. |
| static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, |
| mlir::Type type) { |
| assert(isa_real(type)); |
| if (auto ty = mlir::dyn_cast<fir::RealType>(type)) |
| return kindMap.getFloatSemantics(ty.getFKind()); |
| return mlir::cast<mlir::FloatType>(type).getFloatSemantics(); |
| } |
| |
| static void typeTodo(const llvm::fltSemantics *sem, mlir::Location loc, |
| const std::string &context) { |
| if (sem == &llvm::APFloat::IEEEhalf()) { |
| TODO(loc, "COMPLEX(KIND=2): for " + context + " type"); |
| } else if (sem == &llvm::APFloat::BFloat()) { |
| TODO(loc, "COMPLEX(KIND=3): " + context + " type"); |
| } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| TODO(loc, "COMPLEX(KIND=10): " + context + " type"); |
| } else { |
| TODO(loc, "complex for this precision for " + context + " type"); |
| } |
| } |
| |
| namespace { |
| template <typename S> |
| struct GenericTarget : public CodeGenSpecifics { |
| using CodeGenSpecifics::CodeGenSpecifics; |
| using AT = CodeGenSpecifics::Attributes; |
| |
| mlir::Type complexMemoryType(mlir::Type eleTy) const override { |
| assert(fir::isa_real(eleTy)); |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy |
| return mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}); |
| } |
| |
| mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { |
| auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); |
| auto ptrTy = fir::ReferenceType::get(eleTy); |
| // Use a type that will be translated into LLVM as: |
| // { t*, index } |
| return mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{ptrTy, idxTy}); |
| } |
| |
| Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); |
| auto ptrTy = fir::ReferenceType::get(eleTy); |
| marshal.emplace_back(ptrTy, AT{}); |
| // Return value arguments are grouped as a pair. Others are passed in a |
| // split format with all pointers first (in the declared position) and all |
| // LEN arguments appended after all of the dummy arguments. |
| // NB: Other conventions/ABIs can/should be supported via options. |
| marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, |
| /*sret=*/sret, /*append=*/!sret}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| structArgumentType(mlir::Location loc, fir::RecordType, |
| const Marshalling &) const override { |
| TODO(loc, "passing VALUE BIND(C) derived type for this target"); |
| } |
| |
| CodeGenSpecifics::Marshalling |
| integerArgumentType(mlir::Location loc, |
| mlir::IntegerType argTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| AT::IntegerExtension intExt = AT::IntegerExtension::None; |
| if (argTy.getWidth() < getCIntTypeWidth()) { |
| // isSigned() and isUnsigned() branches below are dead code currently. |
| // If needed, we can generate calls with signed/unsigned argument types |
| // to more precisely match C side (e.g. for Fortran runtime functions |
| // with 'unsigned short' arguments). |
| if (argTy.isSigned()) |
| intExt = AT::IntegerExtension::Sign; |
| else if (argTy.isUnsigned()) |
| intExt = AT::IntegerExtension::Zero; |
| else if (argTy.isSignless()) { |
| // Zero extend for 'i1' and sign extend for other types. |
| if (argTy.getWidth() == 1) |
| intExt = AT::IntegerExtension::Zero; |
| else |
| intExt = AT::IntegerExtension::Sign; |
| } |
| } |
| |
| marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false, |
| /*sret=*/false, /*append=*/false, |
| /*intExt=*/intExt}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| integerReturnType(mlir::Location loc, |
| mlir::IntegerType argTy) const override { |
| return integerArgumentType(loc, argTy); |
| } |
| |
| // Width of 'int' type is 32-bits for almost all targets, except |
| // for AVR and MSP430 (see TargetInfo initializations |
| // in clang/lib/Basic/Targets). |
| unsigned char getCIntTypeWidth() const override { return 32; } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // i386 (x86 32 bit) linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetI386 : public GenericTarget<TargetI386> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 32; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
| assert(fir::isa_real(eleTy)); |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byval, align 4 |
| auto structTy = |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
| marshal.emplace_back(fir::ReferenceType::get(structTy), |
| AT{/*alignment=*/4, /*byval=*/true}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| assert(fir::isa_real(eleTy)); |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // i64 pack both floats in a 64-bit GPR |
| marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
| AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, sret, align 4 |
| auto structTy = mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}); |
| marshal.emplace_back(fir::ReferenceType::get(structTy), |
| AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // i386 (x86 32 bit) Windows target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetI386Win : public GenericTarget<TargetI386Win> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 32; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byval, align 4 |
| auto structTy = |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
| marshal.emplace_back(fir::ReferenceType::get(structTy), |
| AT{/*align=*/4, /*byval=*/true}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // i64 pack both floats in a 64-bit GPR |
| marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
| AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { double, double } struct of 2 double, sret, align 8 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); |
| } else if (sem == &llvm::APFloat::IEEEquad()) { |
| // Use a type that will be translated into LLVM as: |
| // { fp128, fp128 } struct of 2 fp128, sret, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
| } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| // Use a type that will be translated into LLVM as: |
| // { x86_fp80, x86_fp80 } struct of 2 x86_fp80, sret, align 4 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/4, /*byval=*/false, /*sret=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // x86_64 (x86 64 bit) linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetX86_64 : public GenericTarget<TargetX86_64> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // <2 x t> vector of 2 eleTy |
| marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // FIXME: In case of SSE register exhaustion, the ABI here may be |
| // incorrect since LLVM may pass the real via register and the imaginary |
| // part via the stack while the ABI it should be all in register or all |
| // in memory. Register occupancy must be analyzed here. |
| // two distinct double arguments |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| // Use a type that will be translated into LLVM as: |
| // { x86_fp80, x86_fp80 } struct of 2 fp128, byval, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/true}); |
| } else if (sem == &llvm::APFloat::IEEEquad()) { |
| // Use a type that will be translated into LLVM as: |
| // { fp128, fp128 } struct of 2 fp128, byval, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/true}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // <2 x t> vector of 2 eleTy |
| marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { double, double } struct of 2 double |
| marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| } else if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| // { x86_fp80, x86_fp80 } |
| marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| } else if (sem == &llvm::APFloat::IEEEquad()) { |
| // Use a type that will be translated into LLVM as: |
| // { fp128, fp128 } struct of 2 fp128, sret, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| |
| /// X86-64 argument classes from System V ABI version 1.0 section 3.2.3. |
| enum ArgClass { |
| Integer = 0, |
| SSE, |
| SSEUp, |
| X87, |
| X87Up, |
| ComplexX87, |
| NoClass, |
| Memory |
| }; |
| |
| /// Classify an argument type or a field of an aggregate type argument. |
| /// See System V ABI version 1.0 section 3.2.3. |
| /// The Lo and Hi class are set to the class of the lower eight eightbytes |
| /// and upper eight eightbytes on return. |
| /// If this is called for an aggregate field, the caller is responsible to |
| /// do the post-merge. |
| void classify(mlir::Location loc, mlir::Type type, std::uint64_t byteOffset, |
| ArgClass &Lo, ArgClass &Hi) const { |
| Hi = Lo = ArgClass::NoClass; |
| ArgClass ¤t = byteOffset < 8 ? Lo : Hi; |
| // System V AMD64 ABI 3.2.3. version 1.0 |
| llvm::TypeSwitch<mlir::Type>(type) |
| .template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) { |
| if (intTy.getWidth() == 128) |
| Hi = Lo = ArgClass::Integer; |
| else |
| current = ArgClass::Integer; |
| }) |
| .template Case<mlir::FloatType, fir::RealType>([&](mlir::Type floatTy) { |
| const auto *sem = &floatToSemantics(kindMap, floatTy); |
| if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| Lo = ArgClass::X87; |
| Hi = ArgClass::X87Up; |
| } else if (sem == &llvm::APFloat::IEEEquad()) { |
| Lo = ArgClass::SSE; |
| Hi = ArgClass::SSEUp; |
| } else { |
| current = ArgClass::SSE; |
| } |
| }) |
| .template Case<fir::ComplexType>([&](fir::ComplexType cmplx) { |
| const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType()); |
| if (sem == &llvm::APFloat::x87DoubleExtended()) { |
| current = ArgClass::ComplexX87; |
| } else { |
| fir::SequenceType::Shape shape{2}; |
| classifyArray(loc, |
| fir::SequenceType::get(shape, cmplx.getElementType()), |
| byteOffset, Lo, Hi); |
| } |
| }) |
| .template Case<fir::LogicalType>([&](fir::LogicalType logical) { |
| if (kindMap.getLogicalBitsize(logical.getFKind()) == 128) |
| Hi = Lo = ArgClass::Integer; |
| else |
| current = ArgClass::Integer; |
| }) |
| .template Case<fir::CharacterType>( |
| [&](fir::CharacterType character) { current = ArgClass::Integer; }) |
| .template Case<fir::SequenceType>([&](fir::SequenceType seqTy) { |
| // Array component. |
| classifyArray(loc, seqTy, byteOffset, Lo, Hi); |
| }) |
| .template Case<fir::RecordType>([&](fir::RecordType recTy) { |
| // Component that is a derived type. |
| classifyStruct(loc, recTy, byteOffset, Lo, Hi); |
| }) |
| .template Case<fir::VectorType>([&](fir::VectorType vecTy) { |
| // Previously marshalled SSE eight byte for a previous struct |
| // argument. |
| auto *sem = fir::isa_real(vecTy.getEleTy()) |
| ? &floatToSemantics(kindMap, vecTy.getEleTy()) |
| : nullptr; |
| // Not expecting to hit this todo in standard code (it would |
| // require some vector type extension). |
| if (!(sem == &llvm::APFloat::IEEEsingle() && vecTy.getLen() <= 2) && |
| !(sem == &llvm::APFloat::IEEEhalf() && vecTy.getLen() <= 4)) |
| TODO(loc, "passing vector argument to C by value"); |
| current = SSE; |
| }) |
| .Default([&](mlir::Type ty) { |
| if (fir::conformsWithPassByRef(ty)) |
| current = ArgClass::Integer; // Pointers. |
| else |
| TODO(loc, "unsupported component type for BIND(C), VALUE derived " |
| "type argument"); |
| }); |
| } |
| |
| // Classify fields of a derived type starting at \p offset. Returns the new |
| // offset. Post-merge is left to the caller. |
| std::uint64_t classifyStruct(mlir::Location loc, fir::RecordType recTy, |
| std::uint64_t byteOffset, ArgClass &Lo, |
| ArgClass &Hi) const { |
| for (auto component : recTy.getTypeList()) { |
| if (byteOffset > 16) { |
| // See 3.2.3 p. 1 and note 15. Note that when the offset is bigger |
| // than 16 bytes here, it is not a single _m256 and or _m512 entity |
| // that could fit in AVX registers. |
| Lo = Hi = ArgClass::Memory; |
| return byteOffset; |
| } |
| mlir::Type compType = component.second; |
| auto [compSize, compAlign] = |
| fir::getTypeSizeAndAlignment(loc, compType, getDataLayout(), kindMap); |
| byteOffset = llvm::alignTo(byteOffset, compAlign); |
| ArgClass LoComp, HiComp; |
| classify(loc, compType, byteOffset, LoComp, HiComp); |
| Lo = mergeClass(Lo, LoComp); |
| Hi = mergeClass(Hi, HiComp); |
| byteOffset = byteOffset + llvm::alignTo(compSize, compAlign); |
| if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) |
| return byteOffset; |
| } |
| return byteOffset; |
| } |
| |
| // Classify fields of a constant size array type starting at \p offset. |
| // Returns the new offset. Post-merge is left to the caller. |
| void classifyArray(mlir::Location loc, fir::SequenceType seqTy, |
| std::uint64_t byteOffset, ArgClass &Lo, |
| ArgClass &Hi) const { |
| mlir::Type eleTy = seqTy.getEleTy(); |
| const std::uint64_t arraySize = seqTy.getConstantArraySize(); |
| auto [eleSize, eleAlign] = |
| fir::getTypeSizeAndAlignment(loc, eleTy, getDataLayout(), kindMap); |
| std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign); |
| for (std::uint64_t i = 0; i < arraySize; ++i) { |
| byteOffset = llvm::alignTo(byteOffset, eleAlign); |
| if (byteOffset > 16) { |
| // See 3.2.3 p. 1 and note 15. Same as in classifyStruct. |
| Lo = Hi = ArgClass::Memory; |
| return; |
| } |
| ArgClass LoComp, HiComp; |
| classify(loc, eleTy, byteOffset, LoComp, HiComp); |
| Lo = mergeClass(Lo, LoComp); |
| Hi = mergeClass(Hi, HiComp); |
| byteOffset = byteOffset + eleStorageSize; |
| if (Lo == ArgClass::Memory || Hi == ArgClass::Memory) |
| return; |
| } |
| } |
| |
| // Goes through the previously marshalled arguments and count the |
| // register occupancy to check if there are enough registers left. |
| bool hasEnoughRegisters(mlir::Location loc, int neededIntRegisters, |
| int neededSSERegisters, |
| const Marshalling &previousArguments) const { |
| int availIntRegisters = 6; |
| int availSSERegisters = 8; |
| for (auto typeAndAttr : previousArguments) { |
| const auto &attr = std::get<Attributes>(typeAndAttr); |
| if (attr.isByVal()) |
| continue; // Previous argument passed on the stack. |
| ArgClass Lo, Hi; |
| Lo = Hi = ArgClass::NoClass; |
| classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi); |
| // post merge is not needed here since previous aggregate arguments |
| // were marshalled into simpler arguments. |
| if (Lo == ArgClass::Integer) |
| --availIntRegisters; |
| else if (Lo == SSE) |
| --availSSERegisters; |
| if (Hi == ArgClass::Integer) |
| --availIntRegisters; |
| else if (Hi == ArgClass::SSE) |
| --availSSERegisters; |
| } |
| return availSSERegisters >= neededSSERegisters && |
| availIntRegisters >= neededIntRegisters; |
| } |
| |
| /// Argument class merging as described in System V ABI 3.2.3 point 4. |
| ArgClass mergeClass(ArgClass accum, ArgClass field) const { |
| assert((accum != ArgClass::Memory && accum != ArgClass::ComplexX87) && |
| "Invalid accumulated classification during merge."); |
| if (accum == field || field == NoClass) |
| return accum; |
| if (field == ArgClass::Memory) |
| return ArgClass::Memory; |
| if (accum == NoClass) |
| return field; |
| if (accum == Integer || field == Integer) |
| return ArgClass::Integer; |
| if (field == ArgClass::X87 || field == ArgClass::X87Up || |
| field == ArgClass::ComplexX87 || accum == ArgClass::X87 || |
| accum == ArgClass::X87Up) |
| return Memory; |
| return SSE; |
| } |
| |
| /// Argument class post merging as described in System V ABI 3.2.3 point 5. |
| void postMerge(std::uint64_t byteSize, ArgClass &Lo, ArgClass &Hi) const { |
| if (Hi == ArgClass::Memory) |
| Lo = ArgClass::Memory; |
| if (Hi == ArgClass::X87Up && Lo != ArgClass::X87) |
| Lo = ArgClass::Memory; |
| if (byteSize > 16 && (Lo != ArgClass::SSE || Hi != ArgClass::SSEUp)) |
| Lo = ArgClass::Memory; |
| if (Hi == ArgClass::SSEUp && Lo != ArgClass::SSE) |
| Hi = SSE; |
| } |
| |
| /// When \p recTy is a one field record type that can be passed |
| /// like the field on its own, returns the field type. Returns |
| /// a null type otherwise. |
| mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy) const { |
| auto typeList = recTy.getTypeList(); |
| if (typeList.size() != 1) |
| return {}; |
| mlir::Type fieldType = typeList[0].second; |
| if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::RealType, |
| fir::CharacterType, fir::LogicalType>(fieldType)) |
| return fieldType; |
| // Complex field that needs to be split, or array. |
| return {}; |
| } |
| |
| mlir::Type pickLLVMArgType(mlir::Location loc, mlir::MLIRContext *context, |
| ArgClass argClass, |
| std::uint64_t partByteSize) const { |
| if (argClass == ArgClass::SSE) { |
| if (partByteSize > 16) |
| TODO(loc, "passing struct as a real > 128 bits in register"); |
| // Clang uses vector type when several fp fields are marshalled |
| // into a single SSE register (like <n x smallest fp field> ). |
| // It should make no difference from an ABI point of view to just |
| // select an fp type of the right size, and it makes things simpler |
| // here. |
| if (partByteSize > 8) |
| return mlir::FloatType::getF128(context); |
| if (partByteSize > 4) |
| return mlir::FloatType::getF64(context); |
| if (partByteSize > 2) |
| return mlir::FloatType::getF32(context); |
| return mlir::FloatType::getF16(context); |
| } |
| assert(partByteSize <= 8 && |
| "expect integer part of aggregate argument to fit into eight bytes"); |
| if (partByteSize > 4) |
| return mlir::IntegerType::get(context, 64); |
| if (partByteSize > 2) |
| return mlir::IntegerType::get(context, 32); |
| if (partByteSize > 1) |
| return mlir::IntegerType::get(context, 16); |
| return mlir::IntegerType::get(context, 8); |
| } |
| |
| /// Marshal a derived type passed by value like a C struct. |
| CodeGenSpecifics::Marshalling |
| structArgumentType(mlir::Location loc, fir::RecordType recTy, |
| const Marshalling &previousArguments) const override { |
| std::uint64_t byteOffset = 0; |
| ArgClass Lo, Hi; |
| Lo = Hi = ArgClass::NoClass; |
| byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi); |
| postMerge(byteOffset, Lo, Hi); |
| if (Lo == ArgClass::Memory || Lo == ArgClass::X87 || |
| Lo == ArgClass::ComplexX87) |
| return passOnTheStack(loc, recTy); |
| int neededIntRegisters = 0; |
| int neededSSERegisters = 0; |
| if (Lo == ArgClass::SSE) |
| ++neededSSERegisters; |
| else if (Lo == ArgClass::Integer) |
| ++neededIntRegisters; |
| if (Hi == ArgClass::SSE) |
| ++neededSSERegisters; |
| else if (Hi == ArgClass::Integer) |
| ++neededIntRegisters; |
| // C struct should not be split into LLVM registers if LLVM codegen is not |
| // able to later assign actual registers to all of them (struct passing is |
| // all in registers or all on the stack). |
| if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters, |
| previousArguments)) |
| return passOnTheStack(loc, recTy); |
| |
| if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) { |
| CodeGenSpecifics::Marshalling marshal; |
| marshal.emplace_back(fieldType, AT{}); |
| return marshal; |
| } |
| if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) { |
| // Pass a single integer or floating point argument. |
| mlir::Type lowType = |
| pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset); |
| CodeGenSpecifics::Marshalling marshal; |
| marshal.emplace_back(lowType, AT{}); |
| return marshal; |
| } |
| // Split into two integer or floating point arguments. |
| // Note that for the first argument, this will always pick i64 or f64 which |
| // may be bigger than needed if some struct padding ends the first eight |
| // byte (e.g. for `{i32, f64}`). It is valid from an X86-64 ABI and |
| // semantic point of view, but it may not match the LLVM IR interface clang |
| // would produce for the equivalent C code (the assembly will still be |
| // compatible). This allows keeping the logic simpler here since it |
| // avoids computing the "data" size of the Lo part. |
| mlir::Type lowType = pickLLVMArgType(loc, recTy.getContext(), Lo, 8u); |
| mlir::Type hiType = |
| pickLLVMArgType(loc, recTy.getContext(), Hi, byteOffset - 8u); |
| CodeGenSpecifics::Marshalling marshal; |
| marshal.emplace_back(lowType, AT{}); |
| marshal.emplace_back(hiType, AT{}); |
| return marshal; |
| } |
| |
| /// Marshal an argument that must be passed on the stack. |
| CodeGenSpecifics::Marshalling passOnTheStack(mlir::Location loc, |
| mlir::Type ty) const { |
| CodeGenSpecifics::Marshalling marshal; |
| auto sizeAndAlign = |
| fir::getTypeSizeAndAlignment(loc, ty, getDataLayout(), kindMap); |
| // The stack is always 8 byte aligned (note 14 in 3.2.3). |
| unsigned short align = |
| std::max(sizeAndAlign.second, static_cast<unsigned short>(8)); |
| marshal.emplace_back(fir::ReferenceType::get(ty), |
| AT{align, /*byval=*/true, /*sret=*/false}); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // x86_64 (x86 64 bit) Windows target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetX86_64Win : public GenericTarget<TargetX86_64Win> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // i64 pack both floats in a 64-bit GPR |
| marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
| AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { double, double } struct of 2 double, byval, align 8 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/8, /*byval=*/true}); |
| } else if (sem == &llvm::APFloat::IEEEquad() || |
| sem == &llvm::APFloat::x87DoubleExtended()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byval, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/true}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle()) { |
| // i64 pack both floats in a 64-bit GPR |
| marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), |
| AT{}); |
| } else if (sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { double, double } struct of 2 double, sret, align 8 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/8, /*byval=*/false, /*sret=*/true}); |
| } else if (sem == &llvm::APFloat::IEEEquad() || |
| sem == &llvm::APFloat::x87DoubleExtended()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, sret, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // AArch64 linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetAArch64 : public GenericTarget<TargetAArch64> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble() || |
| sem == &llvm::APFloat::IEEEquad()) { |
| // [2 x t] array of 2 eleTy |
| marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble() || |
| sem == &llvm::APFloat::IEEEquad()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy |
| marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // PPC64 (AIX 64 bit) target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetPPC64 : public GenericTarget<TargetPPC64> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // two distinct element type arguments (re, im) |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 element type |
| marshal.emplace_back( |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // PPC64le linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetPPC64le : public GenericTarget<TargetPPC64le> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // two distinct element type arguments (re, im) |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 element type |
| marshal.emplace_back( |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // sparc (sparc 32 bit) target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetSparc : public GenericTarget<TargetSparc> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 32; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location, mlir::Type eleTy) const override { |
| assert(fir::isa_real(eleTy)); |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy |
| auto structTy = |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
| marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| assert(fir::isa_real(eleTy)); |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byval |
| auto structTy = |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}); |
| marshal.emplace_back(fir::ReferenceType::get(structTy), |
| AT{/*alignment=*/0, /*byval=*/true}); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // sparcv9 (sparc 64 bit) target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble()) { |
| // two distinct float, double arguments |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| } else if (sem == &llvm::APFloat::IEEEquad()) { |
| // Use a type that will be translated into LLVM as: |
| // { fp128, fp128 } struct of 2 fp128, byval, align 16 |
| marshal.emplace_back( |
| fir::ReferenceType::get(mlir::TupleType::get( |
| eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})), |
| AT{/*align=*/16, /*byval=*/true}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| // Use a type that will be translated into LLVM as: |
| // { eleTy, eleTy } struct of 2 eleTy |
| marshal.emplace_back( |
| mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}), |
| AT{}); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // RISCV64 linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble()) { |
| // Two distinct element type arguments (re, im) |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byVal |
| marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}), |
| AT{/*alignment=*/0, /*byval=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // AMDGPU linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> { |
| using GenericTarget::GenericTarget; |
| |
| // Default size (in bits) of the index type for strings. |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| TODO(loc, "handle complex argument types"); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| TODO(loc, "handle complex return types"); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // NVPTX linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetNVPTX : public GenericTarget<TargetNVPTX> { |
| using GenericTarget::GenericTarget; |
| |
| // Default size (in bits) of the index type for strings. |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| TODO(loc, "handle complex argument types"); |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| TODO(loc, "handle complex return types"); |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| //===----------------------------------------------------------------------===// |
| // LoongArch64 linux target specifics. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> { |
| using GenericTarget::GenericTarget; |
| |
| static constexpr int defaultWidth = 64; |
| |
| CodeGenSpecifics::Marshalling |
| complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble()) { |
| // Two distinct element type arguments (re, im) |
| marshal.emplace_back(eleTy, AT{}); |
| marshal.emplace_back(eleTy, AT{}); |
| } else { |
| typeTodo(sem, loc, "argument"); |
| } |
| return marshal; |
| } |
| |
| CodeGenSpecifics::Marshalling |
| complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { |
| CodeGenSpecifics::Marshalling marshal; |
| const auto *sem = &floatToSemantics(kindMap, eleTy); |
| if (sem == &llvm::APFloat::IEEEsingle() || |
| sem == &llvm::APFloat::IEEEdouble()) { |
| // Use a type that will be translated into LLVM as: |
| // { t, t } struct of 2 eleTy, byVal |
| marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), |
| mlir::TypeRange{eleTy, eleTy}), |
| AT{/*alignment=*/0, /*byval=*/true}); |
| } else { |
| typeTodo(sem, loc, "return"); |
| } |
| return marshal; |
| } |
| }; |
| } // namespace |
| |
| // Instantiate the overloaded target instance based on the triple value. |
| // TODO: Add other targets to this file as needed. |
| std::unique_ptr<fir::CodeGenSpecifics> |
| fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, |
| KindMapping &&kindMap, llvm::StringRef targetCPU, |
| mlir::LLVM::TargetFeaturesAttr targetFeatures, |
| const mlir::DataLayout &dl) { |
| switch (trp.getArch()) { |
| default: |
| break; |
| case llvm::Triple::ArchType::x86: |
| if (trp.isOSWindows()) |
| return std::make_unique<TargetI386Win>(ctx, std::move(trp), |
| std::move(kindMap), targetCPU, |
| targetFeatures, dl); |
| else |
| return std::make_unique<TargetI386>(ctx, std::move(trp), |
| std::move(kindMap), targetCPU, |
| targetFeatures, dl); |
| case llvm::Triple::ArchType::x86_64: |
| if (trp.isOSWindows()) |
| return std::make_unique<TargetX86_64Win>(ctx, std::move(trp), |
| std::move(kindMap), targetCPU, |
| targetFeatures, dl); |
| else |
| return std::make_unique<TargetX86_64>(ctx, std::move(trp), |
| std::move(kindMap), targetCPU, |
| targetFeatures, dl); |
| case llvm::Triple::ArchType::aarch64: |
| return std::make_unique<TargetAArch64>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::ppc64: |
| return std::make_unique<TargetPPC64>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::ppc64le: |
| return std::make_unique<TargetPPC64le>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::sparc: |
| return std::make_unique<TargetSparc>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::sparcv9: |
| return std::make_unique<TargetSparcV9>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::riscv64: |
| return std::make_unique<TargetRISCV64>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::amdgcn: |
| return std::make_unique<TargetAMDGPU>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::nvptx64: |
| return std::make_unique<TargetNVPTX>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| case llvm::Triple::ArchType::loongarch64: |
| return std::make_unique<TargetLoongArch64>( |
| ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl); |
| } |
| TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); |
| } |