[flang-commits] [flang] [llvm] [flang][cuda] scalarize CUDA device integer array output (PR #209924)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 15 17:00:50 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Valentin Clement (バレンタイン クレメン) (clementval)
<details>
<summary>Changes</summary>
Avoid lowering formatted integer array output in CUDA device code through `_FortranAioOutputDescriptor`. Instead, scalarize integer array expressions during lowering and emit scalar `_FortranAioOutputInteger*` calls for each element.
This keeps CUDA thin I/O on the scalar runtime ABI and avoids unresolved `_FortranAioOutputDescriptor` references when compiling with `-gpu=nordc`.
---
Full diff: https://github.com/llvm/llvm-project/pull/209924.diff
3 Files Affected:
- (modified) flang-rt/lib/runtime/io-api-minimal.cpp (+2-2)
- (modified) flang/lib/Lower/IO.cpp (+89)
- (added) flang/test/Lower/CUDA/cuda-io-bounds.cuf (+17)
``````````diff
diff --git a/flang-rt/lib/runtime/io-api-minimal.cpp b/flang-rt/lib/runtime/io-api-minimal.cpp
index 299d87179f1a3..1c4ae315e398f 100644
--- a/flang-rt/lib/runtime/io-api-minimal.cpp
+++ b/flang-rt/lib/runtime/io-api-minimal.cpp
@@ -34,10 +34,10 @@ enum Iostat IODEF(EndIoStatement)(Cookie cookie) {
template <int KIND, typename INT = CppTypeFor<TypeCategory::Integer, KIND>>
inline RT_API_ATTRS bool FormattedScalarIntegerOutput(
- IoStatementState &io, INT x, const char *whence) {
+ IoStatementState &io, INT x, const char *whence, bool isSigned = true) {
if (io.CheckFormattedStmtType<Direction::Output>(whence)) {
auto edit{io.GetNextDataEdit()};
- return edit && EditIntegerOutput<KIND>(io, *edit, x, /*isSigned=*/true);
+ return edit && EditIntegerOutput<KIND>(io, *edit, x, isSigned);
} else {
return false;
}
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index c1b148a6ed43c..6fccef159eeda 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -24,9 +24,11 @@
#include "flang/Lower/StatementContext.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Lower/VectorSubscripts.h"
+#include "flang/Optimizer/Builder/CUFCommon.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/Complex.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Builder/Runtime/Stop.h"
#include "flang/Optimizer/Builder/Todo.h"
@@ -702,6 +704,89 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
builder);
}
+static mlir::func::FuncOp getIntegerOutputFunc(mlir::Location loc,
+ fir::FirOpBuilder &builder,
+ mlir::IntegerType type) {
+ if (type.isUnsigned())
+ return {};
+ switch (type.getWidth()) {
+ case 8:
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc,
+ builder);
+ case 16:
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc,
+ builder);
+ case 32:
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc,
+ builder);
+ case 64:
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc,
+ builder);
+ case 128:
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc,
+ builder);
+ }
+ return {};
+}
+
+/// In CUDA device code, keep formatted output on the thin scalar I/O ABI when
+/// an integer array expression would otherwise require OutputDescriptor.
+static bool genCudaDeviceIntegerArrayOutput(
+ Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+ mlir::Value cookie, const Fortran::lower::SomeExpr &expr, mlir::Type itemTy,
+ Fortran::lower::StatementContext &stmtCtx) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ if (!cuf::isCUDADeviceContext(builder.getRegion()))
+ return false;
+ auto seqTy = mlir::dyn_cast<fir::SequenceType>(itemTy);
+ if (!seqTy)
+ return false;
+ auto intTy = mlir::dyn_cast<mlir::IntegerType>(seqTy.getEleTy());
+ if (!intTy)
+ return false;
+ mlir::func::FuncOp outputFunc = getIntegerOutputFunc(loc, builder, intTy);
+ if (!outputFunc)
+ return false;
+
+ fir::ExtendedValue arrayExv = converter.genExprValue(loc, expr, stmtCtx);
+ auto *array = arrayExv.getBoxOf<fir::ArrayBoxValue>();
+ if (!array)
+ return false;
+ mlir::Value base = array->getAddr();
+ if (!base)
+ return false;
+
+ hlfir::LoopNest loopNest =
+ hlfir::genLoopNest(loc, builder, array->getExtents(),
+ /*isUnordered=*/false);
+ builder.setInsertionPointToStart(loopNest.body);
+
+ mlir::Value element;
+ if (mlir::isa<fir::SequenceType>(base.getType())) {
+ llvm::SmallVector<mlir::Value> zeroBasedIndices;
+ mlir::Value one =
+ builder.createIntegerConstant(loc, builder.getIndexType(), 1);
+ for (mlir::Value index : loopNest.oneBasedIndices)
+ zeroBasedIndices.push_back(
+ mlir::arith::SubIOp::create(builder, loc, index, one));
+ element = fir::ArrayFetchOp::create(builder, loc, seqTy.getEleTy(), base,
+ zeroBasedIndices, mlir::ValueRange{});
+ } else {
+ mlir::Value shape = builder.genShape(loc, *array);
+ mlir::Type refTy = fir::ReferenceType::get(seqTy.getEleTy());
+ mlir::Value elementAddr = fir::ArrayCoorOp::create(
+ builder, loc, refTy, base, shape, /*slice=*/mlir::Value{},
+ loopNest.oneBasedIndices, /*typeparams=*/mlir::ValueRange{});
+ element = fir::LoadOp::create(builder, loc, elementAddr);
+ }
+ mlir::Type argType = outputFunc.getFunctionType().getInput(1);
+ element = builder.createConvertWithVolatileCast(loc, argType, element);
+ fir::CallOp::create(builder, loc, outputFunc,
+ mlir::ValueRange{cookie, element});
+ builder.setInsertionPointAfter(loopNest.outerOp);
+ return true;
+}
+
/// Generate a sequence of output data transfer calls.
static void genOutputItemList(
Fortran::lower::AbstractConverter &converter, mlir::Value cookie,
@@ -723,6 +808,10 @@ static void genOutputItemList(
if (!expr)
fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
mlir::Type itemTy = converter.genType(*expr);
+ if (isFormatted && !checkResult &&
+ genCudaDeviceIntegerArrayOutput(converter, loc, cookie, *expr, itemTy,
+ stmtCtx))
+ continue;
mlir::func::FuncOp outputFunc =
getOutputFunc(loc, builder, itemTy, isFormatted);
mlir::Type argType = outputFunc.getFunctionType().getInput(1);
diff --git a/flang/test/Lower/CUDA/cuda-io-bounds.cuf b/flang/test/Lower/CUDA/cuda-io-bounds.cuf
new file mode 100644
index 0000000000000..760d3c9389a0e
--- /dev/null
+++ b/flang/test/Lower/CUDA/cuda-io-bounds.cuf
@@ -0,0 +1,17 @@
+! RUN: bbc -emit-fir -fcuda %s -o - | FileCheck %s --implicit-check-not=_FortranAioOutputDescriptor
+
+module cuda_io_bounds
+contains
+ attributes(global) subroutine bounds(a)
+ real, intent(inout) :: a(:)
+ print *, lbound(a), ubound(a)
+ end subroutine
+end module
+
+! CHECK-LABEL: func.func @_QMcuda_io_boundsPbounds
+! CHECK: fir.do_loop
+! CHECK: fir.call @_FortranAioOutputInteger32
+! CHECK: fir.do_loop
+! CHECK: fir.call @_FortranAioOutputInteger32
+! CHECK: fir.call @_FortranAioEndIoStatement
+! CHECK: func.func private @_FortranAioOutputInteger32
``````````
</details>
https://github.com/llvm/llvm-project/pull/209924
More information about the flang-commits
mailing list