[flang-commits] [flang] deb62f5 - [flang][runtime] Clean up asynchronous I/O APIs
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Tue May 24 07:55:07 PDT 2022
Author: Peter Klausler
Date: 2022-05-24T07:54:57-07:00
New Revision: deb62f5ad64931c6d47ef3cbf9bb53f4f651b3ae
URL: https://github.com/llvm/llvm-project/commit/deb62f5ad64931c6d47ef3cbf9bb53f4f651b3ae
DIFF: https://github.com/llvm/llvm-project/commit/deb62f5ad64931c6d47ef3cbf9bb53f4f651b3ae.diff
LOG: [flang][runtime] Clean up asynchronous I/O APIs
Now that the requirements and implementation of asynchronous I/O are
better understood, adjust their I/O runtime APIs. In particular:
1) Remove the BeginAsynchronousOutput/Input APIs; they're not needed,
since any data transfer statement might have ASYNCHRONOUS= and
(if ASYNCHRONOUS='YES') ID= control list specifiers that need to
at least be checked.
2) Add implementations for BeginWait(All) to check for the error
case of a bad unit number and nonzero ID=.
3) Rearrange and comment SetAsynchronous so that it's clear that
it can be called for READ/WRITE as well as for OPEN.
The implementation remains completely synchronous, but should be conforming.
Where opportunities make sense for true asynchronous implementations of
some big block transfers without SIZE= in the future, we'll need to add
a GetAsynchronousId API to capture ID= on a READ or WRITE; add sourceFile
and sourceLine arguments to BeginWait(All) for good error reporting;
track pending operations in unit.h; and add code to force synchronization
to non-asynchronous I/O operations.
Lowering should call SetAsynchronous when ASYNCHRONOUS= appears as
a control list specifier. It should also set ID=x variables to 0
until such time as we support asynchronous operations, if ever.
This patch only removes the removed APIs from lowering.
Differential Revision: https://reviews.llvm.org/D126143
Added:
Modified:
flang/include/flang/Runtime/io-api.h
flang/include/flang/Runtime/iostat.h
flang/lib/Lower/IO.cpp
flang/runtime/io-api.cpp
flang/runtime/io-error.h
flang/runtime/io-stmt.h
flang/runtime/iostat.cpp
flang/runtime/unit.h
Removed:
################################################################################
diff --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h
index eca378d71ab80..66dd1a7a18b7b 100644
--- a/flang/include/flang/Runtime/io-api.h
+++ b/flang/include/flang/Runtime/io-api.h
@@ -143,14 +143,9 @@ Cookie IONAME(BeginUnformattedOutput)(ExternalUnit = DefaultUnit,
Cookie IONAME(BeginUnformattedInput)(ExternalUnit = DefaultUnit,
const char *sourceFile = nullptr, int sourceLine = 0);
-// Asynchronous I/O is supported (at most) for unformatted direct access
-// block transfers.
-AsynchronousId IONAME(BeginAsynchronousOutput)(ExternalUnit, std::int64_t REC,
- const char *, std::size_t, const char *sourceFile = nullptr,
- int sourceLine = 0);
-AsynchronousId IONAME(BeginAsynchronousInput)(ExternalUnit, std::int64_t REC,
- char *, std::size_t, const char *sourceFile = nullptr, int sourceLine = 0);
+// WAIT(ID=)
Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId);
+// WAIT(no ID=)
Cookie IONAME(BeginWaitAll)(ExternalUnit);
// Other I/O statements
@@ -199,6 +194,9 @@ Cookie IONAME(BeginInquireIoLength)(
void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false,
bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false);
+// ASYNCHRONOUS='YES' or 'NO' with no ID= on READ/WRITE/OPEN
+bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t);
+
// Control list options. These return false on a error that the
// Begin...() call has specified will be handled by the caller.
// The interfaces that pass a default-kind CHARACTER argument
@@ -270,14 +268,12 @@ bool IONAME(InputNamelist)(Cookie, const NamelistGroup &);
// Additional specifier interfaces for the connection-list of
// on OPEN statement (only). SetBlank(), SetDecimal(),
-// SetDelim(), GetIoMsg(), SetPad(), SetRound(), & SetSign()
-// are also acceptable for OPEN.
+// SetDelim(), GetIoMsg(), SetPad(), SetRound(), SetSign(),
+// & SetAsynchronous() are also acceptable for OPEN.
// ACCESS=SEQUENTIAL, DIRECT, STREAM
bool IONAME(SetAccess)(Cookie, const char *, std::size_t);
// ACTION=READ, WRITE, or READWRITE
bool IONAME(SetAction)(Cookie, const char *, std::size_t);
-// ASYNCHRONOUS=YES, NO
-bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t);
// CARRIAGECONTROL=LIST, FORTRAN, NONE
bool IONAME(SetCarriagecontrol)(Cookie, const char *, std::size_t);
// CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP
@@ -310,6 +306,9 @@ std::size_t IONAME(GetIoLength)(Cookie);
// end-of-record/file condition is present.
void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG=
+// TODO: for ID= on READ/WRITE(ASYNCHRONOUS='YES')
+// int IONAME(GetAsynchronousId)(Cookie);
+
// INQUIRE() specifiers are mostly identified by their NUL-terminated
// case-insensitive names.
// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT,
diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index f09f25a738e2e..3395207468237 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -70,6 +70,8 @@ enum Iostat {
IostatUnitOverflow,
IostatBadRealInput,
IostatBadScaleFactor,
+ IostatBadAsynchronous,
+ IostatBadWaitUnit,
};
const char *IostatErrorString(int);
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 75f23927c69b0..b8f704d448f2e 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -75,8 +75,7 @@ static constexpr std::tuple<
mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginExternalListOutput),
mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput),
mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput),
- mkIOKey(BeginUnformattedInput), mkIOKey(BeginAsynchronousOutput),
- mkIOKey(BeginAsynchronousInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
+ mkIOKey(BeginUnformattedInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace),
mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit),
mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit),
@@ -1784,8 +1783,6 @@ getBeginDataTransferFunc(mlir::Location loc, fir::FirOpBuilder &builder,
bool isFormatted, bool isListOrNml, bool isInternal,
bool isInternalWithDesc, bool isAsync) {
if constexpr (isInput) {
- if (isAsync)
- return getIORuntimeFunc<mkIOKey(BeginAsynchronousInput)>(loc, builder);
if (isFormatted || isListOrNml) {
if (isInternal) {
if (isInternalWithDesc) {
@@ -1808,8 +1805,6 @@ getBeginDataTransferFunc(mlir::Location loc, fir::FirOpBuilder &builder,
}
return getIORuntimeFunc<mkIOKey(BeginUnformattedInput)>(loc, builder);
} else {
- if (isAsync)
- return getIORuntimeFunc<mkIOKey(BeginAsynchronousOutput)>(loc, builder);
if (isFormatted || isListOrNml) {
if (isInternal) {
if (isInternalWithDesc) {
@@ -1873,12 +1868,9 @@ void genBeginDataTransferCallArgs(
getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size())));
ioArgs.push_back( // buffer length
getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size())));
- } else if (isAsync) { // unit; REC; buffer and length
- ioArgs.push_back(getIOUnit(converter, loc, stmt,
- ioFuncTy.getInput(ioArgs.size()), csi,
- stmtCtx));
- TODO(loc, "asynchronous");
} else { // external IO - maybe explicit format; unit
+ if (isAsync)
+ TODO(loc, "asynchronous");
maybeGetFormatArgs();
ioArgs.push_back(getIOUnit(converter, loc, stmt,
ioFuncTy.getInput(ioArgs.size()), csi,
diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index d5ef844ec557c..18e1a075855a7 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -337,6 +337,21 @@ Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j)
unit, false /*was an existing file*/, sourceFile, sourceLine);
}
+Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id) {
+ // TODO: add and use sourceFile & sourceLine here
+ Terminator oom;
+ // TODO: add and use sourceFile & sourceLine here
+ auto &io{
+ New<NoopStatementState>{oom}(nullptr, 0).release()->ioStatementState()};
+ if (id != 0 && !ExternalFileUnit::LookUp(unitNumber)) {
+ io.GetIoErrorHandler().SetPendingError(IostatBadWaitUnit);
+ }
+ return &io;
+}
+Cookie IONAME(BeginWaitAll)(ExternalUnit unitNumber) {
+ return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/);
+}
+
Cookie IONAME(BeginClose)(
ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
if (ExternalFileUnit * unit{ExternalFileUnit::LookUpForClose(unitNumber)}) {
@@ -475,15 +490,14 @@ static bool YesOrNo(const char *keyword, std::size_t length, const char *what,
bool IONAME(SetAdvance)(
Cookie cookie, const char *keyword, std::size_t length) {
IoStatementState &io{*cookie};
- bool nonAdvancing{
- !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())};
+ IoErrorHandler &handler{io.GetIoErrorHandler()};
+ bool nonAdvancing{!YesOrNo(keyword, length, "ADVANCE", handler)};
if (nonAdvancing && io.GetConnectionState().access == Access::Direct) {
- io.GetIoErrorHandler().SignalError(
- "Non-advancing I/O attempted on direct access file");
+ handler.SignalError("Non-advancing I/O attempted on direct access file");
} else {
io.mutableModes().nonAdvancing = nonAdvancing;
}
- return true;
+ return !handler.InError();
}
bool IONAME(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
@@ -543,9 +557,9 @@ bool IONAME(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
bool IONAME(SetPad)(Cookie cookie, const char *keyword, std::size_t length) {
IoStatementState &io{*cookie};
- io.mutableModes().pad =
- YesOrNo(keyword, length, "PAD", io.GetIoErrorHandler());
- return true;
+ IoErrorHandler &handler{io.GetIoErrorHandler()};
+ io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler);
+ return !handler.InError();
}
bool IONAME(SetPos)(Cookie cookie, std::int64_t pos) {
@@ -713,27 +727,23 @@ bool IONAME(SetAction)(Cookie cookie, const char *keyword, std::size_t length) {
bool IONAME(SetAsynchronous)(
Cookie cookie, const char *keyword, std::size_t length) {
IoStatementState &io{*cookie};
- auto *open{io.get_if<OpenStatementState>()};
- if (!open) {
- io.GetIoErrorHandler().Crash(
- "SetAsynchronous() called when not in an OPEN statement");
- } else if (open->completedOperation()) {
- io.GetIoErrorHandler().Crash(
- "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
- }
- static const char *keywords[]{"YES", "NO", nullptr};
- switch (IdentifyValue(keyword, length, keywords)) {
- case 0:
- open->unit().set_mayAsynchronous(true);
- return true;
- case 1:
- open->unit().set_mayAsynchronous(false);
- return true;
- default:
- open->SignalError(IostatErrorInKeyword, "Invalid ASYNCHRONOUS='%.*s'",
- static_cast<int>(length), keyword);
- return false;
+ IoErrorHandler &handler{io.GetIoErrorHandler()};
+ bool isYes{YesOrNo(keyword, length, "ASYNCHRONOUS", handler)};
+ if (auto *open{io.get_if<OpenStatementState>()}) {
+ if (open->completedOperation()) {
+ handler.Crash(
+ "SetAsynchronous() called after GetNewUnit() for an OPEN statement");
+ }
+ open->unit().set_mayAsynchronous(isYes);
+ } else if (ExternalFileUnit * unit{io.GetExternalFileUnit()}) {
+ if (isYes && !unit->mayAsynchronous()) {
+ handler.SignalError(IostatBadAsynchronous);
+ }
+ } else {
+ handler.Crash("SetAsynchronous() called when not in an OPEN or external "
+ "I/O statement");
}
+ return !handler.InError();
}
bool IONAME(SetCarriagecontrol)(
diff --git a/flang/runtime/io-error.h b/flang/runtime/io-error.h
index 4186ee68af084..565e7153351e7 100644
--- a/flang/runtime/io-error.h
+++ b/flang/runtime/io-error.h
@@ -32,9 +32,6 @@ class IoErrorHandler : public Terminator {
void HasEndLabel() { flags_ |= hasEnd; }
void HasEorLabel() { flags_ |= hasEor; }
void HasIoMsg() { flags_ |= hasIoMsg; }
- void HandleAnything() {
- flags_ = hasIoStat | hasErr | hasEnd | hasEor | hasIoMsg;
- }
bool InError() const {
return ioStat_ != IostatOk || pendingError_ != IostatOk;
diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index 4dcc147a8d210..c9bda49458f06 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -582,7 +582,7 @@ class CloseStatementState : public ExternalIoStatementBase {
CloseStatus status_{CloseStatus::Keep};
};
-// For CLOSE(bad unit) and INQUIRE(unconnected unit)
+// For CLOSE(bad unit), WAIT(bad unit, ID=nonzero) and INQUIRE(unconnected unit)
class NoUnitIoStatementState : public IoStatementBase {
public:
IoStatementState &ioStatementState() { return ioStatementState_; }
diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index e375b28a4c734..de9658fd23d2b 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -83,6 +83,11 @@ const char *IostatErrorString(int iostat) {
return "Bad REAL input value";
case IostatBadScaleFactor:
return "Bad REAL output scale factor (kP)";
+ case IostatBadAsynchronous:
+ return "READ/WRITE(ASYNCHRONOUS='YES') on unit without "
+ "OPEN(ASYNCHRONOUS='YES')";
+ case IostatBadWaitUnit:
+ return "WAIT(ID=nonzero) for a bad unit number";
default:
return nullptr;
}
diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index 9b9d78c6a108b..f38f4cac19360 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -124,7 +124,8 @@ class ExternalFileUnit : public ConnectionState,
Lock lock_;
- // When an I/O statement is in progress on this unit, holds its state.
+ // When a synchronous I/O statement is in progress on this unit, holds its
+ // state.
std::variant<std::monostate, OpenStatementState, CloseStatementState,
ExternalFormattedIoStatementState<Direction::Output>,
ExternalFormattedIoStatementState<Direction::Input>,
More information about the flang-commits
mailing list