1//===-- x87 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_FENV_X86_COMMON_H
10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X86_COMMON_H
11
12#include <stdbool.h>
13
14#include "hdr/stdint_proxy.h"
15#include "hdr/types/fenv_t.h"
16#include "src/__support/macros/attributes.h"
17#include "src/__support/macros/config.h"
18#include "src/__support/macros/properties/architectures.h"
19#include "src/__support/macros/properties/compiler.h"
20#include "src/__support/macros/properties/cpu_features.h"
21
22namespace LIBC_NAMESPACE_DECL {
23namespace fputil {
24
25namespace internal {
26
27// Default order of floating point exception flags in x87 and mxcsr registers:
28// - Bit 0: Invalid Operations
29// - Bit 1: Denormal
30// - Bit 2: Divide-by-zero
31// - Bit 3: Overflow
32// - Bit 4: Underflow
33// - Bit 5: Inexact
34struct ExceptionFlags {
35 static constexpr uint16_t INVALID_F = 0x1;
36 // Some libcs define __FE_DENORM corresponding to the denormal input
37 // exception and include it in FE_ALL_EXCEPTS. We define and use it to
38 // support compiling against headers provided by such libcs.
39 static constexpr uint16_t DENORMAL_F = 0x2;
40 static constexpr uint16_t DIV_BY_ZERO_F = 0x4;
41 static constexpr uint16_t OVERFLOW_F = 0x8;
42 static constexpr uint16_t UNDERFLOW_F = 0x10;
43 static constexpr uint16_t INEXACT_F = 0x20;
44 static constexpr uint16_t ALL_F =
45 static_cast<uint16_t>(INVALID_F | DENORMAL_F | DIV_BY_ZERO_F |
46 OVERFLOW_F | UNDERFLOW_F | INEXACT_F);
47 static constexpr unsigned MXCSR_EXCEPTION_MASK_BIT_POSITION = 7;
48};
49
50LIBC_INLINE static constexpr bool fenv_exceptions_match_x86() {
51 return (FE_INVALID == ExceptionFlags::INVALID_F) &&
52#ifdef __FE_DENORM
53 (__FE_DENORM == ExceptionFlags::DENORMAL_F) &&
54#elif defined(FE_DENORM)
55 (FE_DENORM == ExceptionFlags::DENORMAL_F) &&
56#endif // __FE_DENORM
57 (FE_DIVBYZERO == ExceptionFlags::DIV_BY_ZERO_F) &&
58 (FE_OVERFLOW == ExceptionFlags::OVERFLOW_F) &&
59 (FE_UNDERFLOW == ExceptionFlags::UNDERFLOW_F) &&
60 (FE_INEXACT == ExceptionFlags::INEXACT_F);
61}
62
63// The rounding control values in the x87 control register and the MXCSR
64// register have the same 2-bit enoding but have different bit positions.
65// See below for the bit positions.
66struct RoundingControl {
67 static constexpr uint16_t TO_NEAREST = 0x0;
68 static constexpr uint16_t DOWNWARD = 0x1;
69 static constexpr uint16_t UPWARD = 0x2;
70 static constexpr uint16_t TOWARD_ZERO = 0x3;
71 static constexpr uint16_t ROUNDING_MASK = 0x3;
72 static constexpr unsigned X87_BIT_POSITION = 10;
73 static constexpr unsigned MXCSR_BIT_POSITION = 13;
74 static constexpr uint16_t X87_ROUNDING_MASK = ROUNDING_MASK
75 << X87_BIT_POSITION;
76 static constexpr uint16_t MXCSR_ROUNDING_MASK = ROUNDING_MASK
77 << MXCSR_BIT_POSITION;
78 static constexpr uint16_t RC_ERROR = 0xFFFF;
79};
80
81// Exception flags are individual bits in the corresponding registers.
82// So, we just OR the bit values to get the full set of exceptions.
83LIBC_INLINE static uint16_t get_status_value_from_except(int excepts) {
84 if constexpr (fenv_exceptions_match_x86()) {
85 return static_cast<uint16_t>(excepts & ExceptionFlags::ALL_F);
86 } else {
87 // We will make use of the fact that exception control bits are single
88 // bit flags in the control registers.
89 return ((excepts & FE_INVALID) ? ExceptionFlags::INVALID_F : 0) |
90#ifdef __FE_DENORM
91 ((excepts & __FE_DENORM) ? ExceptionFlags::DENORMAL_F : 0) |
92#elif defined(FE_DENORM)
93 ((excepts & FE_DENORM) ? ExceptionFlags::DENORMAL_F : 0) |
94#endif // __FE_DENORM
95 ((excepts & FE_DIVBYZERO) ? ExceptionFlags::DIV_BY_ZERO_F : 0) |
96 ((excepts & FE_OVERFLOW) ? ExceptionFlags::OVERFLOW_F : 0) |
97 ((excepts & FE_UNDERFLOW) ? ExceptionFlags::UNDERFLOW_F : 0) |
98 ((excepts & FE_INEXACT) ? ExceptionFlags::INEXACT_F : 0);
99 }
100}
101
102LIBC_INLINE static int get_macro_from_exception_status(uint16_t status) {
103 if constexpr (fenv_exceptions_match_x86()) {
104 return status & ExceptionFlags::ALL_F;
105 } else {
106 return ((status & ExceptionFlags::INVALID_F) ? FE_INVALID : 0) |
107#ifdef __FE_DENORM
108 ((status & ExceptionFlags::DENORMAL_F) ? __FE_DENORM : 0) |
109#elif defined(FE_DENORM)
110 ((status & ExceptionFlags::DENORMAL_F) ? FE_DENORM : 0) |
111#endif // __FE_DENORM
112 ((status & ExceptionFlags::DIV_BY_ZERO_F) ? FE_DIVBYZERO : 0) |
113 ((status & ExceptionFlags::OVERFLOW_F) ? FE_OVERFLOW : 0) |
114 ((status & ExceptionFlags::UNDERFLOW_F) ? FE_UNDERFLOW : 0) |
115 ((status & ExceptionFlags::INEXACT_F) ? FE_INEXACT : 0);
116 }
117}
118
119LIBC_INLINE static uint16_t get_rounding_control_from_macro(int rounding) {
120 switch (rounding) {
121 case FE_TONEAREST:
122 return RoundingControl::TO_NEAREST;
123 case FE_DOWNWARD:
124 return RoundingControl::DOWNWARD;
125 case FE_UPWARD:
126 return RoundingControl::UPWARD;
127 case FE_TOWARDZERO:
128 return RoundingControl::TOWARD_ZERO;
129 default:
130 return RoundingControl::RC_ERROR;
131 }
132}
133
134LIBC_INLINE static int get_macro_from_rounding_control(uint16_t rounding) {
135 switch (rounding) {
136 case RoundingControl::TO_NEAREST:
137 return FE_TONEAREST;
138 case RoundingControl::DOWNWARD:
139 return FE_DOWNWARD;
140 case RoundingControl::UPWARD:
141 return FE_UPWARD;
142 case RoundingControl::TOWARD_ZERO:
143 return FE_TOWARDZERO;
144 default:
145 return -1;
146 }
147}
148
149// x87 FPU environment from Intel 64 and IA-32 Architectures Software Developer
150// Manuals - Chapter 8
151// https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
152//
153// The x87 floating point environment will be save/load with FNSTENV/FLDENV
154// instructions, which will return the following 28-byte structure in 32-bit
155// mode (see section 8.1.10, figures 8-9 and 8-10 in the manual linked above),
156// in which we only use the control and status words.
157
158// x87 control word (16-bit) structure: (section 8.1.5 in the manual)
159// - Bit 0: Invalid Exception Mask
160// - Bit 1: Denormal Exception Mask
161// - Bit 2: Division-by-zero Exception Mask
162// - Bit 3: Overflow Exception Mask
163// - Bit 4: Underflow Exception Mask
164// - Bit 5: Inexact Exception Mask
165// - Bit 6-7: Reserved
166// - Bit 8-9: Precision Control
167// 00 - Single Precision
168// 01 - Reserved
169// 10 - Double Precision
170// 11 - Double Extended Precision (default)
171// - Bit 10-11: Rounding Control
172// 00 - Round to nearest, tie to even
173// 01 - Round down (toward -inf)
174// 10 - Round up (toward +inf)
175// 11 - Round toward zero (truncate)
176// - Bit 13-15: Reserved
177
178// x87 status word (16-bit) structure: (section 8.1.3 in the manual)
179// - Bit 0: Invalid Exception
180// - Bit 1: Denormal Exception
181// - Bit 2: Division-by-zero Exception
182// - Bit 3: Overflow Exception
183// - Bit 4: Underflow Exception
184// - Bit 5: Inexact Exception
185// - Bit 6: Stack Fault
186// - Bit 7 Exception Summary Status
187// - Bit 8-10: Condition Code
188// - Bit 11-13: Top-of-stack Pointer
189// - Bit 14: Condition Code
190// - Bit 15: FPU Busy Flag
191struct X87StateDescriptor {
192 uint16_t control_word;
193 uint16_t unused1;
194 uint16_t status_word;
195 uint16_t unused2;
196 uint32_t _[5];
197};
198
199// Putting x87 state descriptor to mxcsr.
200// SSE MXCSR register (32-bit) structure: (section 10.2.3 in the manual)
201// - Bit 0: Invalid Exception
202// - Bit 1: Denormal Exception
203// - Bit 2: Division-by-zero Exception
204// - Bit 3: Overflow Exception
205// - Bit 4: Underflow Exception
206// - Bit 5: Inexact Exception
207// - Bit 6: Denormal Are Zeros (DAZ)
208// - Bit 7: Invalid Exception Mask
209// - Bit 8: Denormal Exception Mask
210// - Bit 9: Division-by-zero Exception Mask
211// - Bit 10: Overflow Exception Mask
212// - Bit 11: Underflow Exception Mask
213// - Bit 12: Inexact Exception Mask
214// - Bit 13-14: Rounding Control
215// - Bit 15: Flush Denormal To Zero (FTZ)
216// - Bit 16-31: Reserved, will raise general-protection exception if set to
217// non-zero.
218// For all of the following exception functions, we assume the excepts are
219// normalized according to x86 and mxcsr exceptions defined in
220// fenv_x86_common.h: ExceptionFlags.
221LIBC_INLINE static uint16_t x87_state_to_mxcsr(const X87StateDescriptor &s) {
222 uint16_t mxcsr = 0;
223 // Copy 6 exception flags from status word.
224 mxcsr = s.status_word & ExceptionFlags::ALL_F;
225 // Copy 6 exception masks from control word.
226 mxcsr |= (s.control_word & ExceptionFlags::ALL_F)
227 << ExceptionFlags::MXCSR_EXCEPTION_MASK_BIT_POSITION;
228 // Copy 2-bit rounding control.
229 mxcsr |= (s.control_word & RoundingControl::X87_ROUNDING_MASK)
230 << (RoundingControl::MXCSR_BIT_POSITION -
231 RoundingControl::X87_BIT_POSITION);
232 return mxcsr;
233}
234
235LIBC_INLINE static void mxcsr_to_x87_state(uint16_t mxcsr,
236 X87StateDescriptor &s) {
237 // Clear exception mask and rounding control.
238 s.control_word &=
239 ~(ExceptionFlags::ALL_F | RoundingControl::X87_ROUNDING_MASK);
240 // Copy 6 exception masks.
241 s.control_word |=
242 (mxcsr >> ExceptionFlags::MXCSR_EXCEPTION_MASK_BIT_POSITION) &
243 ExceptionFlags::ALL_F;
244 // Copy rounding control.
245 s.control_word |=
246 (mxcsr & RoundingControl::MXCSR_ROUNDING_MASK) >>
247 (RoundingControl::MXCSR_BIT_POSITION - RoundingControl::X87_BIT_POSITION);
248 // Clear exception flags
249 s.status_word &= ~ExceptionFlags::ALL_F;
250 // Copy 6 exception status flags.
251 s.status_word |= mxcsr & ExceptionFlags::ALL_F;
252}
253
254} // namespace internal
255
256} // namespace fputil
257} // namespace LIBC_NAMESPACE_DECL
258
259#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X86_COMMON_H
260