Skip to content

Commit

Permalink
[flang] Fix "non-advancing" I/O, support $ in FORMAT
Browse files Browse the repository at this point in the history
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
  • Loading branch information
klausler committed Jun 28, 2021
1 parent e837ce2 commit cd0a122
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 30 deletions.
1 change: 0 additions & 1 deletion flang/runtime/connection.h
Expand Up @@ -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<std::int64_t> leftTabLimit; // offset in current record
Expand Down
2 changes: 2 additions & 0 deletions flang/runtime/format-implementation.h
Expand Up @@ -357,6 +357,8 @@ int FormatControl<CONTEXT>::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<char>(ch));
Expand Down
1 change: 1 addition & 0 deletions flang/runtime/format.h
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions flang/runtime/io-api.cpp
Expand Up @@ -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;
}
Expand Down
19 changes: 12 additions & 7 deletions flang/runtime/io-stmt.cpp
Expand Up @@ -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();
}
Expand Down Expand Up @@ -260,14 +259,20 @@ int NoUnitIoStatementState::EndIoStatement() {
return result;
}

template <Direction DIR>
ExternalIoStatementState<DIR>::ExternalIoStatementState(
ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
: ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{
unit.modes} {}

template <Direction DIR> int ExternalIoStatementState<DIR>::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);
Expand Down Expand Up @@ -375,7 +380,7 @@ ExternalFormattedIoStatementState<DIR, CHAR>::ExternalFormattedIoStatementState(
ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength,
const char *sourceFile, int sourceLine)
: ExternalIoStatementState<DIR>{unit, sourceFile, sourceLine},
mutableModes_{unit.modes}, format_{*this, format, formatLength} {}
format_{*this, format, formatLength} {}

template <Direction DIR, typename CHAR>
int ExternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
Expand Down Expand Up @@ -558,7 +563,7 @@ std::optional<char32_t> IoStatementState::NextInField(
return std::optional<char32_t>{' '};
}
IoErrorHandler &handler{GetIoErrorHandler()};
if (connection.nonAdvancing) {
if (mutableModes().nonAdvancing) {
handler.SignalEor();
} else {
handler.SignalError(IostatRecordReadOverrun);
Expand Down Expand Up @@ -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:
Expand Down
15 changes: 9 additions & 6 deletions flang/runtime/io-stmt.h
Expand Up @@ -320,7 +320,9 @@ template <Direction DIR>
class ExternalIoStatementState : public ExternalIoStatementBase,
public IoDirectionState<DIR> {
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);
Expand All @@ -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 <Direction DIR, typename CHAR>
Expand All @@ -343,18 +351,13 @@ class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
std::size_t formatLength, const char *sourceFile = nullptr,
int sourceLine = 0);
MutableModes &mutableModes() { return mutableModes_; }
int EndIoStatement();
std::optional<DataEdit> GetNextDataEdit(
IoStatementState &, int maxRepeat = 1) {
return format_.GetNextDataEdit(*this, maxRepeat);
}

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<ExternalFormattedIoStatementState> format_;
};

Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/unit-map.cpp
Expand Up @@ -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);
}
}
}
Expand Down
39 changes: 28 additions & 11 deletions flang/runtime/unit.cpp
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -118,7 +118,7 @@ void ExternalFileUnit::OpenUnit(std::optional<OpenStatus> 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);
Expand Down Expand Up @@ -168,7 +168,7 @@ void ExternalFileUnit::OpenAnonymousUnit(std::optional<OpenStatus> status,

void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
DoImpliedEndfile(handler);
Flush(handler);
FlushOutput(handler);
Close(status, handler);
}

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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<std::int64_t>(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);
}
}

Expand Down Expand Up @@ -533,8 +545,6 @@ void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
}

void ExternalFileUnit::EndIoStatement() {
frameOffsetInFile_ += recordOffsetInFrame_;
recordOffsetInFrame_ = 0;
io_.reset();
u_.emplace<std::monostate>();
lock_.Drop();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<ChildIo> current{std::move(child_)};
Terminator &terminator{parent.GetIoErrorHandler()};
Expand Down
2 changes: 2 additions & 0 deletions flang/runtime/unit.h
Expand Up @@ -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 &);
Expand All @@ -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};
Expand Down

0 comments on commit cd0a122

Please sign in to comment.