| 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 | |
| 21 | namespace LIBC_NAMESPACE_DECL { |
| 22 | namespace fputil { |
| 23 | |
| 24 | namespace x87 { |
| 25 | |
| 26 | using internal::ExceptionFlags; |
| 27 | using internal::RoundingControl; |
| 28 | using internal::X87StateDescriptor; |
| 29 | |
| 30 | LIBC_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 | |
| 43 | LIBC_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 | |
| 51 | LIBC_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 | |
| 64 | LIBC_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 | |
| 72 | LIBC_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 | |
| 81 | LIBC_INLINE static void |
| 82 | write_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 | |
| 90 | LIBC_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 | |
| 98 | LIBC_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 | |
| 110 | LIBC_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 | |
| 115 | LIBC_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 | |
| 120 | LIBC_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 | |
| 135 | LIBC_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 | |
| 148 | LIBC_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 | |
| 160 | LIBC_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 | |
| 172 | LIBC_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 | |
| 179 | LIBC_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 |