FLANG

host.h
1 //===-- lib/Evaluate/host.h -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef FORTRAN_EVALUATE_HOST_H_
10 #define FORTRAN_EVALUATE_HOST_H_
11
12 // Define a compile-time mapping between Fortran intrinsic types and host
13 // hardware types if possible. The purpose is to avoid having to do any kind of
14 // assumption on whether a "float" matches the Scalar<Type<TypeCategory::Real,
15 // 4>> outside of this header. The main tools are HostTypeExists<T> and
16 // HostType<T>. HostTypeExists<T>() will return true if and only if a host
17 // hardware type maps to Fortran intrinsic type T. Then HostType<T> can be used
18 // to safely refer to this hardware type.
19
20 #include "flang/Evaluate/type.h"
21 #include <cfenv>
22 #include <complex>
23 #include <cstdint>
24 #include <limits>
25 #include <string>
26 #include <type_traits>
27
28 namespace Fortran::evaluate {
29 namespace host {
30
31 // Helper class to handle host runtime traps, status flag and errno
32 class HostFloatingPointEnvironment {
33 public:
34 void SetUpHostFloatingPointEnvironment(FoldingContext &);
35 void CheckAndRestoreFloatingPointEnvironment(FoldingContext &);
36 bool hasSubnormalFlushingHardwareControl() const {
37 return hasSubnormalFlushingHardwareControl_;
38 }
39 void SetFlag(RealFlag flag) { flags_.set(flag); }
40 bool hardwareFlagsAreReliable() const { return hardwareFlagsAreReliable_; }
41
42 private:
43 std::fenv_t originalFenv_;
44 #if __x86_64__
45 unsigned int originalMxcsr;
46 #endif
47 RealFlags flags_;
48 bool hasSubnormalFlushingHardwareControl_{false};
49 bool hardwareFlagsAreReliable_{true};
50 };
51
52 // Type mapping from F18 types to host types
53 struct UnsupportedType {}; // There is no host type for the F18 type
54
55 template <typename FTN_T> struct HostTypeHelper {
56 using Type = UnsupportedType;
57 };
58 template <typename FTN_T> using HostType = typename HostTypeHelper<FTN_T>::Type;
59
60 template <typename... T> constexpr inline bool HostTypeExists() {
61 return (... && (!std::is_same_v<HostType<T>, UnsupportedType>));
62 }
63
64 // Type mapping from host types to F18 types FortranType<HOST_T> is defined
65 // after all HosTypeHelper definition because it reverses them to avoid
66 // duplication.
67
68 // Scalar conversion utilities from host scalars to F18 scalars
69 template <typename FTN_T>
70 inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
71 static_assert(HostTypeExists<FTN_T>());
72 if constexpr (FTN_T::category == TypeCategory::Complex &&
73 sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
74 // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
75 // complex
76 return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
77 CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
78 } else {
79 return *reinterpret_cast<const Scalar<FTN_T> *>(&x);
80 }
81 }
82
83 // Scalar conversion utilities from F18 scalars to host scalars
84 template <typename FTN_T>
85 inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
86 static_assert(HostTypeExists<FTN_T>());
87 if constexpr (FTN_T::category == TypeCategory::Complex &&
88 sizeof(Scalar<FTN_T>) != sizeof(HostType<FTN_T>)) {
89 // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
90 // complex
91 return HostType<FTN_T>{CastFortranToHost<typename FTN_T::Part>(x.REAL()),
92 CastFortranToHost<typename FTN_T::Part>(x.AIMAG())};
93 } else {
94 return *reinterpret_cast<const HostType<FTN_T> *>(&x);
95 }
96 }
97
98 template <typename T> struct BiggerOrSameHostTypeHelper {
99 using Type =
100 std::conditional_t<HostTypeExists<T>(), HostType<T>, UnsupportedType>;
101 using FortranType = T;
102 };
103
104 template <typename FTN_T>
105 using BiggerOrSameHostType = typename BiggerOrSameHostTypeHelper<FTN_T>::Type;
106 template <typename FTN_T>
107 using BiggerOrSameFortranTypeSupportedOnHost =
108 typename BiggerOrSameHostTypeHelper<FTN_T>::FortranType;
109
110 template <typename... T> constexpr inline bool BiggerOrSameHostTypeExists() {
111 return (... && (!std::is_same_v<BiggerOrSameHostType<T>, UnsupportedType>));
112 }
113
114 // Defining the actual mapping
115 template <> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> {
116 using Type = std::int8_t;
117 };
118
119 template <> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> {
120 using Type = std::int16_t;
121 };
122
123 template <> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> {
124 using Type = std::int32_t;
125 };
126
127 template <> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> {
128 using Type = std::int64_t;
129 };
130
131 template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> {
132 #if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__)
133 using Type = __int128_t;
134 #else
135 using Type = UnsupportedType;
136 #endif
137 };
138
139 // TODO no mapping to host types are defined currently for 16bits float
140 // It should be defined when gcc/clang have a better support for it.
141
142 template <> struct HostTypeHelper<Type<TypeCategory::Real, 4>> {
143 // IEE 754 64bits
144 using Type = std::conditional_t<sizeof(float) == 4 &&
145 std::numeric_limits<float>::is_iec559,
146 float, UnsupportedType>;
147 };
148
149 template <> struct HostTypeHelper<Type<TypeCategory::Real, 8>> {
150 // IEE 754 64bits
151 using Type = std::conditional_t<sizeof(double) == 8 &&
152 std::numeric_limits<double>::is_iec559,
153 double, UnsupportedType>;
154 };
155
156 template <> struct HostTypeHelper<Type<TypeCategory::Real, 10>> {
157 // X87 80bits
158 using Type = std::conditional_t<sizeof(long double) >= 10 &&
159 std::numeric_limits<long double>::digits == 64 &&
160 std::numeric_limits<long double>::max_exponent == 16384,
161 long double, UnsupportedType>;
162 };
163
164 template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
165 // IEE 754 128bits
166 using Type = std::conditional_t<sizeof(long double) == 16 &&
167 std::numeric_limits<long double>::digits == 113 &&
168 std::numeric_limits<long double>::max_exponent == 16384,
169 long double, UnsupportedType>;
170 };
171
172 template <int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> {
173 using RealT = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
174 using Type = std::conditional_t<HostTypeExists<RealT>(),
175 std::complex<HostType<RealT>>, UnsupportedType>;
176 };
177
178 template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> {
179 using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>;
180 };
181
182 template <int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> {
183 using Type =
184 Scalar<typename Fortran::evaluate::Type<TypeCategory::Character, KIND>>;
185 };
186
187 // Type mapping from host types to F18 types. This need to be placed after all
188 // HostTypeHelper specializations.
189 template <typename T, typename... TT> struct IndexInTupleHelper {};
190 template <typename T, typename... TT>
191 struct IndexInTupleHelper<T, std::tuple<TT...>> {
192 static constexpr int value{common::TypeIndex<T, TT...>};
193 };
194 struct UnknownType {}; // the host type does not match any F18 types
195 template <typename HOST_T> struct FortranTypeHelper {
196 using HostTypeMapping =
197 common::MapTemplate<HostType, AllIntrinsicTypes, std::tuple>;
198 static constexpr int index{
199 IndexInTupleHelper<HOST_T, HostTypeMapping>::value};
200 // Both conditional types are "instantiated", so a valid type must be
201 // created for invalid index even if not used.
202 using Type = std::conditional_t<index >= 0,
203 std::tuple_element_t<(index >= 0) ? index : 0, AllIntrinsicTypes>,
204 UnknownType>;
205 };
206
207 template <typename HOST_T>
208 using FortranType = typename FortranTypeHelper<HOST_T>::Type;
209
210 template <typename... HT> constexpr inline bool FortranTypeExists() {
211 return (... && (!std::is_same_v<FortranType<HT>, UnknownType>));
212 }
213
214 // Utility to find "bigger" types that exist on host. By bigger, it is meant
215 // that the bigger type can represent all the values of the smaller types
216 // without information loss.
217 template <TypeCategory cat, int KIND> struct NextBiggerReal {
218 using Type = void;
219 };
220 template <TypeCategory cat> struct NextBiggerReal<cat, 2> {
221 using Type = Fortran::evaluate::Type<cat, 4>;
222 };
223 template <TypeCategory cat> struct NextBiggerReal<cat, 3> {
224 using Type = Fortran::evaluate::Type<cat, 4>;
225 };
226 template <TypeCategory cat> struct NextBiggerReal<cat, 4> {
227 using Type = Fortran::evaluate::Type<cat, 8>;
228 };
229
230 template <TypeCategory cat> struct NextBiggerReal<cat, 8> {
231 using Type = Fortran::evaluate::Type<cat, 10>;
232 };
233
234 template <TypeCategory cat> struct NextBiggerReal<cat, 10> {
235 using Type = Fortran::evaluate::Type<cat, 16>;
236 };
237
238 template <int KIND>
239 struct BiggerOrSameHostTypeHelper<Type<TypeCategory::Real, KIND>> {
240 using T = Fortran::evaluate::Type<TypeCategory::Real, KIND>;
241 using NextT = typename NextBiggerReal<TypeCategory::Real, KIND>::Type;
242 using Type = std::conditional_t<HostTypeExists<T>(), HostType<T>,
243 typename BiggerOrSameHostTypeHelper<NextT>::Type>;
244 using FortranType = std::conditional_t<HostTypeExists<T>(), T,
245 typename BiggerOrSameHostTypeHelper<NextT>::FortranType>;
246 };
247
248 template <int KIND>
249 struct BiggerOrSameHostTypeHelper<Type<TypeCategory::Complex, KIND>> {
250 using T = Fortran::evaluate::Type<TypeCategory::Complex, KIND>;
251 using NextT = typename NextBiggerReal<TypeCategory::Complex, KIND>::Type;
252 using Type = std::conditional_t<HostTypeExists<T>(), HostType<T>,
253 typename BiggerOrSameHostTypeHelper<NextT>::Type>;
254 using FortranType = std::conditional_t<HostTypeExists<T>(), T,
255 typename BiggerOrSameHostTypeHelper<NextT>::FortranType>;
256 };
257 } // namespace host
258 } // namespace Fortran::evaluate
259
260 #endif // FORTRAN_EVALUATE_HOST_H_
Fortran::evaluate::Type< TypeCategory::Complex, KIND >
Definition: type.h:286
Fortran::evaluate::host::HostTypeHelper
Definition: host.h:55
Fortran::evaluate::host::BiggerOrSameHostTypeHelper
Definition: host.h:98
Fortran::evaluate::host::HostFloatingPointEnvironment
Definition: host.h:32
Fortran::evaluate::host::UnknownType
Definition: host.h:194
Fortran::evaluate::Type
Definition: type.h:48
Fortran::evaluate::host::FortranTypeHelper
Definition: host.h:195
Fortran::evaluate::host::NextBiggerReal
Definition: host.h:217
Fortran::evaluate::FoldingContext
Definition: common.h:209
Fortran::evaluate::host::IndexInTupleHelper
Definition: host.h:189
Fortran::evaluate::host::UnsupportedType
Definition: host.h:53