[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