diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h index f5ac48de70989..68098da282f29 100644 --- a/flang/include/flang/Common/uint128.h +++ b/flang/include/flang/Common/uint128.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -// Portable 128-bit unsigned integer arithmetic for use in impoverished -// C++ implementations lacking __uint128_t. +// Portable 128-bit integer arithmetic for use in impoverished C++ +// implementations lacking __uint128_t & __int128_t. #ifndef FORTRAN_COMMON_UINT128_H_ #define FORTRAN_COMMON_UINT128_H_ @@ -47,6 +47,18 @@ template class Int128 { constexpr Int128 &operator=(const Int128 &) = default; constexpr Int128 &operator=(Int128 &&) = default; + constexpr Int128(const Int128 &n) + : low_{n.low()}, high_{n.high()} {} + constexpr Int128(Int128 &&n) : low_{n.low()}, high_{n.high()} {} + constexpr Int128 &operator=(const Int128 &n) { + low_ = n.low(); + high_ = n.high(); + } + constexpr Int128 &operator=(Int128 &&n) { + low_ = n.low(); + high_ = n.high(); + } + constexpr Int128 operator+() const { return *this; } constexpr Int128 operator~() const { return {~high_, ~low_}; } constexpr Int128 operator-() const { return ~*this + 1; } diff --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h index d6ceec9cab519..800244c29884d 100644 --- a/flang/include/flang/Runtime/io-api.h +++ b/flang/include/flang/Runtime/io-api.h @@ -11,6 +11,7 @@ #ifndef FORTRAN_RUNTIME_IO_API_H_ #define FORTRAN_RUNTIME_IO_API_H_ +#include "flang/Common/uint128.h" #include "flang/Runtime/entry-names.h" #include "flang/Runtime/iostat.h" #include @@ -56,7 +57,7 @@ extern "C" { // These functions initiate data transfer statements (READ, WRITE, PRINT). // Example: PRINT *, 666 is implemented as the series of calls: // Cookie cookie{BeginExternalListOutput(DefaultUnit,__FILE__,__LINE__)}; -// OutputInteger64(cookie, 666); +// OutputInteger32(cookie, 666); // EndIoStatement(cookie); // Internal I/O initiation @@ -225,7 +226,13 @@ bool IONAME(OutputUnformattedBlock)( bool IONAME(InputUnformattedBlock)( Cookie, char *, std::size_t, std::size_t elementBytes); // Formatted (including list directed) I/O data items +bool IONAME(OutputInteger8)(Cookie, std::int8_t); +bool IONAME(OutputInteger16)(Cookie, std::int16_t); +bool IONAME(OutputInteger32)(Cookie, std::int32_t); bool IONAME(OutputInteger64)(Cookie, std::int64_t); +#ifdef __SIZEOF_INT128__ +bool IONAME(OutputInteger128)(Cookie, common::int128_t); +#endif bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8); bool IONAME(OutputReal32)(Cookie, float); bool IONAME(InputReal32)(Cookie, float &); diff --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h index f2aa6a6aa326b..6246f3474af85 100644 --- a/flang/runtime/descriptor-io.h +++ b/flang/runtime/descriptor-io.h @@ -42,22 +42,22 @@ inline A &ExtractElement(IoStatementState &io, const Descriptor &descriptor, // automatic repetition counts, like "10*3.14159", for list-directed and // NAMELIST array output. -template +template inline bool FormattedIntegerIO( IoStatementState &io, const Descriptor &descriptor) { std::size_t numElements{descriptor.Elements()}; SubscriptValue subscripts[maxRank]; descriptor.GetLowerBounds(subscripts); + using IntType = CppTypeFor; for (std::size_t j{0}; j < numElements; ++j) { if (auto edit{io.GetNextDataEdit()}) { - A &x{ExtractElement(io, descriptor, subscripts)}; + IntType &x{ExtractElement(io, descriptor, subscripts)}; if constexpr (DIR == Direction::Output) { - if (!EditIntegerOutput(io, *edit, static_cast(x))) { + if (!EditIntegerOutput(io, *edit, x)) { return false; } } else if (edit->descriptor != DataEdit::ListDirectedNullValue) { - if (!EditIntegerInput(io, *edit, reinterpret_cast(&x), - static_cast(sizeof(A)))) { + if (!EditIntegerInput(io, *edit, reinterpret_cast(&x), KIND)) { return false; } } @@ -183,15 +183,16 @@ inline bool FormattedCharacterIO( return true; } -template +template inline bool FormattedLogicalIO( IoStatementState &io, const Descriptor &descriptor) { std::size_t numElements{descriptor.Elements()}; SubscriptValue subscripts[maxRank]; descriptor.GetLowerBounds(subscripts); auto *listOutput{io.get_if>()}; + using IntType = CppTypeFor; for (std::size_t j{0}; j < numElements; ++j) { - A &x{ExtractElement(io, descriptor, subscripts)}; + IntType &x{ExtractElement(io, descriptor, subscripts)}; if (listOutput) { if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) { return false; @@ -377,20 +378,15 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { case TypeCategory::Integer: switch (kind) { case 1: - return FormattedIntegerIO, DIR>( - io, descriptor); + return FormattedIntegerIO<1, DIR>(io, descriptor); case 2: - return FormattedIntegerIO, DIR>( - io, descriptor); + return FormattedIntegerIO<2, DIR>(io, descriptor); case 4: - return FormattedIntegerIO, DIR>( - io, descriptor); + return FormattedIntegerIO<4, DIR>(io, descriptor); case 8: - return FormattedIntegerIO, DIR>( - io, descriptor); + return FormattedIntegerIO<8, DIR>(io, descriptor); case 16: - return FormattedIntegerIO, DIR>( - io, descriptor); + return FormattedIntegerIO<16, DIR>(io, descriptor); default: handler.Crash( "DescriptorIO: Unimplemented INTEGER kind (%d) in descriptor", @@ -452,17 +448,13 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { case TypeCategory::Logical: switch (kind) { case 1: - return FormattedLogicalIO, DIR>( - io, descriptor); + return FormattedLogicalIO<1, DIR>(io, descriptor); case 2: - return FormattedLogicalIO, DIR>( - io, descriptor); + return FormattedLogicalIO<2, DIR>(io, descriptor); case 4: - return FormattedLogicalIO, DIR>( - io, descriptor); + return FormattedLogicalIO<4, DIR>(io, descriptor); case 8: - return FormattedLogicalIO, DIR>( - io, descriptor); + return FormattedLogicalIO<8, DIR>(io, descriptor); default: handler.Crash( "DescriptorIO: Unimplemented LOGICAL kind (%d) in descriptor", diff --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp index d9634b8399493..c5d5d6c1f0ab2 100644 --- a/flang/runtime/edit-output.cpp +++ b/flang/runtime/edit-output.cpp @@ -12,27 +12,27 @@ namespace Fortran::runtime::io { -template -bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, INT n) { - char buffer[130], *end = &buffer[sizeof buffer], *p = end; - bool isNegative{false}; - if constexpr (std::is_same_v) { - isNegative = (n >> (8 * sizeof(INT) - 1)) != 0; - } else { - isNegative = n < 0; - } - UINT un{static_cast(isNegative ? -n : n)}; +template +bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, + common::HostSignedIntType<8 * KIND> n) { + char buffer[130], *end{&buffer[sizeof buffer]}, *p{end}; + bool isNegative{n < 0}; + using Unsigned = common::HostUnsignedIntType<8 * KIND>; + Unsigned un{static_cast(n)}; int signChars{0}; switch (edit.descriptor) { case DataEdit::ListDirected: case 'G': case 'I': + if (isNegative) { + un = -n; + } if (isNegative || (edit.modes.editingFlags & signPlus)) { signChars = 1; // '-' or '+' } while (un > 0) { auto quotient{un / 10u}; - *--p = '0' + static_cast(un - UINT{10} * quotient); + *--p = '0' + static_cast(un - 10u * quotient); un = quotient; } break; @@ -382,8 +382,7 @@ bool RealOutputEditing::EditEXOutput(const DataEdit &) { "EX output editing is not yet implemented"); // TODO } -template -bool RealOutputEditing::Edit(const DataEdit &edit) { +template bool RealOutputEditing::Edit(const DataEdit &edit) { switch (edit.descriptor) { case 'D': return EditEorDOutput(edit); @@ -398,7 +397,7 @@ bool RealOutputEditing::Edit(const DataEdit &edit) { case 'B': case 'O': case 'Z': - return EditIntegerOutput(io_, edit, + return EditIntegerOutput(io_, edit, decimal::BinaryFloatingPointNumber{x_}.raw()); case 'G': return Edit(EditForGOutput(edit)); @@ -503,10 +502,16 @@ bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit, io.Emit(x, std::min(width, len)); } -template bool EditIntegerOutput( +template bool EditIntegerOutput<1>( + IoStatementState &, const DataEdit &, std::int8_t); +template bool EditIntegerOutput<2>( + IoStatementState &, const DataEdit &, std::int16_t); +template bool EditIntegerOutput<4>( + IoStatementState &, const DataEdit &, std::int32_t); +template bool EditIntegerOutput<8>( IoStatementState &, const DataEdit &, std::int64_t); -template bool EditIntegerOutput( - IoStatementState &, const DataEdit &, common::uint128_t); +template bool EditIntegerOutput<16>( + IoStatementState &, const DataEdit &, common::int128_t); template class RealOutputEditing<2>; template class RealOutputEditing<3>; diff --git a/flang/runtime/edit-output.h b/flang/runtime/edit-output.h index 251dc797f5e09..843310e992bc9 100644 --- a/flang/runtime/edit-output.h +++ b/flang/runtime/edit-output.h @@ -29,8 +29,9 @@ namespace Fortran::runtime::io { // The DataEdit reference is const here (and elsewhere in this header) so that // one edit descriptor with a repeat factor may safely serve to edit // multiple elements of an array. -template -bool EditIntegerOutput(IoStatementState &, const DataEdit &, INT); +template +bool EditIntegerOutput( + IoStatementState &, const DataEdit &, common::HostSignedIntType<8 * KIND>); // Encapsulates the state of a REAL output conversion. class RealOutputEditingBase { @@ -98,10 +99,16 @@ bool ListDirectedDefaultCharacterOutput(IoStatementState &, bool EditDefaultCharacterOutput( IoStatementState &, const DataEdit &, const char *, std::size_t); -extern template bool EditIntegerOutput( +extern template bool EditIntegerOutput<1>( + IoStatementState &, const DataEdit &, std::int8_t); +extern template bool EditIntegerOutput<2>( + IoStatementState &, const DataEdit &, std::int16_t); +extern template bool EditIntegerOutput<4>( + IoStatementState &, const DataEdit &, std::int32_t); +extern template bool EditIntegerOutput<8>( IoStatementState &, const DataEdit &, std::int64_t); -extern template bool EditIntegerOutput( - IoStatementState &, const DataEdit &, common::uint128_t); +extern template bool EditIntegerOutput<16>( + IoStatementState &, const DataEdit &, common::int128_t); extern template class RealOutputEditing<2>; extern template class RealOutputEditing<3>; diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp index bfef8a4e5ae2c..061f1552270f0 100644 --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -949,14 +949,52 @@ bool IONAME(InputUnformattedBlock)( return false; } +bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) { + cookie->CheckFormattedStmtType("OutputInteger8"); + StaticDescriptor staticDescriptor; + Descriptor &descriptor{staticDescriptor.descriptor()}; + descriptor.Establish( + TypeCategory::Integer, 1, reinterpret_cast(&n), 0); + return descr::DescriptorIO(*cookie, descriptor); +} + +bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) { + cookie->CheckFormattedStmtType("OutputInteger16"); + StaticDescriptor staticDescriptor; + Descriptor &descriptor{staticDescriptor.descriptor()}; + descriptor.Establish( + TypeCategory::Integer, 2, reinterpret_cast(&n), 0); + return descr::DescriptorIO(*cookie, descriptor); +} + +bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) { + cookie->CheckFormattedStmtType("OutputInteger32"); + StaticDescriptor staticDescriptor; + Descriptor &descriptor{staticDescriptor.descriptor()}; + descriptor.Establish( + TypeCategory::Integer, 4, reinterpret_cast(&n), 0); + return descr::DescriptorIO(*cookie, descriptor); +} + bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) { cookie->CheckFormattedStmtType("OutputInteger64"); StaticDescriptor staticDescriptor; Descriptor &descriptor{staticDescriptor.descriptor()}; descriptor.Establish( - TypeCategory::Integer, sizeof n, reinterpret_cast(&n), 0); + TypeCategory::Integer, 8, reinterpret_cast(&n), 0); + return descr::DescriptorIO(*cookie, descriptor); +} + +#ifdef __SIZEOF_INT128__ +bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) { + cookie->CheckFormattedStmtType("OutputInteger128"); + StaticDescriptor staticDescriptor; + Descriptor &descriptor{staticDescriptor.descriptor()}; + descriptor.Establish( + TypeCategory::Integer, 16, reinterpret_cast(&n), 0); return descr::DescriptorIO(*cookie, descriptor); } +#endif bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) { cookie->CheckFormattedStmtType("InputInteger"); diff --git a/flang/unittests/Runtime/NumericalFormatTest.cpp b/flang/unittests/Runtime/NumericalFormatTest.cpp index e2cad5ff80755..0dd274713696a 100644 --- a/flang/unittests/Runtime/NumericalFormatTest.cpp +++ b/flang/unittests/Runtime/NumericalFormatTest.cpp @@ -66,7 +66,7 @@ TEST(IOApiTests, HelloWorldOutputTest) { // Write string, integer, and logical values to buffer IONAME(OutputAscii)(cookie, "WORLD", 5); IONAME(OutputInteger64)(cookie, 678); - IONAME(OutputInteger64)(cookie, 0xfeedface); + IONAME(OutputInteger32)(cookie, 0xfeedface); IONAME(OutputLogical)(cookie, true); // Ensure IO succeeded