Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,10 @@ def err_drv_loongarch_invalid_simd_option_combination : Error<
"invalid option combination; LASX depends on LSX">;
def err_drv_loongarch_invalid_msimd_EQ : Error<
"invalid argument '%0' to -msimd=; must be one of: none, lsx, lasx">;
// Support for other float specifications for flang in LoongArch64 may be
// added in the future, such as 32-bit fpu or soft fpu.
def err_drv_loongarch_disable_fpu64_for_flang : Error<
"invalid argument '%0'; must support 64-bit fpu for flang in LoongArch64">;

def err_drv_expand_response_file : Error<
"failed to expand response file: %0">;
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,20 @@ void Flang::AddAArch64TargetArgs(const ArgList &Args,
}
}

void Flang::AddLoongArch64TargetArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
const Driver &D = getToolChain().getDriver();
// Currently, flang only support `-mabi=lp64d` in LoongArch64.
if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {
StringRef V = A->getValue();
if (V != "lp64d") {
std::string errMesg = std::string("-mabi=") + std::string(V.data());
D.Diag(diag::err_drv_loongarch_disable_fpu64_for_flang) << errMesg;
}
}
CmdArgs.push_back("-mabi=lp64d");
}

void Flang::AddPPCTargetArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
const Driver &D = getToolChain().getDriver();
Expand Down Expand Up @@ -416,6 +430,7 @@ void Flang::addTargetOptions(const ArgList &Args,
break;
case llvm::Triple::loongarch64:
getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
AddLoongArch64TargetArgs(Args, CmdArgs);
break;
}

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class LLVM_LIBRARY_VISIBILITY Flang : public Tool {
void AddAMDGPUTargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;

/// Add specific options for LoongArch64 target.
///
/// \param [in] Args The list of input driver arguments
/// \param [out] CmdArgs The list of output command arguments
void AddLoongArch64TargetArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;

/// Add specific options for RISC-V target.
///
/// \param [in] Args The list of input driver arguments
Expand Down
308 changes: 308 additions & 0 deletions flang/lib/Optimizer/CodeGen/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,9 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
using GenericTarget::GenericTarget;

static constexpr int defaultWidth = 64;
static constexpr int GRLen = defaultWidth; /* eight bytes */
static constexpr int GRLenInChar = GRLen / 8;
static constexpr int FRLen = defaultWidth; /* eight bytes */

CodeGenSpecifics::Marshalling
complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override {
Expand Down Expand Up @@ -1151,6 +1154,311 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {

return GenericTarget::integerArgumentType(loc, argTy);
}

/// Flatten non-basic types, resulting in an array of types containing only
/// `IntegerType` and `FloatType`.
std::vector<mlir::Type> flattenTypeList(mlir::Location loc,
const mlir::Type type) const {
std::vector<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 complx 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 auto width = kindMap.getLogicalBitsize(logicalTy.getFKind());
if (width != 0)
flatTypes.push_back(
mlir::IntegerType::get(type.getContext(), width));
})
.template Case<fir::CharacterType>([&](fir::CharacterType charTy) {
flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
})
.template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
if (!seqTy.hasDynamicExtents()) {
std::size_t numOfEle = seqTy.getConstantArraySize();
auto eleTy = seqTy.getEleTy();
if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
auto subTypeList = flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
for (std::size_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;
auto subTypeList = flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
}
})
.template Case<fir::VectorType>([&](fir::VectorType vecTy) {
std::size_t numOfEle = vecTy.getLen();
auto eleTy = vecTy.getEleTy();
if (!(mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy))) {
auto subTypeList = flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
for (std::size_t i = 0; i < numOfEle; ++i)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
} else {
std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
}
})
.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;
}

/// Determine if a struct is eligible to be passed in FARs (and GARs) (i.e.,
/// when flattened it contains a single fp value, fp+fp, or int+fp of
/// appropriate size).
bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
mlir::Type &Field1Ty,
mlir::Type &Field2Ty) const {

Field1Ty = Field2Ty = nullptr;
auto flatTypes = flattenTypeList(loc, recTy);
size_t flatSize = flatTypes.size();

// Cannot be eligible if the number of flattened types is equal to 0 or
// greater than 2.
if (flatSize == 0 || flatSize > 2)
return false;

bool isFirstAvaliableFloat = false;

assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
"Type must be int or float after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
auto Size = floatTy.getWidth();
// Can't be eligible if larger than the FP registers. Half precision isn't
// currently supported on LoongArch and the ABI hasn't been confirmed, so
// default to the integer ABI in that case.
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;
}

// flatTypes has two elements
if (flatSize == 2) {
assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[1])) &&
"Type must be integer or float after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
auto 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])) {
// Can't be eligible if an integer type was already found (int+int pairs
// are not eligible).
if (!isFirstAvaliableFloat)
return false;
if (intTy.getWidth() > GRLen)
return false;
Field2Ty = intTy;
return true;
}
}

// return isFirstAvaliableFloat if flatTypes only has one element
return isFirstAvaliableFloat;
}

bool checkTypehasEnoughReg(mlir::Location loc, int &GARsLeft, int &FARsLeft,
const mlir::Type type) const {
if (type == nullptr)
return true;

llvm::TypeSwitch<mlir::Type>(type)
.template Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
const auto width = intTy.getWidth();
assert(width <= 128 &&
"integer type with width more than 128 bits is unexpected");
if (width == 0)
return;
if (width <= GRLen)
--GARsLeft;
else if (width <= 2 * GRLen)
GARsLeft = GARsLeft - 2;
})
.template Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
const auto width = floatTy.getWidth();
assert(width <= 128 &&
"float type with width more than 128 bits is unexpected");
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; // Pointers.
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()) {
// Previous argument passed on the stack, and its address is passed in
// GAR.
--GARsLeft;
continue;
}

// Previous aggregate arguments were marshalled into simpler arguments.
const auto &type = std::get<mlir::Type>(typeAndAttr);
auto flatTypes = flattenTypeList(loc, type);

for (auto &flatTy : flatTypes) {
if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, flatTy))
return false;
}
}

if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, Field1Ty))
return false;
if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, Field2Ty))
return false;
return true;
}

/// LoongArch64 subroutine calling sequence ABI in:
/// https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
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);
auto 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, /*byval=*/!isResult, /*sret=*/isResult});
return marshal;
}

// Pass by FARs(and GARs)
mlir::Type Field1Ty = nullptr, Field2Ty = nullptr;
if (detectFARsEligibleStruct(loc, recTy, Field1Ty, Field2Ty)) {
if (hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments,
Field1Ty, Field2Ty)) {
if (!isResult) {
if (Field1Ty)
marshal.emplace_back(Field1Ty, AT{});
if (Field2Ty)
marshal.emplace_back(Field2Ty, AT{});
} else {
// Field1Ty is always preferred over Field2Ty for assignment, so there
// will never be a case where Field1Ty == nullptr and Field2Ty !=
// nullptr.
if (Field1Ty && !Field2Ty)
marshal.emplace_back(Field1Ty, AT{});
else if (Field1Ty && Field2Ty)
marshal.emplace_back(
mlir::TupleType::get(context,
mlir::TypeRange{Field1Ty, Field2Ty}),
AT{/*alignment=*/0, /*byval=*/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;
}

// recSize > GRLenInChar && recSize <= 2 * GRLenInChar
marshal.emplace_back(
fir::SequenceType::get({2}, mlir::IntegerType::get(context, GRLen)),
AT{});
return marshal;
}

/// 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 {
int GARsLeft = 8;
int FARsLeft = FRLen ? 8 : 0;

return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/false,
previousArguments);
}

CodeGenSpecifics::Marshalling
structReturnType(mlir::Location loc, fir::RecordType recTy) const override {
// The rules for return and argument types are the same.
int GARsLeft = 2;
int FARsLeft = FRLen ? 2 : 0;
return classifyStruct(loc, recTy, GARsLeft, FARsLeft, /*isResult=*/true,
{});
}
};
} // namespace

Expand Down
13 changes: 13 additions & 0 deletions flang/test/Driver/mabi-loongarch.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
! RUN: not %flang -### -c --target=loongarch64-unknown-linux -mabi=lp64s %s 2>&1 | FileCheck --check-prefix=INVALID1 %s
! RUN: not %flang -### -c --target=loongarch64-unknown-linux -mabi=lp64f %s 2>&1 | FileCheck --check-prefix=INVALID2 %s
! RUN: %flang -### -c --target=loongarch64-unknown-linux -mabi=lp64d %s 2>&1 | FileCheck --check-prefix=ABI %s
! RUN: %flang -### -c --target=loongarch64-unknown-linux %s 2>&1 | FileCheck --check-prefix=ABI %s

! REQUIRES: target=loongarch64{{.*}}

! INVALID1: error: invalid argument '-mabi=lp64s'; must support 64-bit fpu for flang in LoongArch64
! INVALID2: error: invalid argument '-mabi=lp64f'; must support 64-bit fpu for flang in LoongArch64

! ABI: "-target-feature" "+d"
! ABI-SAME: "-mabi=lp64d"

Loading
Loading