diff options
Diffstat (limited to 'flang/runtime/unit.cpp')
| -rw-r--r-- | flang/runtime/unit.cpp | 330 |
1 files changed, 18 insertions, 312 deletions
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp index 82f0e68cc20a..6c648d3bd834 100644 --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -5,301 +5,38 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - +// +// Implementation of ExternalFileUnit common for both +// RT_USE_PSEUDO_FILE_UNIT=0 and RT_USE_PSEUDO_FILE_UNIT=1. +// +//===----------------------------------------------------------------------===// #include "unit.h" #include "io-error.h" #include "lock.h" #include "tools.h" -#include "unit-map.h" -#include "flang/Runtime/magic-numbers.h" -#include <cstdio> #include <limits> #include <utility> namespace Fortran::runtime::io { -// 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}; -static ExternalFileUnit *defaultInput{nullptr}; // unit 5 -static ExternalFileUnit *defaultOutput{nullptr}; // unit 6 -static ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension - -void FlushOutputOnCrash(const Terminator &terminator) { - if (!defaultOutput && !errorOutput) { - return; - } - IoErrorHandler handler{terminator}; - handler.HasIoStat(); // prevent nested crash if flush has error - CriticalSection critical{unitMapLock}; - if (defaultOutput) { - defaultOutput->FlushOutput(handler); - } - if (errorOutput) { - errorOutput->FlushOutput(handler); - } -} - -ExternalFileUnit *ExternalFileUnit::LookUp(int unit) { - return GetUnitMap().LookUp(unit); -} - -ExternalFileUnit *ExternalFileUnit::LookUpOrCreate( - int unit, const Terminator &terminator, bool &wasExtant) { - return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant); -} - -ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit, - Direction dir, Fortran::common::optional<bool> isUnformatted, - const Terminator &terminator) { - // Make sure that the returned anonymous unit has been opened - // not just created in the unitMap. - CriticalSection critical{createOpenLock}; - bool exists{false}; - ExternalFileUnit *result{ - GetUnitMap().LookUpOrCreate(unit, terminator, exists)}; - if (result && !exists) { - IoErrorHandler handler{terminator}; - result->OpenAnonymousUnit( - dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace, - Action::ReadWrite, Position::Rewind, Convert::Unknown, handler); - result->isUnformatted = isUnformatted; - } - return result; -} - -ExternalFileUnit *ExternalFileUnit::LookUp( - const char *path, std::size_t pathLen) { - return GetUnitMap().LookUp(path, pathLen); -} - -ExternalFileUnit &ExternalFileUnit::CreateNew( - int unit, const Terminator &terminator) { - bool wasExtant{false}; - ExternalFileUnit *result{ - GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)}; - RUNTIME_CHECK(terminator, result && !wasExtant); - return *result; -} - -ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) { - return GetUnitMap().LookUpForClose(unit); -} - -ExternalFileUnit &ExternalFileUnit::NewUnit( - const Terminator &terminator, bool forChildIo) { - ExternalFileUnit &unit{GetUnitMap().NewUnit(terminator)}; - unit.createdForInternalChildIo_ = forChildIo; - return unit; -} - -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) { - if (convert == Convert::Unknown) { - convert = executionEnvironment.conversion; - } - swapEndianness_ = convert == Convert::Swap || - (convert == Convert::LittleEndian && !isHostLittleEndian) || - (convert == Convert::BigEndian && isHostLittleEndian); - bool impliedClose{false}; - if (IsConnected()) { - bool isSamePath{newPath.get() && path() && pathLength() == newPathLength && - std::memcmp(path(), newPath.get(), newPathLength) == 0}; - if (status && *status != OpenStatus::Old && isSamePath) { - handler.SignalError("OPEN statement for connected unit may not have " - "explicit STATUS= other than 'OLD'"); - return impliedClose; - } - if (!newPath.get() || isSamePath) { - // OPEN of existing unit, STATUS='OLD' or unspecified, not new FILE= - newPath.reset(); - return impliedClose; - } - // Otherwise, OPEN on open unit with new FILE= implies CLOSE - DoImpliedEndfile(handler); - FlushOutput(handler); - TruncateFrame(0, handler); - Close(CloseStatus::Keep, handler); - impliedClose = true; - } - if (newPath.get() && newPathLength > 0) { - if (const auto *already{ - GetUnitMap().LookUp(newPath.get(), newPathLength)}) { - handler.SignalError(IostatOpenAlreadyConnected, - "OPEN(UNIT=%d,FILE='%.*s'): file is already connected to unit %d", - unitNumber_, static_cast<int>(newPathLength), newPath.get(), - already->unitNumber_); - return impliedClose; - } - } - set_path(std::move(newPath), newPathLength); - Open(status.value_or(OpenStatus::Unknown), action, position, handler); - auto totalBytes{knownSize()}; - if (access == Access::Direct) { - if (!openRecl) { - handler.SignalError(IostatOpenBadRecl, - "OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known", - unitNumber()); - } else if (*openRecl <= 0) { - handler.SignalError(IostatOpenBadRecl, - "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid", - unitNumber(), static_cast<std::intmax_t>(*openRecl)); - } else if (totalBytes && (*totalBytes % *openRecl != 0)) { - handler.SignalError(IostatOpenBadRecl, - "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an " - "even divisor of the file size %jd", - unitNumber(), static_cast<std::intmax_t>(*openRecl), - static_cast<std::intmax_t>(*totalBytes)); - } - recordLength = openRecl; - } - endfileRecordNumber.reset(); - currentRecordNumber = 1; - if (totalBytes && access == Access::Direct && openRecl.value_or(0) > 0) { - endfileRecordNumber = 1 + (*totalBytes / *openRecl); - } - if (position == Position::Append) { - if (totalBytes) { - frameOffsetInFile_ = *totalBytes; - } - if (access != Access::Stream) { - if (!endfileRecordNumber) { - // Fake it so that we can backspace relative from the end - endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2; - } - currentRecordNumber = *endfileRecordNumber; - } - } - return impliedClose; -} - -void ExternalFileUnit::OpenAnonymousUnit( - Fortran::common::optional<OpenStatus> status, - Fortran::common::optional<Action> action, Position position, - Convert convert, IoErrorHandler &handler) { - // I/O to an unconnected unit reads/creates a local file, e.g. fort.7 - std::size_t pathMaxLen{32}; - auto path{SizedNew<char>{handler}(pathMaxLen)}; - std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_); - OpenUnit(status, action, position, std::move(path), std::strlen(path.get()), - convert, handler); -} - -void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { - DoImpliedEndfile(handler); - FlushOutput(handler); - Close(status, handler); -} - -void ExternalFileUnit::DestroyClosed() { - GetUnitMap().DestroyClosed(*this); // destroys *this -} - -Iostat ExternalFileUnit::SetDirection(Direction direction) { - if (direction == Direction::Input) { - if (mayRead()) { - direction_ = Direction::Input; - return IostatOk; - } else { - return IostatReadFromWriteOnly; - } - } else { - if (mayWrite()) { - direction_ = Direction::Output; - return IostatOk; - } else { - return IostatWriteToReadOnly; - } - } -} - -UnitMap &ExternalFileUnit::CreateUnitMap() { - Terminator terminator{__FILE__, __LINE__}; - IoErrorHandler handler{terminator}; - UnitMap &newUnitMap{*New<UnitMap>{terminator}().release()}; - - bool wasExtant{false}; - ExternalFileUnit &out{*newUnitMap.LookUpOrCreate( - FORTRAN_DEFAULT_OUTPUT_UNIT, terminator, wasExtant)}; - RUNTIME_CHECK(terminator, !wasExtant); - out.Predefine(1); - handler.SignalError(out.SetDirection(Direction::Output)); - out.isUnformatted = false; - defaultOutput = &out; - - ExternalFileUnit &in{*newUnitMap.LookUpOrCreate( - FORTRAN_DEFAULT_INPUT_UNIT, terminator, wasExtant)}; - RUNTIME_CHECK(terminator, !wasExtant); - in.Predefine(0); - handler.SignalError(in.SetDirection(Direction::Input)); - in.isUnformatted = false; - defaultInput = ∈ - - ExternalFileUnit &error{ - *newUnitMap.LookUpOrCreate(FORTRAN_ERROR_UNIT, terminator, wasExtant)}; - RUNTIME_CHECK(terminator, !wasExtant); - error.Predefine(2); - handler.SignalError(error.SetDirection(Direction::Output)); - error.isUnformatted = false; - errorOutput = &error; +RT_OFFLOAD_VAR_GROUP_BEGIN +RT_VAR_ATTRS ExternalFileUnit *defaultInput{nullptr}; // unit 5 +RT_VAR_ATTRS ExternalFileUnit *defaultOutput{nullptr}; // unit 6 +RT_VAR_ATTRS ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension +RT_OFFLOAD_VAR_GROUP_END - return newUnitMap; -} - -// A back-up atexit() handler for programs that don't terminate with a main -// program END or a STOP statement or other Fortran-initiated program shutdown, -// such as programs with a C main() that terminate normally. It flushes all -// external I/O units. It is registered once the first time that any external -// I/O is attempted. -static void CloseAllExternalUnits() { - IoErrorHandler handler{"Fortran program termination"}; - ExternalFileUnit::CloseAll(handler); -} +RT_OFFLOAD_API_GROUP_BEGIN -UnitMap &ExternalFileUnit::GetUnitMap() { - if (unitMap) { - return *unitMap; - } - { - CriticalSection critical{unitMapLock}; - if (unitMap) { - return *unitMap; - } - unitMap = &CreateUnitMap(); - } - std::atexit(CloseAllExternalUnits); - return *unitMap; -} - -void ExternalFileUnit::CloseAll(IoErrorHandler &handler) { - CriticalSection critical{unitMapLock}; - if (unitMap) { - unitMap->CloseAll(handler); - FreeMemoryAndNullify(unitMap); - } - defaultOutput = nullptr; - defaultInput = nullptr; - errorOutput = nullptr; -} - -void ExternalFileUnit::FlushAll(IoErrorHandler &handler) { - CriticalSection critical{unitMapLock}; - if (unitMap) { - unitMap->FlushAll(handler); - } -} - -static inline void SwapEndianness( +static inline RT_API_ATTRS void SwapEndianness( char *data, std::size_t bytes, std::size_t elementBytes) { if (elementBytes > 1) { auto half{elementBytes >> 1}; for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) { for (std::size_t k{0}; k < half; ++k) { + RT_DIAG_PUSH + RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN std::swap(data[j + k], data[j + elementBytes - 1 - k]); + RT_DIAG_POP } } } @@ -870,7 +607,8 @@ void ExternalFileUnit::BackspaceVariableUnformattedRecord( // There's no portable memrchr(), unfortunately, and strrchr() would // fail on a record with a NUL, so we have to do it the hard way. -static const char *FindLastNewline(const char *str, std::size_t length) { +static RT_API_ATTRS const char *FindLastNewline( + const char *str, std::size_t length) { for (const char *p{str + length}; p >= str; p--) { if (*p == '\n') { return p; @@ -999,39 +737,6 @@ void ExternalFileUnit::PopChildIo(ChildIo &child) { child_.reset(child.AcquirePrevious().release()); // deletes top child } -int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) { - if (!mayAsynchronous()) { - handler.SignalError(IostatBadAsynchronous); - return -1; - } else { - for (int j{0}; 64 * j < maxAsyncIds; ++j) { - if (auto least{asyncIdAvailable_[j].LeastElement()}) { - asyncIdAvailable_[j].reset(*least); - return 64 * j + static_cast<int>(*least); - } - } - handler.SignalError(IostatTooManyAsyncOps); - return -1; - } -} - -bool ExternalFileUnit::Wait(int id) { - if (static_cast<std::size_t>(id) >= maxAsyncIds || - asyncIdAvailable_[id / 64].test(id % 64)) { - return false; - } else { - if (id == 0) { // means "all IDs" - for (int j{0}; 64 * j < maxAsyncIds; ++j) { - asyncIdAvailable_[j].set(); - } - asyncIdAvailable_[0].reset(0); - } else { - asyncIdAvailable_[id / 64].set(id % 64); - } - return true; - } -} - std::int32_t ExternalFileUnit::ReadHeaderOrFooter(std::int64_t frameOffset) { std::int32_t word; char *wordPtr{reinterpret_cast<char *>(&word)}; @@ -1067,4 +772,5 @@ Iostat ChildIo::CheckFormattingAndDirection( } } +RT_OFFLOAD_API_GROUP_END } // namespace Fortran::runtime::io |
