[flang-commits] [flang] [flang][runtime] Added pseudo file unit for simplified PRINT. (PR #86134)
via flang-commits
flang-commits at lists.llvm.org
Thu Mar 21 08:32:26 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-runtime
Author: Slava Zakharin (vzakhari)
<details>
<summary>Changes</summary>
A file unit is emulated via a temporary buffer that accumulates
the output, which is printed out via std::printf at the end
of the IO statement. This implementation will be used for the offload devices.
---
Full diff: https://github.com/llvm/llvm-project/pull/86134.diff
5 Files Affected:
- (modified) flang/runtime/io-stmt.cpp (+11)
- (modified) flang/runtime/lock.h (+15-2)
- (modified) flang/runtime/tools.h (+21)
- (modified) flang/runtime/unit.cpp (+135)
- (modified) flang/runtime/unit.h (+53-2)
``````````diff
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 075d7b5ae518a4..7746775f657444 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -227,7 +227,18 @@ ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; }
int ExternalIoStatementBase::EndIoStatement() {
CompleteOperation();
auto result{IoStatementBase::EndIoStatement()};
+#if defined(RT_USE_PSEUDO_FILE_UNIT)
+ // Fetch the unit pointer before *this disappears.
+ ExternalFileUnit *unitPtr{&unit_};
+#endif
unit_.EndIoStatement(); // annihilates *this in unit_.u_
+#if defined(RT_USE_PSEUDO_FILE_UNIT)
+ // The pseudo file units are dynamically allocated
+ // and are not tracked in the unit map.
+ // They have to be destructed and deallocated here.
+ unitPtr->~ExternalFileUnit();
+ FreeMemory(unitPtr);
+#endif
return result;
}
diff --git a/flang/runtime/lock.h b/flang/runtime/lock.h
index 5fdcf4745c21c2..61b06a62ff7c88 100644
--- a/flang/runtime/lock.h
+++ b/flang/runtime/lock.h
@@ -12,6 +12,7 @@
#define FORTRAN_RUNTIME_LOCK_H_
#include "terminator.h"
+#include "tools.h"
// Avoid <mutex> if possible to avoid introduction of C++ runtime
// library dependence.
@@ -35,7 +36,17 @@ namespace Fortran::runtime {
class Lock {
public:
-#if USE_PTHREADS
+#if RT_USE_PSEUDO_LOCK
+ // No lock implementation, e.g. for using together
+ // with RT_USE_PSEUDO_FILE_UNIT.
+ // The users of Lock class may use it under
+ // USE_PTHREADS and otherwise, so it has to provide
+ // all the interfaces.
+ void Take() {}
+ bool Try() { return true; }
+ void Drop() {}
+ bool TakeIfNoDeadlock() { return true; }
+#elif USE_PTHREADS
Lock() { pthread_mutex_init(&mutex_, nullptr); }
~Lock() { pthread_mutex_destroy(&mutex_); }
void Take() {
@@ -79,7 +90,9 @@ class Lock {
}
private:
-#if USE_PTHREADS
+#if RT_USE_PSEUDO_FILE_UNIT
+ // No state.
+#elif USE_PTHREADS
pthread_mutex_t mutex_{};
volatile bool isBusy_{false};
volatile pthread_t holder_;
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index df25eb8882335b..c70a1b438e3329 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -21,6 +21,27 @@
#include <map>
#include <type_traits>
+/// \macro RT_PRETTY_FUNCTION
+/// Gets a user-friendly looking function signature for the current scope
+/// using the best available method on each platform. The exact format of the
+/// resulting string is implementation specific and non-portable, so this should
+/// only be used, for example, for logging or diagnostics.
+/// Copy of LLVM_PRETTY_FUNCTION
+#if defined(_MSC_VER)
+#define RT_PRETTY_FUNCTION __FUNCSIG__
+#elif defined(__GNUC__) || defined(__clang__)
+#define RT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define RT_PRETTY_FUNCTION __func__
+#endif
+
+#if defined(RT_DEVICE_COMPILATION)
+// Use the pseudo lock and pseudo file unit implementations
+// for the device.
+#define RT_USE_PSEUDO_LOCK 1
+#define RT_USE_PSEUDO_FILE_UNIT 1
+#endif
+
namespace Fortran::runtime {
class Terminator;
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index 82f0e68cc20a26..8d09502d3bfe94 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -18,15 +18,18 @@
namespace Fortran::runtime::io {
+#if !defined(RT_USE_PSEUDO_FILE_UNIT)
// The per-unit data structures are created on demand so that Fortran I/O
// should work without a Fortran main program.
static Lock unitMapLock;
static Lock createOpenLock;
static UnitMap *unitMap{nullptr};
+#endif // !defined(RT_USE_PSEUDO_FILE_UNIT)
static ExternalFileUnit *defaultInput{nullptr}; // unit 5
static ExternalFileUnit *defaultOutput{nullptr}; // unit 6
static ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension
+#if !defined(RT_USE_PSEUDO_FILE_UNIT)
void FlushOutputOnCrash(const Terminator &terminator) {
if (!defaultOutput && !errorOutput) {
return;
@@ -41,7 +44,11 @@ void FlushOutputOnCrash(const Terminator &terminator) {
errorOutput->FlushOutput(handler);
}
}
+#else // defined(RT_USE_PSEUDO_FILE_UNIT)
+void FlushOutputOnCrash(const Terminator &terminator) {}
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
+#if !defined(RT_USE_PSEUDO_FILE_UNIT)
ExternalFileUnit *ExternalFileUnit::LookUp(int unit) {
return GetUnitMap().LookUp(unit);
}
@@ -292,6 +299,70 @@ void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
unitMap->FlushAll(handler);
}
}
+#else // defined(RT_USE_PSEUDO_FILE_UNIT)
+ExternalFileUnit *ExternalFileUnit::LookUp(int unit) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+ExternalFileUnit *ExternalFileUnit::LookUpOrCreate(
+ int unit, const Terminator &, bool &wasExtant) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
+ Direction direction, Fortran::common::optional<bool> isUnformatted,
+ const Terminator &terminator) {
+ if (direction != Direction::Output) {
+ terminator.Crash("ExternalFileUnit only supports output IO");
+ }
+ return New<ExternalFileUnit>{terminator}(unit).release();
+}
+ExternalFileUnit *ExternalFileUnit::LookUp(
+ const char *path, std::size_t pathLen) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+ExternalFileUnit &ExternalFileUnit::CreateNew(int unit, const Terminator &) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+ExternalFileUnit &ExternalFileUnit::NewUnit(
+ const Terminator &, bool forChildIo) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+
+bool ExternalFileUnit::OpenUnit(Fortran::common::optional<OpenStatus> status,
+ Fortran::common::optional<Action> action, Position position,
+ OwningPtr<char> &&newPath, std::size_t newPathLength, Convert convert,
+ IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+
+void ExternalFileUnit::OpenAnonymousUnit(
+ Fortran::common::optional<OpenStatus> status,
+ Fortran::common::optional<Action> action, Position position,
+ Convert convert, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+
+void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+
+void ExternalFileUnit::DestroyClosed() {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+
+Iostat ExternalFileUnit::SetDirection(Direction direction) {
+ if (direction != Direction::Output) {
+ return IostatReadFromWriteOnly;
+ }
+ direction_ = direction;
+ return IostatOk;
+}
+
+void ExternalFileUnit::CloseAll(IoErrorHandler &handler) {}
+void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {}
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
static inline void SwapEndianness(
char *data, std::size_t bytes, std::size_t elementBytes) {
@@ -999,6 +1070,7 @@ void ExternalFileUnit::PopChildIo(ChildIo &child) {
child_.reset(child.AcquirePrevious().release()); // deletes top child
}
+#if !defined(RT_USE_PSEUDO_FILE_UNIT)
int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) {
if (!mayAsynchronous()) {
handler.SignalError(IostatBadAsynchronous);
@@ -1031,6 +1103,14 @@ bool ExternalFileUnit::Wait(int id) {
return true;
}
}
+#else // defined(RT_USE_PSEUDO_FILE_UNIT)
+int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+bool ExternalFileUnit::Wait(int id) {
+ Terminator{__FILE__, __LINE__}.Crash("unsupported");
+}
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
std::int32_t ExternalFileUnit::ReadHeaderOrFooter(std::int64_t frameOffset) {
std::int32_t word;
@@ -1066,5 +1146,60 @@ Iostat ChildIo::CheckFormattingAndDirection(
return IostatOk;
}
}
+#if defined(RT_USE_PSEUDO_FILE_UNIT)
+void PseudoOpenFile::set_mayAsynchronous(bool yes) {
+ if (yes) {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+ }
+}
+Fortran::common::optional<PseudoOpenFile::FileOffset>
+PseudoOpenFile::knownSize() const {
+ Terminator{__FILE__, __LINE__}.Crash("unsupported");
+}
+void PseudoOpenFile::Open(OpenStatus, Fortran::common::optional<Action>,
+ Position, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+void PseudoOpenFile::Close(CloseStatus, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+std::size_t PseudoOpenFile::Read(FileOffset, char *, std::size_t minBytes,
+ std::size_t maxBytes, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+std::size_t PseudoOpenFile::Write(FileOffset at, const char *buffer,
+ std::size_t bytes, IoErrorHandler &handler) {
+ if (at) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+ }
+ // TODO: use persistent string buffer that can be reallocated
+ // as needed, and only freed at destruction of *this.
+ auto string{SizedNew<char>{handler}(bytes + 1)};
+ std::memcpy(string.get(), buffer, bytes);
+ string.get()[bytes] = '\0';
+ std::printf("%s", string.get());
+ return bytes;
+}
+void PseudoOpenFile::Truncate(FileOffset, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+int PseudoOpenFile::ReadAsynchronously(
+ FileOffset, char *, std::size_t, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+int PseudoOpenFile::WriteAsynchronously(
+ FileOffset, const char *, std::size_t, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+void PseudoOpenFile::Wait(int id, IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+void PseudoOpenFile::WaitAll(IoErrorHandler &handler) {
+ handler.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+Position PseudoOpenFile::InquirePosition() const {
+ Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
+}
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
} // namespace Fortran::runtime::io
diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index fc5bead7e1d930..7086197fe35b6b 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -32,9 +32,60 @@ namespace Fortran::runtime::io {
class UnitMap;
class ChildIo;
+#if defined(RT_USE_PSEUDO_FILE_UNIT)
+// A flavor of OpenFile class that pretends to be a terminal,
+// and only provides basic buffering of the output
+// in an internal buffer, and Write's the output
+// using std::printf(). Since it does not rely on file system
+// APIs, it can be used to implement external output
+// for offload devices.
+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);
+ // 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,
+ 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(
+ FileOffset, const char *, std::size_t, IoErrorHandler &);
+ void Wait(int id, IoErrorHandler &);
+ void WaitAll(IoErrorHandler &);
+ Position InquirePosition() const;
+};
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
+
+#if !defined(RT_USE_PSEUDO_FILE_UNIT)
+using OpenFileClass = OpenFile;
+using FileFrameClass = FileFrame<ExternalFileUnit>;
+#else // defined(RT_USE_PSEUDO_FILE_UNIT)
+using OpenFileClass = PseudoOpenFile;
+// Use not so big buffer for the pseudo file unit frame.
+using FileFrameClass = FileFrame<ExternalFileUnit, 1024>;
+#endif // defined(RT_USE_PSEUDO_FILE_UNIT)
+
class ExternalFileUnit : public ConnectionState,
- public OpenFile,
- public FileFrame<ExternalFileUnit> {
+ public OpenFileClass,
+ public FileFrameClass {
public:
static constexpr int maxAsyncIds{64 * 16};
``````````
</details>
https://github.com/llvm/llvm-project/pull/86134
More information about the flang-commits
mailing list