Skip to content

Commit

Permalink
[flang] Use faster path for default formatted character input
Browse files Browse the repository at this point in the history
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
  • Loading branch information
klausler committed Mar 7, 2022
1 parent 6564a70 commit ef7f6f7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 26 deletions.
56 changes: 46 additions & 10 deletions flang/runtime/edit-input.cpp
Expand Up @@ -567,24 +567,60 @@ bool EditDefaultCharacterInput(
if (io.GetConnectionState().IsAtEOF()) {
return false;
}
std::optional<int> 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<std::int64_t>(length)};
while (std::optional<char32_t> 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<std::size_t>(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<std::size_t>(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;
}

Expand Down
35 changes: 20 additions & 15 deletions flang/runtime/io-stmt.cpp
Expand Up @@ -544,25 +544,30 @@ std::optional<char32_t> 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<char32_t>{' '};
}
if (CheckForEndOfRecord()) { // do padding
--*remaining;
return std::optional<char32_t>{' '};
}
}
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(
Expand Down
4 changes: 4 additions & 0 deletions flang/runtime/io-stmt.h
Expand Up @@ -169,6 +169,10 @@ class IoStatementState {
std::optional<char32_t> NextInField(
std::optional<int> &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<char32_t> GetNextNonBlank() {
auto ch{GetCurrentChar()};
Expand Down
3 changes: 2 additions & 1 deletion flang/runtime/unit.cpp
Expand Up @@ -299,7 +299,8 @@ bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
static_cast<std::intmax_t>(*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.
Expand Down

0 comments on commit ef7f6f7

Please sign in to comment.