[Flang] LoongArch64 support for BIND(C) derived types in mabi=lp64d. (#117108)
This patch: - Supports both the passing and returning of BIND(C) type parameters. - Adds `mabi` check for LoongArch64. Currently, flang only supports `mabi=` option set to `lp64d` in LoongArch64, other ABIs will report an error and may be supported in the future. Reference ABI: https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
This commit is contained in:
@@ -203,6 +203,18 @@ 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") {
|
||||
D.Diag(diag::err_drv_argument_not_allowed_with) << "-mabi" << V;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Flang::AddPPCTargetArgs(const ArgList &Args,
|
||||
ArgStringList &CmdArgs) const {
|
||||
const Driver &D = getToolChain().getDriver();
|
||||
@@ -416,6 +428,7 @@ void Flang::addTargetOptions(const ArgList &Args,
|
||||
break;
|
||||
case llvm::Triple::loongarch64:
|
||||
getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
|
||||
AddLoongArch64TargetArgs(Args, CmdArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,13 @@ private:
|
||||
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
|
||||
|
||||
@@ -1172,6 +1172,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 {
|
||||
@@ -1242,6 +1245,313 @@ 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`.
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
llvm::SmallVector<mlir::Type> 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 integerType or floatType after flattening");
|
||||
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
|
||||
const unsigned 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 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])) {
|
||||
// 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 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; // 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);
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
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, /*byval=*/!isResult, /*sret=*/isResult});
|
||||
return marshal;
|
||||
}
|
||||
|
||||
// Pass by FARs(and GARs)
|
||||
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 {
|
||||
// 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
|
||||
|
||||
|
||||
10
flang/test/Driver/mabi-loongarch.f90
Normal file
10
flang/test/Driver/mabi-loongarch.f90
Normal file
@@ -0,0 +1,10 @@
|
||||
! 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
|
||||
|
||||
! INVALID1: error: invalid argument '-mabi' not allowed with 'lp64s'
|
||||
! INVALID2: error: invalid argument '-mabi' not allowed with 'lp64f'
|
||||
|
||||
! ABI: "-target-feature" "+d"
|
||||
|
||||
232
flang/test/Fir/struct-passing-loongarch64-byreg.fir
Normal file
232
flang/test/Fir/struct-passing-loongarch64-byreg.fir
Normal file
@@ -0,0 +1,232 @@
|
||||
/// Test LoongArch64 ABI rewrite of struct passed by value (BIND(C), VALUE derived types).
|
||||
/// This test test cases where the struct can be passed in registers.
|
||||
/// Test cases can be roughly divided into two categories:
|
||||
/// - struct with a single intrinsic component;
|
||||
/// - sturct with more than one field;
|
||||
/// Since the argument marshalling logic is largely the same within each category,
|
||||
/// only the first example in each category checks the entire invocation process,
|
||||
/// while the other examples only check the signatures.
|
||||
|
||||
// REQUIRES: loongarch-registered-target
|
||||
// RUN: fir-opt --split-input-file --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s
|
||||
|
||||
|
||||
/// *********************** Struct with a single intrinsic component *********************** ///
|
||||
|
||||
!ty_i16 = !fir.type<ti16{i:i16}>
|
||||
!ty_i32 = !fir.type<ti32{i:i32}>
|
||||
!ty_i64 = !fir.type<ti64{i:i64}>
|
||||
!ty_i128 = !fir.type<ti128{i:i128}>
|
||||
!ty_f16 = !fir.type<tf16{i:f16}>
|
||||
!ty_f32 = !fir.type<tf32{i:f32}>
|
||||
!ty_f64 = !fir.type<tf64{i:f64}>
|
||||
!ty_f128 = !fir.type<tf128{i:f128}>
|
||||
!ty_bf16 = !fir.type<tbf16{i:bf16}>
|
||||
!ty_char1 = !fir.type<tchar1{i:!fir.char<1>}>
|
||||
!ty_char2 = !fir.type<tchar2{i:!fir.char<1,2>}>
|
||||
!ty_log1 = !fir.type<tlog1{i:!fir.logical<1>}>
|
||||
!ty_log2 = !fir.type<tlog2{i:!fir.logical<2>}>
|
||||
!ty_log4 = !fir.type<tlog4{i:!fir.logical<4>}>
|
||||
!ty_log8 = !fir.type<tlog8{i:!fir.logical<8>}>
|
||||
!ty_log16 = !fir.type<tlog16{i:!fir.logical<16>}>
|
||||
!ty_cmplx_f32 = !fir.type<tcmplx_f32{i:complex<f32>}>
|
||||
!ty_cmplx_f64 = !fir.type<tcmplx_f64{i:complex<f64>}>
|
||||
|
||||
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", llvm.target_triple = "loongarch64-unknown-linux-gnu"} {
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i16(i64)
|
||||
func.func private @test_func_i16(%arg0: !ty_i16)
|
||||
// CHECK-LABEL: func.func @test_call_i16(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<ti16{i:i16}>>) {
|
||||
func.func @test_call_i16(%arg0: !fir.ref<!ty_i16>) {
|
||||
// CHECK: %[[IN:.*]] = fir.load %[[ARG0]] : !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
// CHECK: %[[STACK:.*]] = llvm.intr.stacksave : !llvm.ptr
|
||||
// CHECK: %[[ARR:.*]] = fir.alloca i64
|
||||
// CHECK: %[[CVT:.*]] = fir.convert %[[ARR]] : (!fir.ref<i64>) -> !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
// CHECK: fir.store %[[IN]] to %[[CVT]] : !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
// CHECK: %[[LD:.*]] = fir.load %[[ARR]] : !fir.ref<i64>
|
||||
%in = fir.load %arg0 : !fir.ref<!ty_i16>
|
||||
// CHECK: fir.call @test_func_i16(%[[LD]]) : (i64) -> ()
|
||||
// CHECK: llvm.intr.stackrestore %[[STACK]] : !llvm.ptr
|
||||
fir.call @test_func_i16(%in) : (!ty_i16) -> ()
|
||||
// CHECK: return
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32(i64)
|
||||
func.func private @test_func_i32(%arg0: !ty_i32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64(i64)
|
||||
func.func private @test_func_i64(%arg0: !ty_i64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i128(i128)
|
||||
func.func private @test_func_i128(%arg0: !ty_i128)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f16(i64)
|
||||
func.func private @test_func_f16(%arg0: !ty_f16)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32(f32)
|
||||
func.func private @test_func_f32(%arg0: !ty_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64(f64)
|
||||
func.func private @test_func_f64(%arg0: !ty_f64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f128(i128)
|
||||
func.func private @test_func_f128(%arg0: !ty_f128)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_bf16(i64)
|
||||
func.func private @test_func_bf16(%arg0: !ty_bf16)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_char1(i64)
|
||||
func.func private @test_func_char1(%arg0: !ty_char1)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_char2(i64)
|
||||
func.func private @test_func_char2(%arg0: !ty_char2)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log1(i64)
|
||||
func.func private @test_func_log1(%arg0: !ty_log1)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log2(i64)
|
||||
func.func private @test_func_log2(%arg0: !ty_log2)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log4(i64)
|
||||
func.func private @test_func_log4(%arg0: !ty_log4)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log8(i64)
|
||||
func.func private @test_func_log8(%arg0: !ty_log8)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log16(i128)
|
||||
func.func private @test_func_log16(%arg0: !ty_log16)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_cmplx_f32(f32, f32)
|
||||
func.func private @test_func_cmplx_f32(%arg0: !ty_cmplx_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_cmplx_f64(f64, f64)
|
||||
func.func private @test_func_cmplx_f64(%arg0: !ty_cmplx_f64)
|
||||
}
|
||||
|
||||
|
||||
/// *************************** Struct with more than one field **************************** ///
|
||||
|
||||
// -----
|
||||
|
||||
!ty_i32_f32 = !fir.type<ti32_f32{i:i32,j:f32}>
|
||||
!ty_i32_f64 = !fir.type<ti32_f64{i:i32,j:f64}>
|
||||
!ty_i64_f32 = !fir.type<ti64_f32{i:i64,j:f32}>
|
||||
!ty_i64_f64 = !fir.type<ti64_f64{i:i64,j:f64}>
|
||||
!ty_f64_i64 = !fir.type<tf64_i64{i:f64,j:i64}>
|
||||
!ty_f16_f16 = !fir.type<tf16_f16{i:f16,j:f16}>
|
||||
!ty_f32_f32 = !fir.type<tf32_f32{i:f32,j:f32}>
|
||||
!ty_f64_f64 = !fir.type<tf64_f64{i:f64,j:f64}>
|
||||
!ty_f32_i32_i32 = !fir.type<tf32_i32_i32{i:f32,j:i32,k:i32}>
|
||||
!ty_f32_f32_i32 = !fir.type<tf32_f32_i32{i:f32,j:f32,k:i32}>
|
||||
!ty_f32_f32_f32 = !fir.type<tf32_f32_f32{i:f32,j:f32,k:f32}>
|
||||
|
||||
!ty_i8_a8 = !fir.type<ti8_a8{i:!fir.array<8xi8>}>
|
||||
!ty_i8_a16 = !fir.type<ti8_a16{i:!fir.array<16xi8>}>
|
||||
!ty_f32_a2 = !fir.type<tf32_a2{i:!fir.array<2xf32>}>
|
||||
!ty_f64_a2 = !fir.type<tf64_a2{i:!fir.array<2xf64>}>
|
||||
!ty_nested_i32_f32 = !fir.type<t11{i:!ty_i32_f32}>
|
||||
!ty_nested_i8_a8_i32 = !fir.type<t12{i:!ty_i8_a8, j:i32}>
|
||||
!ty_char1_a8 = !fir.type<t_char_a8{i:!fir.array<8x!fir.char<1>>}>
|
||||
|
||||
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", llvm.target_triple = "loongarch64-unknown-linux-gnu"} {
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32_f32(i32, f32)
|
||||
func.func private @test_func_i32_f32(%arg0: !ty_i32_f32)
|
||||
// CHECK-LABEL: func.func @test_call_i32_f32(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>) {
|
||||
func.func @test_call_i32_f32(%arg0: !fir.ref<!ty_i32_f32>) {
|
||||
// CHECK: %[[IN:.*]] = fir.load %[[ARG0]] : !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
// CHECK: %[[STACK:.*]] = llvm.intr.stacksave : !llvm.ptr
|
||||
// CHECK: %[[ARR:.*]] = fir.alloca tuple<i32, f32>
|
||||
// CHECK: %[[CVT:.*]] = fir.convert %[[ARR]] : (!fir.ref<tuple<i32, f32>>) -> !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
// CHECK: fir.store %[[IN]] to %[[CVT]] : !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
// CHECK: %[[LD:.*]] = fir.load %[[ARR]] : !fir.ref<tuple<i32, f32>>
|
||||
// CHECK: %[[VAL_0:.*]] = fir.extract_value %[[LD]], [0 : i32] : (tuple<i32, f32>) -> i32
|
||||
// CHECK: %[[VAL_1:.*]] = fir.extract_value %[[LD]], [1 : i32] : (tuple<i32, f32>) -> f32
|
||||
%in = fir.load %arg0 : !fir.ref<!ty_i32_f32>
|
||||
// CHECK: fir.call @test_func_i32_f32(%[[VAL_0]], %[[VAL_1]]) : (i32, f32) -> ()
|
||||
// CHECK: llvm.intr.stackrestore %[[STACK]] : !llvm.ptr
|
||||
fir.call @test_func_i32_f32(%in) : (!ty_i32_f32) -> ()
|
||||
// CHECK: return
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32_f64(i32, f64)
|
||||
func.func private @test_func_i32_f64(%arg0: !ty_i32_f64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64_f32(i64, f32)
|
||||
func.func private @test_func_i64_f32(%arg0: !ty_i64_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64_f64(i64, f64)
|
||||
func.func private @test_func_i64_f64(%arg0: !ty_i64_f64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_i64(f64, i64)
|
||||
func.func private @test_func_f64_i64(%arg0: !ty_f64_i64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f16_f16(i64)
|
||||
func.func private @test_func_f16_f16(%arg0: !ty_f16_f16)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32(f32, f32)
|
||||
func.func private @test_func_f32_f32(%arg0: !ty_f32_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_f64(f64, f64)
|
||||
func.func private @test_func_f64_f64(%arg0: !ty_f64_f64)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_i32_i32(!fir.array<2xi64>)
|
||||
func.func private @test_func_f32_i32_i32(%arg0: !ty_f32_i32_i32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32_i32(!fir.array<2xi64>)
|
||||
func.func private @test_func_f32_f32_i32(%arg0: !ty_f32_f32_i32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32_f32(!fir.array<2xi64>)
|
||||
func.func private @test_func_f32_f32_f32(%arg0: !ty_f32_f32_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i8_a8(i64)
|
||||
func.func private @test_func_i8_a8(%arg0: !ty_i8_a8)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i8_a16(!fir.array<2xi64>)
|
||||
func.func private @test_func_i8_a16(%arg0: !ty_i8_a16)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_a2(f32, f32)
|
||||
func.func private @test_func_f32_a2(%arg0: !ty_f32_a2)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_a2(f64, f64)
|
||||
func.func private @test_func_f64_a2(%arg0: !ty_f64_a2)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_nested_i32_f32(i32, f32)
|
||||
func.func private @test_func_nested_i32_f32(%arg0: !ty_nested_i32_f32)
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_nested_i8_a8_i32(!fir.array<2xi64>)
|
||||
func.func private @test_func_nested_i8_a8_i32(%arg0: !ty_nested_i8_a8_i32)
|
||||
|
||||
|
||||
// CHECK: func.func private @not_enough_int_reg_1(i32, i32, i32, i32, i32, i32, i32, i32, i64)
|
||||
func.func private @not_enough_int_reg_1(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32, %arg4: i32,
|
||||
%arg5: i32, %arg6: i32, %arg7: i32, %arg8: !ty_i32_f32)
|
||||
|
||||
// CHECK: func.func private @not_enough_int_reg_1b(!fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, !fir.ref<i32>, i64)
|
||||
func.func private @not_enough_int_reg_1b(%arg0: !fir.ref<i32>, %arg1: !fir.ref<i32>, %arg2: !fir.ref<i32>, %arg3: !fir.ref<i32>, %arg4: !fir.ref<i32>,
|
||||
%arg5: !fir.ref<i32>, %arg6: !fir.ref<i32>, %arg7: !fir.ref<i32>, %arg8: !ty_i32_f32)
|
||||
|
||||
// CHECK: func.func private @not_enough_int_reg_2(i32, i32, i32, i32, i32, i32, i32, i32, !fir.array<2xi64>)
|
||||
func.func private @not_enough_int_reg_2(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32, %arg4: i32,
|
||||
%arg5: i32, %arg6: i32, %arg7: i32, %arg8: !ty_i64_f64)
|
||||
|
||||
// CHECK: func.func private @not_enough_fp_reg_1(f32, f32, f32, f32, f32, f32, f32, f32, i64)
|
||||
func.func private @not_enough_fp_reg_1(%arg0: f32, %arg1: f32, %arg2: f32, %arg3: f32, %arg4: f32,
|
||||
%arg5: f32, %arg6: f32, %arg7: f32, %arg8: !ty_i32_f32)
|
||||
|
||||
// CHECK: func.func private @not_enough_fp_reg_1b(!fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, !fir.ref<f32>, i64)
|
||||
func.func private @not_enough_fp_reg_1b(%arg0: !fir.ref<f32>, %arg1: !fir.ref<f32>, %arg2: !fir.ref<f32>, %arg3: !fir.ref<f32>, %arg4: !fir.ref<f32>,
|
||||
%arg5: !fir.ref<f32>, %arg6: !fir.ref<f32>, %arg7: !fir.ref<f32>, %arg8: !ty_i32_f32)
|
||||
|
||||
// CHECK: func.func private @not_enough_fp_reg_2(f32, f32, f32, f32, f32, f32, f32, f32, !fir.array<2xi64>)
|
||||
func.func private @not_enough_fp_reg_2(%arg0: f32, %arg1: f32, %arg2: f32, %arg3: f32, %arg4: f32,
|
||||
%arg5: f32, %arg6: f32, %arg7: f32, %arg8: !ty_i64_f64)
|
||||
|
||||
// CHECK: func.func private @char_not_enough_int_reg(i32, i32, i32, i32, i32, i32, i32, i32, i64)
|
||||
func.func private @char_not_enough_int_reg(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32, %arg4: i32,
|
||||
%arg5: i32, %arg6: i32, %arg7: i32, %arg8: !ty_char1_a8)
|
||||
}
|
||||
80
flang/test/Fir/struct-passing-return-loongarch64-bystack.fir
Normal file
80
flang/test/Fir/struct-passing-return-loongarch64-bystack.fir
Normal file
@@ -0,0 +1,80 @@
|
||||
/// Test LoongArch64 ABI rewrite of struct passed and returned by value (BIND(C), VALUE derived types).
|
||||
/// This test test cases where the struct must be passed or returned on the stack.
|
||||
|
||||
// REQUIRES: loongarch-registered-target
|
||||
// RUN: tco --target=loongarch64-unknown-linux-gnu %s | FileCheck %s
|
||||
|
||||
!ty_int_toobig = !fir.type<int_toobig{i:!fir.array<5xi32>}>
|
||||
!ty_int_toobig_align16 = !fir.type<int_toobig_align16{i:i128,j:i8}>
|
||||
!ty_fp_toobig = !fir.type<fp_toobig{i:!fir.array<5xf64>}>
|
||||
!ty_fp_toobig_align16 = !fir.type<fp_toobig_align16{i:f128,j:f32}>
|
||||
|
||||
!ty_i32_f32 = !fir.type<i32_f32{i:i32,j:f32}>
|
||||
!ty_nested_toobig = !fir.type<nested_toobig{i:!fir.array<3x!ty_i32_f32>}>
|
||||
!ty_badly_aligned = !fir.type<badly_aligned{i:f32,j:f64,k:f32}>
|
||||
!ty_logical_toobig = !fir.type<logical_toobig{i:!fir.array<17x!fir.logical<1>>}>
|
||||
!ty_cmplx_toobig = !fir.type<cmplx_toobig{i:!fir.array<4xcomplex<f32>>}>
|
||||
!ty_char_toobig = !fir.type<char_toobig{i:!fir.array<17x!fir.char<1>>}>
|
||||
!ty_cmplx_f128 = !fir.type<cmplx_f128{i:complex<f128>}>
|
||||
|
||||
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", llvm.target_triple = "loongarch64-unknown-linux-gnu"} {
|
||||
|
||||
// CHECK: declare void @takes_int_toobig(ptr byval(%int_toobig) align 4)
|
||||
func.func private @takes_int_toobig(%arg0: !ty_int_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_int_toobig(ptr sret(%int_toobig) align 4)
|
||||
func.func private @return_int_toobig() -> !ty_int_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_int_toobig_align16(ptr byval(%int_toobig_align16) align 16)
|
||||
func.func private @takes_int_toobig_align16(%arg0: !ty_int_toobig_align16) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_int_toobig_align16(ptr sret(%int_toobig_align16) align 16)
|
||||
func.func private @return_int_toobig_align16() -> !ty_int_toobig_align16 attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_fp_toobig(ptr byval(%fp_toobig) align 8)
|
||||
func.func private @takes_fp_toobig(%arg0: !ty_fp_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_fp_toobig(ptr sret(%fp_toobig) align 8)
|
||||
func.func private @return_fp_toobig() -> !ty_fp_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_fp_toobig_align16(ptr byval(%fp_toobig_align16) align 16)
|
||||
func.func private @takes_fp_toobig_align16(%arg0: !ty_fp_toobig_align16) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_fp_toobig_align16(ptr sret(%fp_toobig_align16) align 16)
|
||||
func.func private @return_fp_toobig_align16() -> !ty_fp_toobig_align16 attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_nested_toobig(ptr byval(%nested_toobig) align 4)
|
||||
func.func private @takes_nested_toobig(%arg0: !ty_nested_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_nested_toobig(ptr sret(%nested_toobig) align 4)
|
||||
func.func private @return_nested_toobig() -> !ty_nested_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_badly_aligned(ptr byval(%badly_aligned) align 8)
|
||||
func.func private @takes_badly_aligned(%arg0: !ty_badly_aligned) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_badly_aligned(ptr sret(%badly_aligned) align 8)
|
||||
func.func private @return_badly_aligned() -> !ty_badly_aligned attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_logical_toobig(ptr byval(%logical_toobig) align 1)
|
||||
func.func private @takes_logical_toobig(%arg0: !ty_logical_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_logical_toobig(ptr sret(%logical_toobig) align 1)
|
||||
func.func private @return_logical_toobig() -> !ty_logical_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_cmplx_toobig(ptr byval(%cmplx_toobig) align 4)
|
||||
func.func private @takes_cmplx_toobig(%arg0: !ty_cmplx_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_cmplx_toobig(ptr sret(%cmplx_toobig) align 4)
|
||||
func.func private @return_cmplx_toobig() -> !ty_cmplx_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_char_toobig(ptr byval(%char_toobig) align 1)
|
||||
func.func private @takes_char_toobig(%arg0: !ty_char_toobig) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
// CHECK: declare void @return_char_toobig(ptr sret(%char_toobig) align 1)
|
||||
func.func private @return_char_toobig() -> !ty_char_toobig attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare { i32, float } @takes_and_return(float, float, float, float, float, float, float, float, ptr byval(%cmplx_f128) align 16)
|
||||
func.func private @takes_and_return(%arg0: f32, %arg1: f32, %arg2: f32, %arg3: f32, %arg4: f32,
|
||||
%arg5: f32, %arg6: f32, %arg7: f32, %arg8: !ty_cmplx_f128) -> !ty_i32_f32 attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_and_return2(ptr sret(%cmplx_f128) align 16, i32, i32, i32, i32, i32, i32, i32, i32, i64)
|
||||
func.func private @takes_and_return2(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32, %arg4: i32,
|
||||
%arg5: i32, %arg6: i32, %arg7: i32, %arg8: !ty_i32_f32) -> !ty_cmplx_f128 attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
|
||||
// CHECK: declare void @takes_multi_byval_arguments(ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, ptr byval(%cmplx_f128) align 16, i32, float, i64)
|
||||
func.func private @takes_multi_byval_arguments(%arg0: !ty_cmplx_f128, %arg1: !ty_cmplx_f128, %arg2: !ty_cmplx_f128, %arg3: !ty_cmplx_f128,
|
||||
%arg4: !ty_cmplx_f128, %arg5: !ty_cmplx_f128, %arg6: !ty_cmplx_f128,
|
||||
%arg7: !ty_i32_f32, %arg8: !ty_i32_f32) attributes {fir.proc_attrs = #fir.proc_attrs<bind_c>}
|
||||
}
|
||||
|
||||
200
flang/test/Fir/struct-return-loongarch64-byreg.fir
Normal file
200
flang/test/Fir/struct-return-loongarch64-byreg.fir
Normal file
@@ -0,0 +1,200 @@
|
||||
/// Test LoongArch64 ABI rewrite of struct returned by value (BIND(C), VALUE derived types).
|
||||
/// This test test cases where the struct can be returned in registers.
|
||||
/// Test cases can be roughly divided into two categories:
|
||||
/// - struct with a single intrinsic component;
|
||||
/// - sturct with more than one field;
|
||||
/// Since the argument marshalling logic is largely the same within each category,
|
||||
/// only the first example in each category checks the entire invocation process,
|
||||
/// while the other examples only check the signatures.
|
||||
|
||||
// REQUIRES: loongarch-registered-target
|
||||
// RUN: fir-opt --split-input-file --target-rewrite="target=loongarch64-unknown-linux-gnu" %s | FileCheck %s
|
||||
|
||||
|
||||
/// *********************** Struct with a single intrinsic component *********************** ///
|
||||
|
||||
!ty_i16 = !fir.type<ti16{i:i16}>
|
||||
!ty_i32 = !fir.type<ti32{i:i32}>
|
||||
!ty_i64 = !fir.type<ti64{i:i64}>
|
||||
!ty_i128 = !fir.type<ti128{i:i128}>
|
||||
!ty_f16 = !fir.type<tf16{i:f16}>
|
||||
!ty_f32 = !fir.type<tf32{i:f32}>
|
||||
!ty_f64 = !fir.type<tf64{i:f64}>
|
||||
!ty_f128 = !fir.type<tf128{i:f128}>
|
||||
!ty_bf16 = !fir.type<tbf16{i:bf16}>
|
||||
!ty_char1 = !fir.type<tchar1{i:!fir.char<1>}>
|
||||
!ty_char2 = !fir.type<tchar2{i:!fir.char<1,2>}>
|
||||
!ty_log1 = !fir.type<tlog1{i:!fir.logical<1>}>
|
||||
!ty_log2 = !fir.type<tlog2{i:!fir.logical<2>}>
|
||||
!ty_log4 = !fir.type<tlog4{i:!fir.logical<4>}>
|
||||
!ty_log8 = !fir.type<tlog8{i:!fir.logical<8>}>
|
||||
!ty_log16 = !fir.type<tlog16{i:!fir.logical<16>}>
|
||||
!ty_cmplx_f32 = !fir.type<tcmplx_f32{i:complex<f32>}>
|
||||
!ty_cmplx_f64 = !fir.type<tcmplx_f64{i:complex<f64>}>
|
||||
|
||||
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", llvm.target_triple = "loongarch64-unknown-linux-gnu"} {
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i16() -> i64
|
||||
func.func private @test_func_i16() -> !ty_i16
|
||||
// CHECK-LABEL: func.func @test_call_i16(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<ti16{i:i16}>>) {
|
||||
func.func @test_call_i16(%arg0: !fir.ref<!ty_i16>) {
|
||||
// CHECK: %[[OUT:.*]] = fir.call @test_func_i16() : () -> i64
|
||||
// CHECK: %[[STACK:.*]] = llvm.intr.stacksave : !llvm.ptr
|
||||
// CHECK: %[[ARR:.*]] = fir.alloca i64
|
||||
// CHECK: fir.store %[[OUT]] to %[[ARR]] : !fir.ref<i64>
|
||||
// CHECK: %[[CVT:.*]] = fir.convert %[[ARR]] : (!fir.ref<i64>) -> !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
// CHECK: %[[LD:.*]] = fir.load %[[CVT]] : !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
// CHECK: llvm.intr.stackrestore %[[STACK]] : !llvm.ptr
|
||||
%out = fir.call @test_func_i16() : () -> !ty_i16
|
||||
// CHECK: fir.store %[[LD]] to %[[ARG0]] : !fir.ref<!fir.type<ti16{i:i16}>>
|
||||
fir.store %out to %arg0 : !fir.ref<!ty_i16>
|
||||
// CHECK: return
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32() -> i64
|
||||
func.func private @test_func_i32() -> !ty_i32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64() -> i64
|
||||
func.func private @test_func_i64() -> !ty_i64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i128() -> i128
|
||||
func.func private @test_func_i128() -> !ty_i128
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f16() -> i64
|
||||
func.func private @test_func_f16() -> !ty_f16
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32() -> f32
|
||||
func.func private @test_func_f32() -> !ty_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64() -> f64
|
||||
func.func private @test_func_f64() -> !ty_f64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f128() -> i128
|
||||
func.func private @test_func_f128() -> !ty_f128
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_bf16() -> i64
|
||||
func.func private @test_func_bf16() -> !ty_bf16
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_char1() -> i64
|
||||
func.func private @test_func_char1() -> !ty_char1
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_char2() -> i64
|
||||
func.func private @test_func_char2() -> !ty_char2
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log1() -> i64
|
||||
func.func private @test_func_log1() -> !ty_log1
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log2() -> i64
|
||||
func.func private @test_func_log2() -> !ty_log2
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log4() -> i64
|
||||
func.func private @test_func_log4() -> !ty_log4
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log8() -> i64
|
||||
func.func private @test_func_log8() -> !ty_log8
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_log16() -> i128
|
||||
func.func private @test_func_log16() -> !ty_log16
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_cmplx_f32() -> tuple<f32, f32>
|
||||
func.func private @test_func_cmplx_f32() -> !ty_cmplx_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_cmplx_f64() -> tuple<f64, f64>
|
||||
func.func private @test_func_cmplx_f64() -> !ty_cmplx_f64
|
||||
}
|
||||
|
||||
|
||||
/// *************************** Struct with more than one field **************************** ///
|
||||
|
||||
// -----
|
||||
|
||||
!ty_i32_f32 = !fir.type<ti32_f32{i:i32,j:f32}>
|
||||
!ty_i32_f64 = !fir.type<ti32_f64{i:i32,j:f64}>
|
||||
!ty_i64_f32 = !fir.type<ti64_f32{i:i64,j:f32}>
|
||||
!ty_i64_f64 = !fir.type<ti64_f64{i:i64,j:f64}>
|
||||
!ty_f64_i64 = !fir.type<tf64_i64{i:f64,j:i64}>
|
||||
!ty_f16_f16 = !fir.type<tf16_f16{i:f16,j:f16}>
|
||||
!ty_f32_f32 = !fir.type<tf32_f32{i:f32,j:f32}>
|
||||
!ty_f64_f64 = !fir.type<tf64_f64{i:f64,j:f64}>
|
||||
!ty_f32_i32_i32 = !fir.type<tf32_i32_i32{i:f32,j:i32,k:i32}>
|
||||
!ty_f32_f32_i32 = !fir.type<tf32_f32_i32{i:f32,j:f32,k:i32}>
|
||||
!ty_f32_f32_f32 = !fir.type<tf32_f32_f32{i:f32,j:f32,k:f32}>
|
||||
|
||||
!ty_i8_a8 = !fir.type<ti8_a8{i:!fir.array<8xi8>}>
|
||||
!ty_i8_a16 = !fir.type<ti8_a16{i:!fir.array<16xi8>}>
|
||||
!ty_f32_a2 = !fir.type<tf32_a2{i:!fir.array<2xf32>}>
|
||||
!ty_f64_a2 = !fir.type<tf64_a2{i:!fir.array<2xf64>}>
|
||||
!ty_nested_i32_f32 = !fir.type<t11{i:!ty_i32_f32}>
|
||||
!ty_nested_i8_a8_i32 = !fir.type<t12{i:!ty_i8_a8, j:i32}>
|
||||
|
||||
module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", llvm.target_triple = "loongarch64-unknown-linux-gnu"} {
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32_f32() -> tuple<i32, f32>
|
||||
func.func private @test_func_i32_f32() -> !ty_i32_f32
|
||||
// CHECK-LABEL: func.func @test_call_i32_f32(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>) {
|
||||
func.func @test_call_i32_f32(%arg0: !fir.ref<!ty_i32_f32>) {
|
||||
// CHECK: %[[OUT:.*]] = fir.call @test_func_i32_f32() : () -> tuple<i32, f32>
|
||||
// CHECK: %[[STACK:.*]] = llvm.intr.stacksave : !llvm.ptr
|
||||
// CHECK: %[[ARR:.*]] = fir.alloca tuple<i32, f32>
|
||||
// CHECK: fir.store %[[OUT]] to %[[ARR]] : !fir.ref<tuple<i32, f32>>
|
||||
// CHECK: %[[CVT:.*]] = fir.convert %[[ARR]] : (!fir.ref<tuple<i32, f32>>) -> !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
// CHECK: %[[LD:.*]] = fir.load %[[CVT]] : !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
// CHECK: llvm.intr.stackrestore %[[STACK]] : !llvm.ptr
|
||||
%out = fir.call @test_func_i32_f32() : () -> !ty_i32_f32
|
||||
// CHECK: fir.store %[[LD]] to %[[ARG0]] : !fir.ref<!fir.type<ti32_f32{i:i32,j:f32}>>
|
||||
fir.store %out to %arg0 : !fir.ref<!ty_i32_f32>
|
||||
// CHECK: return
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i32_f64() -> tuple<i32, f64>
|
||||
func.func private @test_func_i32_f64() -> !ty_i32_f64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64_f32() -> tuple<i64, f32>
|
||||
func.func private @test_func_i64_f32() -> !ty_i64_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i64_f64() -> tuple<i64, f64>
|
||||
func.func private @test_func_i64_f64() -> !ty_i64_f64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_i64() -> tuple<f64, i64>
|
||||
func.func private @test_func_f64_i64() -> !ty_f64_i64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f16_f16() -> i64
|
||||
func.func private @test_func_f16_f16() -> !ty_f16_f16
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32() -> tuple<f32, f32>
|
||||
func.func private @test_func_f32_f32() -> !ty_f32_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_f64() -> tuple<f64, f64>
|
||||
func.func private @test_func_f64_f64() -> !ty_f64_f64
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_i32_i32() -> !fir.array<2xi64>
|
||||
func.func private @test_func_f32_i32_i32() -> !ty_f32_i32_i32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32_i32() -> !fir.array<2xi64>
|
||||
func.func private @test_func_f32_f32_i32() -> !ty_f32_f32_i32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_f32_f32() -> !fir.array<2xi64>
|
||||
func.func private @test_func_f32_f32_f32() -> !ty_f32_f32_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i8_a8() -> i64
|
||||
func.func private @test_func_i8_a8() -> !ty_i8_a8
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_i8_a16() -> !fir.array<2xi64>
|
||||
func.func private @test_func_i8_a16() -> !ty_i8_a16
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f32_a2() -> tuple<f32, f32>
|
||||
func.func private @test_func_f32_a2() -> !ty_f32_a2
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_f64_a2() -> tuple<f64, f64>
|
||||
func.func private @test_func_f64_a2() -> !ty_f64_a2
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_nested_i32_f32() -> tuple<i32, f32>
|
||||
func.func private @test_func_nested_i32_f32() -> !ty_nested_i32_f32
|
||||
|
||||
// CHECK-LABEL: func.func private @test_func_nested_i8_a8_i32() -> !fir.array<2xi64>
|
||||
func.func private @test_func_nested_i8_a8_i32() -> !ty_nested_i8_a8_i32
|
||||
}
|
||||
Reference in New Issue
Block a user