From cd0a1226b50081e86eb75a89d01e8782423971a0 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Mon, 28 Jun 2021 11:41:04 -0700 Subject: [PATCH] [flang] Fix "non-advancing" I/O, support $ in FORMAT Non-advancing I/O was failing; ExternalFileUnit was losing track of what writes had been committed to the file. Fixed. Also, support the common extension of $ and \ in a FORMAT as being equivalent to ADVANCE=NO. Differential Revision: https://reviews.llvm.org/D105046 --- flang/runtime/connection.h | 1 - flang/runtime/format-implementation.h | 2 ++ flang/runtime/format.h | 1 + flang/runtime/io-api.cpp | 9 ++++--- flang/runtime/io-stmt.cpp | 19 ++++++++----- flang/runtime/io-stmt.h | 15 ++++++----- flang/runtime/unit-map.cpp | 2 +- flang/runtime/unit.cpp | 39 +++++++++++++++++++-------- flang/runtime/unit.h | 2 ++ 9 files changed, 60 insertions(+), 30 deletions(-) diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h index 6eb6b62ccab7e..6d0678f18abfa 100644 --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -49,7 +49,6 @@ struct ConnectionState : public ConnectionAttributes { std::int64_t currentRecordNumber{1}; // 1 is first std::int64_t positionInRecord{0}; // offset in current record std::int64_t furthestPositionInRecord{0}; // max(position+bytes) - bool nonAdvancing{false}; // ADVANCE='NO' // Set at end of non-advancing I/O data transfer std::optional leftTabLimit; // offset in current record diff --git a/flang/runtime/format-implementation.h b/flang/runtime/format-implementation.h index 63ca682eb3e7a..8c41a984693fa 100644 --- a/flang/runtime/format-implementation.h +++ b/flang/runtime/format-implementation.h @@ -357,6 +357,8 @@ int FormatControl::CueUpNextDataEdit(Context &context, bool stop) { } } else if (ch == '/') { context.AdvanceRecord(repeat && *repeat > 0 ? *repeat : 1); + } else if (ch == '$' || ch == '\\') { + context.mutableModes().nonAdvancing = true; } else { context.SignalError(IostatErrorInFormat, "Invalid character '%c' in FORMAT", static_cast(ch)); diff --git a/flang/runtime/format.h b/flang/runtime/format.h index 77daa38f3262e..1989aa79a98ee 100644 --- a/flang/runtime/format.h +++ b/flang/runtime/format.h @@ -35,6 +35,7 @@ struct MutableModes { char delim{'\0'}; // DELIM= short scale{0}; // kP bool inNamelist{false}; // skip ! comments + bool nonAdvancing{false}; // ADVANCE='NO', or $ or \ in FORMAT }; // A single edit descriptor extracted from a FORMAT diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp index d1b13cb330eba..8996b44669dda 100644 --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -437,12 +437,13 @@ static bool YesOrNo(const char *keyword, std::size_t length, const char *what, bool IONAME(SetAdvance)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; - ConnectionState &connection{io.GetConnectionState()}; - connection.nonAdvancing = - !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler()); - if (connection.nonAdvancing && connection.access == Access::Direct) { + bool nonAdvancing{ + !YesOrNo(keyword, length, "ADVANCE", io.GetIoErrorHandler())}; + if (nonAdvancing && io.GetConnectionState().access == Access::Direct) { io.GetIoErrorHandler().SignalError( "Non-advancing I/O attempted on direct access file"); + } else { + io.mutableModes().nonAdvancing = nonAdvancing; } return true; } diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp index 3432f847cce51..56ea6129d5501 100644 --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -203,9 +203,8 @@ MutableModes &ExternalIoStatementBase::mutableModes() { return unit_.modes; } ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; } int ExternalIoStatementBase::EndIoStatement() { - if (unit_.nonAdvancing) { + if (mutableModes().nonAdvancing) { unit_.leftTabLimit = unit_.furthestPositionInRecord; - unit_.nonAdvancing = false; } else { unit_.leftTabLimit.reset(); } @@ -260,14 +259,20 @@ int NoUnitIoStatementState::EndIoStatement() { return result; } +template +ExternalIoStatementState::ExternalIoStatementState( + ExternalFileUnit &unit, const char *sourceFile, int sourceLine) + : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{ + unit.modes} {} + template int ExternalIoStatementState::EndIoStatement() { if constexpr (DIR == Direction::Input) { BeginReadingRecord(); // in case there were no I/O items - if (!unit().nonAdvancing) { + if (!mutableModes().nonAdvancing) { FinishReadingRecord(); } } else { - if (!unit().nonAdvancing) { + if (!mutableModes().nonAdvancing) { unit().AdvanceRecord(*this); } unit().FlushIfTerminal(*this); @@ -375,7 +380,7 @@ ExternalFormattedIoStatementState::ExternalFormattedIoStatementState( ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength, const char *sourceFile, int sourceLine) : ExternalIoStatementState{unit, sourceFile, sourceLine}, - mutableModes_{unit.modes}, format_{*this, format, formatLength} {} + format_{*this, format, formatLength} {} template int ExternalFormattedIoStatementState::EndIoStatement() { @@ -558,7 +563,7 @@ std::optional IoStatementState::NextInField( return std::optional{' '}; } IoErrorHandler &handler{GetIoErrorHandler()}; - if (connection.nonAdvancing) { + if (mutableModes().nonAdvancing) { handler.SignalEor(); } else { handler.SignalError(IostatRecordReadOverrun); @@ -867,7 +872,7 @@ int ExternalMiscIoStatementState::EndIoStatement() { ExternalFileUnit &ext{unit()}; switch (which_) { case Flush: - ext.Flush(*this); + ext.FlushOutput(*this); std::fflush(nullptr); // flushes C stdio output streams (12.9(2)) break; case Backspace: diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h index 34c4a47363c0d..49964359a48ba 100644 --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -320,7 +320,9 @@ template class ExternalIoStatementState : public ExternalIoStatementBase, public IoDirectionState { public: - using ExternalIoStatementBase::ExternalIoStatementBase; + ExternalIoStatementState( + ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0); + MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); bool Emit(const char *, std::size_t, std::size_t elementBytes); bool Emit(const char *, std::size_t); @@ -333,6 +335,12 @@ class ExternalIoStatementState : public ExternalIoStatementBase, void HandleAbsolutePosition(std::int64_t); bool BeginReadingRecord(); void FinishReadingRecord(); + +private: + // These are forked from ConnectionState's modes at the beginning + // of each formatted I/O statement so they may be overridden by control + // edit descriptors during the statement. + MutableModes mutableModes_; }; template @@ -343,7 +351,6 @@ class ExternalFormattedIoStatementState : public ExternalIoStatementState, ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format, std::size_t formatLength, const char *sourceFile = nullptr, int sourceLine = 0); - MutableModes &mutableModes() { return mutableModes_; } int EndIoStatement(); std::optional GetNextDataEdit( IoStatementState &, int maxRepeat = 1) { @@ -351,10 +358,6 @@ class ExternalFormattedIoStatementState : public ExternalIoStatementState, } private: - // These are forked from ConnectionState's modes at the beginning - // of each formatted I/O statement so they may be overridden by control - // edit descriptors during the statement. - MutableModes mutableModes_; FormatControl format_; }; diff --git a/flang/runtime/unit-map.cpp b/flang/runtime/unit-map.cpp index 915c747371850..2a7e414b3facc 100644 --- a/flang/runtime/unit-map.cpp +++ b/flang/runtime/unit-map.cpp @@ -67,7 +67,7 @@ void UnitMap::FlushAll(IoErrorHandler &handler) { CriticalSection critical{lock_}; for (int j{0}; j < buckets_; ++j) { for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { - p->unit.Flush(handler); + p->unit.FlushOutput(handler); } } } diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp index aafb71fb6d73c..e1ff6e7fd2930 100644 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -32,7 +32,7 @@ void FlushOutputOnCrash(const Terminator &terminator) { if (defaultOutput) { IoErrorHandler handler{terminator}; handler.HasIoStat(); // prevent nested crash if flush has error - defaultOutput->Flush(handler); + defaultOutput->FlushOutput(handler); } } @@ -118,7 +118,7 @@ void ExternalFileUnit::OpenUnit(std::optional status, } // Otherwise, OPEN on open unit with new FILE= implies CLOSE DoImpliedEndfile(handler); - Flush(handler); + FlushOutput(handler); Close(CloseStatus::Keep, handler); } set_path(std::move(newPath), newPathLength); @@ -168,7 +168,7 @@ void ExternalFileUnit::OpenAnonymousUnit(std::optional status, void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { DoImpliedEndfile(handler); - Flush(handler); + FlushOutput(handler); Close(status, handler); } @@ -462,12 +462,9 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) { ok = ok && Emit("\n", 1, 1, handler); // TODO: Windows CR+LF } } - frameOffsetInFile_ += - recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord); - recordOffsetInFrame_ = 0; + CommitWrites(); impliedEndfile_ = true; ++currentRecordNumber; - BeginRecord(); return ok; } } @@ -499,9 +496,24 @@ void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) { } } +void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) { + if (!mayPosition()) { + auto frameAt{FrameAt()}; + if (frameOffsetInFile_ >= frameAt && + frameOffsetInFile_ < + static_cast(frameAt + FrameLength())) { + // A Flush() that's about to happen to a non-positionable file + // needs to advance frameOffsetInFile_ to prevent attempts at + // impossible seeks + CommitWrites(); + } + } + Flush(handler); +} + void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { if (isTerminal()) { - Flush(handler); + FlushOutput(handler); } } @@ -533,8 +545,6 @@ void ExternalFileUnit::Rewind(IoErrorHandler &handler) { } void ExternalFileUnit::EndIoStatement() { - frameOffsetInFile_ += recordOffsetInFrame_; - recordOffsetInFrame_ = 0; io_.reset(); u_.emplace(); lock_.Drop(); @@ -585,7 +595,7 @@ void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord( void ExternalFileUnit::BeginSequentialVariableFormattedInputRecord( IoErrorHandler &handler) { if (this == defaultInput && defaultOutput) { - defaultOutput->Flush(handler); + defaultOutput->FlushOutput(handler); } std::size_t length{0}; do { @@ -701,6 +711,13 @@ void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) { impliedEndfile_ = false; } +void ExternalFileUnit::CommitWrites() { + frameOffsetInFile_ += + recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord); + recordOffsetInFrame_ = 0; + BeginRecord(); +} + ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) { OwningPtr current{std::move(child_)}; Terminator &terminator{parent.GetIoErrorHandler()}; diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h index 68876ff536399..99ba05d78bee6 100644 --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -82,6 +82,7 @@ class ExternalFileUnit : public ConnectionState, void FinishReadingRecord(IoErrorHandler &); bool AdvanceRecord(IoErrorHandler &); void BackspaceRecord(IoErrorHandler &); + void FlushOutput(IoErrorHandler &); void FlushIfTerminal(IoErrorHandler &); void Endfile(IoErrorHandler &); void Rewind(IoErrorHandler &); @@ -107,6 +108,7 @@ class ExternalFileUnit : public ConnectionState, bool SetSequentialVariableFormattedRecordLength(); void DoImpliedEndfile(IoErrorHandler &); void DoEndfile(IoErrorHandler &); + void CommitWrites(); int unitNumber_{-1}; Direction direction_{Direction::Output};