This PR adds initial debug support for derived type. It handles
`RecordType` and generates appropriate `DICompositeTypeAttr`. The
`TypeInfoOp` is used to get information about the parent and location of
the derived type.
We use `getTypeSizeAndAlignment` to get the size and alignment of the
components of the derived types. This function needed a few changes to
be suitable to be used here:
1. The `getTypeSizeAndAlignment` errored out on unsupported type which
would not work with incremental way we are building debug support. A new
variant of this function has been that returns an std::optional. The original
function has been renamed to `getTypeSizeAndAlignmentOrCrash` as it
will call `TODO()` for unsupported types.
2. The Character type was returning size of just element and not the
whole string which has been fixed.
The testcase checks for offsets of the components which had to be
hardcoded in the test. So the testcase is currently enabled on x86_64.
With this PR in place, this is how the debugging of derived types look
like:
```
type :: t_date
integer :: year, month, day
end type
type :: t_address
integer :: house_number
end type
type, extends(t_address) :: t_person
character(len=20) name
end type
type, extends(t_person) :: t_employee
type(t_date) :: hired_date
real :: monthly_salary
end type
type(t_employee) :: employee
(gdb) p employee
$1 = ( t_person = ( t_address = ( house_number = 1 ), name = 'John', ' ' <repeats 16 times> ), hired_date = ( year = 2020, month = 1, day = 20 ), monthly_salary = 3.1400001 )
```
1127 lines
45 KiB
C++
1127 lines
45 KiB
C++
//===-- 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::getTypeSizeAndAlignmentOrCrash(
|
|
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::getTypeSizeAndAlignmentOrCrash(
|
|
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::getTypeSizeAndAlignmentOrCrash(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");
|
|
}
|
|
|
|
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, llvm::StringRef tuneCPU) {
|
|
std::unique_ptr<fir::CodeGenSpecifics> CGS = fir::CodeGenSpecifics::get(
|
|
ctx, std::move(trp), std::move(kindMap), targetCPU, targetFeatures, dl);
|
|
|
|
CGS->tuneCPU = tuneCPU;
|
|
return CGS;
|
|
}
|