Skip to content

Commit 45b5c79

Browse files
committed
[flang] Extend & fix per-I/O-statement state (ext. I/O work part 7)
The per-I/O-statement state structures need to support missing external I/O statements, and some bugs found in testing with formatted input and record advancement are fixed. The effects of these changes will not be visible until further patches to the I/O API handlers are pushed. Reviewed By: tskeith Differential Revision: https://reviews.llvm.org/D83151
1 parent 85e1443 commit 45b5c79

File tree

3 files changed

+91
-33
lines changed

3 files changed

+91
-33
lines changed

flang/runtime/io-stmt.cpp

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "tools.h"
1414
#include "unit.h"
1515
#include <algorithm>
16+
#include <cstdio>
1617
#include <cstring>
1718
#include <limits>
1819

@@ -182,10 +183,10 @@ int NoopCloseStatementState::EndIoStatement() {
182183
}
183184

184185
template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
186+
if (!unit().nonAdvancing) {
187+
unit().AdvanceRecord(*this);
188+
}
185189
if constexpr (DIR == Direction::Output) {
186-
if (!unit().nonAdvancing) {
187-
unit().AdvanceRecord(*this);
188-
}
189190
unit().FlushIfTerminal(*this);
190191
}
191192
return ExternalIoStatementBase::EndIoStatement();
@@ -291,7 +292,7 @@ void IoStatementState::BackspaceRecord() {
291292
}
292293

293294
void IoStatementState::HandleRelativePosition(std::int64_t n) {
294-
return std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
295+
std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
295296
}
296297

297298
int IoStatementState::EndIoStatement() {
@@ -347,15 +348,22 @@ bool IoStatementState::EmitField(
347348
}
348349
}
349350

350-
void IoStatementState::SkipSpaces(std::optional<int> &remaining) {
351-
if (!remaining || *remaining > 0) {
352-
for (auto ch{GetCurrentChar()}; ch && ch == ' '; ch = GetCurrentChar()) {
351+
std::optional<char32_t> IoStatementState::SkipSpaces(
352+
std::optional<int> &remaining) {
353+
while (!remaining || *remaining > 0) {
354+
if (auto ch{GetCurrentChar()}) {
355+
if (*ch != ' ') {
356+
return ch;
357+
}
353358
HandleRelativePosition(1);
354-
if (remaining && !--*remaining) {
355-
break;
359+
if (remaining) {
360+
--*remaining;
356361
}
362+
} else {
363+
break;
357364
}
358365
}
366+
return std::nullopt;
359367
}
360368

361369
std::optional<char32_t> IoStatementState::NextInField(
@@ -372,6 +380,7 @@ std::optional<char32_t> IoStatementState::NextInField(
372380
case '\'':
373381
case '"':
374382
case '*':
383+
case '\n': // for stream access
375384
break;
376385
default:
377386
HandleRelativePosition(1);
@@ -385,7 +394,8 @@ std::optional<char32_t> IoStatementState::NextInField(
385394
return next;
386395
}
387396
const ConnectionState &connection{GetConnectionState()};
388-
if (!connection.IsAtEOF() && connection.recordLength &&
397+
if (!connection.IsAtEOF() && connection.isFixedRecordLength &&
398+
connection.recordLength &&
389399
connection.positionInRecord >= *connection.recordLength) {
390400
if (connection.modes.pad) { // PAD='YES'
391401
--*remaining;
@@ -553,29 +563,39 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
553563
return edit;
554564
}
555565

566+
template <Direction DIR>
567+
bool UnformattedIoStatementState<DIR>::Receive(char *data, std::size_t bytes) {
568+
if constexpr (DIR == Direction::Output) {
569+
this->Crash(
570+
"UnformattedIoStatementState::Receive() called for output statement");
571+
}
572+
return this->unit().Receive(data, bytes, *this);
573+
}
574+
556575
template <Direction DIR>
557576
int UnformattedIoStatementState<DIR>::EndIoStatement() {
558-
auto &ext{static_cast<ExternalIoStatementState<DIR> &>(*this)};
559-
ExternalFileUnit &unit{ext.unit()};
560-
if (unit.access == Access::Sequential && !unit.recordLength.has_value()) {
561-
// Overwrite the first four bytes of the record with its length,
562-
// and also append the length. These four bytes were skipped over
563-
// in BeginUnformattedOutput().
564-
// TODO: Break very large records up into subrecords with negative
565-
// headers &/or footers
566-
union {
567-
std::uint32_t u;
568-
char c[sizeof u];
569-
} u;
570-
u.u = unit.furthestPositionInRecord - sizeof u.c;
571-
// TODO: Convert record length to little-endian on big-endian host?
572-
if (!(ext.Emit(u.c, sizeof u.c) &&
573-
(ext.HandleAbsolutePosition(0), ext.Emit(u.c, sizeof u.c)) &&
574-
ext.AdvanceRecord())) {
575-
return false;
577+
ExternalFileUnit &unit{this->unit()};
578+
if constexpr (DIR == Direction::Output) {
579+
if (unit.access == Access::Sequential && !unit.isFixedRecordLength) {
580+
// Append the length of a sequential unformatted variable-length record
581+
// as its footer, then overwrite the reserved first four bytes of the
582+
// record with its length as its header. These four bytes were skipped
583+
// over in BeginUnformattedOutput().
584+
// TODO: Break very large records up into subrecords with negative
585+
// headers &/or footers
586+
union {
587+
std::uint32_t u;
588+
char c[sizeof u];
589+
} u;
590+
u.u = unit.furthestPositionInRecord - sizeof u;
591+
// TODO: Convert record length to little-endian on big-endian host?
592+
if (!(this->Emit(u.c, sizeof u) &&
593+
(this->HandleAbsolutePosition(0), this->Emit(u.c, sizeof u)))) {
594+
return false;
595+
}
576596
}
577597
}
578-
return ext.EndIoStatement();
598+
return ExternalIoStatementState<DIR>::EndIoStatement();
579599
}
580600

581601
template class InternalIoStatementState<Direction::Output>;
@@ -592,4 +612,25 @@ template class ExternalListIoStatementState<Direction::Output>;
592612
template class ExternalListIoStatementState<Direction::Input>;
593613
template class UnformattedIoStatementState<Direction::Output>;
594614
template class UnformattedIoStatementState<Direction::Input>;
615+
616+
int ExternalMiscIoStatementState::EndIoStatement() {
617+
ExternalFileUnit &ext{unit()};
618+
switch (which_) {
619+
case Flush:
620+
ext.Flush(*this);
621+
std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
622+
break;
623+
case Backspace:
624+
ext.BackspaceRecord(*this);
625+
break;
626+
case Endfile:
627+
ext.Endfile(*this);
628+
break;
629+
case Rewind:
630+
ext.Rewind(*this);
631+
break;
632+
}
633+
return ExternalIoStatementBase::EndIoStatement();
634+
}
635+
595636
} // namespace Fortran::runtime::io

flang/runtime/io-stmt.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ template <Direction, typename CHAR = char>
3636
class ExternalFormattedIoStatementState;
3737
template <Direction> class ExternalListIoStatementState;
3838
template <Direction> class UnformattedIoStatementState;
39+
class ExternalMiscIoStatementState;
3940

4041
// The Cookie type in the I/O API is a pointer (for C) to this class.
4142
class IoStatementState {
@@ -73,7 +74,8 @@ class IoStatementState {
7374

7475
bool EmitRepeated(char, std::size_t);
7576
bool EmitField(const char *, std::size_t length, std::size_t width);
76-
void SkipSpaces(std::optional<int> &remaining);
77+
78+
std::optional<char32_t> SkipSpaces(std::optional<int> &remaining);
7779
std::optional<char32_t> NextInField(std::optional<int> &remaining);
7880
std::optional<char32_t> GetNextNonBlank(); // can advance record
7981

@@ -94,7 +96,8 @@ class IoStatementState {
9496
std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>,
9597
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
9698
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
97-
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>>
99+
std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
100+
std::reference_wrapper<ExternalMiscIoStatementState>>
98101
u_;
99102
};
100103

@@ -235,7 +238,7 @@ class ExternalIoStatementState : public ExternalIoStatementBase,
235238
public:
236239
using ExternalIoStatementBase::ExternalIoStatementBase;
237240
int EndIoStatement();
238-
bool Emit(const char *, std::size_t chars /* not bytes */);
241+
bool Emit(const char *, std::size_t);
239242
bool Emit(const char16_t *, std::size_t chars /* not bytes */);
240243
bool Emit(const char32_t *, std::size_t chars /* not bytes */);
241244
std::optional<char32_t> GetCurrentChar();
@@ -280,6 +283,7 @@ template <Direction DIR>
280283
class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
281284
public:
282285
using ExternalIoStatementState<DIR>::ExternalIoStatementState;
286+
bool Receive(char *, std::size_t);
283287
int EndIoStatement();
284288
};
285289

@@ -353,5 +357,17 @@ extern template class FormatControl<
353357
extern template class FormatControl<
354358
ExternalFormattedIoStatementState<Direction::Input>>;
355359

360+
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
361+
public:
362+
enum Which { Flush, Backspace, Endfile, Rewind };
363+
ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
364+
const char *sourceFile = nullptr, int sourceLine = 0)
365+
: ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
366+
int EndIoStatement();
367+
368+
private:
369+
Which which_;
370+
};
371+
356372
} // namespace Fortran::runtime::io
357373
#endif // FORTRAN_RUNTIME_IO_STMT_H_

flang/runtime/unit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ class ExternalFileUnit : public ConnectionState,
106106
ExternalListIoStatementState<Direction::Output>,
107107
ExternalListIoStatementState<Direction::Input>,
108108
UnformattedIoStatementState<Direction::Output>,
109-
UnformattedIoStatementState<Direction::Input>>
109+
UnformattedIoStatementState<Direction::Input>,
110+
ExternalMiscIoStatementState>
110111
u_;
111112

112113
// Points to the active alternative (if any) in u_ for use as a Cookie

0 commit comments

Comments
 (0)