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#if HAS_QUADMATHLIB
21#include "quadmath_wrapper.h"
22#include "flang/Common/float128.h"
23#endif
24#include "flang/Evaluate/type.h"
25#include <cfenv>
26#include <complex>
27#include <cstdint>
28#include <limits>
29#include <type_traits>
30
31namespace Fortran::evaluate {
32namespace host {
33
34// Helper class to handle host runtime traps, status flag and errno
36public:
37 void SetUpHostFloatingPointEnvironment(FoldingContext &);
38 void CheckAndRestoreFloatingPointEnvironment(FoldingContext &);
39 bool hasSubnormalFlushingHardwareControl() const {
40 return hasSubnormalFlushingHardwareControl_;
41 }
42 void SetFlag(RealFlag flag) { flags_.set(flag); }
43 bool hardwareFlagsAreReliable() const { return hardwareFlagsAreReliable_; }
44
45private:
46 std::fenv_t originalFenv_;
47#if __x86_64__
48 unsigned int originalMxcsr;
49#endif
50 RealFlags flags_;
51 bool hasSubnormalFlushingHardwareControl_{false};
52 bool hardwareFlagsAreReliable_{true};
53};
54
55// Type mapping from F18 types to host types
56struct UnsupportedType {}; // There is no host type for the F18 type
57
58template <typename FTN_T> struct HostTypeHelper {
59 using Type = UnsupportedType;
60};
61template <typename FTN_T> using HostType = typename HostTypeHelper<FTN_T>::Type;
62
63template <typename... T> constexpr inline bool HostTypeExists() {
64 return (... && (!std::is_same_v<HostType<T>, UnsupportedType>));
65}
66
67// Type mapping from host types to F18 types FortranType<HOST_T> is defined
68// after all HosTypeHelper definition because it reverses them to avoid
69// duplication.
70
71// Scalar conversion utilities from host scalars to F18 scalars
72template <typename FTN_T>
73inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
74 static_assert(HostTypeExists<FTN_T>());
75 if constexpr (FTN_T::category == TypeCategory::Complex &&
76 Scalar<FTN_T>::bytesStored() != sizeof(HostType<FTN_T>)) {
77 // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
78 // complex
79 return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
80 CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
81 } else {
82 static_assert(Scalar<FTN_T>::bytesStored() == sizeof(HostType<FTN_T>));
83 return Scalar<FTN_T>::FromRawBytes(&x, sizeof(x));
84 }
85}
86
87// Scalar conversion utilities from F18 scalars to host scalars.
88template <typename FTN_T>
89inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
90 static_assert(HostTypeExists<FTN_T>());
91 if constexpr (FTN_T::category == TypeCategory::Complex &&
92 Scalar<FTN_T>::bytesStored() != sizeof(HostType<FTN_T>)) {
93 using FortranPartType = typename FTN_T::Part;
94 return HostType<FTN_T>{CastFortranToHost<FortranPartType>(x.REAL()),
95 CastFortranToHost<FortranPartType>(x.AIMAG())};
96 } else {
97 static_assert(Scalar<FTN_T>::bytesStored() == sizeof(HostType<FTN_T>));
98 HostType<FTN_T> result;
99 x.StoreRawBytes(&result, sizeof(result));
100 return result;
101 }
102}
103
104template <> struct HostTypeHelper<Type<TypeCategory::Integer, 1>> {
105 using Type = std::int8_t;
106};
107
108template <> struct HostTypeHelper<Type<TypeCategory::Integer, 2>> {
109 using Type = std::int16_t;
110};
111
112template <> struct HostTypeHelper<Type<TypeCategory::Integer, 4>> {
113 using Type = std::int32_t;
114};
115
116template <> struct HostTypeHelper<Type<TypeCategory::Integer, 8>> {
117 using Type = std::int64_t;
118};
119
120template <> struct HostTypeHelper<Type<TypeCategory::Integer, 16>> {
121#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__)
122 using Type = __int128_t;
123#else
124 using Type = UnsupportedType;
125#endif
126};
127
128// TODO no mapping to host types are defined currently for 16bits float
129// It should be defined when gcc/clang have a better support for it.
130
131template <>
133 Type<TypeCategory::Real, common::RealKindForPrecision(24)>> {
134 // IEEE 754 32bits
135 using Type = std::conditional_t<sizeof(float) == 4 &&
136 std::numeric_limits<float>::is_iec559,
137 float, UnsupportedType>;
138};
139
140template <>
142 Type<TypeCategory::Real, common::RealKindForPrecision(53)>> {
143 // IEEE 754 64bits
144 using Type = std::conditional_t<sizeof(double) == 8 &&
145 std::numeric_limits<double>::is_iec559,
146 double, UnsupportedType>;
147};
148
149template <>
151 Type<TypeCategory::Real, common::RealKindForPrecision(64)>> {
152 // X87 80bits
153 using Type = std::conditional_t<sizeof(long double) >= 10 &&
154 std::numeric_limits<long double>::digits == 64 &&
155 std::numeric_limits<long double>::max_exponent == 16384,
156 long double, UnsupportedType>;
157};
158
159#if HAS_QUADMATHLIB
160template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
161 // IEEE 754 128bits
162 using Type = __float128;
163};
164#else
165template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
166 // IEEE 754 128bits
167 using Type = std::conditional_t<sizeof(long double) == 16 &&
168 std::numeric_limits<long double>::digits == 113 &&
169 std::numeric_limits<long double>::max_exponent == 16384,
170 long double, UnsupportedType>;
171};
172#endif
173
174template <int KIND> struct HostTypeHelper<Type<TypeCategory::Complex, KIND>> {
176 using Type = std::conditional_t<HostTypeExists<RealT>(),
177 std::complex<HostType<RealT>>, UnsupportedType>;
178};
179
180#if HAS_QUADMATHLIB
181template <> struct HostTypeHelper<Type<TypeCategory::Complex, 16>> {
183 using Type = __complex128;
184};
185#endif
186
187template <int KIND> struct HostTypeHelper<Type<TypeCategory::Logical, KIND>> {
188 using Type = std::conditional_t<KIND <= 8, std::uint8_t, UnsupportedType>;
189};
190
191template <int KIND> struct HostTypeHelper<Type<TypeCategory::Character, KIND>> {
192 using Type =
193 Scalar<typename Fortran::evaluate::Type<TypeCategory::Character, KIND>>;
194};
195
196// Type mapping from host types to F18 types. This need to be placed after all
197// HostTypeHelper specializations.
198template <typename T, typename... TT> struct IndexInTupleHelper {};
199template <typename T, typename... TT>
200struct IndexInTupleHelper<T, std::tuple<TT...>> {
201 static constexpr int value{common::TypeIndex<T, TT...>};
202};
203struct UnknownType {}; // the host type does not match any F18 types
204template <typename HOST_T> struct FortranTypeHelper {
205 using HostTypeMapping =
206 common::MapTemplate<HostType, AllIntrinsicTypes, std::tuple>;
207 static constexpr int index{
209 // Both conditional types are "instantiated", so a valid type must be
210 // created for invalid index even if not used.
211 using Type = std::conditional_t<index >= 0,
212 std::tuple_element_t<(index >= 0) ? index : 0, AllIntrinsicTypes>,
214};
215
216template <typename HOST_T>
217using FortranType = typename FortranTypeHelper<HOST_T>::Type;
218
219template <typename... HT> constexpr inline bool FortranTypeExists() {
220 return (... && (!std::is_same_v<FortranType<HT>, UnknownType>));
221}
222
223} // namespace host
224} // namespace Fortran::evaluate
225
226#endif // FORTRAN_EVALUATE_HOST_H_
Definition common.h:217
Definition type.h:56
Definition call.h:34