From d56fdc8e95df3431b67d33fe4b03a08406897339 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Fri, 2 Oct 2020 12:39:05 -0700 Subject: [PATCH] [flang][msvc] Avoid dependence on long double MSVC does not support a distinct 80-bit extended precision "long double" type. Rework the I/O runtime to avoid using native C/C++ type names. Centralize the mappings between the KIND= type parameters of REAL and their binary precisions in the common real.h header file, and use KIND type parameter values rather than binary precisions for clarity where appropriate. This patch, if successful, should obviate the need for Differential review D88511. (This patch anticipates a successful review of D88688, which fixes the function that maps each kind of real to its maximum number of significant decimal digits.) Differential revision: https://reviews.llvm.org/D88752 --- flang/include/flang/Common/real.h | 82 ++++++++++++++----- .../flang/Decimal/binary-floating-point.h | 1 + flang/include/flang/Evaluate/type.h | 51 ++---------- flang/runtime/descriptor-io.h | 56 +++++++------ flang/runtime/edit-input.cpp | 19 +++-- flang/runtime/edit-input.h | 19 +++-- flang/runtime/edit-output.cpp | 11 +-- flang/runtime/edit-output.h | 20 ++--- 8 files changed, 136 insertions(+), 123 deletions(-) diff --git a/flang/include/flang/Common/real.h b/flang/include/flang/Common/real.h index 6ff9e441f2ce..036f665d3da6 100644 --- a/flang/include/flang/Common/real.h +++ b/flang/include/flang/Common/real.h @@ -20,20 +20,20 @@ namespace Fortran::common { // Total representation size in bits for each type static constexpr int BitsForBinaryPrecision(int binaryPrecision) { switch (binaryPrecision) { - case 8: - return 16; // IEEE single (truncated): 1+8+7 - case 11: - return 16; // IEEE half precision: 1+5+10 - case 24: - return 32; // IEEE single precision: 1+8+23 - case 53: - return 64; // IEEE double precision: 1+11+52 - case 64: - return 80; // x87 extended precision: 1+15+64 - case 106: - return 128; // "double-double": 2*(1+11+52) - case 113: - return 128; // IEEE quad precision: 1+15+112 + case 8: // IEEE single (truncated): 1+8+7 with implicit bit + return 16; + case 11: // IEEE half precision: 1+5+10 with implicit bit + return 16; + case 24: // IEEE single precision: 1+8+23 with implicit bit + return 32; + case 53: // IEEE double precision: 1+11+52 with implicit bit + return 64; + case 64: // x87 extended precision: 1+15+64, no implicit bit + return 80; + case 106: // "double-double": 2*(1+11+52 with implicit bit) + return 128; + case 113: // IEEE quad precision: 1+15+112 with implicit bit + return 128; default: return -1; } @@ -44,25 +44,65 @@ static constexpr int BitsForBinaryPrecision(int binaryPrecision) { // with the minimum exponent (biased to 1) and all fractional bits set. static constexpr int MaxDecimalConversionDigits(int binaryPrecision) { switch (binaryPrecision) { - case 8: + case 8: // IEEE single (truncated): 1+8+7 with implicit bit return 96; - case 11: + case 11: // IEEE half precision: 1+5+10 with implicit bit return 21; - case 24: + case 24: // IEEE single precision: 1+8+23 with implicit bit return 112; - case 53: + case 53: // IEEE double precision: 1+11+52 with implicit bit return 767; - case 64: + case 64: // x87 extended precision: 1+15+64, no implicit bit return 11514; - case 106: + case 106: // "double-double": 2*(1+11+52 with implicit bit) return 2 * 767; - case 113: + case 113: // IEEE quad precision: 1+15+112 with implicit bit return 11563; default: return -1; } } +static constexpr int RealKindForPrecision(int binaryPrecision) { + switch (binaryPrecision) { + case 8: // IEEE single (truncated): 1+8+7 with implicit bit + return 3; + case 11: // IEEE half precision: 1+5+10 with implicit bit + return 2; + case 24: // IEEE single precision: 1+8+23 with implicit bit + return 4; + case 53: // IEEE double precision: 1+11+52 with implicit bit + return 8; + case 64: // x87 extended precision: 1+15+64, no implicit bit + return 10; + // TODO: case 106: return kind for double/double + case 113: // IEEE quad precision: 1+15+112 with implicit bit + return 16; + default: + return -1; + } +} + +static constexpr int PrecisionOfRealKind(int kind) { + switch (kind) { + case 2: // IEEE half precision: 1+5+10 with implicit bit + return 11; + case 3: // IEEE single (truncated): 1+8+7 with implicit bit + return 8; + case 4: // IEEE single precision: 1+8+23 with implicit bit + return 24; + case 8: // IEEE double precision: 1+11+52 with implicit bit + return 53; + case 10: // x87 extended precision: 1+15+64, no implicit bit + return 64; + // TODO: case kind for double/double: return 106; + case 16: // IEEE quad precision: 1+15+112 with implicit bit + return 113; + default: + return -1; + } +} + template class RealDetails { private: // Converts bit widths to whole decimal digits diff --git a/flang/include/flang/Decimal/binary-floating-point.h b/flang/include/flang/Decimal/binary-floating-point.h index 24c23b0ce5ce..b2ff4197ce70 100644 --- a/flang/include/flang/Decimal/binary-floating-point.h +++ b/flang/include/flang/Decimal/binary-floating-point.h @@ -48,6 +48,7 @@ public: const BinaryFloatingPointNumber &that) = default; constexpr BinaryFloatingPointNumber &operator=( BinaryFloatingPointNumber &&that) = default; + constexpr explicit BinaryFloatingPointNumber(RawType raw) : raw_{raw} {} RawType raw() const { return raw_; } diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h index 183cb6de2781..0619f9290cbf 100644 --- a/flang/include/flang/Evaluate/type.h +++ b/flang/include/flang/Evaluate/type.h @@ -24,6 +24,7 @@ #include "real.h" #include "flang/Common/Fortran.h" #include "flang/Common/idioms.h" +#include "flang/Common/real.h" #include "flang/Common/template.h" #include #include @@ -235,51 +236,13 @@ public: using Scalar = value::Integer<8 * KIND>; }; -// REAL(KIND=2) is IEEE half-precision (16 bits) -template <> -class Type : public TypeBase { +template +class Type + : public TypeBase { public: - using Scalar = - value::Real::Scalar, 11>; -}; - -// REAL(KIND=3) identifies the "other" half-precision format, which is -// basically REAL(4) without its least-order 16 fraction bits. -template <> -class Type : public TypeBase { -public: - using Scalar = - value::Real::Scalar, 8>; -}; - -// REAL(KIND=4) is IEEE-754 single precision (32 bits) -template <> -class Type : public TypeBase { -public: - using Scalar = - value::Real::Scalar, 24>; -}; - -// REAL(KIND=8) is IEEE double precision (64 bits) -template <> -class Type : public TypeBase { -public: - using Scalar = - value::Real::Scalar, 53>; -}; - -// REAL(KIND=10) is x87 FPU extended precision (80 bits, all explicit) -template <> -class Type : public TypeBase { -public: - using Scalar = value::Real, 64>; -}; - -// REAL(KIND=16) is IEEE quad precision (128 bits) -template <> -class Type : public TypeBase { -public: - using Scalar = value::Real, 113>; + static constexpr int precision{common::PrecisionOfRealKind(KIND)}; + static constexpr int bits{common::BitsForBinaryPrecision(precision)}; + using Scalar = value::Real, precision>; }; // The KIND type parameter on COMPLEX is the kind of each of its components. diff --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h index 22552f27c169..f98797d78b50 100644 --- a/flang/runtime/descriptor-io.h +++ b/flang/runtime/descriptor-io.h @@ -61,21 +61,22 @@ inline bool FormattedIntegerIO( return true; } -template +template inline bool FormattedRealIO( IoStatementState &io, const Descriptor &descriptor) { std::size_t numElements{descriptor.Elements()}; SubscriptValue subscripts[maxRank]; descriptor.GetLowerBounds(subscripts); + using RawType = typename RealOutputEditing::BinaryFloatingPoint; for (std::size_t j{0}; j < numElements; ++j) { if (auto edit{io.GetNextDataEdit()}) { - A &x{ExtractElement(io, descriptor, subscripts)}; + RawType &x{ExtractElement(io, descriptor, subscripts)}; if constexpr (DIR == Direction::Output) { - if (!RealOutputEditing{io, x}.Edit(*edit)) { + if (!RealOutputEditing{io, x}.Edit(*edit)) { return false; } } else if (edit->descriptor != DataEdit::ListDirectedNullValue) { - if (!EditRealInput(io, *edit, reinterpret_cast(&x))) { + if (!EditRealInput(io, *edit, reinterpret_cast(&x))) { return false; } } @@ -90,7 +91,7 @@ inline bool FormattedRealIO( return true; } -template +template inline bool FormattedComplexIO( IoStatementState &io, const Descriptor &descriptor) { std::size_t numElements{descriptor.Elements()}; @@ -98,14 +99,15 @@ inline bool FormattedComplexIO( descriptor.GetLowerBounds(subscripts); bool isListOutput{ io.get_if>() != nullptr}; + using RawType = typename RealOutputEditing::BinaryFloatingPoint; for (std::size_t j{0}; j < numElements; ++j) { - A *x{&ExtractElement(io, descriptor, subscripts)}; + RawType *x{&ExtractElement(io, descriptor, subscripts)}; if (isListOutput) { DataEdit rEdit, iEdit; rEdit.descriptor = DataEdit::ListDirectedRealPart; iEdit.descriptor = DataEdit::ListDirectedImaginaryPart; - if (!RealOutputEditing{io, x[0]}.Edit(rEdit) || - !RealOutputEditing{io, x[1]}.Edit(iEdit)) { + if (!RealOutputEditing{io, x[0]}.Edit(rEdit) || + !RealOutputEditing{io, x[1]}.Edit(iEdit)) { return false; } } else { @@ -114,12 +116,12 @@ inline bool FormattedComplexIO( if (!edit) { return false; } else if constexpr (DIR == Direction::Output) { - if (!RealOutputEditing{io, *x}.Edit(*edit)) { + if (!RealOutputEditing{io, *x}.Edit(*edit)) { return false; } } else if (edit->descriptor == DataEdit::ListDirectedNullValue) { break; - } else if (!EditRealInput( + } else if (!EditRealInput( io, *edit, reinterpret_cast(x))) { return false; } @@ -275,18 +277,19 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { } case TypeCategory::Real: switch (kind) { + case 2: + return FormattedRealIO<2, DIR>(io, descriptor); + case 3: + return FormattedRealIO<3, DIR>(io, descriptor); case 4: - return FormattedRealIO<24, float, DIR>(io, descriptor); + return FormattedRealIO<4, DIR>(io, descriptor); case 8: - return FormattedRealIO<53, double, DIR>(io, descriptor); -#if __x86_64__ + return FormattedRealIO<8, DIR>(io, descriptor); case 10: - return FormattedRealIO<64, long double, DIR>(io, descriptor); -#else + return FormattedRealIO<10, DIR>(io, descriptor); + // TODO: case double/double case 16: - return FormattedRealIO<113, long double, DIR>(io, descriptor); -#endif - // TODO cases 2, 3 + return FormattedRealIO<16, DIR>(io, descriptor); default: io.GetIoErrorHandler().Crash( "DescriptorIO: Unimplemented REAL kind (%d) in descriptor", kind); @@ -294,18 +297,19 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) { } case TypeCategory::Complex: switch (kind) { + case 2: + return FormattedComplexIO<2, DIR>(io, descriptor); + case 3: + return FormattedComplexIO<3, DIR>(io, descriptor); case 4: - return FormattedComplexIO<24, float, DIR>(io, descriptor); + return FormattedComplexIO<4, DIR>(io, descriptor); case 8: - return FormattedComplexIO<53, double, DIR>(io, descriptor); -#if __x86_64__ + return FormattedComplexIO<8, DIR>(io, descriptor); case 10: - return FormattedComplexIO<64, long double, DIR>(io, descriptor); -#else + return FormattedComplexIO<10, DIR>(io, descriptor); + // TODO: case double/double case 16: - return FormattedComplexIO<113, long double, DIR>(io, descriptor); -#endif - // TODO cases 2, 3 + return FormattedComplexIO<16, DIR>(io, descriptor); default: io.GetIoErrorHandler().Crash( "DescriptorIO: Unimplemented COMPLEX kind (%d) in descriptor", diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp index da281aa68e43..08693f251b07 100644 --- a/flang/runtime/edit-input.cpp +++ b/flang/runtime/edit-input.cpp @@ -260,8 +260,9 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io, return got; } -template +template bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) { + constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)}; static constexpr int maxDigits{ common::MaxDecimalConversionDigits(binaryPrecision)}; static constexpr int bufferSize{maxDigits + 18}; @@ -294,8 +295,9 @@ bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) { return true; } -template +template bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) { + constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)}; switch (edit.descriptor) { case DataEdit::ListDirected: case DataEdit::ListDirectedRealPart: @@ -304,7 +306,7 @@ bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) { case 'E': // incl. EN, ES, & EX case 'D': case 'G': - return EditCommonRealInput(io, edit, n); + return EditCommonRealInput(io, edit, n); case 'B': return EditBOZInput( io, edit, n, 2, common::BitsForBinaryPrecision(binaryPrecision)); @@ -459,10 +461,11 @@ bool EditDefaultCharacterInput( return true; } +template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *); template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *); -template bool EditRealInput<11>(IoStatementState &, const DataEdit &, void *); -template bool EditRealInput<24>(IoStatementState &, const DataEdit &, void *); -template bool EditRealInput<53>(IoStatementState &, const DataEdit &, void *); -template bool EditRealInput<64>(IoStatementState &, const DataEdit &, void *); -template bool EditRealInput<113>(IoStatementState &, const DataEdit &, void *); +template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *); +// TODO: double/double +template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *); } // namespace Fortran::runtime::io diff --git a/flang/runtime/edit-input.h b/flang/runtime/edit-input.h index c628b9c7e673..a8b0e76cfefd 100644 --- a/flang/runtime/edit-input.h +++ b/flang/runtime/edit-input.h @@ -17,24 +17,25 @@ namespace Fortran::runtime::io { bool EditIntegerInput(IoStatementState &, const DataEdit &, void *, int kind); -template +template bool EditRealInput(IoStatementState &, const DataEdit &, void *); bool EditLogicalInput(IoStatementState &, const DataEdit &, bool &); bool EditDefaultCharacterInput( IoStatementState &, const DataEdit &, char *, std::size_t); +extern template bool EditRealInput<2>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<3>( + IoStatementState &, const DataEdit &, void *); +extern template bool EditRealInput<4>( + IoStatementState &, const DataEdit &, void *); extern template bool EditRealInput<8>( IoStatementState &, const DataEdit &, void *); -extern template bool EditRealInput<11>( +extern template bool EditRealInput<10>( IoStatementState &, const DataEdit &, void *); -extern template bool EditRealInput<24>( - IoStatementState &, const DataEdit &, void *); -extern template bool EditRealInput<53>( - IoStatementState &, const DataEdit &, void *); -extern template bool EditRealInput<64>( - IoStatementState &, const DataEdit &, void *); -extern template bool EditRealInput<113>( +// TODO: double/double +extern template bool EditRealInput<16>( IoStatementState &, const DataEdit &, void *); } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_EDIT_INPUT_H_ diff --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp index 145e01044144..31ba9f152d74 100644 --- a/flang/runtime/edit-output.cpp +++ b/flang/runtime/edit-output.cpp @@ -495,10 +495,11 @@ template bool EditIntegerOutput( template bool EditIntegerOutput( IoStatementState &, const DataEdit &, common::uint128_t); +template class RealOutputEditing<2>; +template class RealOutputEditing<3>; +template class RealOutputEditing<4>; template class RealOutputEditing<8>; -template class RealOutputEditing<11>; -template class RealOutputEditing<24>; -template class RealOutputEditing<53>; -template class RealOutputEditing<64>; -template class RealOutputEditing<113>; +template class RealOutputEditing<10>; +// TODO: double/double +template class RealOutputEditing<16>; } // namespace Fortran::runtime::io diff --git a/flang/runtime/edit-output.h b/flang/runtime/edit-output.h index d819c1007caf..251dc797f5e0 100644 --- a/flang/runtime/edit-output.h +++ b/flang/runtime/edit-output.h @@ -60,18 +60,17 @@ protected: char exponent_[16]; }; -template -class RealOutputEditing : public RealOutputEditingBase { +template class RealOutputEditing : public RealOutputEditingBase { public: + static constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)}; + using BinaryFloatingPoint = + decimal::BinaryFloatingPointNumber; template RealOutputEditing(IoStatementState &io, A x) : RealOutputEditingBase{io}, x_{x} {} bool Edit(const DataEdit &); private: - using BinaryFloatingPoint = - decimal::BinaryFloatingPointNumber; - // The DataEdit arguments here are const references or copies so that // the original DataEdit can safely serve multiple array elements when // it has a repeat count. @@ -104,12 +103,13 @@ extern template bool EditIntegerOutput( extern template bool EditIntegerOutput( IoStatementState &, const DataEdit &, common::uint128_t); +extern template class RealOutputEditing<2>; +extern template class RealOutputEditing<3>; +extern template class RealOutputEditing<4>; extern template class RealOutputEditing<8>; -extern template class RealOutputEditing<11>; -extern template class RealOutputEditing<24>; -extern template class RealOutputEditing<53>; -extern template class RealOutputEditing<64>; -extern template class RealOutputEditing<113>; +extern template class RealOutputEditing<10>; +// TODO: double/double +extern template class RealOutputEditing<16>; } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_EDIT_OUTPUT_H_