[flang-commits] [flang] [flang] RISCV64 (lp64/lp64d) support for BIND(C) derived types (PR #198335)
Philipp Rados via flang-commits
flang-commits at lists.llvm.org
Tue Sep 22 04:21:54 PDT 2026
================
@@ -1425,6 +1428,316 @@ struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
}
return marshal;
}
+
+ bool hasHardFloatABI(mlir::Location loc) const {
+ auto abi = getTargetABI();
+
+ if (abi == "lp64d")
+ return true;
+ if (abi == "lp64")
+ return false;
+ TODO(loc, "RISCV64 BIND(C) support for " + abi);
+ }
+
+ CodeGenSpecifics::Marshalling
+ passOnTheStack(unsigned short recAlign, mlir::Type ty, bool isResult) const {
+ CodeGenSpecifics::Marshalling marshal;
+ // The stack is always 8 byte aligned
+ unsigned short align = std::max(recAlign, static_cast<unsigned short>(8));
+ marshal.emplace_back(fir::ReferenceType::get(ty),
+ AT{align, /*byval=*/!isResult, /*sret=*/isResult});
+ return marshal;
+ }
+
+ const llvm::SmallVector<mlir::Type>
+ flattenTypeList(mlir::Location loc, const mlir::Type type) const {
+ llvm::SmallVector<mlir::Type> flatTypes;
+
+ llvm::TypeSwitch<mlir::Type>(type)
+ .Case([&](mlir::IntegerType intTy) {
+ if (intTy.getWidth() <= 128)
+ flatTypes.push_back(intTy);
+ else
+ TODO(loc,
+ "integerType with width exceeding 128 bits is unsupported");
+ })
+ .Case([&](mlir::FloatType floatTy) {
+ if (floatTy.getWidth() <= 64)
+ flatTypes.push_back(floatTy);
+ else
+ TODO(loc, "128 bit float is not supported by RISCV64");
+ })
+ .Case([&](mlir::ComplexType cmplx) {
+ const auto *sem = &floatToSemantics(kindMap, cmplx.getElementType());
+ if (sem == &llvm::APFloat::IEEEsingle() ||
+ sem == &llvm::APFloat::IEEEdouble())
+ std::fill_n(std::back_inserter(flatTypes), 2,
+ cmplx.getElementType());
+ else
+ TODO(loc, "unsupported complex type(not IEEEsingle, IEEEdouble"
+ "as a structure component for BIND(C), "
+ "VALUE derived type argument and type return");
+ })
+ .Case([&](fir::LogicalType logicalTy) {
+ const unsigned width =
+ kindMap.getLogicalBitsize(logicalTy.getFKind());
+ flatTypes.push_back(mlir::IntegerType::get(type.getContext(), width));
+ })
+ .Case([&](fir::CharacterType charTy) {
+ if (charTy.getLen() == 1)
+ flatTypes.push_back(mlir::IntegerType::get(type.getContext(), 8));
+ else
+ TODO(loc,
+ "fir.type value arg character components must have length 1");
+ })
+ .Case([&](fir::SequenceType seqTy) {
+ if (!seqTy.hasDynamicExtents()) {
+ const std::uint64_t numOfEle = seqTy.getConstantArraySize();
+ mlir::Type eleTy = seqTy.getEleTy();
+ // Don't check for subtype again if element-type is scalar.
+ if (mlir::isa<mlir::IntegerType, mlir::FloatType, fir::LogicalType>(
+ eleTy)) {
+ std::fill_n(std::back_inserter(flatTypes), numOfEle, eleTy);
+ } else {
+ 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
+ TODO(loc, "unsupported dynamic extent sequence type as a structure "
+ "component for BIND(C), "
+ "VALUE derived type argument and type return");
+ })
+ .Case([&](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));
+ }
+ })
+ .Case([&](fir::VectorType vecTy) {
+ TODO(loc, "passing vector argument to C by value is not supported");
+ })
+ .Default([&](mlir::Type ty) {
+ if (fir::conformsWithPassByRef(ty))
+ flatTypes.push_back(
+ mlir::IntegerType::get(type.getContext(), defaultWidth));
+ else
+ TODO(loc, "unsupported component type for BIND(C), VALUE derived "
+ "type argument and type return");
+ });
+
+ return flatTypes;
+ }
+
+ static bool floatAndCanPassInRegister(const mlir::Type &ty) {
+ return mlir::isa<mlir::FloatType>(ty) &&
+ mlir::cast<mlir::FloatType>(ty).getWidth() <= defaultWidth;
+ }
+
+ static bool integerAndCanPassInRegister(const mlir::Type &ty) {
+ return mlir::isa<mlir::IntegerType>(ty) &&
+ mlir::cast<mlir::IntegerType>(ty).getWidth() <= defaultWidth;
+ }
+
+ void checkAvailableRegisters(mlir::Location loc,
+ const Marshalling &previousArguments,
+ int &gprArgs, int &fprArgs) const {
+ for (auto [ty, attr] : previousArguments) {
+ if (gprArgs <= 0 && fprArgs <= 0)
+ break;
+
+ if (attr.isByVal()) {
+ if (gprArgs)
+ gprArgs--;
+ continue;
+ }
+
+ llvm::TypeSwitch<mlir::Type>(ty)
+ .Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
+ if (gprArgs > 1 && intTy.getWidth() > 64)
+ gprArgs -= 2;
+ else if (gprArgs)
+ gprArgs--;
+ })
+ .Case<mlir::FloatType>([&](mlir::FloatType floatTy) {
+ if (fprArgs)
+ fprArgs--;
+ })
+ .Case<fir::SequenceType>([&](fir::SequenceType seqTy) {
+ auto sizeSeqTy = fir::getTypeSizeAndAlignmentOrCrash(
+ loc, seqTy, getDataLayout(), kindMap)
+ .first;
+ assert((sizeSeqTy <= 2 * defaultWidthBytes) &&
+ "arrays can't be passed by value to bind(c) and "
+ "if array is a record field it was marshalled before");
+
+ if (sizeSeqTy <= defaultWidthBytes && gprArgs) {
+ gprArgs--;
+ return;
+ }
+ if (sizeSeqTy <= 2 * defaultWidthBytes && gprArgs) {
+ // We try to use two registers.
+ if (gprArgs > 1)
+ gprArgs -= 2;
+ else
+ gprArgs--;
+ }
+ })
+ // NOTE: Tuples are only used to marshal result types and so can't
+ // appear in `previousArguments`.
+ .Default([&](mlir::Type ty) {
+ if (fir::conformsWithPassByRef(ty) && gprArgs)
+ gprArgs--;
+ });
+ }
+ }
+
+ CodeGenSpecifics::Marshalling
+ getIntCCArgs(mlir::MLIRContext *context,
+ CodeGenSpecifics::Marshalling marshal, std::uint64_t recordSize,
+ std::uint64_t recordAlign) const {
+ assert(recordSize <= defaultWidthBytes * 2);
+
+ // NOTE: Clang doesn't handle split struct case when only a single register
+ // remains. In general it lets the code generator take care of properly
+ // handling excess integer register usage, do the same for Flang.
+ // For more info see comment in: llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+ if (recordSize <= defaultWidthBytes) {
+ // Pass this as an integer.
+ int width = llvm::PowerOf2Ceil(recordSize * 8);
+ marshal.emplace_back(mlir::IntegerType::get(context, width), AT{});
+ return marshal;
+ }
+
+ // Splitting of large scalars is handled in the backend.
+ if (recordAlign == 2 * defaultWidthBytes) {
+ marshal.emplace_back(mlir::IntegerType::get(context, 2 * defaultWidth),
+ AT{});
+ return marshal;
+ }
+
+ auto intTy = mlir::IntegerType::get(context, defaultWidth);
+ marshal.emplace_back(fir::SequenceType::get({2}, intTy), AT{});
+ return marshal;
+ }
+
+ CodeGenSpecifics::Marshalling
+ classifyStruct(mlir::Location loc, fir::RecordType recTy, int gprArgs,
+ int fprArgs, bool isResult,
+ const Marshalling &previousArguments) const {
+ auto [recordSize, recordAlign] = fir::getTypeSizeAndAlignmentOrCrash(
+ loc, recTy, getDataLayout(), kindMap);
+
+ CodeGenSpecifics::Marshalling marshal;
+ mlir::MLIRContext *context = recTy.getContext();
+
+ // Have to do this first to catch any illegal types in the record.
+ const llvm::SmallVector<mlir::Type> &flattenedTypes =
----------------
prados-oc wrote:
Changed, thanks!
https://github.com/llvm/llvm-project/pull/198335
More information about the flang-commits
mailing list