1//===-- x86_64 floating point env manipulation functions --------*- 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 LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENVIMPL_H
10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENVIMPL_H
11
12#include "hdr/fenv_macros.h"
13#include "hdr/stdint_proxy.h"
14#include "hdr/types/fenv_t.h"
15#include "src/__support/CPP/bit.h"
16#include "src/__support/macros/attributes.h" // LIBC_INLINE
17#include "src/__support/macros/config.h"
18#include "src/__support/macros/optimization.h"
19#include "src/__support/macros/properties/architectures.h"
20#include "src/__support/macros/properties/compiler.h"
21#include "src/__support/macros/properties/types.h"
22
23#if !defined(LIBC_TARGET_ARCH_IS_X86)
24#error "Invalid include"
25#endif
26
27#if (defined(__i386__) && !defined(__SSE__)) || \
28 (defined(_M_IX86_FP) && (_M_IX86_FP == 0))
29// When SSE is not available, we will only touch x87 floating point environment.
30#include "src/__support/FPUtil/x86_64/fenv_x87_only.h"
31#else // __SSE__
32
33#ifndef LIBC_COMPILER_IS_MSVC
34#include "src/__support/FPUtil/x86_64/fenv_x87_utils.h"
35#endif // !LIBC_COMPILER_IS_MSVC
36
37#include "src/__support/FPUtil/x86_64/fenv_mxcsr_utils.h"
38
39namespace LIBC_NAMESPACE_DECL {
40namespace fputil {
41
42LIBC_INLINE static int clear_except(int excepts) {
43 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
44 sse::clear_except(excepts: x86_excepts);
45
46#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
47 x87::clear_except(excepts: x86_excepts);
48#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
49
50 return 0;
51}
52
53LIBC_INLINE static int test_except(int excepts) {
54 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
55 uint16_t tested_excepts = sse::test_except(excepts: x86_excepts);
56
57#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
58 tested_excepts |= x87::test_except(excepts: x86_excepts);
59#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
60
61 return internal::get_macro_from_exception_status(status: tested_excepts);
62}
63
64LIBC_INLINE static int get_except() {
65 uint16_t excepts = sse::get_except();
66
67#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
68 excepts |= x87::get_except();
69#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
70
71 return internal::get_macro_from_exception_status(status: excepts);
72}
73
74LIBC_INLINE static int set_except(int excepts) {
75 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
76 sse::set_except(x86_excepts);
77
78#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
79 x87::set_except(x86_excepts);
80#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
81
82 return 0;
83}
84
85// We will only OR sse exception flags. Even though this might make x87 and
86// sse exception flags not in sync, the results will be synchronized when
87// reading with get_except or test_except.
88LIBC_INLINE static int raise_except(int excepts) {
89 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
90 sse::raise_except(excepts: x86_excepts);
91 return 0;
92}
93
94LIBC_INLINE static int enable_except(int excepts) {
95 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
96 uint16_t old_excepts = sse::enable_except(excepts: x86_excepts);
97
98#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
99 old_excepts |= x87::enable_except(excepts: x86_excepts);
100#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
101
102 return internal::get_macro_from_exception_status(status: old_excepts);
103}
104
105LIBC_INLINE static int disable_except(int excepts) {
106 uint16_t x86_excepts = internal::get_status_value_from_except(excepts);
107 uint16_t old_excepts = sse::disable_except(excepts: x86_excepts);
108
109#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
110 old_excepts |= x87::disable_except(excepts: x86_excepts);
111#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
112
113 return internal::get_macro_from_exception_status(status: old_excepts);
114}
115
116LIBC_INLINE static int get_round() {
117 uint16_t rounding_mode = sse::get_round();
118 return internal::get_macro_from_rounding_control(rounding: rounding_mode);
119}
120
121LIBC_INLINE static int set_round(int rounding_mode) {
122 uint16_t rounding = internal::get_rounding_control_from_macro(rounding: rounding_mode);
123 if (LIBC_UNLIKELY(rounding == internal::RoundingControl::RC_ERROR))
124 return -1;
125 sse::set_round(rounding);
126
127#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
128 x87::set_round(rounding);
129#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
130
131 return 0;
132}
133
134LIBC_INLINE static int get_env(fenv_t *env) {
135#ifndef LIBC_COMPILER_IS_MSVC
136 if constexpr (sizeof(fenv_t) >= sizeof(internal::X87StateDescriptor)) {
137 // The fenv_t is expected to have x87 floating point environment.
138 internal::X87StateDescriptor x87_state;
139 x87::get_x87_state_descriptor(s&: x87_state);
140 uint32_t mxcsr = static_cast<uint32_t>(sse::get_mxcsr());
141 if constexpr (sizeof(fenv_t) == sizeof(internal::X87StateDescriptor)) {
142 // The fenv_t is expected to have only x87 floating point environment, so
143 // we merge sse data to x87 state.
144 internal::mxcsr_to_x87_state(mxcsr: static_cast<uint16_t>(mxcsr), s&: x87_state);
145 // Copy the state data;
146 const char *x87_state_ptr = reinterpret_cast<const char *>(&x87_state);
147 char *fenv_ptr = reinterpret_cast<char *>(env);
148 cpp::inline_copy<sizeof(x87_state)>(from: x87_state_ptr, to: fenv_ptr);
149 } else {
150 // We expect to have at least extra 32-bit for mxcsr register in the
151 // fenv_t.
152 static_assert(
153 sizeof(sizeof(fenv_t) >=
154 sizeof(internal::X87StateDescriptor) + sizeof(uint32_t)));
155 const char *x87_state_ptr = reinterpret_cast<const char *>(&x87_state);
156 const char *mxcsr_ptr = reinterpret_cast<const char *>(&mxcsr);
157 char *fenv_ptr = reinterpret_cast<char *>(env);
158 cpp::inline_copy<sizeof(x87_state)>(from: x87_state_ptr, to: fenv_ptr);
159 cpp::inline_copy<sizeof(mxcsr)>(from: mxcsr_ptr, to: fenv_ptr + sizeof(x87_state));
160 }
161 } else
162#endif // LIBC_COMPILER_IS_MSVC
163 if constexpr (sizeof(fenv_t) == 2 * sizeof(uint32_t)) {
164 // fenv_t has 2 * uint32_t to store mxcsr with control + status
165 // separately. We will just duplicate mxcsr on those two fields.
166 uint32_t mxcsr = static_cast<uint32_t>(sse::get_mxcsr());
167 const char *mxcsr_ptr = reinterpret_cast<const char *>(&mxcsr);
168 char *fenv_ptr = reinterpret_cast<char *>(env);
169 cpp::inline_copy<sizeof(mxcsr)>(from: mxcsr_ptr, to: fenv_ptr);
170 cpp::inline_copy<sizeof(mxcsr)>(from: mxcsr_ptr, to: fenv_ptr + sizeof(mxcsr));
171 } else {
172 // Just copy mxcsr over to fenv_t.
173 // Make sure fenv_t is big enough.
174 static_assert(sizeof(fenv_t) >= sizeof(uint32_t));
175 uint32_t mxcsr = static_cast<uint32_t>(sse::get_mxcsr());
176 const char *mxcsr_ptr = reinterpret_cast<const char *>(&mxcsr);
177 char *fenv_ptr = reinterpret_cast<char *>(env);
178 cpp::inline_copy<sizeof(mxcsr)>(from: mxcsr_ptr, to: fenv_ptr);
179 }
180 return 0;
181}
182
183LIBC_INLINE static int set_env(const fenv_t *env) {
184 if (env == FE_DFL_ENV) {
185#ifndef LIBC_COMPILER_IS_MSVC
186 x87::initialize_x87_state();
187#endif // LIBC_COMPILER_IS_MSVC
188 // Initial state of mxcsr:
189 // Round-to-nearest, all exceptions are masked, all exception flags are
190 // cleared.
191 sse::write_mxcsr(w: 0x1f80);
192 return 0;
193 }
194
195#ifndef LIBC_COMPILER_IS_MSVC
196 if constexpr (sizeof(fenv_t) > sizeof(internal::X87StateDescriptor)) {
197 // We expect to have at least extra 32-bit for mxcsr register in the fenv_t.
198 static_assert(
199 sizeof(sizeof(fenv_t) >=
200 sizeof(internal::X87StateDescriptor) + sizeof(uint32_t)));
201 internal::X87StateDescriptor x87_state;
202 uint32_t mxcsr = 0;
203
204 char *x87_state_ptr = reinterpret_cast<char *>(&x87_state);
205 char *mxcsr_ptr = reinterpret_cast<char *>(&mxcsr);
206 const char *fenv_ptr = reinterpret_cast<const char *>(env);
207
208 cpp::inline_copy<sizeof(x87_state)>(from: fenv_ptr, to: x87_state_ptr);
209 cpp::inline_copy<sizeof(mxcsr)>(from: fenv_ptr + sizeof(x87_state), to: mxcsr_ptr);
210
211 x87::write_x87_state_descriptor(s: x87_state);
212 sse::write_mxcsr(w: mxcsr);
213 } else if constexpr (sizeof(fenv_t) == sizeof(internal::X87StateDescriptor)) {
214 const internal::X87StateDescriptor *x87_state_ptr =
215 reinterpret_cast<const internal::X87StateDescriptor *>(env);
216 uint32_t mxcsr = internal::x87_state_to_mxcsr(s: *x87_state_ptr);
217
218 x87::write_x87_state_descriptor(s: *x87_state_ptr);
219 sse::write_mxcsr(w: mxcsr);
220 } else
221#endif // LIBC_COMPILER_IS_MSVC
222 if constexpr (sizeof(fenv_t) == 2 * sizeof(uint32_t)) {
223 // fenv_t has 2 * uint32_t to store mxcsr with control + status
224 // separately. We will just merge mxcsr on those two fields.
225 uint32_t mxcsr = 0, mxcsr_hi = 0;
226 char *mxcsr_ptr = reinterpret_cast<char *>(&mxcsr);
227 char *mxcsr_hi_ptr = reinterpret_cast<char *>(&mxcsr_hi);
228 const char *fenv_ptr = reinterpret_cast<const char *>(env);
229 cpp::inline_copy<sizeof(mxcsr)>(from: fenv_ptr, to: mxcsr_ptr);
230 cpp::inline_copy<sizeof(mxcsr_hi)>(from: fenv_ptr + sizeof(mxcsr),
231 to: mxcsr_hi_ptr);
232 sse::write_mxcsr(w: mxcsr | mxcsr_hi);
233 } else {
234 // Just copy mxcsr over to fenv_t.
235 // Make sure fenv_t is big enough.
236 static_assert(sizeof(fenv_t) >= sizeof(uint32_t));
237 uint32_t mxcsr = 0;
238 char *mxcsr_ptr = reinterpret_cast<char *>(&mxcsr);
239 const char *fenv_ptr = reinterpret_cast<const char *>(env);
240 cpp::inline_copy<sizeof(mxcsr)>(from: fenv_ptr, to: mxcsr_ptr);
241 sse::write_mxcsr(w: mxcsr);
242 }
243
244 return 0;
245}
246
247} // namespace fputil
248} // namespace LIBC_NAMESPACE_DECL
249
250#endif // __SSE__
251
252#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENVIMPL_H
253