[clang] [flang] [Flang] LoongArch64 support for BIND(C) derived types in mabi=lp64d. (PR #117108)
Zhaoxin Yang via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 28 17:14:02 PST 2024
https://github.com/ylzsx updated https://github.com/llvm/llvm-project/pull/117108
>From 32e04b6538486006c98c6b805b1057110c3a2c1a Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Wed, 20 Nov 2024 17:30:43 +0800
Subject: [PATCH 1/5] [Flang] LoongArch64 support for BIND(C) derived types.
This patch supports both the passing and returning of BIND(C) type parameters.
Reference ABI:
https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc#subroutine-calling-sequence
---
flang/lib/Optimizer/CodeGen/Target.cpp | 308 ++++++++++++++++++
.../Fir/struct-passing-loongarch64-byreg.fir | 232 +++++++++++++
...uct-passing-return-loongarch64-bystack.fir | 80 +++++
.../Fir/struct-return-loongarch64-byreg.fir | 200 ++++++++++++
4 files changed, 820 insertions(+)
create mode 100644 flang/test/Fir/struct-passing-loongarch64-byreg.fir
create mode 100644 flang/test/Fir/struct-passing-return-loongarch64-bystack.fir
create mode 100644 flang/test/Fir/struct-return-loongarch64-byreg.fir
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index 9ec055b1aecabb..90ce51552c687f 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -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 {
@@ -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
diff --git a/flang/test/Fir/struct-passing-loongarch64-byreg.fir b/flang/test/Fir/struct-passing-loongarch64-byreg.fir
new file mode 100644
index 00000000000000..576ea6459e17a0
--- /dev/null
+++ b/flang/test/Fir/struct-passing-loongarch64-byreg.fir
@@ -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<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)
+}
diff --git a/flang/test/Fir/struct-passing-return-loongarch64-bystack.fir b/flang/test/Fir/struct-passing-return-loongarch64-bystack.fir
new file mode 100644
index 00000000000000..5041a39e697988
--- /dev/null
+++ b/flang/test/Fir/struct-passing-return-loongarch64-bystack.fir
@@ -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>}
+}
+
diff --git a/flang/test/Fir/struct-return-loongarch64-byreg.fir b/flang/test/Fir/struct-return-loongarch64-byreg.fir
new file mode 100644
index 00000000000000..b64cdc7ac7099f
--- /dev/null
+++ b/flang/test/Fir/struct-return-loongarch64-byreg.fir
@@ -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<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
+}
>From f846bb55c460c5bf58826a38b04ae333d248f663 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Mon, 25 Nov 2024 20:38:17 +0800
Subject: [PATCH 2/5] [Flang] Only `lp64d` abi is supported in LoongArch64.
This patch:
- 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.
---
.../include/clang/Basic/DiagnosticDriverKinds.td | 4 ++++
clang/lib/Driver/ToolChains/Flang.cpp | 15 +++++++++++++++
clang/lib/Driver/ToolChains/Flang.h | 7 +++++++
flang/test/Driver/mabi-loongarch.f90 | 13 +++++++++++++
4 files changed, 39 insertions(+)
create mode 100644 flang/test/Driver/mabi-loongarch.f90
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 5155b23d151c04..ec21b2487bf888 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -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">;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 11070c23c75f4a..439a834cf11edb 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -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();
@@ -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;
}
diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h
index 4d7d0b8cd9ea55..7c24a623af3931 100644
--- a/clang/lib/Driver/ToolChains/Flang.h
+++ b/clang/lib/Driver/ToolChains/Flang.h
@@ -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
diff --git a/flang/test/Driver/mabi-loongarch.f90 b/flang/test/Driver/mabi-loongarch.f90
new file mode 100644
index 00000000000000..8084780453f461
--- /dev/null
+++ b/flang/test/Driver/mabi-loongarch.f90
@@ -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"
+
>From 167ab16868bc98ea3682dc5befd56e1a1716891e Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Tue, 26 Nov 2024 17:52:22 +0800
Subject: [PATCH 3/5] Fixes for review
---
.../include/clang/Basic/DiagnosticDriverKinds.td | 4 ----
clang/lib/Driver/ToolChains/Flang.cpp | 4 +---
flang/test/Driver/mabi-loongarch.f90 | 15 ++++++---------
3 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ec21b2487bf888..5155b23d151c04 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -807,10 +807,6 @@ 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">;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 439a834cf11edb..def22ecaf74f19 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -210,11 +210,9 @@ void Flang::AddLoongArch64TargetArgs(const ArgList &Args,
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;
+ D.Diag(diag::err_drv_argument_not_allowed_with) << "-mabi" << V;
}
}
- CmdArgs.push_back("-mabi=lp64d");
}
void Flang::AddPPCTargetArgs(const ArgList &Args,
diff --git a/flang/test/Driver/mabi-loongarch.f90 b/flang/test/Driver/mabi-loongarch.f90
index 8084780453f461..a3a0283bcbb211 100644
--- a/flang/test/Driver/mabi-loongarch.f90
+++ b/flang/test/Driver/mabi-loongarch.f90
@@ -1,13 +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
+! 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
+! INVALID1: error: invalid argument '-mabi' not allowed with 'lp64s'
+! INVALID2: error: invalid argument '-mabi' not allowed with 'lp64f'
! ABI: "-target-feature" "+d"
-! ABI-SAME: "-mabi=lp64d"
>From 63fb287a7139e1845b51f259c2cd5e237a4c4f70 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Wed, 27 Nov 2024 10:09:46 +0800
Subject: [PATCH 4/5] Fixes for review
---
flang/lib/Optimizer/CodeGen/Target.cpp | 98 ++++++++++---------
.../Fir/struct-passing-loongarch64-byreg.fir | 2 +-
.../Fir/struct-return-loongarch64-byreg.fir | 2 +-
3 files changed, 54 insertions(+), 48 deletions(-)
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index 90ce51552c687f..b603a9fa82b0bc 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -1183,20 +1183,25 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
"VALUE derived type argument and type return");
})
.template Case<fir::LogicalType>([&](fir::LogicalType logicalTy) {
- const auto width = kindMap.getLogicalBitsize(logicalTy.getFKind());
+ 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) {
- flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
+ 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()) {
std::size_t numOfEle = seqTy.getConstantArraySize();
- auto eleTy = seqTy.getEleTy();
+ mlir::Type eleTy = seqTy.getEleTy();
if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
- auto subTypeList = flattenTypeList(loc, eleTy);
+ std::vector<mlir::Type> subTypeList = flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
for (std::size_t i = 0; i < numOfEle; ++i)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
@@ -1209,18 +1214,18 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
"VALUE derived type argument and type return");
})
.template Case<fir::RecordType>([&](fir::RecordType recTy) {
- for (auto component : recTy.getTypeList()) {
+ for (auto &component : recTy.getTypeList()) {
mlir::Type eleTy = component.second;
- auto subTypeList = flattenTypeList(loc, eleTy);
+ std::vector<mlir::Type> 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();
+ mlir::Type eleTy = vecTy.getEleTy();
if (!(mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy))) {
- auto subTypeList = flattenTypeList(loc, eleTy);
+ std::vector<mlir::Type> subTypeList = flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
for (std::size_t i = 0; i < numOfEle; ++i)
llvm::copy(subTypeList, std::back_inserter(flatTypes));
@@ -1244,11 +1249,11 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
/// 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 {
+ mlir::Type &field1Ty,
+ mlir::Type &field2Ty) const {
- Field1Ty = Field2Ty = nullptr;
- auto flatTypes = flattenTypeList(loc, recTy);
+ field1Ty = field2Ty = nullptr;
+ std::vector<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
@@ -1259,31 +1264,31 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
bool isFirstAvaliableFloat = false;
assert((mlir::isa<mlir::IntegerType, mlir::FloatType>(flatTypes[0])) &&
- "Type must be int or float after flattening");
+ "Type must be integerType or floatType after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[0])) {
- auto Size = floatTy.getWidth();
+ 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;
+ field1Ty = floatTy;
} else if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(flatTypes[0])) {
if (intTy.getWidth() > GRLen)
return false;
- Field1Ty = intTy;
+ 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");
+ "Type must be integerType or floatType after flattening");
if (auto floatTy = mlir::dyn_cast<mlir::FloatType>(flatTypes[1])) {
- auto Size = floatTy.getWidth();
+ const unsigned Size = floatTy.getWidth();
if (Size > FRLen || Size < 32)
return false;
- Field2Ty = floatTy;
+ 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
@@ -1292,7 +1297,7 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
return false;
if (intTy.getWidth() > GRLen)
return false;
- Field2Ty = intTy;
+ field2Ty = intTy;
return true;
}
}
@@ -1308,9 +1313,10 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
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");
+ 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)
@@ -1319,9 +1325,9 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
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");
+ 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)
@@ -1344,10 +1350,10 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
bool hasEnoughRegisters(mlir::Location loc, int GARsLeft, int FARsLeft,
const Marshalling &previousArguments,
- const mlir::Type &Field1Ty,
- const mlir::Type &Field2Ty) const {
+ const mlir::Type &field1Ty,
+ const mlir::Type &field2Ty) const {
- for (auto typeAndAttr : previousArguments) {
+ 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
@@ -1358,7 +1364,7 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
// Previous aggregate arguments were marshalled into simpler arguments.
const auto &type = std::get<mlir::Type>(typeAndAttr);
- auto flatTypes = flattenTypeList(loc, type);
+ std::vector<mlir::Type> flatTypes = flattenTypeList(loc, type);
for (auto &flatTy : flatTypes) {
if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, flatTy))
@@ -1366,9 +1372,9 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
}
}
- if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, Field1Ty))
+ if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, field1Ty))
return false;
- if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, Field2Ty))
+ if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, field2Ty))
return false;
return true;
}
@@ -1383,7 +1389,7 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
auto [recSize, recAlign] = fir::getTypeSizeAndAlignmentOrCrash(
loc, recTy, getDataLayout(), kindMap);
- auto context = recTy.getContext();
+ mlir::MLIRContext *context = recTy.getContext();
if (recSize == 0) {
TODO(loc, "unsupported empty struct type for BIND(C), "
@@ -1398,25 +1404,25 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
}
// Pass by FARs(and GARs)
- mlir::Type Field1Ty = nullptr, Field2Ty = nullptr;
- if (detectFARsEligibleStruct(loc, recTy, Field1Ty, Field2Ty)) {
+ mlir::Type field1Ty = nullptr, field2Ty = nullptr;
+ if (detectFARsEligibleStruct(loc, recTy, field1Ty, field2Ty)) {
if (hasEnoughRegisters(loc, GARsLeft, FARsLeft, previousArguments,
- Field1Ty, Field2Ty)) {
+ field1Ty, field2Ty)) {
if (!isResult) {
- if (Field1Ty)
- marshal.emplace_back(Field1Ty, AT{});
- if (Field2Ty)
- marshal.emplace_back(Field2Ty, AT{});
+ 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 !=
+ // 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)
+ if (field1Ty && !field2Ty)
+ marshal.emplace_back(field1Ty, AT{});
+ else if (field1Ty && field2Ty)
marshal.emplace_back(
mlir::TupleType::get(context,
- mlir::TypeRange{Field1Ty, Field2Ty}),
+ mlir::TypeRange{field1Ty, field2Ty}),
AT{/*alignment=*/0, /*byval=*/true});
}
return marshal;
diff --git a/flang/test/Fir/struct-passing-loongarch64-byreg.fir b/flang/test/Fir/struct-passing-loongarch64-byreg.fir
index 576ea6459e17a0..585884504aacf3 100644
--- a/flang/test/Fir/struct-passing-loongarch64-byreg.fir
+++ b/flang/test/Fir/struct-passing-loongarch64-byreg.fir
@@ -23,7 +23,7 @@
!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<2>}>
+!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>}>
diff --git a/flang/test/Fir/struct-return-loongarch64-byreg.fir b/flang/test/Fir/struct-return-loongarch64-byreg.fir
index b64cdc7ac7099f..eb3d4d50d88668 100644
--- a/flang/test/Fir/struct-return-loongarch64-byreg.fir
+++ b/flang/test/Fir/struct-return-loongarch64-byreg.fir
@@ -23,7 +23,7 @@
!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<2>}>
+!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>}>
>From 86f1011dc7c17a89e3bfb242ef85c4f8686bee37 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Thu, 28 Nov 2024 21:27:26 +0800
Subject: [PATCH 5/5] Fixes for review: VectorType ABI handle and others.
Currently, the function `fir::getTypeSizeAndAlignmentOrCrash` does
not yet handle the size and alignment for `fir::VectorType`, but we
are still using it for now. As a result, it will report a TODO message,
and this functionality will be implemented in a future patch.
---
flang/lib/Optimizer/CodeGen/Target.cpp | 94 ++++++++++++--------------
1 file changed, 45 insertions(+), 49 deletions(-)
diff --git a/flang/lib/Optimizer/CodeGen/Target.cpp b/flang/lib/Optimizer/CodeGen/Target.cpp
index b603a9fa82b0bc..185147bc37b234 100644
--- a/flang/lib/Optimizer/CodeGen/Target.cpp
+++ b/flang/lib/Optimizer/CodeGen/Target.cpp
@@ -1157,9 +1157,9 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
/// 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::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) {
@@ -1178,7 +1178,7 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
std::fill_n(std::back_inserter(flatTypes), 2,
cmplx.getElementType());
else
- TODO(loc, "unsupported complx type(not IEEEsingle, IEEEdouble, "
+ TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble, "
"IEEEquad) as a structure component for BIND(C), "
"VALUE derived type argument and type return");
})
@@ -1198,12 +1198,13 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
})
.template Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
if (!seqTy.hasDynamicExtents()) {
- std::size_t numOfEle = seqTy.getConstantArraySize();
+ const std::uint64_t numOfEle = seqTy.getConstantArraySize();
mlir::Type eleTy = seqTy.getEleTy();
if (!mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy)) {
- std::vector<mlir::Type> subTypeList = flattenTypeList(loc, eleTy);
+ llvm::SmallVector<mlir::Type> subTypeList =
+ flattenTypeList(loc, eleTy);
if (subTypeList.size() != 0)
- for (std::size_t i = 0; i < numOfEle; ++i)
+ 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);
@@ -1216,22 +1217,20 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
.template Case<fir::RecordType>([&](fir::RecordType recTy) {
for (auto &component : recTy.getTypeList()) {
mlir::Type eleTy = component.second;
- std::vector<mlir::Type> subTypeList = flattenTypeList(loc, eleTy);
+ 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) {
- std::size_t numOfEle = vecTy.getLen();
- mlir::Type eleTy = vecTy.getEleTy();
- if (!(mlir::isa<mlir::IntegerType, mlir::FloatType>(eleTy))) {
- std::vector<mlir::Type> 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);
- }
+ 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))
@@ -1251,9 +1250,8 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
bool detectFARsEligibleStruct(mlir::Location loc, fir::RecordType recTy,
mlir::Type &field1Ty,
mlir::Type &field2Ty) const {
-
field1Ty = field2Ty = nullptr;
- std::vector<mlir::Type> flatTypes = flattenTypeList(loc, recTy);
+ 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
@@ -1306,9 +1304,9 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
return isFirstAvaliableFloat;
}
- bool checkTypehasEnoughReg(mlir::Location loc, int &GARsLeft, int &FARsLeft,
- const mlir::Type type) const {
- if (type == nullptr)
+ bool checkTypeHasEnoughRegs(mlir::Location loc, int &GARsLeft, int &FARsLeft,
+ const mlir::Type type) const {
+ if (!type)
return true;
llvm::TypeSwitch<mlir::Type>(type)
@@ -1352,7 +1350,6 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
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()) {
@@ -1364,17 +1361,17 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
// Previous aggregate arguments were marshalled into simpler arguments.
const auto &type = std::get<mlir::Type>(typeAndAttr);
- std::vector<mlir::Type> flatTypes = flattenTypeList(loc, type);
+ llvm::SmallVector<mlir::Type> flatTypes = flattenTypeList(loc, type);
for (auto &flatTy : flatTypes) {
- if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, flatTy))
+ if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, flatTy))
return false;
}
}
- if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, field1Ty))
+ if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field1Ty))
return false;
- if (!checkTypehasEnoughReg(loc, GARsLeft, FARsLeft, field2Ty))
+ if (!checkTypeHasEnoughRegs(loc, GARsLeft, FARsLeft, field2Ty))
return false;
return true;
}
@@ -1405,28 +1402,27 @@ struct TargetLoongArch64 : public GenericTarget<TargetLoongArch64> {
// 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 (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) {
More information about the cfe-commits
mailing list