| //===-- lib/Evaluate/host.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 FORTRAN_EVALUATE_HOST_H_ |
| #define FORTRAN_EVALUATE_HOST_H_ |
| |
| // Define a compile-time mapping between Fortran intrinsic types and host |
| // hardware types if possible. The purpose is to avoid having to do any kind of |
| // assumption on whether a "float" matches the Scalar<Type<TypeCategory::Real, |
| // 4>> outside of this header. The main tools are HostTypeExists<T> and |
| // HostType<T>. HostTypeExists<T>() will return true if and only if a host |
| // hardware type maps to Fortran intrinsic type T. Then HostType<T> can be used |
| // to safely refer to this hardware type. |
| |
| #include "flang/Evaluate/type.h" |
| #include <cfenv> |
| #include <complex> |
| #include <cstdint> |
| #include <limits> |
| #include <string> |
| #include <type_traits> |
| |
| namespace Fortran::evaluate { |
| namespace host { |
| |
| // Helper class to handle host runtime traps, status flag and errno |
| class HostFloatingPointEnvironment { |
| public: |
| void SetUpHostFloatingPointEnvironment(FoldingContext &); |
| void CheckAndRestoreFloatingPointEnvironment(FoldingContext &); |
| bool hasSubnormalFlushingHardwareControl() const { |
| return hasSubnormalFlushingHardwareControl_; |
| } |
| void SetFlag(RealFlag flag) { flags_.set(flag); } |
| bool hardwareFlagsAreReliable() const { return hardwareFlagsAreReliable_; } |
| |
| private: |
| std::fenv_t originalFenv_; |
| #if __x86_64__ |
| unsigned int originalMxcsr; |
| #endif |
| RealFlags flags_; |
| bool hasSubnormalFlushingHardwareControl_{false}; |
| bool hardwareFlagsAreReliable_{true}; |
| }; |
| |
| // Type mapping from F18 types to host types |
| struct UnsupportedType {}; // There is no host type for the F18 type |
| |
| template <typename FTN_T> struct HostTypeHelper { |
| using Type = UnsupportedType; |
| }; |
| template <typename FTN_T> using HostType = typename HostTypeHelper<FTN_T>::Type; |
| |
| template <typename... T> constexpr inline bool HostTypeExists() { |
| return (... && (!std::is_same_v<HostType<T>, UnsupportedType>)); |
| } |
| |
| // Type mapping from host types to F18 types FortranType<HOST_T> is defined |
| // after all HosTypeHelper definition because it reverses them to avoid |
| // duplication. |
| |
| // Scalar conversion utilities from host scalars to F18 scalars |
| template <typename FTN_T> |
| inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) { |
| static_assert(HostTypeExists<FTN_T>()); |
| if constexpr (FTN_T::category == TypeCategory::Complex && |
| sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) { |
| // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for |
| // complex |
| return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)), |
| CastHostToFortran<typename FTN_T::Part>(std::imag(x))}; |
| } else { |
| return *reinterpret_cast<const Scalar<FTN_T> *>(&x); |
| } |
| } |
| |
| // Scalar conversion utilities from F18 scalars to host scalars |
| template <typename FTN_T> |
| inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) { |
| static_assert(HostTypeExists<FTN_T>()); |
| if constexpr (FTN_T::category == TypeCategory::Complex && |
| sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) { |
| // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for |
| // complex |
| return HostType<FTN_T>{CastFortranToHost<typename FTN_T::Part>(x.REAL()), |
| CastFortranToHost<typename FTN_T::Part>(x.AIMAG())}; |
| } else { |
| return *reinterpret_cast<const HostType<FTN_T> *>(&x); |
| } |
| } |
| |
| // Defining the actual mapping |
| template <> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> { |
| using Type = std::int8_t; |
| }; |
| |
| template <> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> { |
| using Type = std::int16_t; |
| }; |
| |
| template <> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> { |
| using Type = std::int32_t; |
| }; |
| |
| template <> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> { |
| using Type = std::int64_t; |
| }; |
| |
| template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> { |
| #if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) |
| using Type = __int128_t; |
| #else |
| using Type = UnsupportedType; |
| #endif |
| }; |
| |
| // TODO no mapping to host types are defined currently for 16bits float |
| // It should be defined when gcc/clang have a better support for it. |
| |
| template <> |
| struct HostTypeHelper< |
| Type<TypeCategory::Real, common::RealKindForPrecision(24)>> { |
| // IEEE 754 32bits |
| using Type = std::conditional_t<sizeof(float) == 4 && |
| std::numeric_limits<float>::is_iec559, |
| float, UnsupportedType>; |
| }; |
| |
| template <> |
| struct HostTypeHelper< |
| Type<TypeCategory::Real, common::RealKindForPrecision(53)>> { |
| // IEEE 754 64bits |
| using Type = std::conditional_t<sizeof(double) == 8 && |
| std::numeric_limits<double>::is_iec559, |
| double, UnsupportedType>; |
| }; |
| |
| template <> |
| struct HostTypeHelper< |
| Type<TypeCategory::Real, common::RealKindForPrecision(64)>> { |
| // X87 80bits |
| using Type = std::conditional_t<sizeof(long double) >= 10 && |
| std::numeric_limits<long double>::digits == 64 && |
| std::numeric_limits<long double>::max_exponent == 16384, |
| long double, UnsupportedType>; |
| }; |
| |
| template <> |
| struct HostTypeHelper< |
| Type<TypeCategory::Real, common::RealKindForPrecision(113)>> { |
| // IEEE 754 128bits |
| using Type = std::conditional_t<sizeof(long double) == 16 && |
| std::numeric_limits<long double>::digits == 113 && |
| std::numeric_limits<long double>::max_exponent == 16384, |
| long double, UnsupportedType>; |
| }; |
| |
| template <int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> { |
| using RealT = Fortran::evaluate::Type<TypeCategory::Real, KIND>; |
| using Type = std::conditional_t<HostTypeExists<RealT>(), |
| std::complex<HostType<RealT>>, UnsupportedType>; |
| }; |
| |
| template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> { |
| using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>; |
| }; |
| |
| template <int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> { |
| using Type = |
| Scalar<typename Fortran::evaluate::Type<TypeCategory::Character, KIND>>; |
| }; |
| |
| // Type mapping from host types to F18 types. This need to be placed after all |
| // HostTypeHelper specializations. |
| template <typename T, typename... TT> struct IndexInTupleHelper {}; |
| template <typename T, typename... TT> |
| struct IndexInTupleHelper<T, std::tuple<TT...>> { |
| static constexpr int value{common::TypeIndex<T, TT...>}; |
| }; |
| struct UnknownType {}; // the host type does not match any F18 types |
| template <typename HOST_T> struct FortranTypeHelper { |
| using HostTypeMapping = |
| common::MapTemplate<HostType, AllIntrinsicTypes, std::tuple>; |
| static constexpr int index{ |
| IndexInTupleHelper<HOST_T, HostTypeMapping>::value}; |
| // Both conditional types are "instantiated", so a valid type must be |
| // created for invalid index even if not used. |
| using Type = std::conditional_t<index >= 0, |
| std::tuple_element_t<(index >= 0) ? index : 0, AllIntrinsicTypes>, |
| UnknownType>; |
| }; |
| |
| template <typename HOST_T> |
| using FortranType = typename FortranTypeHelper<HOST_T>::Type; |
| |
| template <typename... HT> constexpr inline bool FortranTypeExists() { |
| return (... && (!std::is_same_v<FortranType<HT>, UnknownType>)); |
| } |
| |
| } // namespace host |
| } // namespace Fortran::evaluate |
| |
| #endif // FORTRAN_EVALUATE_HOST_H_ |