[flang-commits] [flang] [llvm] [flang][runtime] Formatted input optimizations (PR #134715)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Tue Apr 8 11:10:47 PDT 2025
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/134715
>From a49a1fa3b4b4d72944b9cb61dfc3f156ee307343 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Fri, 4 Apr 2025 14:59:01 -0700
Subject: [PATCH] [flang][runtime] Formatted input optimizations
Make some minor tweaks (inlining, caching) to the formatting input
path to improve integer input in a SPEC code. (None of the I/O
library has been tuned yet for performance, and there are some
easy optimizations for common cases.) Input integer values are
now calculated with native C/C++ 128-bit integers.
A benchmark that only reads about 5M lines of three integer values each
speeds up from over 8 seconds to under 3 in my environment with these
changeds.
If this works out, the code here can be used to optimize the formatted
input paths for real and character data, too.
Fixes https://github.com/llvm/llvm-project/issues/134026.
---
.../include/flang-rt/runtime/connection.h | 33 +++--
flang-rt/include/flang-rt/runtime/io-stmt.h | 117 +++++++++++++++---
flang-rt/lib/runtime/connection.cpp | 27 ----
flang-rt/lib/runtime/edit-input.cpp | 39 ++++--
flang-rt/lib/runtime/io-api.cpp | 15 +--
flang-rt/lib/runtime/io-stmt.cpp | 70 +++++++----
flang-rt/lib/runtime/unit.cpp | 10 +-
flang/include/flang/Common/uint128.h | 2 +
8 files changed, 219 insertions(+), 94 deletions(-)
diff --git a/flang-rt/include/flang-rt/runtime/connection.h b/flang-rt/include/flang-rt/runtime/connection.h
index 03d9658e7067b..601669942cdd6 100644
--- a/flang-rt/include/flang-rt/runtime/connection.h
+++ b/flang-rt/include/flang-rt/runtime/connection.h
@@ -45,19 +45,36 @@ struct ConnectionAttributes {
};
struct ConnectionState : public ConnectionAttributes {
- RT_API_ATTRS bool
- IsAtEOF() const; // true when read has hit EOF or endfile record
- RT_API_ATTRS bool
- IsAfterEndfile() const; // true after ENDFILE until repositioned
+ RT_API_ATTRS bool IsAtEOF() const {
+ // true when read has hit EOF or endfile record
+ return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber;
+ }
+ RT_API_ATTRS bool IsAfterEndfile() const {
+ // true after ENDFILE until repositioned
+ return endfileRecordNumber && currentRecordNumber > *endfileRecordNumber;
+ }
// All positions and measurements are always in units of bytes,
// not characters. Multi-byte character encodings are possible in
// both internal I/O (when the character kind of the variable is 2 or 4)
// and external formatted I/O (when the encoding is UTF-8).
- RT_API_ATTRS std::size_t RemainingSpaceInRecord() const;
- RT_API_ATTRS bool NeedAdvance(std::size_t) const;
- RT_API_ATTRS void HandleAbsolutePosition(std::int64_t);
- RT_API_ATTRS void HandleRelativePosition(std::int64_t);
+ RT_API_ATTRS std::size_t RemainingSpaceInRecord() const {
+ auto recl{recordLength.value_or(openRecl.value_or(
+ executionEnvironment.listDirectedOutputLineLengthLimit))};
+ return positionInRecord >= recl ? 0 : recl - positionInRecord;
+ }
+ RT_API_ATTRS bool NeedAdvance(std::size_t width) const {
+ return positionInRecord > 0 && width > RemainingSpaceInRecord();
+ }
+ RT_API_ATTRS void HandleAbsolutePosition(std::int64_t n) {
+ positionInRecord = (n < 0 ? 0 : n) + leftTabLimit.value_or(0);
+ }
+ RT_API_ATTRS void HandleRelativePosition(std::int64_t n) {
+ auto least{leftTabLimit.value_or(0)};
+ auto newPos{positionInRecord + n};
+ positionInRecord = newPos < least ? least : newPos;
+ ;
+ }
RT_API_ATTRS void BeginRecord() {
positionInRecord = 0;
diff --git a/flang-rt/include/flang-rt/runtime/io-stmt.h b/flang-rt/include/flang-rt/runtime/io-stmt.h
index 33cc91271ab12..c9c193335c940 100644
--- a/flang-rt/include/flang-rt/runtime/io-stmt.h
+++ b/flang-rt/include/flang-rt/runtime/io-stmt.h
@@ -130,20 +130,94 @@ class IoStatementState {
}
// Vacant after the end of the current record
- RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentChar(
+ RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentCharSlow(
std::size_t &byteCount);
+ // For faster formatted input editing, this structure can be built by
+ // GetUpcomingFastAsciiField() and used to save significant time in
+ // GetCurrentChar, NextInField() and other input utilities when the input
+ // is buffered, does not require UTF-8 conversion, and comprises only
+ // single byte characters.
+ class FastAsciiField {
+ public:
+ RT_API_ATTRS FastAsciiField(ConnectionState &connection)
+ : connection_{connection} {}
+ RT_API_ATTRS FastAsciiField(
+ ConnectionState &connection, const char *start, std::size_t bytes)
+ : connection_{connection}, at_{start}, limit_{start + bytes} {
+ CheckForAsterisk();
+ }
+ RT_API_ATTRS ConnectionState &connection() { return connection_; }
+ RT_API_ATTRS std::size_t got() const { return got_; }
+
+ RT_API_ATTRS bool MustUseSlowPath() const { return at_ == nullptr; }
+
+ RT_API_ATTRS Fortran::common::optional<char32_t> Next() const {
+ if (at_ && at_ < limit_) {
+ return *at_;
+ } else {
+ return std::nullopt;
+ }
+ }
+ RT_API_ATTRS void NextRecord(IoStatementState &io) {
+ if (at_) {
+ if (std::size_t bytes{io.GetNextInputBytes(at_)}) {
+ limit_ = at_ + bytes;
+ CheckForAsterisk();
+ } else {
+ at_ = limit_ = nullptr;
+ }
+ }
+ }
+ RT_API_ATTRS void Advance(int gotten, std::size_t bytes) {
+ if (at_ && at_ < limit_) {
+ ++at_;
+ got_ += gotten;
+ }
+ connection_.HandleRelativePosition(bytes);
+ }
+ RT_API_ATTRS bool MightHaveAsterisk() const { return !at_ || hasAsterisk_; }
+
+ private:
+ RT_API_ATTRS void CheckForAsterisk() {
+ hasAsterisk_ =
+ at_ && at_ < limit_ && std::memchr(at_, '*', limit_ - at_) != nullptr;
+ }
+
+ ConnectionState &connection_;
+ const char *at_{nullptr};
+ const char *limit_{nullptr};
+ std::size_t got_{0}; // for READ(..., SIZE=)
+ bool hasAsterisk_{false};
+ };
+
+ RT_API_ATTRS FastAsciiField GetUpcomingFastAsciiField();
+
+ RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentChar(
+ std::size_t &byteCount, FastAsciiField *field = nullptr) {
+ if (field) {
+ if (auto ch{field->Next()}) {
+ byteCount = ch ? 1 : 0;
+ return ch;
+ } else if (!field->MustUseSlowPath()) {
+ return std::nullopt;
+ }
+ }
+ return GetCurrentCharSlow(byteCount);
+ }
+
// The result of CueUpInput() and the "remaining" arguments to SkipSpaces()
// and NextInField() are always in units of bytes, not characters; the
// distinction matters for internal input from CHARACTER(KIND=2 and 4).
// For fixed-width fields, return the number of remaining bytes.
// Skip over leading blanks.
- RT_API_ATTRS Fortran::common::optional<int> CueUpInput(const DataEdit &edit) {
+ RT_API_ATTRS Fortran::common::optional<int> CueUpInput(
+ const DataEdit &edit, FastAsciiField *fastField = nullptr) {
Fortran::common::optional<int> remaining;
if (edit.IsListDirected()) {
std::size_t byteCount{0};
- GetNextNonBlank(byteCount);
+ GetNextNonBlank(byteCount, fastField);
} else {
if (edit.width.value_or(0) > 0) {
remaining = *edit.width;
@@ -152,16 +226,17 @@ class IoStatementState {
*remaining *= bytesPerChar;
}
}
- SkipSpaces(remaining);
+ SkipSpaces(remaining, fastField);
}
return remaining;
}
RT_API_ATTRS Fortran::common::optional<char32_t> SkipSpaces(
- Fortran::common::optional<int> &remaining) {
+ Fortran::common::optional<int> &remaining,
+ FastAsciiField *fastField = nullptr) {
while (!remaining || *remaining > 0) {
std::size_t byteCount{0};
- if (auto ch{GetCurrentChar(byteCount)}) {
+ if (auto ch{GetCurrentChar(byteCount, fastField)}) {
if (*ch != ' ' && *ch != '\t') {
return ch;
}
@@ -172,7 +247,11 @@ class IoStatementState {
GotChar(byteCount);
*remaining -= byteCount;
}
- HandleRelativePosition(byteCount);
+ if (fastField) {
+ fastField->Advance(0, byteCount);
+ } else {
+ HandleRelativePosition(byteCount);
+ }
} else {
break;
}
@@ -183,25 +262,35 @@ class IoStatementState {
// Acquires the next input character, respecting any applicable field width
// or separator character.
RT_API_ATTRS Fortran::common::optional<char32_t> NextInField(
- Fortran::common::optional<int> &remaining, const DataEdit &);
+ Fortran::common::optional<int> &remaining, const DataEdit &,
+ FastAsciiField *field = nullptr);
// Detect and signal any end-of-record condition after input.
// Returns true if at EOR and remaining input should be padded with blanks.
- RT_API_ATTRS bool CheckForEndOfRecord(std::size_t afterReading);
+ RT_API_ATTRS bool CheckForEndOfRecord(
+ std::size_t afterReading, const ConnectionState &);
// Skips spaces, advances records, and ignores NAMELIST comments
RT_API_ATTRS Fortran::common::optional<char32_t> GetNextNonBlank(
- std::size_t &byteCount) {
- auto ch{GetCurrentChar(byteCount)};
+ std::size_t &byteCount, FastAsciiField *fastField = nullptr) {
+ auto ch{GetCurrentChar(byteCount, fastField)};
bool inNamelist{mutableModes().inNamelist};
while (!ch || *ch == ' ' || *ch == '\t' || *ch == '\n' ||
(inNamelist && *ch == '!')) {
if (ch && (*ch == ' ' || *ch == '\t' || *ch == '\n')) {
- HandleRelativePosition(byteCount);
- } else if (!AdvanceRecord()) {
+ if (fastField) {
+ fastField->Advance(0, byteCount);
+ } else {
+ HandleRelativePosition(byteCount);
+ }
+ } else if (AdvanceRecord()) {
+ if (fastField) {
+ fastField->NextRecord(*this);
+ }
+ } else {
return Fortran::common::nullopt;
}
- ch = GetCurrentChar(byteCount);
+ ch = GetCurrentChar(byteCount, fastField);
}
return ch;
}
diff --git a/flang-rt/lib/runtime/connection.cpp b/flang-rt/lib/runtime/connection.cpp
index 2f01dbbb95920..2e02717f5425b 100644
--- a/flang-rt/lib/runtime/connection.cpp
+++ b/flang-rt/lib/runtime/connection.cpp
@@ -9,37 +9,10 @@
#include "flang-rt/runtime/connection.h"
#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/io-stmt.h"
-#include <algorithm>
namespace Fortran::runtime::io {
RT_OFFLOAD_API_GROUP_BEGIN
-RT_API_ATTRS std::size_t ConnectionState::RemainingSpaceInRecord() const {
- auto recl{recordLength.value_or(openRecl.value_or(
- executionEnvironment.listDirectedOutputLineLengthLimit))};
- return positionInRecord >= recl ? 0 : recl - positionInRecord;
-}
-
-RT_API_ATTRS bool ConnectionState::NeedAdvance(std::size_t width) const {
- return positionInRecord > 0 && width > RemainingSpaceInRecord();
-}
-
-RT_API_ATTRS bool ConnectionState::IsAtEOF() const {
- return endfileRecordNumber && currentRecordNumber >= *endfileRecordNumber;
-}
-
-RT_API_ATTRS bool ConnectionState::IsAfterEndfile() const {
- return endfileRecordNumber && currentRecordNumber > *endfileRecordNumber;
-}
-
-RT_API_ATTRS void ConnectionState::HandleAbsolutePosition(std::int64_t n) {
- positionInRecord = std::max(n, std::int64_t{0}) + leftTabLimit.value_or(0);
-}
-
-RT_API_ATTRS void ConnectionState::HandleRelativePosition(std::int64_t n) {
- positionInRecord = std::max(leftTabLimit.value_or(0), positionInRecord + n);
-}
-
SavedPosition::SavedPosition(IoStatementState &io) : io_{io} {
ConnectionState &conn{io_.GetConnectionState()};
saved_ = conn;
diff --git a/flang-rt/lib/runtime/edit-input.cpp b/flang-rt/lib/runtime/edit-input.cpp
index 99a266648f95c..0a7b09f836c7d 100644
--- a/flang-rt/lib/runtime/edit-input.cpp
+++ b/flang-rt/lib/runtime/edit-input.cpp
@@ -169,17 +169,18 @@ static inline RT_API_ATTRS char32_t GetRadixPointChar(const DataEdit &edit) {
// Prepares input from a field, and returns the sign, if any, else '\0'.
static RT_API_ATTRS char ScanNumericPrefix(IoStatementState &io,
const DataEdit &edit, Fortran::common::optional<char32_t> &next,
- Fortran::common::optional<int> &remaining) {
- remaining = io.CueUpInput(edit);
- next = io.NextInField(remaining, edit);
+ Fortran::common::optional<int> &remaining,
+ IoStatementState::FastAsciiField *fastField = nullptr) {
+ remaining = io.CueUpInput(edit, fastField);
+ next = io.NextInField(remaining, edit, fastField);
char sign{'\0'};
if (next) {
if (*next == '-' || *next == '+') {
sign = *next;
if (!edit.IsListDirected()) {
- io.SkipSpaces(remaining);
+ io.SkipSpaces(remaining, fastField);
}
- next = io.NextInField(remaining, edit);
+ next = io.NextInField(remaining, edit, fastField);
}
}
return sign;
@@ -213,17 +214,18 @@ RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
}
Fortran::common::optional<int> remaining;
Fortran::common::optional<char32_t> next;
- char sign{ScanNumericPrefix(io, edit, next, remaining)};
+ auto fastField{io.GetUpcomingFastAsciiField()};
+ char sign{ScanNumericPrefix(io, edit, next, remaining, &fastField)};
if (sign == '-' && !isSigned) {
io.GetIoErrorHandler().SignalError("Negative sign in UNSIGNED input field");
return false;
}
- common::UnsignedInt128 value{0};
+ common::uint128_t value{0};
bool any{!!sign};
bool overflow{false};
const char32_t comma{GetSeparatorChar(edit)};
- static constexpr auto maxu128{~common::UnsignedInt128{0}};
- for (; next; next = io.NextInField(remaining, edit)) {
+ static constexpr auto maxu128{~common::uint128_t{0}};
+ for (; next; next = io.NextInField(remaining, edit, &fastField)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
if (edit.modes.editingFlags & blankZero) {
@@ -243,7 +245,7 @@ RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
// input, like a few other Fortran compilers do.
// TODO: also process exponents? Some compilers do, but they obviously
// can't just be ignored.
- while ((next = io.NextInField(remaining, edit))) {
+ while ((next = io.NextInField(remaining, edit, &fastField))) {
if (*next < '0' || *next > '9') {
break;
}
@@ -271,7 +273,7 @@ RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
return false;
}
if (isSigned) {
- auto maxForKind{common::UnsignedInt128{1} << ((8 * kind) - 1)};
+ auto maxForKind{common::uint128_t{1} << ((8 * kind) - 1)};
overflow |= value >= maxForKind && (value > maxForKind || sign != '-');
} else {
auto maxForKind{maxu128 >> (((16 - kind) * 8) + (isSigned ? 1 : 0))};
@@ -287,7 +289,16 @@ RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
}
if (any || !io.GetIoErrorHandler().InError()) {
// The value is stored in the lower order bits on big endian platform.
- // When memcpy, shift the value to the higher order bit.
+ // For memcpy, shift the value to the highest order bits.
+#if USING_NATIVE_INT128_T
+ auto shft{static_cast<int>(sizeof value - kind)};
+ if (!isHostLittleEndian && shft >= 0) {
+ auto l{value << shft};
+ std::memcpy(n, &l, kind);
+ } else {
+ std::memcpy(n, &value, kind); // a blank field means zero
+ }
+#else
auto shft{static_cast<int>(sizeof(value.low())) - kind};
// For kind==8 (i.e. shft==0), the value is stored in low_ in big endian.
if (!isHostLittleEndian && shft >= 0) {
@@ -296,6 +307,8 @@ RT_API_ATTRS bool EditIntegerInput(IoStatementState &io, const DataEdit &edit,
} else {
std::memcpy(n, &value, kind); // a blank field means zero
}
+#endif
+ io.GotChar(fastField.got());
return true;
} else {
return false;
@@ -1070,7 +1083,7 @@ RT_API_ATTRS bool EditCharacterInput(IoStatementState &io, const DataEdit &edit,
readyBytes = io.GetNextInputBytes(input);
if (readyBytes == 0 ||
(readyBytes < remainingChars && edit.modes.nonAdvancing)) {
- if (io.CheckForEndOfRecord(readyBytes)) {
+ if (io.CheckForEndOfRecord(readyBytes, connection)) {
if (readyBytes == 0) {
// PAD='YES' and no more data
Fortran::runtime::fill_n(x, lengthChars, ' ');
diff --git a/flang-rt/lib/runtime/io-api.cpp b/flang-rt/lib/runtime/io-api.cpp
index 2b7ec9134689b..6af0121437cd5 100644
--- a/flang-rt/lib/runtime/io-api.cpp
+++ b/flang-rt/lib/runtime/io-api.cpp
@@ -1057,14 +1057,15 @@ bool IODEF(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) {
}
bool IODEF(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
- if (!cookie->CheckFormattedStmtType<Direction::Input>("InputInteger")) {
- return false;
+ IoStatementState &io{*cookie};
+ if (io.BeginReadingRecord()) {
+ if (auto edit{io.GetNextDataEdit()}) {
+ return edit->descriptor == DataEdit::ListDirectedNullValue ||
+ EditIntegerInput(io, *edit, reinterpret_cast<void *>(&n), kind,
+ /*isSigned=*/true);
+ }
}
- StaticDescriptor<0> staticDescriptor;
- Descriptor &descriptor{staticDescriptor.descriptor()};
- descriptor.Establish(
- TypeCategory::Integer, kind, reinterpret_cast<void *>(&n), 0);
- return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
+ return false;
}
bool IODEF(InputReal32)(Cookie cookie, float &x) {
diff --git a/flang-rt/lib/runtime/io-stmt.cpp b/flang-rt/lib/runtime/io-stmt.cpp
index 636351f560b0a..dcfd35d0de37c 100644
--- a/flang-rt/lib/runtime/io-stmt.cpp
+++ b/flang-rt/lib/runtime/io-stmt.cpp
@@ -596,7 +596,7 @@ ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {
[](auto &x) { return x.get().GetExternalFileUnit(); }, u_);
}
-Fortran::common::optional<char32_t> IoStatementState::GetCurrentChar(
+Fortran::common::optional<char32_t> IoStatementState::GetCurrentCharSlow(
std::size_t &byteCount) {
const char *p{nullptr};
std::size_t bytes{GetNextInputBytes(p)};
@@ -628,12 +628,24 @@ Fortran::common::optional<char32_t> IoStatementState::GetCurrentChar(
}
}
+IoStatementState::FastAsciiField IoStatementState::GetUpcomingFastAsciiField() {
+ ConnectionState &connection{GetConnectionState()};
+ if (!connection.isUTF8 && connection.internalIoCharKind <= 1) {
+ const char *p{nullptr};
+ if (std::size_t bytes{GetNextInputBytes(p)}) {
+ return FastAsciiField(connection, p, bytes);
+ }
+ }
+ return FastAsciiField(connection);
+}
+
Fortran::common::optional<char32_t> IoStatementState::NextInField(
- Fortran::common::optional<int> &remaining, const DataEdit &edit) {
+ Fortran::common::optional<int> &remaining, const DataEdit &edit,
+ FastAsciiField *field) {
std::size_t byteCount{0};
if (!remaining) { // Stream, list-directed, or NAMELIST
- if (auto next{GetCurrentChar(byteCount)}) {
- if (edit.IsListDirected()) {
+ if (auto next{GetCurrentChar(byteCount, field)}) {
+ if ((*next < '0' || *next == ';') && edit.IsListDirected()) {
// list-directed or NAMELIST: check for separators
switch (*next) {
case ' ':
@@ -666,21 +678,30 @@ Fortran::common::optional<char32_t> IoStatementState::NextInField(
break;
}
}
- HandleRelativePosition(byteCount);
- GotChar(byteCount);
+ if (field) {
+ field->Advance(1, byteCount);
+ } else {
+ HandleRelativePosition(byteCount);
+ GotChar(byteCount);
+ }
return next;
}
} else if (*remaining > 0) {
- if (auto next{GetCurrentChar(byteCount)}) {
+ if (auto next{GetCurrentChar(byteCount, field)}) {
if (byteCount > static_cast<std::size_t>(*remaining)) {
return Fortran::common::nullopt;
}
*remaining -= byteCount;
- HandleRelativePosition(byteCount);
- GotChar(byteCount);
+ if (field) {
+ field->Advance(1, byteCount);
+ } else {
+ HandleRelativePosition(byteCount);
+ GotChar(byteCount);
+ }
return next;
}
- if (CheckForEndOfRecord(0)) { // do padding
+ if (CheckForEndOfRecord(0,
+ field ? field->connection() : GetConnectionState())) { // do padding
--*remaining;
return Fortran::common::optional<char32_t>{' '};
}
@@ -688,8 +709,8 @@ Fortran::common::optional<char32_t> IoStatementState::NextInField(
return Fortran::common::nullopt;
}
-bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) {
- const ConnectionState &connection{GetConnectionState()};
+bool IoStatementState::CheckForEndOfRecord(
+ std::size_t afterReading, const ConnectionState &connection) {
if (!connection.IsAtEOF()) {
if (auto length{connection.EffectiveRecordLength()}) {
if (connection.positionInRecord +
@@ -798,7 +819,6 @@ Fortran::common::optional<DataEdit>
ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
IoStatementState &io, int maxRepeat) {
// N.B. list-directed transfers cannot be nonadvancing (C1221)
- ConnectionState &connection{io.GetConnectionState()};
DataEdit edit;
edit.descriptor = DataEdit::ListDirected;
edit.repeat = 1; // may be overridden below
@@ -839,14 +859,15 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
imaginaryPart_ = true;
edit.descriptor = DataEdit::ListDirectedImaginaryPart;
}
- auto ch{io.GetNextNonBlank(byteCount)};
+ auto fastField{io.GetUpcomingFastAsciiField()};
+ auto ch{io.GetNextNonBlank(byteCount, &fastField)};
if (ch && *ch == comma && eatComma_) {
// Consume comma & whitespace after previous item.
// This includes the comma between real and imaginary components
// in list-directed/NAMELIST complex input.
// (When DECIMAL='COMMA', the comma is actually a semicolon.)
- io.HandleRelativePosition(byteCount);
- ch = io.GetNextNonBlank(byteCount);
+ fastField.Advance(0, byteCount);
+ ch = io.GetNextNonBlank(byteCount, &fastField);
}
eatComma_ = true;
if (!ch) {
@@ -864,8 +885,9 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
if (imaginaryPart_) { // can't repeat components
return edit;
}
- if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count
- auto start{connection.positionInRecord};
+ if (*ch >= '0' && *ch <= '9' && fastField.MightHaveAsterisk()) {
+ // look for "r*" repetition count
+ auto start{fastField.connection().positionInRecord};
int r{0};
do {
static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
@@ -874,12 +896,12 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
break;
}
r = 10 * r + (*ch - '0');
- io.HandleRelativePosition(byteCount);
- ch = io.GetCurrentChar(byteCount);
+ fastField.Advance(0, byteCount);
+ ch = io.GetCurrentChar(byteCount, &fastField);
} while (ch && *ch >= '0' && *ch <= '9');
if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero
- io.HandleRelativePosition(byteCount);
- ch = io.GetCurrentChar(byteCount);
+ fastField.Advance(0, byteCount);
+ ch = io.GetCurrentChar(byteCount, &fastField);
if (ch && *ch == '/') { // r*/
hitSlash_ = true;
edit.descriptor = DataEdit::ListDirectedNullValue;
@@ -894,12 +916,12 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
repeatPosition_.emplace(io);
}
} else { // not a repetition count, just an integer value; rewind
- connection.positionInRecord = start;
+ fastField.connection().positionInRecord = start;
}
}
if (!imaginaryPart_ && ch && *ch == '(') {
realPart_ = true;
- io.HandleRelativePosition(byteCount);
+ fastField.connection().HandleRelativePosition(byteCount);
edit.descriptor = DataEdit::ListDirectedRealPart;
}
return edit;
diff --git a/flang-rt/lib/runtime/unit.cpp b/flang-rt/lib/runtime/unit.cpp
index 199287d7237fd..5f52fa2781db5 100644
--- a/flang-rt/lib/runtime/unit.cpp
+++ b/flang-rt/lib/runtime/unit.cpp
@@ -135,6 +135,13 @@ bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
std::size_t ExternalFileUnit::GetNextInputBytes(
const char *&p, IoErrorHandler &handler) {
RUNTIME_CHECK(handler, direction_ == Direction::Input);
+ if (access == Access::Sequential &&
+ positionInRecord < recordLength.value_or(positionInRecord)) {
+ // Fast path for variable-length formatted input: the whole record
+ // must be in frame as a result of newline detection for record length.
+ p = Frame() + recordOffsetInFrame_ + positionInRecord;
+ return *recordLength - positionInRecord;
+ }
std::size_t length{1};
if (auto recl{EffectiveRecordLength()}) {
if (positionInRecord < *recl) {
@@ -443,7 +450,6 @@ void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
DoImpliedEndfile(handler);
SetPosition(0);
currentRecordNumber = 1;
- leftTabLimit.reset();
anyWriteSinceLastPositioning_ = false;
}
}
@@ -455,6 +461,8 @@ void ExternalFileUnit::SetPosition(std::int64_t pos) {
directAccessRecWasSet_ = true;
}
BeginRecord();
+ beganReadingRecord_ = false; // for positioning after nonadvancing input
+ leftTabLimit.reset();
}
void ExternalFileUnit::Sought(std::int64_t zeroBasedPos) {
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index faecb88737a0b..c4bc4689a1eaa 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -278,9 +278,11 @@ using SignedInt128 = Int128<true>;
#if !AVOID_NATIVE_UINT128_T && (defined __GNUC__ || defined __clang__) && \
defined __SIZEOF_INT128__
+#define USING_NATIVE_INT128_T 1
using uint128_t = __uint128_t;
using int128_t = __int128_t;
#else
+#undef USING_NATIVE_INT128_T
using uint128_t = UnsignedInt128;
using int128_t = SignedInt128;
#endif
More information about the flang-commits
mailing list