From ef7f6f7cd7f7956c8bebaf8b29a9895ebbd35af2 Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Wed, 23 Feb 2022 11:45:21 -0800 Subject: [PATCH] [flang] Use faster path for default formatted character input Rather than reading default character variables in formatted input one byte at a time via NextInField(), skip and read them via blocks of available buffer data. This eliminates a bottleneck that affected reads of large character values. (It also exposed a problem with sequential reads with RECL= set on the OPEN statement, so that's fixed too.) Differential Revision: https://reviews.llvm.org/D121144 --- flang/runtime/edit-input.cpp | 56 +++++++++++++++++++++++++++++------- flang/runtime/io-stmt.cpp | 35 ++++++++++++---------- flang/runtime/io-stmt.h | 4 +++ flang/runtime/unit.cpp | 3 +- 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp index 2e9cd80aa6f9a..ee35bd4c76cde 100644 --- a/flang/runtime/edit-input.cpp +++ b/flang/runtime/edit-input.cpp @@ -567,24 +567,60 @@ bool EditDefaultCharacterInput( if (io.GetConnectionState().IsAtEOF()) { return false; } - std::optional remaining{length}; + std::size_t remaining{length}; if (edit.width && *edit.width > 0) { remaining = *edit.width; } // When the field is wider than the variable, we drop the leading // characters. When the variable is wider than the field, there's // trailing padding. - std::int64_t skip{*remaining - static_cast(length)}; - while (std::optional next{io.NextInField(remaining, edit)}) { - if (skip > 0) { - --skip; - io.GotChar(-1); - } else { - *x++ = *next; - --length; + const char *input{nullptr}; + std::size_t ready{0}; + bool hitEnd{false}; + if (remaining > length) { + // Discard leading bytes. + // These bytes don't count towards INQUIRE(IOLENGTH=). + std::size_t skip{remaining - length}; + do { + if (ready == 0) { + ready = io.GetNextInputBytes(input); + if (ready == 0) { + hitEnd = true; + break; + } + } + std::size_t chunk{std::min(skip, ready)}; + io.HandleRelativePosition(chunk); + ready -= chunk; + input += chunk; + skip -= chunk; + } while (skip > 0); + remaining = length; + } + // Transfer payload bytes; these do count. + while (remaining > 0) { + if (ready == 0) { + ready = io.GetNextInputBytes(input); + if (ready == 0) { + hitEnd = true; + break; + } } + std::size_t chunk{std::min(remaining, ready)}; + std::memcpy(x, input, chunk); + x += chunk; + input += chunk; + io.GotChar(chunk); + io.HandleRelativePosition(chunk); + ready -= chunk; + remaining -= chunk; + length -= chunk; + } + // Pad the remainder of the input variable, if any. + std::memset(x, ' ', length); + if (hitEnd) { + io.CheckForEndOfRecord(); // signal any needed error } - std::fill_n(x, length, ' '); return true; } diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp index 4f47e36fe78f7..b86559631d1db 100644 --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -544,25 +544,30 @@ std::optional IoStatementState::NextInField( GotChar(); return next; } - const ConnectionState &connection{GetConnectionState()}; - if (!connection.IsAtEOF()) { - if (auto length{connection.EffectiveRecordLength()}) { - if (connection.positionInRecord >= *length) { - IoErrorHandler &handler{GetIoErrorHandler()}; - if (mutableModes().nonAdvancing) { - handler.SignalEor(); - } else if (connection.openRecl && !connection.modes.pad) { - handler.SignalError(IostatRecordReadOverrun); - } - if (connection.modes.pad) { // PAD='YES' - --*remaining; - return std::optional{' '}; - } + if (CheckForEndOfRecord()) { // do padding + --*remaining; + return std::optional{' '}; + } + } + return std::nullopt; +} + +bool IoStatementState::CheckForEndOfRecord() { + const ConnectionState &connection{GetConnectionState()}; + if (!connection.IsAtEOF()) { + if (auto length{connection.EffectiveRecordLength()}) { + if (connection.positionInRecord >= *length) { + IoErrorHandler &handler{GetIoErrorHandler()}; + if (mutableModes().nonAdvancing) { + handler.SignalEor(); + } else if (connection.openRecl && !connection.modes.pad) { + handler.SignalError(IostatRecordReadOverrun); } + return connection.modes.pad; // PAD='YES' } } } - return std::nullopt; + return false; } bool IoStatementState::Inquire( diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h index 75cf327f05d7c..fef1e261cde29 100644 --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -169,6 +169,10 @@ class IoStatementState { std::optional NextInField( std::optional &remaining, const DataEdit &); + // Detect and signal any end-of-record condition after input. + // Returns true if at EOR and remaining input should be padded with blanks. + bool CheckForEndOfRecord(); + // Skips spaces, advances records, and ignores NAMELIST comments std::optional GetNextNonBlank() { auto ch{GetCurrentChar()}; diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp index 6e6db78592783..cef971d00c740 100644 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -299,7 +299,8 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes, static_cast(*openRecl)); return false; } - } else if (recordLength) { + } + if (recordLength) { // It is possible for recordLength to have a value now for a // variable-length output record if the previous operation // was a BACKSPACE or non advancing input statement.