xref: /llvm-project/flang/lib/Evaluate/host.h (revision 478e0b58605c4be16f1590f9b67889290ab45dab)
164ab3302SCarolineConcatto //===-- lib/Evaluate/host.h -------------------------------------*- C++ -*-===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #ifndef FORTRAN_EVALUATE_HOST_H_
1064ab3302SCarolineConcatto #define FORTRAN_EVALUATE_HOST_H_
1164ab3302SCarolineConcatto 
1264ab3302SCarolineConcatto // Define a compile-time mapping between Fortran intrinsic types and host
1364ab3302SCarolineConcatto // hardware types if possible. The purpose is to avoid having to do any kind of
1464ab3302SCarolineConcatto // assumption on whether a "float" matches the Scalar<Type<TypeCategory::Real,
1564ab3302SCarolineConcatto // 4>> outside of this header. The main tools are HostTypeExists<T> and
1664ab3302SCarolineConcatto // HostType<T>. HostTypeExists<T>() will return true if and only if a host
1764ab3302SCarolineConcatto // hardware type maps to Fortran intrinsic type T. Then HostType<T> can be used
1864ab3302SCarolineConcatto // to safely refer to this hardware type.
1964ab3302SCarolineConcatto 
20*478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB
21*478e0b58SPeter Steinfeld #include "quadmath.h"
22*478e0b58SPeter Steinfeld #include "flang/Common/float128.h"
23*478e0b58SPeter Steinfeld #endif
2464ab3302SCarolineConcatto #include "flang/Evaluate/type.h"
2564ab3302SCarolineConcatto #include <cfenv>
2664ab3302SCarolineConcatto #include <complex>
2764ab3302SCarolineConcatto #include <cstdint>
2864ab3302SCarolineConcatto #include <limits>
2964ab3302SCarolineConcatto #include <string>
3064ab3302SCarolineConcatto #include <type_traits>
3164ab3302SCarolineConcatto 
3264ab3302SCarolineConcatto namespace Fortran::evaluate {
3364ab3302SCarolineConcatto namespace host {
3464ab3302SCarolineConcatto 
3564ab3302SCarolineConcatto // Helper class to handle host runtime traps, status flag and errno
3664ab3302SCarolineConcatto class HostFloatingPointEnvironment {
3764ab3302SCarolineConcatto public:
3864ab3302SCarolineConcatto   void SetUpHostFloatingPointEnvironment(FoldingContext &);
3964ab3302SCarolineConcatto   void CheckAndRestoreFloatingPointEnvironment(FoldingContext &);
hasSubnormalFlushingHardwareControl()4064ab3302SCarolineConcatto   bool hasSubnormalFlushingHardwareControl() const {
4164ab3302SCarolineConcatto     return hasSubnormalFlushingHardwareControl_;
4264ab3302SCarolineConcatto   }
SetFlag(RealFlag flag)4364ab3302SCarolineConcatto   void SetFlag(RealFlag flag) { flags_.set(flag); }
hardwareFlagsAreReliable()4464ab3302SCarolineConcatto   bool hardwareFlagsAreReliable() const { return hardwareFlagsAreReliable_; }
4564ab3302SCarolineConcatto 
4664ab3302SCarolineConcatto private:
4764ab3302SCarolineConcatto   std::fenv_t originalFenv_;
482ebf4b6eSIsuru Fernando #if __x86_64__
492ebf4b6eSIsuru Fernando   unsigned int originalMxcsr;
502ebf4b6eSIsuru Fernando #endif
5164ab3302SCarolineConcatto   RealFlags flags_;
5264ab3302SCarolineConcatto   bool hasSubnormalFlushingHardwareControl_{false};
5364ab3302SCarolineConcatto   bool hardwareFlagsAreReliable_{true};
5464ab3302SCarolineConcatto };
5564ab3302SCarolineConcatto 
5664ab3302SCarolineConcatto // Type mapping from F18 types to host types
5764ab3302SCarolineConcatto struct UnsupportedType {}; // There is no host type for the F18 type
5864ab3302SCarolineConcatto 
5964ab3302SCarolineConcatto template <typename FTN_T> struct HostTypeHelper {
6064ab3302SCarolineConcatto   using Type = UnsupportedType;
6164ab3302SCarolineConcatto };
6264ab3302SCarolineConcatto template <typename FTN_T> using HostType = typename HostTypeHelper<FTN_T>::Type;
6364ab3302SCarolineConcatto 
HostTypeExists()6464ab3302SCarolineConcatto template <typename... T> constexpr inline bool HostTypeExists() {
6564ab3302SCarolineConcatto   return (... && (!std::is_same_v<HostType<T>, UnsupportedType>));
6664ab3302SCarolineConcatto }
6764ab3302SCarolineConcatto 
6864ab3302SCarolineConcatto // Type mapping from host types to F18 types FortranType<HOST_T> is defined
6964ab3302SCarolineConcatto // after all HosTypeHelper definition because it reverses them to avoid
7064ab3302SCarolineConcatto // duplication.
7164ab3302SCarolineConcatto 
7264ab3302SCarolineConcatto // Scalar conversion utilities from host scalars to F18 scalars
7364ab3302SCarolineConcatto template <typename FTN_T>
CastHostToFortran(const HostType<FTN_T> & x)7464ab3302SCarolineConcatto inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
7564ab3302SCarolineConcatto   static_assert(HostTypeExists<FTN_T>());
7664ab3302SCarolineConcatto   if constexpr (FTN_T::category == TypeCategory::Complex &&
7764ab3302SCarolineConcatto       sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
7864ab3302SCarolineConcatto     // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
7964ab3302SCarolineConcatto     // complex
8064ab3302SCarolineConcatto     return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
8164ab3302SCarolineConcatto         CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
8264ab3302SCarolineConcatto   } else {
8364ab3302SCarolineConcatto     return *reinterpret_cast<const Scalar<FTN_T> *>(&x);
8464ab3302SCarolineConcatto   }
8564ab3302SCarolineConcatto }
8664ab3302SCarolineConcatto 
8782cf35bcSPeter Klausler // Scalar conversion utilities from F18 scalars to host scalars.
8864ab3302SCarolineConcatto template <typename FTN_T>
CastFortranToHost(const Scalar<FTN_T> & x)8964ab3302SCarolineConcatto inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
9064ab3302SCarolineConcatto   static_assert(HostTypeExists<FTN_T>());
9182cf35bcSPeter Klausler   if constexpr (FTN_T::category == TypeCategory::Complex) {
9282cf35bcSPeter Klausler     using FortranPartType = typename FTN_T::Part;
9382cf35bcSPeter Klausler     return HostType<FTN_T>{CastFortranToHost<FortranPartType>(x.REAL()),
9482cf35bcSPeter Klausler         CastFortranToHost<FortranPartType>(x.AIMAG())};
9582cf35bcSPeter Klausler   } else if constexpr (std::is_same_v<FTN_T, Type<TypeCategory::Real, 10>>) {
9682cf35bcSPeter Klausler     // x87 80-bit floating-point occupies 16 bytes as a C "long double";
9782cf35bcSPeter Klausler     // copy the data to avoid a legitimate (but benign due to little-endianness)
9882cf35bcSPeter Klausler     // warning from GCC >= 11.2.0.
9982cf35bcSPeter Klausler     HostType<FTN_T> y;
10082cf35bcSPeter Klausler     std::memcpy(&y, &x, sizeof x);
10182cf35bcSPeter Klausler     return y;
10264ab3302SCarolineConcatto   } else {
10382cf35bcSPeter Klausler     static_assert(sizeof x == sizeof(HostType<FTN_T>));
10464ab3302SCarolineConcatto     return *reinterpret_cast<const HostType<FTN_T> *>(&x);
10564ab3302SCarolineConcatto   }
10664ab3302SCarolineConcatto }
10764ab3302SCarolineConcatto 
10864ab3302SCarolineConcatto template <> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> {
10964ab3302SCarolineConcatto   using Type = std::int8_t;
11064ab3302SCarolineConcatto };
11164ab3302SCarolineConcatto 
11264ab3302SCarolineConcatto template <> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> {
11364ab3302SCarolineConcatto   using Type = std::int16_t;
11464ab3302SCarolineConcatto };
11564ab3302SCarolineConcatto 
11664ab3302SCarolineConcatto template <> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> {
11764ab3302SCarolineConcatto   using Type = std::int32_t;
11864ab3302SCarolineConcatto };
11964ab3302SCarolineConcatto 
12064ab3302SCarolineConcatto template <> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> {
12164ab3302SCarolineConcatto   using Type = std::int64_t;
12264ab3302SCarolineConcatto };
12364ab3302SCarolineConcatto 
12464ab3302SCarolineConcatto template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> {
12564ab3302SCarolineConcatto #if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__)
12664ab3302SCarolineConcatto   using Type = __int128_t;
12764ab3302SCarolineConcatto #else
12864ab3302SCarolineConcatto   using Type = UnsupportedType;
12964ab3302SCarolineConcatto #endif
13064ab3302SCarolineConcatto };
13164ab3302SCarolineConcatto 
13264ab3302SCarolineConcatto // TODO no mapping to host types are defined currently for 16bits float
13364ab3302SCarolineConcatto // It should be defined when gcc/clang have a better support for it.
13464ab3302SCarolineConcatto 
13594d9a4fdSJean Perier template <>
13694d9a4fdSJean Perier struct HostTypeHelper<
13794d9a4fdSJean Perier     Type<TypeCategory::Real, common::RealKindForPrecision(24)>> {
13894d9a4fdSJean Perier   // IEEE 754 32bits
13964ab3302SCarolineConcatto   using Type = std::conditional_t<sizeof(float) == 4 &&
14064ab3302SCarolineConcatto           std::numeric_limits<float>::is_iec559,
14164ab3302SCarolineConcatto       float, UnsupportedType>;
14264ab3302SCarolineConcatto };
14364ab3302SCarolineConcatto 
14494d9a4fdSJean Perier template <>
14594d9a4fdSJean Perier struct HostTypeHelper<
14694d9a4fdSJean Perier     Type<TypeCategory::Real, common::RealKindForPrecision(53)>> {
14749bbb8b6Speter klausler   // IEEE 754 64bits
14864ab3302SCarolineConcatto   using Type = std::conditional_t<sizeof(double) == 8 &&
14964ab3302SCarolineConcatto           std::numeric_limits<double>::is_iec559,
15064ab3302SCarolineConcatto       double, UnsupportedType>;
15164ab3302SCarolineConcatto };
15264ab3302SCarolineConcatto 
15394d9a4fdSJean Perier template <>
15494d9a4fdSJean Perier struct HostTypeHelper<
15594d9a4fdSJean Perier     Type<TypeCategory::Real, common::RealKindForPrecision(64)>> {
15664ab3302SCarolineConcatto   // X87 80bits
15764ab3302SCarolineConcatto   using Type = std::conditional_t<sizeof(long double) >= 10 &&
15864ab3302SCarolineConcatto           std::numeric_limits<long double>::digits == 64 &&
15964ab3302SCarolineConcatto           std::numeric_limits<long double>::max_exponent == 16384,
16064ab3302SCarolineConcatto       long double, UnsupportedType>;
16164ab3302SCarolineConcatto };
16264ab3302SCarolineConcatto 
163*478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB
164*478e0b58SPeter Steinfeld template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
165*478e0b58SPeter Steinfeld   // IEEE 754 128bits
166*478e0b58SPeter Steinfeld   using Type = __float128;
167*478e0b58SPeter Steinfeld };
168*478e0b58SPeter Steinfeld #else
169*478e0b58SPeter Steinfeld template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
17049bbb8b6Speter klausler   // IEEE 754 128bits
17164ab3302SCarolineConcatto   using Type = std::conditional_t<sizeof(long double) == 16 &&
17264ab3302SCarolineConcatto           std::numeric_limits<long double>::digits == 113 &&
17364ab3302SCarolineConcatto           std::numeric_limits<long double>::max_exponent == 16384,
17464ab3302SCarolineConcatto       long double, UnsupportedType>;
17564ab3302SCarolineConcatto };
176*478e0b58SPeter Steinfeld #endif
17764ab3302SCarolineConcatto 
17864ab3302SCarolineConcatto template <int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> {
17964ab3302SCarolineConcatto   using RealT = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
18064ab3302SCarolineConcatto   using Type = std::conditional_t<HostTypeExists<RealT>(),
18164ab3302SCarolineConcatto       std::complex<HostType<RealT>>, UnsupportedType>;
18264ab3302SCarolineConcatto };
18364ab3302SCarolineConcatto 
184*478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB
185*478e0b58SPeter Steinfeld template <> struct HostTypeHelper<Type<TypeCategory::Complex, 16>> {
186*478e0b58SPeter Steinfeld   using RealT = Fortran::evaluate::Type<TypeCategory::Real, 16>;
187*478e0b58SPeter Steinfeld   using Type = __complex128;
188*478e0b58SPeter Steinfeld };
189*478e0b58SPeter Steinfeld #endif
190*478e0b58SPeter Steinfeld 
19164ab3302SCarolineConcatto template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> {
19264ab3302SCarolineConcatto   using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>;
19364ab3302SCarolineConcatto };
19464ab3302SCarolineConcatto 
19564ab3302SCarolineConcatto template <int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> {
19664ab3302SCarolineConcatto   using Type =
19764ab3302SCarolineConcatto       Scalar<typename Fortran::evaluate::Type<TypeCategory::Character, KIND>>;
19864ab3302SCarolineConcatto };
19964ab3302SCarolineConcatto 
20064ab3302SCarolineConcatto // Type mapping from host types to F18 types. This need to be placed after all
20164ab3302SCarolineConcatto // HostTypeHelper specializations.
20264ab3302SCarolineConcatto template <typename T, typename... TT> struct IndexInTupleHelper {};
20364ab3302SCarolineConcatto template <typename T, typename... TT>
20464ab3302SCarolineConcatto struct IndexInTupleHelper<T, std::tuple<TT...>> {
20564ab3302SCarolineConcatto   static constexpr int value{common::TypeIndex<T, TT...>};
20664ab3302SCarolineConcatto };
20764ab3302SCarolineConcatto struct UnknownType {}; // the host type does not match any F18 types
20864ab3302SCarolineConcatto template <typename HOST_T> struct FortranTypeHelper {
20964ab3302SCarolineConcatto   using HostTypeMapping =
21064ab3302SCarolineConcatto       common::MapTemplate<HostType, AllIntrinsicTypes, std::tuple>;
21164ab3302SCarolineConcatto   static constexpr int index{
21264ab3302SCarolineConcatto       IndexInTupleHelper<HOST_T, HostTypeMapping>::value};
21364ab3302SCarolineConcatto   // Both conditional types are "instantiated", so a valid type must be
21464ab3302SCarolineConcatto   // created for invalid index even if not used.
21564ab3302SCarolineConcatto   using Type = std::conditional_t<index >= 0,
21664ab3302SCarolineConcatto       std::tuple_element_t<(index >= 0) ? index : 0, AllIntrinsicTypes>,
21764ab3302SCarolineConcatto       UnknownType>;
21864ab3302SCarolineConcatto };
21964ab3302SCarolineConcatto 
22064ab3302SCarolineConcatto template <typename HOST_T>
22164ab3302SCarolineConcatto using FortranType = typename FortranTypeHelper<HOST_T>::Type;
22264ab3302SCarolineConcatto 
22364ab3302SCarolineConcatto template <typename... HT> constexpr inline bool FortranTypeExists() {
22464ab3302SCarolineConcatto   return (... && (!std::is_same_v<FortranType<HT>, UnknownType>));
22564ab3302SCarolineConcatto }
22664ab3302SCarolineConcatto 
2271f879005STim Keith } // namespace host
2281f879005STim Keith } // namespace Fortran::evaluate
22964ab3302SCarolineConcatto 
23064ab3302SCarolineConcatto #endif // FORTRAN_EVALUATE_HOST_H_
231