203 changes: 113 additions & 90 deletions flang/runtime/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ class UnitMap;
class ChildIo;
class ExternalFileUnit;

RT_OFFLOAD_VAR_GROUP_BEGIN
// Predefined file units.
extern ExternalFileUnit *defaultInput; // unit 5
extern ExternalFileUnit *defaultOutput; // unit 6
extern ExternalFileUnit *errorOutput; // unit 0 extension
extern RT_VAR_ATTRS ExternalFileUnit *defaultInput; // unit 5
extern RT_VAR_ATTRS ExternalFileUnit *defaultOutput; // unit 6
extern RT_VAR_ATTRS ExternalFileUnit *errorOutput; // unit 0 extension
RT_OFFLOAD_VAR_GROUP_END

#if defined(RT_USE_PSEUDO_FILE_UNIT)
// A flavor of OpenFile class that pretends to be a terminal,
Expand All @@ -49,34 +51,36 @@ class PseudoOpenFile {
public:
using FileOffset = std::int64_t;

const char *path() const { return nullptr; }
std::size_t pathLength() const { return 0; }
void set_path(OwningPtr<char> &&, std::size_t bytes) {}
bool mayRead() const { return false; }
bool mayWrite() const { return true; }
bool mayPosition() const { return false; }
bool mayAsynchronous() const { return false; }
void set_mayAsynchronous(bool yes);
RT_API_ATTRS const char *path() const { return nullptr; }
RT_API_ATTRS std::size_t pathLength() const { return 0; }
RT_API_ATTRS void set_path(OwningPtr<char> &&, std::size_t bytes) {}
RT_API_ATTRS bool mayRead() const { return false; }
RT_API_ATTRS bool mayWrite() const { return true; }
RT_API_ATTRS bool mayPosition() const { return false; }
RT_API_ATTRS bool mayAsynchronous() const { return false; }
RT_API_ATTRS void set_mayAsynchronous(bool yes);
// Pretend to be a terminal to force the output
// at the end of IO statement.
bool isTerminal() const { return true; }
bool isWindowsTextFile() const { return false; }
Fortran::common::optional<FileOffset> knownSize() const;
bool IsConnected() const { return false; }
void Open(OpenStatus, Fortran::common::optional<Action>, Position,
IoErrorHandler &);
void Predefine(int fd) {}
void Close(CloseStatus, IoErrorHandler &);
std::size_t Read(FileOffset, char *, std::size_t minBytes,
RT_API_ATTRS bool isTerminal() const { return true; }
RT_API_ATTRS bool isWindowsTextFile() const { return false; }
RT_API_ATTRS Fortran::common::optional<FileOffset> knownSize() const;
RT_API_ATTRS bool IsConnected() const { return false; }
RT_API_ATTRS void Open(OpenStatus, Fortran::common::optional<Action>,
Position, IoErrorHandler &);
RT_API_ATTRS void Predefine(int fd) {}
RT_API_ATTRS void Close(CloseStatus, IoErrorHandler &);
RT_API_ATTRS std::size_t Read(FileOffset, char *, std::size_t minBytes,
std::size_t maxBytes, IoErrorHandler &);
std::size_t Write(FileOffset, const char *, std::size_t, IoErrorHandler &);
void Truncate(FileOffset, IoErrorHandler &);
int ReadAsynchronously(FileOffset, char *, std::size_t, IoErrorHandler &);
int WriteAsynchronously(
RT_API_ATTRS std::size_t Write(
FileOffset, const char *, std::size_t, IoErrorHandler &);
RT_API_ATTRS void Truncate(FileOffset, IoErrorHandler &);
RT_API_ATTRS int ReadAsynchronously(
FileOffset, char *, std::size_t, IoErrorHandler &);
RT_API_ATTRS int WriteAsynchronously(
FileOffset, const char *, std::size_t, IoErrorHandler &);
void Wait(int id, IoErrorHandler &);
void WaitAll(IoErrorHandler &);
Position InquirePosition() const;
RT_API_ATTRS void Wait(int id, IoErrorHandler &);
RT_API_ATTRS void WaitAll(IoErrorHandler &);
RT_API_ATTRS Position InquirePosition() const;
};
#endif // defined(RT_USE_PSEUDO_FILE_UNIT)

Expand All @@ -95,44 +99,51 @@ class ExternalFileUnit : public ConnectionState,
public:
static constexpr int maxAsyncIds{64 * 16};

explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {
explicit RT_API_ATTRS ExternalFileUnit(int unitNumber)
: unitNumber_{unitNumber} {
isUTF8 = executionEnvironment.defaultUTF8;
for (int j{0}; 64 * j < maxAsyncIds; ++j) {
asyncIdAvailable_[j].set();
}
asyncIdAvailable_[0].reset(0);
}
~ExternalFileUnit() {}
RT_API_ATTRS ~ExternalFileUnit() {}

int unitNumber() const { return unitNumber_; }
bool swapEndianness() const { return swapEndianness_; }
bool createdForInternalChildIo() const { return createdForInternalChildIo_; }
RT_API_ATTRS int unitNumber() const { return unitNumber_; }
RT_API_ATTRS bool swapEndianness() const { return swapEndianness_; }
RT_API_ATTRS bool createdForInternalChildIo() const {
return createdForInternalChildIo_;
}

static ExternalFileUnit *LookUp(int unit);
static ExternalFileUnit *LookUpOrCreate(
static RT_API_ATTRS ExternalFileUnit *LookUp(int unit);
static RT_API_ATTRS ExternalFileUnit *LookUpOrCreate(
int unit, const Terminator &, bool &wasExtant);
static ExternalFileUnit *LookUpOrCreateAnonymous(int unit, Direction,
Fortran::common::optional<bool> isUnformatted, const Terminator &);
static ExternalFileUnit *LookUp(const char *path, std::size_t pathLen);
static ExternalFileUnit &CreateNew(int unit, const Terminator &);
static ExternalFileUnit *LookUpForClose(int unit);
static ExternalFileUnit &NewUnit(const Terminator &, bool forChildIo);
static void CloseAll(IoErrorHandler &);
static void FlushAll(IoErrorHandler &);
static RT_API_ATTRS ExternalFileUnit *LookUpOrCreateAnonymous(int unit,
Direction, Fortran::common::optional<bool> isUnformatted,
const Terminator &);
static RT_API_ATTRS ExternalFileUnit *LookUp(
const char *path, std::size_t pathLen);
static RT_API_ATTRS ExternalFileUnit &CreateNew(int unit, const Terminator &);
static RT_API_ATTRS ExternalFileUnit *LookUpForClose(int unit);
static RT_API_ATTRS ExternalFileUnit &NewUnit(
const Terminator &, bool forChildIo);
static RT_API_ATTRS void CloseAll(IoErrorHandler &);
static RT_API_ATTRS void FlushAll(IoErrorHandler &);

// Returns true if an existing unit was closed
bool OpenUnit(Fortran::common::optional<OpenStatus>,
RT_API_ATTRS bool OpenUnit(Fortran::common::optional<OpenStatus>,
Fortran::common::optional<Action>, Position, OwningPtr<char> &&path,
std::size_t pathLength, Convert, IoErrorHandler &);
void OpenAnonymousUnit(Fortran::common::optional<OpenStatus>,
RT_API_ATTRS void OpenAnonymousUnit(Fortran::common::optional<OpenStatus>,
Fortran::common::optional<Action>, Position, Convert, IoErrorHandler &);
void CloseUnit(CloseStatus, IoErrorHandler &);
void DestroyClosed();
RT_API_ATTRS void CloseUnit(CloseStatus, IoErrorHandler &);
RT_API_ATTRS void DestroyClosed();

Iostat SetDirection(Direction);
RT_API_ATTRS Iostat SetDirection(Direction);

template <typename A, typename... X>
IoStatementState &BeginIoStatement(const Terminator &terminator, X &&...xs) {
RT_API_ATTRS IoStatementState &BeginIoStatement(
const Terminator &terminator, X &&...xs) {
// Take lock_ and hold it until EndIoStatement().
#if USE_PTHREADS
if (!lock_.TakeIfNoDeadlock()) {
Expand All @@ -141,7 +152,10 @@ class ExternalFileUnit : public ConnectionState,
#else
lock_.Take();
#endif
RT_DIAG_PUSH
RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
RT_DIAG_POP
if constexpr (!std::is_same_v<A, OpenStatementState>) {
state.mutableModes() = ConnectionState::modes;
}
Expand All @@ -150,50 +164,54 @@ class ExternalFileUnit : public ConnectionState,
return *io_;
}

bool Emit(
RT_API_ATTRS bool Emit(
const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
std::size_t GetNextInputBytes(const char *&, IoErrorHandler &);
bool BeginReadingRecord(IoErrorHandler &);
void FinishReadingRecord(IoErrorHandler &);
bool AdvanceRecord(IoErrorHandler &);
void BackspaceRecord(IoErrorHandler &);
void FlushOutput(IoErrorHandler &);
void FlushIfTerminal(IoErrorHandler &);
void Endfile(IoErrorHandler &);
void Rewind(IoErrorHandler &);
void EndIoStatement();
bool SetStreamPos(std::int64_t, IoErrorHandler &); // one-based, for POS=
bool SetDirectRec(std::int64_t, IoErrorHandler &); // one-based, for REC=
std::int64_t InquirePos() const {
RT_API_ATTRS bool Receive(
char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
RT_API_ATTRS std::size_t GetNextInputBytes(const char *&, IoErrorHandler &);
RT_API_ATTRS bool BeginReadingRecord(IoErrorHandler &);
RT_API_ATTRS void FinishReadingRecord(IoErrorHandler &);
RT_API_ATTRS bool AdvanceRecord(IoErrorHandler &);
RT_API_ATTRS void BackspaceRecord(IoErrorHandler &);
RT_API_ATTRS void FlushOutput(IoErrorHandler &);
RT_API_ATTRS void FlushIfTerminal(IoErrorHandler &);
RT_API_ATTRS void Endfile(IoErrorHandler &);
RT_API_ATTRS void Rewind(IoErrorHandler &);
RT_API_ATTRS void EndIoStatement();
RT_API_ATTRS bool SetStreamPos(
std::int64_t, IoErrorHandler &); // one-based, for POS=
RT_API_ATTRS bool SetDirectRec(
std::int64_t, IoErrorHandler &); // one-based, for REC=
RT_API_ATTRS std::int64_t InquirePos() const {
// 12.6.2.11 defines POS=1 as the beginning of file
return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1;
}

ChildIo *GetChildIo() { return child_.get(); }
ChildIo &PushChildIo(IoStatementState &);
void PopChildIo(ChildIo &);
RT_API_ATTRS ChildIo *GetChildIo() { return child_.get(); }
RT_API_ATTRS ChildIo &PushChildIo(IoStatementState &);
RT_API_ATTRS void PopChildIo(ChildIo &);

int GetAsynchronousId(IoErrorHandler &);
bool Wait(int);
RT_API_ATTRS int GetAsynchronousId(IoErrorHandler &);
RT_API_ATTRS bool Wait(int);

private:
static UnitMap &CreateUnitMap();
static UnitMap &GetUnitMap();
const char *FrameNextInput(IoErrorHandler &, std::size_t);
void SetPosition(std::int64_t, IoErrorHandler &); // zero-based
void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &);
void BeginVariableFormattedInputRecord(IoErrorHandler &);
void BackspaceFixedRecord(IoErrorHandler &);
void BackspaceVariableUnformattedRecord(IoErrorHandler &);
void BackspaceVariableFormattedRecord(IoErrorHandler &);
bool SetVariableFormattedRecordLength();
void DoImpliedEndfile(IoErrorHandler &);
void DoEndfile(IoErrorHandler &);
void CommitWrites();
bool CheckDirectAccess(IoErrorHandler &);
void HitEndOnRead(IoErrorHandler &);
std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset);
static RT_API_ATTRS UnitMap &CreateUnitMap();
static RT_API_ATTRS UnitMap &GetUnitMap();
RT_API_ATTRS const char *FrameNextInput(IoErrorHandler &, std::size_t);
RT_API_ATTRS void SetPosition(std::int64_t, IoErrorHandler &); // zero-based
RT_API_ATTRS void BeginSequentialVariableUnformattedInputRecord(
IoErrorHandler &);
RT_API_ATTRS void BeginVariableFormattedInputRecord(IoErrorHandler &);
RT_API_ATTRS void BackspaceFixedRecord(IoErrorHandler &);
RT_API_ATTRS void BackspaceVariableUnformattedRecord(IoErrorHandler &);
RT_API_ATTRS void BackspaceVariableFormattedRecord(IoErrorHandler &);
RT_API_ATTRS bool SetVariableFormattedRecordLength();
RT_API_ATTRS void DoImpliedEndfile(IoErrorHandler &);
RT_API_ATTRS void DoEndfile(IoErrorHandler &);
RT_API_ATTRS void CommitWrites();
RT_API_ATTRS bool CheckDirectAccess(IoErrorHandler &);
RT_API_ATTRS void HitEndOnRead(IoErrorHandler &);
RT_API_ATTRS std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset);

Lock lock_;

Expand Down Expand Up @@ -238,23 +256,28 @@ class ExternalFileUnit : public ConnectionState,
// be a child I/O statement.
class ChildIo {
public:
ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous)
RT_API_ATTRS ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous)
: parent_{parent}, previous_{std::move(previous)} {}

IoStatementState &parent() const { return parent_; }
RT_API_ATTRS IoStatementState &parent() const { return parent_; }

void EndIoStatement();
RT_API_ATTRS void EndIoStatement();

template <typename A, typename... X>
IoStatementState &BeginIoStatement(X &&...xs) {
RT_API_ATTRS IoStatementState &BeginIoStatement(X &&...xs) {
RT_DIAG_PUSH
RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
RT_DIAG_POP
io_.emplace(state);
return *io_;
}

OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); }
RT_API_ATTRS OwningPtr<ChildIo> AcquirePrevious() {
return std::move(previous_);
}

Iostat CheckFormattingAndDirection(bool unformatted, Direction);
RT_API_ATTRS Iostat CheckFormattingAndDirection(bool unformatted, Direction);

private:
IoStatementState &parent_;
Expand Down
6 changes: 5 additions & 1 deletion flang/runtime/utf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
namespace Fortran::runtime {

// clang-format off
const std::uint8_t UTF8FirstByteTable[256]{
RT_OFFLOAD_VAR_GROUP_BEGIN
const RT_CONST_VAR_ATTRS std::uint8_t UTF8FirstByteTable[256]{
/* 00 - 7F: 7 bit payload in single byte */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
Expand All @@ -37,8 +38,10 @@ const std::uint8_t UTF8FirstByteTable[256]{
/* FE: 32 bit payload */ 7,
/* FF: invalid */ 0
};
RT_OFFLOAD_VAR_GROUP_END
// clang-format on

RT_OFFLOAD_API_GROUP_BEGIN
// Non-minimal encodings are accepted.
Fortran::common::optional<char32_t> DecodeUTF8(const char *p0) {
const std::uint8_t *p{reinterpret_cast<const std::uint8_t *>(p0)};
Expand Down Expand Up @@ -107,5 +110,6 @@ std::size_t EncodeUTF8(char *p0, char32_t ucs) {
return 7;
}
}
RT_OFFLOAD_API_GROUP_END

} // namespace Fortran::runtime
14 changes: 8 additions & 6 deletions flang/runtime/utf.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,22 @@ namespace Fortran::runtime {

// Derive the length of a UTF-8 character encoding from its first byte.
// A zero result signifies an invalid encoding.
extern const std::uint8_t UTF8FirstByteTable[256];
static inline std::size_t MeasureUTF8Bytes(char first) {
RT_OFFLOAD_VAR_GROUP_BEGIN
extern const RT_CONST_VAR_ATTRS std::uint8_t UTF8FirstByteTable[256];
static constexpr std::size_t maxUTF8Bytes{7};
RT_OFFLOAD_VAR_GROUP_END

static inline RT_API_ATTRS std::size_t MeasureUTF8Bytes(char first) {
return UTF8FirstByteTable[static_cast<std::uint8_t>(first)];
}

static constexpr std::size_t maxUTF8Bytes{7};

// Ensure that all bytes are present in sequence in the input buffer
// before calling; use MeasureUTF8Bytes(first byte) to count them.
Fortran::common::optional<char32_t> DecodeUTF8(const char *);
RT_API_ATTRS Fortran::common::optional<char32_t> DecodeUTF8(const char *);

// Ensure that at least maxUTF8Bytes remain in the output
// buffer before calling.
std::size_t EncodeUTF8(char *, char32_t);
RT_API_ATTRS std::size_t EncodeUTF8(char *, char32_t);

} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_UTF_H_