diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt index bc81e1b1887b7..4f7627eac81f2 100644 --- a/flang/runtime/CMakeLists.txt +++ b/flang/runtime/CMakeLists.txt @@ -136,6 +136,7 @@ set(sources inquiry.cpp internal-unit.cpp io-api.cpp + io-api-minimal.cpp io-error.cpp io-stmt.cpp iostat.cpp diff --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h index 0b188a12a0198..ff5f683c6da52 100644 --- a/flang/runtime/descriptor-io.h +++ b/flang/runtime/descriptor-io.h @@ -499,8 +499,7 @@ static RT_API_ATTRS bool DescriptorIO(IoStatementState &io, return false; } if (!io.get_if>()) { - io.GetIoErrorHandler().Crash( - "DescriptorIO() called for wrong I/O direction"); + handler.Crash("DescriptorIO() called for wrong I/O direction"); return false; } if constexpr (DIR == Direction::Input) { diff --git a/flang/runtime/io-api-common.h b/flang/runtime/io-api-common.h new file mode 100644 index 0000000000000..e9e050f6e7e61 --- /dev/null +++ b/flang/runtime/io-api-common.h @@ -0,0 +1,93 @@ +//===-- runtime/io-api-common.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FLANG_RUNTIME_IO_API_COMMON_H_ +#define FLANG_RUNTIME_IO_API_COMMON_H_ + +#include "io-stmt.h" +#include "terminator.h" +#include "unit.h" +#include "flang/Common/api-attrs.h" +#include "flang/Common/optional.h" +#include "flang/Runtime/io-api.h" + +namespace Fortran::runtime::io { + +static inline RT_API_ATTRS Cookie NoopUnit(const Terminator &terminator, + int unitNumber, enum Iostat iostat = IostatOk) { + Cookie cookie{&New{terminator}( + terminator.sourceFileName(), terminator.sourceLine(), unitNumber) + .release() + ->ioStatementState()}; + if (iostat != IostatOk) { + cookie->GetIoErrorHandler().SetPendingError(iostat); + } + return cookie; +} + +static inline RT_API_ATTRS ExternalFileUnit *GetOrCreateUnit(int unitNumber, + Direction direction, Fortran::common::optional isUnformatted, + const Terminator &terminator, Cookie &errorCookie) { + if (ExternalFileUnit * + unit{ExternalFileUnit::LookUpOrCreateAnonymous( + unitNumber, direction, isUnformatted, terminator)}) { + errorCookie = nullptr; + return unit; + } else { + errorCookie = NoopUnit(terminator, unitNumber, IostatBadUnitNumber); + return nullptr; + } +} + +template class STATE, typename... A> +RT_API_ATTRS Cookie BeginExternalListIO( + int unitNumber, const char *sourceFile, int sourceLine, A &&...xs) { + Terminator terminator{sourceFile, sourceLine}; + Cookie errorCookie{nullptr}; + ExternalFileUnit *unit{GetOrCreateUnit( + unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)}; + if (!unit) { + return errorCookie; + } + if (!unit->isUnformatted.has_value()) { + unit->isUnformatted = false; + } + Iostat iostat{IostatOk}; + if (*unit->isUnformatted) { + iostat = IostatFormattedIoOnUnformattedUnit; + } + if (ChildIo * child{unit->GetChildIo()}) { + if (iostat == IostatOk) { + iostat = child->CheckFormattingAndDirection(false, DIR); + } + if (iostat == IostatOk) { + return &child->BeginIoStatement>( + *child, sourceFile, sourceLine); + } else { + return &child->BeginIoStatement( + iostat, nullptr /* no unit */, sourceFile, sourceLine); + } + } else { + if (iostat == IostatOk && unit->access == Access::Direct) { + iostat = IostatListIoOnDirectAccessUnit; + } + if (iostat == IostatOk) { + iostat = unit->SetDirection(DIR); + } + if (iostat == IostatOk) { + return &unit->BeginIoStatement>( + terminator, std::forward(xs)..., *unit, sourceFile, sourceLine); + } else { + return &unit->BeginIoStatement( + terminator, iostat, unit, sourceFile, sourceLine); + } + } +} + +} // namespace Fortran::runtime::io +#endif // FLANG_RUNTIME_IO_API_COMMON_H_ diff --git a/flang/runtime/io-api-minimal.cpp b/flang/runtime/io-api-minimal.cpp new file mode 100644 index 0000000000000..ad76fe3de0324 --- /dev/null +++ b/flang/runtime/io-api-minimal.cpp @@ -0,0 +1,163 @@ +//===-- runtime/io-api-minimal.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Implements the subset of the I/O statement API needed for basic +// list-directed output (PRINT *) of intrinsic types. + +#include "edit-output.h" +#include "format.h" +#include "io-api-common.h" +#include "io-stmt.h" +#include "terminator.h" +#include "tools.h" +#include "unit.h" +#include "flang/Runtime/io-api.h" + +namespace Fortran::runtime::io { +RT_EXT_API_GROUP_BEGIN + +Cookie IODEF(BeginExternalListOutput)( + ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { + return BeginExternalListIO( + unitNumber, sourceFile, sourceLine); +} + +enum Iostat IODEF(EndIoStatement)(Cookie cookie) { + IoStatementState &io{*cookie}; + return static_cast(io.EndIoStatement()); +} + +template > +inline RT_API_ATTRS bool FormattedScalarIntegerOutput( + IoStatementState &io, INT x, const char *whence) { + if (io.CheckFormattedStmtType(whence)) { + auto edit{io.GetNextDataEdit()}; + return edit && EditIntegerOutput(io, *edit, x); + } else { + return false; + } +} + +bool IODEF(OutputInteger8)(Cookie cookie, std::int8_t n) { + return FormattedScalarIntegerOutput<1>(*cookie, n, "OutputInteger8"); +} + +bool IODEF(OutputInteger16)(Cookie cookie, std::int16_t n) { + return FormattedScalarIntegerOutput<2>(*cookie, n, "OutputInteger16"); +} + +bool IODEF(OutputInteger32)(Cookie cookie, std::int32_t n) { + return FormattedScalarIntegerOutput<4>(*cookie, n, "OutputInteger32"); +} + +bool IODEF(OutputInteger64)(Cookie cookie, std::int64_t n) { + return FormattedScalarIntegerOutput<8>(*cookie, n, "OutputInteger64"); +} + +#ifdef __SIZEOF_INT128__ +bool IODEF(OutputInteger128)(Cookie cookie, common::int128_t n) { + return FormattedScalarIntegerOutput<16>(*cookie, n, "OutputInteger128"); +} +#endif + +template ::BinaryFloatingPoint> +inline RT_API_ATTRS bool FormattedScalarRealOutput( + IoStatementState &io, REAL x, const char *whence) { + if (io.CheckFormattedStmtType(whence)) { + auto edit{io.GetNextDataEdit()}; + return edit && RealOutputEditing{io, x}.Edit(*edit); + } else { + return false; + } +} + +bool IODEF(OutputReal32)(Cookie cookie, float x) { + return FormattedScalarRealOutput<4>(*cookie, x, "OutputReal32"); +} + +bool IODEF(OutputReal64)(Cookie cookie, double x) { + return FormattedScalarRealOutput<8>(*cookie, x, "OutputReal64"); +} + +template ::BinaryFloatingPoint> +inline RT_API_ATTRS bool FormattedScalarComplexOutput( + IoStatementState &io, REAL re, REAL im, const char *whence) { + if (io.CheckFormattedStmtType(whence)) { + if (io.get_if>() != nullptr) { + DataEdit rEdit, iEdit; + rEdit.descriptor = DataEdit::ListDirectedRealPart; + iEdit.descriptor = DataEdit::ListDirectedImaginaryPart; + rEdit.modes = iEdit.modes = io.mutableModes(); + return RealOutputEditing{io, re}.Edit(rEdit) && + RealOutputEditing{io, im}.Edit(iEdit); + } else { + auto reEdit{io.GetNextDataEdit()}; + if (reEdit && RealOutputEditing{io, re}.Edit(*reEdit)) { + auto imEdit{io.GetNextDataEdit()}; + return imEdit && RealOutputEditing{io, im}.Edit(*imEdit); + } + } + } + return false; +} + +bool IODEF(OutputComplex32)(Cookie cookie, float re, float im) { + return FormattedScalarComplexOutput<4>(*cookie, re, im, "OutputComplex32"); +} + +bool IODEF(OutputComplex64)(Cookie cookie, double re, double im) { + return FormattedScalarComplexOutput<8>(*cookie, re, im, "OutputComplex64"); +} + +bool IODEF(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { + IoStatementState &io{*cookie}; + if (!x) { + io.GetIoErrorHandler().Crash("Null address for character output item"); + } else if (auto *listOutput{ + io.get_if>()}) { + return ListDirectedCharacterOutput(io, *listOutput, x, length); + } else if (io.CheckFormattedStmtType("OutputAscii")) { + auto edit{io.GetNextDataEdit()}; + return edit && EditCharacterOutput(io, *edit, x, length); + } else { + return false; + } +} + +bool IODEF(OutputLogical)(Cookie cookie, bool truth) { + IoStatementState &io{*cookie}; + if (auto *listOutput{ + io.get_if>()}) { + return ListDirectedLogicalOutput(io, *listOutput, truth); + } else if (io.CheckFormattedStmtType("OutputAscii")) { + auto edit{io.GetNextDataEdit()}; + return edit && EditLogicalOutput(io, *edit, truth); + } else { + return false; + } +} + +} // namespace Fortran::runtime::io + +#if defined(_LIBCPP_VERBOSE_ABORT) +// Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency +// on the version provided by libc++. + +void std::__libcpp_verbose_abort(char const *format, ...) { + va_list list; + va_start(list, format); + std::vfprintf(stderr, format, list); + va_end(list); + + std::abort(); +} +#endif + +RT_EXT_API_GROUP_END diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp index ccb5b576451dd..bfc678c2ed02d 100644 --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -8,12 +8,18 @@ // Implements the I/O statement API +// template function BeginExternalListIo<> is in runtime/io-api-common.h. +// APIs BeginExternalListOutput, OutputInteger{8,16,32,64,128}, +// OutputReal{32,64}, OutputComplex{32,64}, OutputAscii, & EndIoStatement() +// are in runtime/io-api-minimal.cpp. + #include "flang/Runtime/io-api.h" #include "descriptor-io.h" #include "edit-input.h" #include "edit-output.h" #include "environment.h" #include "format.h" +#include "io-api-common.h" #include "io-stmt.h" #include "terminator.h" #include "tools.h" @@ -157,83 +163,6 @@ Cookie IODEF(BeginInternalFormattedInput)(const char *internal, sourceFile, sourceLine); } -static RT_API_ATTRS Cookie NoopUnit(const Terminator &terminator, - int unitNumber, enum Iostat iostat = IostatOk) { - Cookie cookie{&New{terminator}( - terminator.sourceFileName(), terminator.sourceLine(), unitNumber) - .release() - ->ioStatementState()}; - if (iostat != IostatOk) { - cookie->GetIoErrorHandler().SetPendingError(iostat); - } - return cookie; -} - -static RT_API_ATTRS ExternalFileUnit *GetOrCreateUnit(int unitNumber, - Direction direction, Fortran::common::optional isUnformatted, - const Terminator &terminator, Cookie &errorCookie) { - if (ExternalFileUnit * - unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, direction, isUnformatted, terminator)}) { - errorCookie = nullptr; - return unit; - } else { - errorCookie = NoopUnit(terminator, unitNumber, IostatBadUnitNumber); - return nullptr; - } -} - -template class STATE, typename... A> -RT_API_ATTRS Cookie BeginExternalListIO( - int unitNumber, const char *sourceFile, int sourceLine, A &&...xs) { - Terminator terminator{sourceFile, sourceLine}; - Cookie errorCookie{nullptr}; - ExternalFileUnit *unit{GetOrCreateUnit( - unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)}; - if (!unit) { - return errorCookie; - } - if (!unit->isUnformatted.has_value()) { - unit->isUnformatted = false; - } - Iostat iostat{IostatOk}; - if (*unit->isUnformatted) { - iostat = IostatFormattedIoOnUnformattedUnit; - } - if (ChildIo * child{unit->GetChildIo()}) { - if (iostat == IostatOk) { - iostat = child->CheckFormattingAndDirection(false, DIR); - } - if (iostat == IostatOk) { - return &child->BeginIoStatement>( - *child, sourceFile, sourceLine); - } else { - return &child->BeginIoStatement( - iostat, nullptr /* no unit */, sourceFile, sourceLine); - } - } else { - if (iostat == IostatOk && unit->access == Access::Direct) { - iostat = IostatListIoOnDirectAccessUnit; - } - if (iostat == IostatOk) { - iostat = unit->SetDirection(DIR); - } - if (iostat == IostatOk) { - return &unit->BeginIoStatement>( - terminator, std::forward(xs)..., *unit, sourceFile, sourceLine); - } else { - return &unit->BeginIoStatement( - terminator, iostat, unit, sourceFile, sourceLine); - } - } -} - -Cookie IODEF(BeginExternalListOutput)( - ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { - return BeginExternalListIO( - unitNumber, sourceFile, sourceLine); -} - Cookie IODEF(BeginExternalListInput)( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { return BeginExternalListIO( @@ -1139,63 +1068,6 @@ bool IODEF(InputDescriptor)(Cookie cookie, const Descriptor &descriptor) { return descr::DescriptorIO(*cookie, descriptor); } -bool IODEF(OutputInteger8)(Cookie cookie, std::int8_t n) { - if (!cookie->CheckFormattedStmtType("OutputInteger8")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Integer, 1, reinterpret_cast(&n), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -bool IODEF(OutputInteger16)(Cookie cookie, std::int16_t n) { - if (!cookie->CheckFormattedStmtType("OutputInteger16")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Integer, 2, reinterpret_cast(&n), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -bool IODEF(OutputInteger32)(Cookie cookie, std::int32_t n) { - if (!cookie->CheckFormattedStmtType("OutputInteger32")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Integer, 4, reinterpret_cast(&n), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -bool IODEF(OutputInteger64)(Cookie cookie, std::int64_t n) { - if (!cookie->CheckFormattedStmtType("OutputInteger64")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Integer, 8, reinterpret_cast(&n), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -#ifdef __SIZEOF_INT128__ -bool IODEF(OutputInteger128)(Cookie cookie, common::int128_t n) { - if (!cookie->CheckFormattedStmtType("OutputInteger128")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Integer, 16, reinterpret_cast(&n), 0); - return descr::DescriptorIO(*cookie, descriptor); -} -#endif - bool IODEF(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { if (!cookie->CheckFormattedStmtType("InputInteger")) { return false; @@ -1207,26 +1079,6 @@ bool IODEF(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { return descr::DescriptorIO(*cookie, descriptor); } -bool IODEF(OutputReal32)(Cookie cookie, float x) { - if (!cookie->CheckFormattedStmtType("OutputReal32")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast(&x), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -bool IODEF(OutputReal64)(Cookie cookie, double x) { - if (!cookie->CheckFormattedStmtType("OutputReal64")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast(&x), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - bool IODEF(InputReal32)(Cookie cookie, float &x) { if (!cookie->CheckFormattedStmtType("InputReal32")) { return false; @@ -1247,30 +1099,6 @@ bool IODEF(InputReal64)(Cookie cookie, double &x) { return descr::DescriptorIO(*cookie, descriptor); } -bool IODEF(OutputComplex32)(Cookie cookie, float r, float i) { - if (!cookie->CheckFormattedStmtType("OutputComplex32")) { - return false; - } - float z[2]{r, i}; - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Complex, 4, reinterpret_cast(&z), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - -bool IODEF(OutputComplex64)(Cookie cookie, double r, double i) { - if (!cookie->CheckFormattedStmtType("OutputComplex64")) { - return false; - } - double z[2]{r, i}; - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Complex, 8, reinterpret_cast(&z), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - bool IODEF(InputComplex32)(Cookie cookie, float z[2]) { if (!cookie->CheckFormattedStmtType("InputComplex32")) { return false; @@ -1305,10 +1133,6 @@ bool IODEF(OutputCharacter)( return descr::DescriptorIO(*cookie, descriptor); } -bool IODEF(OutputAscii)(Cookie cookie, const char *x, std::size_t length) { - return IONAME(OutputCharacter(cookie, x, length, 1)); -} - bool IODEF(InputCharacter)( Cookie cookie, char *x, std::size_t length, int kind) { if (!cookie->CheckFormattedStmtType("InputCharacter")) { @@ -1324,17 +1148,6 @@ bool IODEF(InputAscii)(Cookie cookie, char *x, std::size_t length) { return IONAME(InputCharacter)(cookie, x, length, 1); } -bool IODEF(OutputLogical)(Cookie cookie, bool truth) { - if (!cookie->CheckFormattedStmtType("OutputLogical")) { - return false; - } - StaticDescriptor<0> staticDescriptor; - Descriptor &descriptor{staticDescriptor.descriptor()}; - descriptor.Establish( - TypeCategory::Logical, sizeof truth, reinterpret_cast(&truth), 0); - return descr::DescriptorIO(*cookie, descriptor); -} - bool IODEF(InputLogical)(Cookie cookie, bool &truth) { if (!cookie->CheckFormattedStmtType("InputLogical")) { return false; @@ -1446,11 +1259,6 @@ bool IODEF(InquireInteger64)( return false; } -enum Iostat IODEF(EndIoStatement)(Cookie cookie) { - IoStatementState &io{*cookie}; - return static_cast(io.EndIoStatement()); -} - template static RT_API_ATTRS enum Iostat CheckUnitNumberInRangeImpl(INT unit, bool handleError, char *ioMsg, std::size_t ioMsgLength, @@ -1503,19 +1311,3 @@ enum Iostat IODEF(CheckUnitNumberInRange128)(common::int128_t unit, #endif } // namespace Fortran::runtime::io - -#if defined(_LIBCPP_VERBOSE_ABORT) -// Provide own definition for `std::__libcpp_verbose_abort` to avoid dependency -// on the version provided by libc++. - -void std::__libcpp_verbose_abort(char const *format, ...) { - va_list list; - va_start(list, format); - std::vfprintf(stderr, format, list); - va_end(list); - - std::abort(); -} -#endif - -RT_EXT_API_GROUP_END