| 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 | |
| 22 | namespace LIBC_NAMESPACE_DECL { |
| 23 | namespace fputil { |
| 24 | |
| 25 | namespace 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 |
| 34 | struct 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 | |
| 50 | LIBC_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. |
| 66 | struct 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. |
| 83 | LIBC_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 | |
| 102 | LIBC_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 | |
| 119 | LIBC_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 | |
| 134 | LIBC_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 |
| 191 | struct 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. |
| 221 | LIBC_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 | |
| 235 | LIBC_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 |