1//===-- x87 floating point env manipulation utilities -----------*- 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_FENV_X87_UTILS_H
10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X87_UTILS_H
11
12#include "hdr/stdint_proxy.h"
13#include "hdr/types/fenv_t.h"
14#include "src/__support/FPUtil/x86_64/fenv_x86_common.h"
15#include "src/__support/macros/attributes.h" // LIBC_INLINE
16#include "src/__support/macros/config.h"
17#include "src/__support/macros/properties/architectures.h"
18#include "src/__support/macros/properties/compiler.h"
19#include "src/__support/macros/sanitizer.h"
20
21namespace LIBC_NAMESPACE_DECL {
22namespace fputil {
23
24namespace x87 {
25
26using internal::ExceptionFlags;
27using internal::RoundingControl;
28using internal::X87StateDescriptor;
29
30LIBC_INLINE static uint16_t get_x87_control_word() {
31 uint16_t w;
32
33#ifdef LIBC_COMPILER_IS_MSVC
34 __asm fstcw w;
35#else // !LIBC_COMPILER_IS_MSVC
36 asm volatile("fnstcw %0" : "=m"(w)::);
37 LIBC_MSAN_UNPOISON(&w, sizeof(w));
38#endif // LIBC_COMPILER_IS_MSVC
39
40 return w;
41}
42
43LIBC_INLINE static void write_x87_control_word(uint16_t w) {
44#ifdef LIBC_COMPILER_IS_MSVC
45 __asm fldcw w;
46#else // !LIBC_COMPILER_IS_MSVC
47 asm volatile("fldcw %0" : : "m"(w) :);
48#endif // LIBC_COMPILER_IS_MSVC
49}
50
51LIBC_INLINE static uint16_t get_x87_status_word() {
52 uint16_t w;
53
54#ifdef LIBC_COMPILER_IS_MSVC
55 __asm fnstsw w;
56#else // !LIBC_COMPILER_IS_MSVC
57 asm volatile("fnstsw %0" : "=m"(w)::);
58 LIBC_MSAN_UNPOISON(&w, sizeof(w));
59#endif // LIBC_COMPILER_IS_MSVC
60
61 return w;
62}
63
64LIBC_INLINE static void clear_x87_exceptions() {
65#ifdef LIBC_COMPILER_IS_MSVC
66 __asm fnclex;
67#else // !LIBC_COMPILER_IS_MSVC
68 asm volatile("fnclex" : : :);
69#endif // LIBC_COMPILER_IS_MSVC
70}
71
72LIBC_INLINE static void get_x87_state_descriptor(X87StateDescriptor &s) {
73#ifdef LIBC_COMPILER_IS_MSVC
74 __asm fnstenv s;
75#else // !LIBC_COMPILER_IS_MSVC
76 asm volatile("fnstenv %0" : "=m"(s));
77 LIBC_MSAN_UNPOISON(&s, sizeof(s));
78#endif // LIBC_COMPILER_IS_MSVC
79}
80
81LIBC_INLINE static void
82write_x87_state_descriptor(const X87StateDescriptor &s) {
83#ifdef LIBC_COMPILER_IS_MSVC
84 __asm fldenv s;
85#else // !LIBC_COMPILER_IS_MSVC
86 asm volatile("fldenv %0" : : "m"(s) :);
87#endif // LIBC_COMPILER_IS_MSVC
88}
89
90LIBC_INLINE static void initialize_x87_state() {
91#ifdef LIBC_COMPILER_IS_MSVC
92 __asm fninit;
93#else // !LIBC_COMPILER_IS_MSVC
94 asm volatile("fninit" : : :);
95#endif // LIBC_COMPILER_IS_MSVC
96}
97
98LIBC_INLINE static void clear_except(uint16_t excepts) {
99 if (excepts == ExceptionFlags::ALL_F) {
100 clear_x87_exceptions();
101 return;
102 }
103
104 X87StateDescriptor x87_descriptor;
105 get_x87_state_descriptor(s&: x87_descriptor);
106 x87_descriptor.status_word &= static_cast<uint16_t>(~excepts);
107 write_x87_state_descriptor(s: x87_descriptor);
108}
109
110LIBC_INLINE static uint16_t test_except(uint16_t excepts) {
111 uint16_t x87_status = get_x87_status_word();
112 return static_cast<uint16_t>(x87_status & excepts);
113}
114
115LIBC_INLINE static uint16_t get_except() {
116 uint16_t x87_control = get_x87_control_word();
117 return static_cast<uint16_t>((~x87_control) & ExceptionFlags::ALL_F);
118}
119
120LIBC_INLINE static void set_except(uint16_t excepts) {
121 X87StateDescriptor x87_descriptor;
122 get_x87_state_descriptor(s&: x87_descriptor);
123 uint16_t current_excepts =
124 static_cast<uint16_t>(x87_descriptor.status_word & ExceptionFlags::ALL_F);
125 // Do nothing if excepts are unchanged.
126 if (current_excepts == excepts)
127 return;
128 // Clear excepts.
129 x87_descriptor.status_word &= static_cast<uint16_t>(~ExceptionFlags::ALL_F);
130 // Set excepts.
131 x87_descriptor.status_word |= excepts;
132 write_x87_state_descriptor(s: x87_descriptor);
133}
134
135LIBC_INLINE static void raise_except(uint16_t excepts) {
136 X87StateDescriptor x87_descriptor;
137 get_x87_state_descriptor(s&: x87_descriptor);
138 uint16_t current_excepts =
139 static_cast<uint16_t>(x87_descriptor.status_word & ExceptionFlags::ALL_F);
140 // Do nothing if excepts are unchanged.
141 if ((current_excepts | excepts) == current_excepts)
142 return;
143 // Update excepts.
144 x87_descriptor.status_word |= excepts;
145 write_x87_state_descriptor(s: x87_descriptor);
146}
147
148LIBC_INLINE static uint16_t enable_except(uint16_t excepts) {
149 uint16_t x87_control = get_x87_control_word();
150 uint16_t old_excepts =
151 static_cast<uint16_t>(~x87_control & ExceptionFlags::ALL_F);
152 // Only update if excepts are not enabled.
153 if ((excepts | old_excepts) != old_excepts) {
154 x87_control &= static_cast<uint16_t>(~excepts);
155 write_x87_control_word(w: x87_control);
156 }
157 return old_excepts;
158}
159
160LIBC_INLINE static uint16_t disable_except(uint16_t excepts) {
161 uint16_t x87_control = get_x87_control_word();
162 uint16_t old_excepts =
163 static_cast<uint16_t>(~x87_control & ExceptionFlags::ALL_F);
164 // Only update excepts if some of the excepts are enabled.
165 if ((x87_control | excepts) != x87_control) {
166 x87_control |= excepts;
167 write_x87_control_word(w: x87_control);
168 }
169 return old_excepts;
170}
171
172LIBC_INLINE static uint16_t get_round() {
173 uint16_t x87_control = get_x87_control_word();
174 return static_cast<uint16_t>(
175 (x87_control >> RoundingControl::X87_BIT_POSITION) &
176 RoundingControl::ROUNDING_MASK);
177}
178
179LIBC_INLINE static void set_round(uint16_t rounding_mode) {
180 uint16_t x87_control = get_x87_control_word();
181 rounding_mode <<= RoundingControl::X87_BIT_POSITION;
182 uint16_t x87_control_new =
183 (x87_control & (~RoundingControl::X87_ROUNDING_MASK)) | rounding_mode;
184 // Only update if rounding mode changes.
185 if (x87_control_new != x87_control)
186 write_x87_control_word(w: x87_control_new);
187}
188
189} // namespace x87
190
191} // namespace fputil
192} // namespace LIBC_NAMESPACE_DECL
193
194#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X87_UTILS_H
195