[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
This commit is contained in:
peter klausler 2020-10-02 12:39:05 -07:00
parent 8da0df3d6d
commit d56fdc8e95
8 changed files with 136 additions and 123 deletions

View File

@ -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 <int BINARY_PRECISION> class RealDetails {
private:
// Converts bit widths to whole decimal digits

View File

@ -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_; }

View File

@ -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 <cinttypes>
#include <optional>
@ -235,51 +236,13 @@ public:
using Scalar = value::Integer<8 * KIND>;
};
// REAL(KIND=2) is IEEE half-precision (16 bits)
template <>
class Type<TypeCategory::Real, 2> : public TypeBase<TypeCategory::Real, 2> {
template <int KIND>
class Type<TypeCategory::Real, KIND>
: public TypeBase<TypeCategory::Real, KIND> {
public:
using Scalar =
value::Real<typename Type<TypeCategory::Integer, 2>::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<TypeCategory::Real, 3> : public TypeBase<TypeCategory::Real, 3> {
public:
using Scalar =
value::Real<typename Type<TypeCategory::Integer, 2>::Scalar, 8>;
};
// REAL(KIND=4) is IEEE-754 single precision (32 bits)
template <>
class Type<TypeCategory::Real, 4> : public TypeBase<TypeCategory::Real, 4> {
public:
using Scalar =
value::Real<typename Type<TypeCategory::Integer, 4>::Scalar, 24>;
};
// REAL(KIND=8) is IEEE double precision (64 bits)
template <>
class Type<TypeCategory::Real, 8> : public TypeBase<TypeCategory::Real, 8> {
public:
using Scalar =
value::Real<typename Type<TypeCategory::Integer, 8>::Scalar, 53>;
};
// REAL(KIND=10) is x87 FPU extended precision (80 bits, all explicit)
template <>
class Type<TypeCategory::Real, 10> : public TypeBase<TypeCategory::Real, 10> {
public:
using Scalar = value::Real<value::Integer<80>, 64>;
};
// REAL(KIND=16) is IEEE quad precision (128 bits)
template <>
class Type<TypeCategory::Real, 16> : public TypeBase<TypeCategory::Real, 16> {
public:
using Scalar = value::Real<value::Integer<128>, 113>;
static constexpr int precision{common::PrecisionOfRealKind(KIND)};
static constexpr int bits{common::BitsForBinaryPrecision(precision)};
using Scalar = value::Real<value::Integer<bits>, precision>;
};
// The KIND type parameter on COMPLEX is the kind of each of its components.

View File

@ -61,21 +61,22 @@ inline bool FormattedIntegerIO(
return true;
}
template <int PREC, typename A, Direction DIR>
template <int KIND, Direction DIR>
inline bool FormattedRealIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
for (std::size_t j{0}; j < numElements; ++j) {
if (auto edit{io.GetNextDataEdit()}) {
A &x{ExtractElement<A>(io, descriptor, subscripts)};
RawType &x{ExtractElement<RawType>(io, descriptor, subscripts)};
if constexpr (DIR == Direction::Output) {
if (!RealOutputEditing<PREC>{io, x}.Edit(*edit)) {
if (!RealOutputEditing<KIND>{io, x}.Edit(*edit)) {
return false;
}
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
if (!EditRealInput<PREC>(io, *edit, reinterpret_cast<void *>(&x))) {
if (!EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
return false;
}
}
@ -90,7 +91,7 @@ inline bool FormattedRealIO(
return true;
}
template <int PREC, typename A, Direction DIR>
template <int KIND, Direction DIR>
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<ListDirectedStatementState<Direction::Output>>() != nullptr};
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
for (std::size_t j{0}; j < numElements; ++j) {
A *x{&ExtractElement<A>(io, descriptor, subscripts)};
RawType *x{&ExtractElement<RawType>(io, descriptor, subscripts)};
if (isListOutput) {
DataEdit rEdit, iEdit;
rEdit.descriptor = DataEdit::ListDirectedRealPart;
iEdit.descriptor = DataEdit::ListDirectedImaginaryPart;
if (!RealOutputEditing<PREC>{io, x[0]}.Edit(rEdit) ||
!RealOutputEditing<PREC>{io, x[1]}.Edit(iEdit)) {
if (!RealOutputEditing<KIND>{io, x[0]}.Edit(rEdit) ||
!RealOutputEditing<KIND>{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<PREC>{io, *x}.Edit(*edit)) {
if (!RealOutputEditing<KIND>{io, *x}.Edit(*edit)) {
return false;
}
} else if (edit->descriptor == DataEdit::ListDirectedNullValue) {
break;
} else if (!EditRealInput<PREC>(
} else if (!EditRealInput<KIND>(
io, *edit, reinterpret_cast<void *>(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",

View File

@ -260,8 +260,9 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
return got;
}
template <int binaryPrecision>
template <int KIND>
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 <int binaryPrecision>
template <int KIND>
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<binaryPrecision>(io, edit, n);
return EditCommonRealInput<KIND>(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

View File

@ -17,24 +17,25 @@ namespace Fortran::runtime::io {
bool EditIntegerInput(IoStatementState &, const DataEdit &, void *, int kind);
template <int binaryPrecision>
template <int KIND>
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_

View File

@ -495,10 +495,11 @@ template bool EditIntegerOutput<std::int64_t, std::uint64_t>(
template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
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

View File

@ -60,18 +60,17 @@ protected:
char exponent_[16];
};
template <int binaryPrecision = 53>
class RealOutputEditing : public RealOutputEditingBase {
template <int KIND> class RealOutputEditing : public RealOutputEditingBase {
public:
static constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
using BinaryFloatingPoint =
decimal::BinaryFloatingPointNumber<binaryPrecision>;
template <typename A>
RealOutputEditing(IoStatementState &io, A x)
: RealOutputEditingBase{io}, x_{x} {}
bool Edit(const DataEdit &);
private:
using BinaryFloatingPoint =
decimal::BinaryFloatingPointNumber<binaryPrecision>;
// 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<std::int64_t, std::uint64_t>(
extern template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
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_