[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