[flang-commits] [flang] 4393e37 - [flang] Implement READ(SIZE=) and INQUIRE(IOLENGTH=) in runtime

peter klausler via flang-commits flang-commits at lists.llvm.org
Thu Sep 23 10:28:08 PDT 2021


Author: peter klausler
Date: 2021-09-23T10:27:56-07:00
New Revision: 4393e3776b41471afbd37cb13fe5b777243fedd1

URL: https://github.com/llvm/llvm-project/commit/4393e3776b41471afbd37cb13fe5b777243fedd1
DIFF: https://github.com/llvm/llvm-project/commit/4393e3776b41471afbd37cb13fe5b777243fedd1.diff

LOG: [flang] Implement READ(SIZE=) and INQUIRE(IOLENGTH=) in runtime

Count input characters corresponding to formatted edit descriptors
for READ(SIZE=); count output bytes for INQUIRE(IOLENGTH=).

The I/O APIs GetSize() and GetLength() were adjusted to return
std::size_t as function results.

Basic unit tests were added (and others fixed).

Differential Revision: https://reviews.llvm.org/D110291

Added: 
    

Modified: 
    flang/include/flang/Runtime/io-api.h
    flang/runtime/descriptor-io.h
    flang/runtime/edit-input.cpp
    flang/runtime/io-api.cpp
    flang/runtime/io-stmt.cpp
    flang/runtime/io-stmt.h
    flang/runtime/unit.cpp
    flang/unittests/Runtime/ExternalIOTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h
index e6b4617d35772..d6ceec9cab519 100644
--- a/flang/include/flang/Runtime/io-api.h
+++ b/flang/include/flang/Runtime/io-api.h
@@ -280,10 +280,10 @@ bool IONAME(SetFile)(Cookie, const char *, std::size_t chars);
 bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
 
 // READ(SIZE=), after all input items
-bool IONAME(GetSize)(Cookie, std::int64_t, int kind = 8);
+std::size_t IONAME(GetSize)(Cookie);
 
 // INQUIRE(IOLENGTH=), after all output items
-bool IONAME(GetIoLength)(Cookie, std::int64_t, int kind = 8);
+std::size_t IONAME(GetIoLength)(Cookie);
 
 // GetIoMsg() does not modify its argument unless an error or
 // end-of-record/file condition is present.

diff  --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h
index e8cfc333649cc..f2aa6a6aa326b 100644
--- a/flang/runtime/descriptor-io.h
+++ b/flang/runtime/descriptor-io.h
@@ -315,7 +315,9 @@ static bool UnformattedDescriptorIO(
     // Regular derived type unformatted I/O, not user-defined
     auto *externalUnf{io.get_if<ExternalUnformattedIoStatementState<DIR>>()};
     auto *childUnf{io.get_if<ChildUnformattedIoStatementState<DIR>>()};
-    RUNTIME_CHECK(handler, externalUnf != nullptr || childUnf != nullptr);
+    auto *inq{
+        DIR == Direction::Output ? io.get_if<InquireIOLengthState>() : nullptr};
+    RUNTIME_CHECK(handler, externalUnf || childUnf || inq);
     std::size_t elementBytes{descriptor.ElementBytes()};
     std::size_t numElements{descriptor.Elements()};
     SubscriptValue subscripts[maxRank];
@@ -326,7 +328,8 @@ static bool UnformattedDescriptorIO(
                       std::size_t elementBytes) -> bool {
       if constexpr (DIR == Direction::Output) {
         return externalUnf ? externalUnf->Emit(&x, totalBytes, elementBytes)
-                           : childUnf->Emit(&x, totalBytes, elementBytes);
+            : childUnf     ? childUnf->Emit(&x, totalBytes, elementBytes)
+                           : inq->Emit(&x, totalBytes, elementBytes);
       } else {
         return externalUnf ? externalUnf->Receive(&x, totalBytes, elementBytes)
                            : childUnf->Receive(&x, totalBytes, elementBytes);
@@ -363,7 +366,7 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) {
       return false;
     }
   }
-  if (!io.get_if<FormattedIoStatementState>()) {
+  if (!io.get_if<FormattedIoStatementState<DIR>>()) {
     return UnformattedDescriptorIO<DIR>(io, descriptor);
   }
   IoErrorHandler &handler{io.GetIoErrorHandler()};

diff  --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp
index 004c3ca35f05c..139da61418a02 100644
--- a/flang/runtime/edit-input.cpp
+++ b/flang/runtime/edit-input.cpp
@@ -56,6 +56,7 @@ static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit,
   if (next) {
     negative = *next == '-';
     if (negative || *next == '+') {
+      io.GotChar();
       io.SkipSpaces(remaining);
       next = io.NextInField(remaining);
     }
@@ -453,6 +454,7 @@ bool EditDefaultCharacterInput(
        next = io.NextInField(remaining)) {
     if (skip > 0) {
       --skip;
+      io.GotChar(-1);
     } else {
       *x++ = *next;
       --length;

diff  --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 43c59039965e4..bfef8a4e5ae2c 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -925,6 +925,8 @@ bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x,
   if (auto *unf{io.get_if<
           ExternalUnformattedIoStatementState<Direction::Output>>()}) {
     return unf->Emit(x, length, elementBytes);
+  } else if (auto *inq{io.get_if<InquireIOLengthState>()}) {
+    return inq->Emit(x, length, elementBytes);
   }
   io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O "
                                "statement that is not unformatted output");
@@ -1080,6 +1082,27 @@ bool IONAME(InputLogical)(Cookie cookie, bool &truth) {
   return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
 }
 
+std::size_t IONAME(GetSize)(Cookie cookie) {
+  IoStatementState &io{*cookie};
+  if (const auto *formatted{
+          io.get_if<FormattedIoStatementState<Direction::Input>>()}) {
+    return formatted->GetEditDescriptorChars();
+  }
+  io.GetIoErrorHandler().Crash(
+      "GetIoSize() called for an I/O statement that is not a formatted READ()");
+  return 0;
+}
+
+std::size_t IONAME(GetIoLength)(Cookie cookie) {
+  IoStatementState &io{*cookie};
+  if (const auto *inq{io.get_if<InquireIOLengthState>()}) {
+    return inq->bytes();
+  }
+  io.GetIoErrorHandler().Crash("GetIoLength() called for an I/O statement that "
+                               "is not INQUIRE(IOLENGTH=)");
+  return 0;
+}
+
 void IONAME(GetIoMsg)(Cookie cookie, char *msg, std::size_t length) {
   IoErrorHandler &handler{cookie->GetIoErrorHandler()};
   if (handler.InError()) { // leave "msg" alone when no error

diff  --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 94de61c509530..57e484c6c1e9b 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -521,6 +521,7 @@ std::optional<char32_t> IoStatementState::SkipSpaces(
       }
       HandleRelativePosition(1);
       if (remaining) {
+        GotChar();
         --*remaining;
       }
     } else {
@@ -556,6 +557,7 @@ std::optional<char32_t> IoStatementState::NextInField(
     if (auto next{GetCurrentChar()}) {
       --*remaining;
       HandleRelativePosition(1);
+      GotChar();
       return next;
     }
     const ConnectionState &connection{GetConnectionState()};
@@ -610,6 +612,25 @@ bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
   return std::visit([&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
 }
 
+void IoStatementState::GotChar(int n) {
+  if (auto *formattedIn{
+          get_if<FormattedIoStatementState<Direction::Input>>()}) {
+    formattedIn->GotChar(n);
+  } else {
+    GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
+                              "statement that is not formatted input");
+  }
+}
+
+std::size_t
+FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {
+  return chars_;
+}
+
+void FormattedIoStatementState<Direction::Input>::GotChar(int n) {
+  chars_ += n;
+}
+
 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
     IoStatementState &io, std::size_t length, bool isCharacter) {
   if (length == 0) {
@@ -1325,4 +1346,25 @@ InquireIOLengthState::InquireIOLengthState(
     const char *sourceFile, int sourceLine)
     : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
 
+bool InquireIOLengthState::Emit(
+    const char *, std::size_t n, std::size_t elementBytes) {
+  bytes_ += n * elementBytes;
+  return true;
+}
+
+bool InquireIOLengthState::Emit(const char *p, std::size_t n) {
+  bytes_ += sizeof *p * n;
+  return true;
+}
+
+bool InquireIOLengthState::Emit(const char16_t *p, std::size_t n) {
+  bytes_ += sizeof *p * n;
+  return true;
+}
+
+bool InquireIOLengthState::Emit(const char32_t *p, std::size_t n) {
+  bytes_ += sizeof *p * n;
+  return true;
+}
+
 } // namespace Fortran::runtime::io

diff  --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index dfaed3eaf2c0b..24bbaf1923643 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -52,7 +52,19 @@ struct OutputStatementState {};
 template <Direction D>
 using IoDirectionState = std::conditional_t<D == Direction::Input,
     InputStatementState, OutputStatementState>;
-struct FormattedIoStatementState {};
+
+// Common state for all kinds of formatted I/O
+template <Direction D> class FormattedIoStatementState {};
+template <> class FormattedIoStatementState<Direction::Input> {
+public:
+  std::size_t GetEditDescriptorChars() const;
+  void GotChar(int);
+
+private:
+  // Account of characters read for edit descriptors (i.e., formatted I/O
+  // with a FORMAT, not list-directed or NAMELIST), not including padding.
+  std::size_t chars_{0}; // for READ(SIZE=)
+};
 
 // The Cookie type in the I/O API is a pointer (for C) to this class.
 class IoStatementState {
@@ -83,6 +95,7 @@ class IoStatementState {
   bool Inquire(InquiryKeywordHash, bool &);
   bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING=
   bool Inquire(InquiryKeywordHash, std::int64_t &);
+  void GotChar(signed int = 1); // for READ(SIZE=); can be <0
 
   MutableModes &mutableModes();
   ConnectionState &GetConnectionState();
@@ -115,8 +128,7 @@ class IoStatementState {
   std::optional<char32_t> GetNextNonBlank();
 
   template <Direction D> void CheckFormattedStmtType(const char *name) {
-    if (!get_if<FormattedIoStatementState>() ||
-        !get_if<IoDirectionState<D>>()) {
+    if (!get_if<FormattedIoStatementState<D>>()) {
       GetIoErrorHandler().Crash(
           "%s called for I/O statement that is not formatted %s", name,
           D == Direction::Output ? "output" : "input");
@@ -191,7 +203,7 @@ struct IoStatementBase : public IoErrorHandler {
 template <Direction> class ListDirectedStatementState;
 template <>
 class ListDirectedStatementState<Direction::Output>
-    : public FormattedIoStatementState {
+    : public FormattedIoStatementState<Direction::Output> {
 public:
   bool EmitLeadingSpaceOrAdvance(
       IoStatementState &, std::size_t = 1, bool isCharacter = false);
@@ -209,7 +221,7 @@ class ListDirectedStatementState<Direction::Output>
 };
 template <>
 class ListDirectedStatementState<Direction::Input>
-    : public FormattedIoStatementState {
+    : public FormattedIoStatementState<Direction::Input> {
 public:
   // Skips value separators, handles repetition and null values.
   // Vacant when '/' appears; present with descriptor == ListDirectedNullValue
@@ -269,7 +281,7 @@ class InternalIoStatementState : public IoStatementBase,
 template <Direction DIR, typename CHAR>
 class InternalFormattedIoStatementState
     : public InternalIoStatementState<DIR, CHAR>,
-      public FormattedIoStatementState {
+      public FormattedIoStatementState<DIR> {
 public:
   using CharType = CHAR;
   using typename InternalIoStatementState<DIR, CharType>::Buffer;
@@ -353,8 +365,9 @@ class ExternalIoStatementState : public ExternalIoStatementBase,
 };
 
 template <Direction DIR, typename CHAR>
-class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
-                                          public FormattedIoStatementState {
+class ExternalFormattedIoStatementState
+    : public ExternalIoStatementState<DIR>,
+      public FormattedIoStatementState<DIR> {
 public:
   using CharType = CHAR;
   ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
@@ -411,7 +424,7 @@ class ChildIoStatementState : public IoStatementBase,
 
 template <Direction DIR, typename CHAR>
 class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
-                                       public FormattedIoStatementState {
+                                       public FormattedIoStatementState<DIR> {
 public:
   using CharType = CHAR;
   ChildFormattedIoStatementState(ChildIo &, const CharType *format,
@@ -584,6 +597,10 @@ class InquireIOLengthState : public NoUnitIoStatementState,
 public:
   InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0);
   std::size_t bytes() const { return bytes_; }
+  bool Emit(const char *, std::size_t, std::size_t elementBytes);
+  bool Emit(const char *, std::size_t);
+  bool Emit(const char16_t *, std::size_t chars);
+  bool Emit(const char32_t *, std::size_t chars);
 
 private:
   std::size_t bytes_{0};

diff  --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index b823789ea0887..baeb54efaaa68 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -768,8 +768,13 @@ void ChildIo::EndIoStatement() {
 
 bool ChildIo::CheckFormattingAndDirection(Terminator &terminator,
     const char *what, bool unformatted, Direction direction) {
-  bool parentIsUnformatted{!parent_.get_if<FormattedIoStatementState>()};
   bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
+  bool parentIsFormatted{parentIsInput
+          ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
+              nullptr
+          : parent_.get_if<FormattedIoStatementState<Direction::Output>>() !=
+              nullptr};
+  bool parentIsUnformatted{!parentIsFormatted};
   if (unformatted != parentIsUnformatted) {
     terminator.Crash("Child %s attempted on %s parent I/O unit", what,
         parentIsUnformatted ? "unformatted" : "formatted");

diff  --git a/flang/unittests/Runtime/ExternalIOTest.cpp b/flang/unittests/Runtime/ExternalIOTest.cpp
index 0b2accf43496e..13347a7730af8 100644
--- a/flang/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang/unittests/Runtime/ExternalIOTest.cpp
@@ -41,6 +41,15 @@ TEST(ExternalIOTests, TestDirectUnformatted) {
   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
       << "EndIoStatement() for OpenNewUnit";
 
+  // INQUIRE(IOLENGTH=) j
+  io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
+  ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
+      io, reinterpret_cast<const char *>(&buffer), 1, recl))
+      << "OutputUnformattedBlock() for InquireIoLength";
+  ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for InquireIoLength";
+
   static constexpr int records{10};
   for (int j{1}; j <= records; ++j) {
     // WRITE(UNIT=unit,REC=j) j
@@ -49,7 +58,7 @@ TEST(ExternalIOTests, TestDirectUnformatted) {
 
     buffer = j;
     ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
-        io, reinterpret_cast<const char *>(&buffer), recl, recl))
+        io, reinterpret_cast<const char *>(&buffer), 1, recl))
         << "OutputUnformattedBlock()";
 
     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
@@ -61,7 +70,7 @@ TEST(ExternalIOTests, TestDirectUnformatted) {
     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
     ASSERT_TRUE(IONAME(InputUnformattedBlock)(
-        io, reinterpret_cast<char *>(&buffer), recl, recl))
+        io, reinterpret_cast<char *>(&buffer), 1, recl))
         << "InputUnformattedBlock()";
 
     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
@@ -158,6 +167,17 @@ TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
       << "EndIoStatement() for OpenNewUnit";
 
+  // INQUIRE(IOLENGTH=) j, ...
+  io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
+  for (int j{1}; j <= 3; ++j) {
+    ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
+        io, reinterpret_cast<const char *>(&buffer), 1, recl))
+        << "OutputUnformattedBlock() for InquireIoLength";
+  }
+  ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for InquireIoLength";
+
   static const int records{10};
   for (int j{1}; j <= records; ++j) {
     // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
@@ -417,7 +437,7 @@ TEST(ExternalIOTests, TestSequentialVariableFormatted) {
         << "EndIoStatement() for Backspace (before read)";
 
     std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
-    // READ(UNIT=unit,FMT=fmt) n; check
+    // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
     io = IONAME(BeginExternalFormattedInput)(
         fmt, std::strlen(fmt), unit, __FILE__, __LINE__);
 
@@ -426,6 +446,9 @@ TEST(ExternalIOTests, TestSequentialVariableFormatted) {
       ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
     }
 
+    std::size_t chars{IONAME(GetSize)(io)};
+    ASSERT_EQ(chars, j * 4u)
+        << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
         << "EndIoStatement() for InputInteger";
     for (int k{0}; k < j; ++k) {


        


More information about the flang-commits mailing list