#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 {
if (isZeroExt())
return "llvm.zeroext";
else if (isSignExt())
return "llvm.signext";
return {};
}
}
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
mlir::Type type) {
assert(isa_real(type));
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));
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);
return mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{ptrTy, idxTy});
}
Marshalling boxcharArgumentType(mlir::Type eleTy) 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{});
marshal.emplace_back(idxTy, AT{0, false,
false, true});
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
structReturnType(mlir::Location loc, fir::RecordType ty) const override {
TODO(loc, "returning 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()) {
if (argTy.isSigned())
intExt = AT::IntegerExtension::Sign;
else if (argTy.isUnsigned())
intExt = AT::IntegerExtension::Zero;
else if (argTy.isSignless()) {
if (argTy.getWidth() == 1)
intExt = AT::IntegerExtension::Zero;
else
intExt = AT::IntegerExtension::Sign;
}
}
marshal.emplace_back(argTy, AT{0, false,
false, false,
intExt});
return marshal;
}
CodeGenSpecifics::Marshalling
integerReturnType(mlir::Location loc,
mlir::IntegerType argTy) const override {
return integerArgumentType(loc, argTy);
}
unsigned char getCIntTypeWidth() const override { return 32; }
};
}
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;
auto structTy =
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
marshal.emplace_back(fir::ReferenceType::get(structTy),
AT{4, 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()) {
marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
auto structTy = mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy});
marshal.emplace_back(fir::ReferenceType::get(structTy),
AT{4, false, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
};
}
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;
auto structTy =
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
marshal.emplace_back(fir::ReferenceType::get(structTy),
AT{4, 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()) {
marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{8, false, true});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, false, true});
} else if (sem == &llvm::APFloat::x87DoubleExtended()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{4, false, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
};
}
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()) {
marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(eleTy, AT{});
marshal.emplace_back(eleTy, AT{});
} else if (sem == &llvm::APFloat::x87DoubleExtended()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, true});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, 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()) {
marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{});
} else if (sem == &llvm::APFloat::x87DoubleExtended()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, false, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
enum ArgClass {
Integer = 0,
SSE,
SSEUp,
X87,
X87Up,
ComplexX87,
NoClass,
Memory
};
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;
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>([&](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<mlir::ComplexType>([&](mlir::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) {
classifyArray(loc, seqTy, byteOffset, Lo, Hi);
})
.template Case<fir::RecordType>([&](fir::RecordType recTy) {
classifyStruct(loc, recTy, byteOffset, Lo, Hi);
})
.template Case<fir::VectorType>([&](fir::VectorType vecTy) {
auto *sem = fir::isa_real(vecTy.getEleTy())
? &floatToSemantics(kindMap, vecTy.getEleTy())
: nullptr;
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;
else
TODO(loc, "unsupported component type for BIND(C), VALUE derived "
"type argument");
});
}
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) {
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;
}
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) {
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;
}
}
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;
ArgClass Lo, Hi;
Lo = Hi = ArgClass::NoClass;
classify(loc, std::get<mlir::Type>(typeAndAttr), 0, Lo, Hi);
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;
}
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;
}
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;
}
mlir::Type passAsFieldIfOneFieldStruct(fir::RecordType recTy,
bool allowComplex = false) const {
auto typeList = recTy.getTypeList();
if (typeList.size() != 1)
return {};
mlir::Type fieldType = typeList[0].second;
if (mlir::isa<mlir::FloatType, mlir::IntegerType, fir::LogicalType>(
fieldType))
return fieldType;
if (allowComplex && mlir::isa<mlir::ComplexType>(fieldType))
return fieldType;
if (mlir::isa<fir::CharacterType>(fieldType)) {
assert(mlir::cast<fir::CharacterType>(fieldType).getLen() == 1 &&
"fir.type value arg character components must have length 1");
return fieldType;
}
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");
if (partByteSize > 8)
return mlir::Float128Type::get(context);
if (partByteSize > 4)
return mlir::Float64Type::get(context);
if (partByteSize > 2)
return mlir::Float32Type::get(context);
return mlir::Float16Type::get(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);
}
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, false);
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;
if (!hasEnoughRegisters(loc, neededIntRegisters, neededSSERegisters,
previousArguments))
return passOnTheStack(loc, recTy, false);
if (auto fieldType = passAsFieldIfOneFieldStruct(recTy)) {
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(fieldType, AT{});
return marshal;
}
if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
mlir::Type lowType =
pickLLVMArgType(loc, recTy.getContext(), Lo, byteOffset);
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(lowType, AT{});
return marshal;
}
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;
}
CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
std::uint64_t byteOffset = 0;
ArgClass Lo, Hi;
Lo = Hi = ArgClass::NoClass;
byteOffset = classifyStruct(loc, recTy, byteOffset, Lo, Hi);
mlir::MLIRContext *context = recTy.getContext();
postMerge(byteOffset, Lo, Hi);
if (Lo == ArgClass::Memory)
return passOnTheStack(loc, recTy, true);
if (auto fieldType =
passAsFieldIfOneFieldStruct(recTy, true)) {
if (auto complexType = mlir::dyn_cast<mlir::ComplexType>(fieldType))
return complexReturnType(loc, complexType.getElementType());
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(fieldType, AT{});
return marshal;
}
if (Hi == ArgClass::NoClass || Hi == ArgClass::SSEUp) {
mlir::Type lowType = pickLLVMArgType(loc, context, Lo, byteOffset);
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(lowType, AT{});
return marshal;
}
CodeGenSpecifics::Marshalling marshal;
mlir::Type lowType = pickLLVMArgType(loc, context, Lo, 8u);
mlir::Type hiType = pickLLVMArgType(loc, context, Hi, byteOffset - 8u);
marshal.emplace_back(mlir::TupleType::get(context, {lowType, hiType}),
AT{});
return marshal;
}
CodeGenSpecifics::Marshalling
passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign =
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
unsigned short align =
std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, !isResult, isResult});
return marshal;
}
};
}
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()) {
marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{8, true});
} else if (sem == &llvm::APFloat::IEEEquad() ||
sem == &llvm::APFloat::x87DoubleExtended()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, 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()) {
marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{8, false, true});
} else if (sem == &llvm::APFloat::IEEEquad() ||
sem == &llvm::APFloat::x87DoubleExtended()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, false, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
};
}
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()) {
marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
} else {
typeTodo(sem, loc, "argument");
}
return marshal;
}
CodeGenSpecifics::Marshalling
integerArgumentType(mlir::Location loc,
mlir::IntegerType argTy) const override {
if (argTy.getWidth() < getCIntTypeWidth() && argTy.isSignless()) {
AT::IntegerExtension intExt;
if (argTy.getWidth() == 1) {
intExt = AT::IntegerExtension::Zero;
} else {
if (triple.isOSDarwin()) {
intExt = AT::IntegerExtension::Sign;
} else {
intExt = AT::IntegerExtension::None;
}
}
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(argTy, AT{0, false,
false, false,
intExt});
return marshal;
}
return GenericTarget::integerArgumentType(loc, argTy);
}
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()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
static std::optional<std::vector<mlir::Type>>
flattenTypeList(const RecordType::TypeList &types) {
std::vector<mlir::Type> flatTypes;
flatTypes.reserve(types.size());
for (auto [c, type] : types) {
if (auto recTy = mlir::dyn_cast<RecordType>(type)) {
auto subTypeList = flattenTypeList(recTy.getTypeList());
if (!subTypeList)
return std::nullopt;
llvm::copy(*subTypeList, std::back_inserter(flatTypes));
continue;
}
if (auto seqTy = mlir::dyn_cast<SequenceType>(type)) {
if (seqTy.hasDynamicExtents())
return std::nullopt;
std::size_t n = seqTy.getConstantArraySize();
auto eleTy = seqTy.getElementType();
if (auto recTy = mlir::dyn_cast<RecordType>(eleTy)) {
auto subTypeList = flattenTypeList(recTy.getTypeList());
if (!subTypeList)
return std::nullopt;
for (std::size_t i = 0; i < n; ++i)
llvm::copy(*subTypeList, std::back_inserter(flatTypes));
} else {
std::fill_n(std::back_inserter(flatTypes),
seqTy.getConstantArraySize(), eleTy);
}
continue;
}
flatTypes.push_back(type);
}
return flatTypes;
}
static std::optional<int> usedRegsForHFA(fir::RecordType ty) {
RecordType::TypeList types = ty.getTypeList();
if (types.empty() || types.size() > 4)
return std::nullopt;
std::optional<std::vector<mlir::Type>> flatTypes = flattenTypeList(types);
if (!flatTypes || flatTypes->size() > 4) {
return std::nullopt;
}
if (!isa_real(flatTypes->front())) {
return std::nullopt;
}
return llvm::all_equal(*flatTypes) ? std::optional<int>{flatTypes->size()}
: std::nullopt;
}
struct NRegs {
int n{0};
bool isSimd{false};
};
NRegs usedRegsForRecordType(mlir::Location loc, fir::RecordType type) const {
if (std::optional<int> size = usedRegsForHFA(type))
return {*size, true};
auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash(
loc, type, getDataLayout(), kindMap);
if (size <= 16)
return {static_cast<int>((size + 7) / 8), false};
return {};
}
NRegs usedRegsForType(mlir::Location loc, mlir::Type type) const {
return llvm::TypeSwitch<mlir::Type, NRegs>(type)
.Case<mlir::IntegerType>([&](auto intTy) {
return intTy.getWidth() == 128 ? NRegs{2, false} : NRegs{1, false};
})
.Case<mlir::FloatType>([&](auto) { return NRegs{1, true}; })
.Case<mlir::ComplexType>([&](auto) { return NRegs{2, true}; })
.Case<fir::LogicalType>([&](auto) { return NRegs{1, false}; })
.Case<fir::CharacterType>([&](auto) { return NRegs{1, false}; })
.Case<fir::SequenceType>([&](auto ty) {
assert(ty.getShape().size() == 1 &&
"invalid array dimensions in BIND(C)");
NRegs nregs = usedRegsForType(loc, ty.getEleTy());
nregs.n *= ty.getShape()[0];
return nregs;
})
.Case<fir::RecordType>(
[&](auto ty) { return usedRegsForRecordType(loc, ty); })
.Case<fir::VectorType>([&](auto) {
TODO(loc, "passing vector argument to C by value is not supported");
return NRegs{};
})
.Default([&](auto ty) {
if (fir::conformsWithPassByRef(ty))
return NRegs{1, false};
TODO(loc, "unsupported component type for BIND(C), VALUE derived "
"type argument");
return NRegs{};
});
}
bool hasEnoughRegisters(mlir::Location loc, fir::RecordType type,
const Marshalling &previousArguments) const {
int availIntRegisters = 8;
int availSIMDRegisters = 8;
for (auto [type, attr] : previousArguments) {
if (availIntRegisters <= 0 || availSIMDRegisters <= 0)
break;
if (attr.isByVal())
continue;
NRegs nregs = usedRegsForType(loc, type);
if (nregs.isSimd)
availSIMDRegisters -= nregs.n;
else
availIntRegisters -= nregs.n;
}
NRegs nregs = usedRegsForRecordType(loc, type);
if (nregs.isSimd)
return nregs.n <= availSIMDRegisters;
return nregs.n <= availIntRegisters;
}
CodeGenSpecifics::Marshalling
passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign =
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
unsigned short align =
std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, !isResult, isResult});
return marshal;
}
CodeGenSpecifics::Marshalling
structType(mlir::Location loc, fir::RecordType type, bool isResult) const {
NRegs nregs = usedRegsForRecordType(loc, type);
if (nregs.n == 0)
return passOnTheStack(loc, type, isResult);
CodeGenSpecifics::Marshalling marshal;
mlir::Type pcsType;
if (nregs.isSimd) {
pcsType = type;
} else {
pcsType = fir::SequenceType::get(
nregs.n, mlir::IntegerType::get(type.getContext(), 64));
}
marshal.emplace_back(pcsType, AT{});
return marshal;
}
CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType ty,
const Marshalling &previousArguments) const override {
if (!hasEnoughRegisters(loc, ty, previousArguments)) {
return passOnTheStack(loc, ty, false);
}
return structType(loc, ty, false);
}
CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType ty) const override {
return structType(loc, ty, true);
}
};
}
namespace {
struct TargetPPC : public GenericTarget<TargetPPC> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 32;
CodeGenSpecifics::Marshalling
complexArgumentType(mlir::Location, mlir::Type eleTy) const override {
CodeGenSpecifics::Marshalling marshal;
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;
marshal.emplace_back(
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
AT{});
return marshal;
}
};
}
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;
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;
marshal.emplace_back(
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
AT{});
return marshal;
}
CodeGenSpecifics::Marshalling
structType(mlir::Location loc, fir::RecordType ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
unsigned short align{
std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, !isResult, isResult});
return marshal;
}
CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType ty,
const Marshalling &previousArguments) const override {
return structType(loc, ty, false);
}
CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType ty) const override {
return structType(loc, ty, true);
}
};
}
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;
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;
marshal.emplace_back(
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
AT{});
return marshal;
}
unsigned getElemWidth(mlir::Type ty) const {
unsigned width{};
llvm::TypeSwitch<mlir::Type>(ty)
.template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
auto elemType{
mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
width = elemType.getWidth();
})
.template Case<mlir::FloatType>(
[&](mlir::FloatType real) { width = real.getWidth(); });
return width;
}
bool hasSameFloatAndWidth(
fir::RecordType recTy,
std::pair<mlir::Type, unsigned> &firstTypeAndWidth) const {
for (auto comp : recTy.getTypeList()) {
mlir::Type compType{comp.second};
if (mlir::isa<fir::RecordType>(compType)) {
auto rc{hasSameFloatAndWidth(mlir::cast<fir::RecordType>(compType),
firstTypeAndWidth)};
if (!rc)
return false;
} else {
mlir::Type ty;
bool isFloatType{false};
if (mlir::isa<mlir::FloatType, mlir::ComplexType>(compType)) {
ty = compType;
isFloatType = true;
} else if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(compType)) {
ty = seqTy.getEleTy();
isFloatType = mlir::isa<mlir::FloatType, mlir::ComplexType>(ty);
}
if (!isFloatType) {
return false;
}
auto width{getElemWidth(ty)};
if (firstTypeAndWidth.first == nullptr) {
firstTypeAndWidth.first = ty;
firstTypeAndWidth.second = width;
} else if (width != firstTypeAndWidth.second) {
return false;
}
}
}
return true;
}
CodeGenSpecifics::Marshalling
passOnTheStack(mlir::Location loc, mlir::Type ty, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap)};
unsigned short align{
std::max(sizeAndAlign.second, static_cast<unsigned short>(8))};
marshal.emplace_back(fir::ReferenceType::get(ty),
AT{align, !isResult, isResult});
return marshal;
}
CodeGenSpecifics::Marshalling
structType(mlir::Location loc, fir::RecordType recTy, bool isResult) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, recTy, getDataLayout(), kindMap)};
auto recordTypeSize{sizeAndAlign.first};
mlir::Type seqTy;
std::pair<mlir::Type, unsigned> firstTyAndWidth{nullptr, 0};
constexpr uint64_t maxNoOfFloats{8};
mlir::Type elemTy{mlir::IntegerType::get(recTy.getContext(), defaultWidth)};
uint64_t nElem{static_cast<uint64_t>(
std::ceil(static_cast<float>(recordTypeSize * 8) / defaultWidth))};
if (hasSameFloatAndWidth(recTy, firstTyAndWidth)) {
uint64_t n{};
auto firstType{firstTyAndWidth.first};
if (auto cmplx = mlir::dyn_cast<mlir::ComplexType>(firstType)) {
auto fltType{mlir::dyn_cast<mlir::FloatType>(cmplx.getElementType())};
n = static_cast<uint64_t>(8 * recordTypeSize / fltType.getWidth());
if (n <= maxNoOfFloats) {
nElem = n;
elemTy = fltType;
}
} else if (mlir::isa<mlir::FloatType>(firstType)) {
auto elemSizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, firstType, getDataLayout(), kindMap)};
n = static_cast<uint64_t>(recordTypeSize / elemSizeAndAlign.first);
if (n <= maxNoOfFloats) {
nElem = n;
elemTy = firstType;
}
}
assert(n > 0 && "unexpected type");
}
if (isResult &&
((mlir::isa<mlir::FloatType>(elemTy) && nElem > maxNoOfFloats) ||
!mlir::isa<mlir::FloatType>(elemTy))) {
return passOnTheStack(loc, recTy, isResult);
}
seqTy = fir::SequenceType::get(nElem, elemTy);
marshal.emplace_back(seqTy, AT{});
return marshal;
}
CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType recType,
const Marshalling &previousArguments) const override {
auto sizeAndAlign{fir::getTypeSizeAndAlignmentOrCrash(
loc, recType, getDataLayout(), kindMap)};
if (sizeAndAlign.first > 64) {
return passOnTheStack(loc, recType, false);
}
return structType(loc, recType, false);
}
CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType recType) const override {
return structType(loc, recType, true);
}
};
}
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;
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;
auto structTy =
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
marshal.emplace_back(fir::ReferenceType::get(structTy),
AT{0, true});
return marshal;
}
};
}
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()) {
marshal.emplace_back(eleTy, AT{});
marshal.emplace_back(eleTy, AT{});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, true});
} else {
typeTodo(sem, loc, "argument");
}
return marshal;
}
CodeGenSpecifics::Marshalling
complexReturnType(mlir::Location loc, mlir::Type eleTy) const override {
CodeGenSpecifics::Marshalling marshal;
marshal.emplace_back(
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
AT{});
return marshal;
}
};
}
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()) {
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()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{0, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
};
}
namespace {
struct TargetAMDGPU : public GenericTarget<TargetAMDGPU> {
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()) {
marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
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()) {
marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
} else if (sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
};
}
namespace {
struct TargetNVPTX : public GenericTarget<TargetNVPTX> {
using GenericTarget::GenericTarget;
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 {
struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
using GenericTarget::GenericTarget;
static constexpr int defaultWidth = 64;
static constexpr int GRLen = defaultWidth;
static constexpr int GRLenInChar = GRLen / 8;
static constexpr int FRLen = defaultWidth;
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()) {
marshal.emplace_back(eleTy, AT{});
marshal.emplace_back(eleTy, AT{});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, 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() ||
sem == &llvm::APFloat::IEEEdouble()) {
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
mlir::TypeRange{eleTy, eleTy}),
AT{0, true});
} else if (sem == &llvm::APFloat::IEEEquad()) {
marshal.emplace_back(
fir::ReferenceType::get(mlir::TupleType::get(
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
AT{16, false, true});
} else {
typeTodo(sem, loc, "return");
}
return marshal;
}
CodeGenSpecifics::Marshalling
integerArgumentType(mlir::Location loc,
mlir::IntegerType argTy) const override {
if (argTy.getWidth() == 32) {
CodeGenSpecifics::Marshalling marshal;
AT::IntegerExtension intExt = AT::IntegerExtension::Sign;
marshal.emplace_back(argTy, AT{0, false,
false, false,
intExt});
return marshal;
}
return GenericTarget::integerArgumentType(loc, argTy);
}
llvm::SmallVector<mlir::Type> flattenTypeList(mlir::Location loc,
const mlir::Type type) const {
llvm::SmallVector<mlir::Type> flatTypes;
llvm::TypeSwitch<mlir::Type>(type)
.template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
if (intTy.getWidth() != 0)
flatTypes.push_back(intTy);
})
.template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
if (floatTy.getWidth() != 0)
flatTypes.push_back(floatTy);
})
.template Case<mlir::ComplexType>([&](mlir::ComplexType cmplx) {
const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
if (sem == &llvm::APFloat::IEEEsingle() ||
sem == &llvm::APFloat::IEEEdouble() ||
sem == &llvm::APFloat::IEEEquad())
std::fill_n(std::back_inserter(flatTypes), 2,
cmplx.getElementType());
else
TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "
"IEEEquad) as a structure component for BIND(C), "
"VALUE derived type argument and type return");
})
.template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {
const unsigned width =
kindMap.getLogicalBitsize(logicalTy.getFKind());
if (width != 0)
flatTypes.push_back(
mlir::IntegerType::get(type.getContext(), width));
})
.template Case<fir::CharacterType>([&](fir::CharacterType charTy) {
assert(kindMap.getCharacterBitsize(charTy.getFKind()) <= 8 &&
"the bit size of characterType as an interoperable type must "
"not exceed 8");
for (unsigned i = 0; i < charTy.getLen(); ++i)
flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
})
.template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
if (!seqTy.hasDynamicExtents()) {
const std::uint64_t numOfEle = seqTy.getConstantArraySize();
mlir::Type eleTy = seqTy.getEleTy();
if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
llvm::SmallVector<mlir::Type> subTypeList =
flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
for (std::uint64_t i = 0; i < numOfEle; ++i)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
} else {
std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
}
} else
TODO(loc, "unsupported dynamic extent sequence type as a structure "
"component for BIND(C), "
"VALUE derived type argument and type return");
})
.template Case<fir::RecordType>([&](fir::RecordType recTy) {
for (auto &component : recTy.getTypeList()) {
mlir::Type eleTy = component.second;
llvm::SmallVector<mlir::Type> subTypeList =
flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
}
})
.template Case<fir::VectorType>([&](fir::VectorType vecTy) {
auto sizeAndAlign = fir::getTypeSizeAndAlignmentOrCrash(
loc, vecTy, getDataLayout(), kindMap);
if (sizeAndAlign.first == 2 * GRLenInChar)
flatTypes.push_back(
mlir::IntegerType::get(type.getContext(), 2 * GRLen));
else
TODO(loc, "unsupported vector width(must be 128 bits)");
})
.Default([&](mlir::Type ty) {
if (fir::conformsWithPassByRef(ty))
flatTypes.push_back(
mlir::IntegerType::get(type.getContext(), GRLen));
else
TODO(loc, "unsupported component type for BIND(C), VALUE derived "
"type argument and type return");
});
return flatTypes;
}
bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
mlir::Type &field1Ty,
mlir::Type &field2Ty) const {
field1Ty = field2Ty = nullptr;
llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);
size_t flatSize = flatTypes.size();
if (flatSize == 0 || flatSize > 2)
return false;
bool isFirstAvaliableFloat = false;
assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
"Type must be integerType or floatType after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
const unsigned Size = floatTy.getWidth();
if (Size > FRLen || Size < 32)
return false;
isFirstAvaliableFloat = true;
field1Ty = floatTy;
} else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
if (intTy.getWidth() > GRLen)
return false;
field1Ty = intTy;
}
if (flatSize == 2) {
assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&
"Type must be integerType or floatType after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
const unsigned Size = floatTy.getWidth();
if (Size > FRLen || Size < 32)
return false;
field2Ty = floatTy;
return true;
} else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[1])) {
if (!isFirstAvaliableFloat)
return false;
if (intTy.getWidth() > GRLen)
return false;
field2Ty = intTy;
return true;
}
}
return isFirstAvaliableFloat;
}
bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,
const mlir::Type type) const {
if (!type)
return true;
llvm::TypeSwitch<mlir::Type>(type)
.template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
const unsigned width = intTy.getWidth();
if (width > 128)
TODO(loc,
"integerType with width exceeding 128 bits is unsupported");
if (width == 0)
return;
if (width <= GRLen)
--GARsLeft;
else if (width <= 2 * GRLen)
GARsLeft = GARsLeft - 2;
})
.template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
const unsigned width = floatTy.getWidth();
if (width > 128)
TODO(loc, "floatType with width exceeding 128 bits is unsupported");
if (width == 0)
return;
if (width == 32 || width == 64)
--FARsLeft;
else if (width <= GRLen)
--GARsLeft;
else if (width <= 2 * GRLen)
GARsLeft = GARsLeft - 2;
})
.Default([&](mlir::Type ty) {
if (fir::conformsWithPassByRef(ty))
--GARsLeft;
else
TODO(loc, "unsupported component type for BIND(C), VALUE derived "
"type argument and type return");
});
return GARsLeft >= 0 && FARsLeft >= 0;
}
bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,
const Marshalling &previousArguments,
const mlir::Type &field1Ty,
const mlir::Type &field2Ty) const {
for (auto &typeAndAttr : previousArguments) {
const auto &attr = std::get<Attributes>(typeAndAttr);
if (attr.isByVal()) {
--GARsLeft;
continue;
}
const auto &type = std::get<mlir::Type>(typeAndAttr);
llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);
for (auto &flatTy : flatTypes) {
if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))
return false;
}
}
if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
return false;
if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
return false;
return true;
}
CodeGenSpecifics::Marshalling
classifyStruct(mlir::Location loc, fir::RecordType recTy, int GARsLeft,
int FARsLeft, bool isResult,
const Marshalling &previousArguments) const {
CodeGenSpecifics::Marshalling marshal;
auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(
loc, recTy, getDataLayout(), kindMap);
mlir::MLIRContext *context = recTy.getContext();
if (recSize == 0) {
TODO(loc, "unsupported empty struct type for BIND(C), "
"VALUE derived type argument and type return");
}
if (recSize > 2 * GRLenInChar) {
marshal.emplace_back(
fir::ReferenceType::get(recTy),
AT{recAlign, !isResult, isResult});
return marshal;
}
mlir::Type field1Ty = nullptr, field2Ty = nullptr;
if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty) &&
hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments, field1Ty,
field2Ty)) {
if (!isResult) {
if (field1Ty)
marshal.emplace_back(field1Ty, AT{});
if (field2Ty)
marshal.emplace_back(field2Ty, AT{});
} else {
if (field1Ty && !field2Ty)
marshal.emplace_back(field1Ty, AT{});
else if (field1Ty && field2Ty)
marshal.emplace_back(
mlir::TupleType::get(context,
mlir::TypeRange{field1Ty, field2Ty}),
AT{0, true});
}
return marshal;
}
if (recSize <= GRLenInChar) {
marshal.emplace_back(mlir::IntegerType::get(context, GRLen), AT{});
return marshal;
}
if (recAlign == 2 * GRLenInChar) {
marshal.emplace_back(mlir::IntegerType::get(context, 2 * GRLen), AT{});
return marshal;
}
marshal.emplace_back(
fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
AT{});
return marshal;
}
CodeGenSpecifics::Marshalling
structArgumentType(mlir::Location loc, fir::RecordType recTy,
const Marshalling &previousArguments) const override {
int GARsLeft = 8;
int FARsLeft = FRLen ? 8 : 0;
return classifyStruct(loc, recTy, GARsLeft, FARsLeft, false,
previousArguments);
}
CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
int GARsLeft = 2;
int FARsLeft = FRLen ? 2 : 0;
return classifyStruct(loc, recTy, GARsLeft, FARsLeft, true,
{});
}
};
}
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::ppc:
return std::make_unique<TargetPPC>(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;
}