1 //===-- arm 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_ARM_FENVIMPL_H 10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H 11 12 #include "hdr/fenv_macros.h" 13 #include "hdr/types/fenv_t.h" 14 #include "src/__support/FPUtil/FPBits.h" 15 #include "src/__support/macros/attributes.h" // For LIBC_INLINE 16 #include "src/__support/macros/config.h" 17 #include <stdint.h> 18 19 namespace LIBC_NAMESPACE_DECL { 20 namespace fputil { 21 22 struct FEnv { 23 // Arm floating point state is all stored in a single 32-bit register named 24 // fpscr. 25 uint32_t fpscr; 26 static constexpr uint32_t RoundingControlBitPosition = 22; 27 static constexpr uint32_t ExceptionControlBitPosition = 8; 28 29 static constexpr uint32_t TONEAREST = 0x0; 30 static constexpr uint32_t UPWARD = 0x1; 31 static constexpr uint32_t DOWNWARD = 0x2; 32 static constexpr uint32_t TOWARDZERO = 0x3; 33 34 static constexpr uint32_t INVALID_ENABLE = 0x1; 35 static constexpr uint32_t DIVBYZERO_ENABLE = 0x2; 36 static constexpr uint32_t OVERFLOW_ENABLE = 0x4; 37 static constexpr uint32_t UNDERFLOW_ENABLE = 0x8; 38 static constexpr uint32_t INEXACT_ENABLE = 0x10; 39 static constexpr uint32_t DENORMAL_ENABLE = 0x20; 40 41 static constexpr uint32_t INVALID_STATUS = 0x1; 42 static constexpr uint32_t DIVBYZERO_STATUS = 0x2; 43 static constexpr uint32_t OVERFLOW_STATUS = 0x4; 44 static constexpr uint32_t UNDERFLOW_STATUS = 0x8; 45 static constexpr uint32_t INEXACT_STATUS = 0x10; 46 static constexpr uint32_t DENORMAL_STATUS = 0x80; 47 48 LIBC_INLINE static uint32_t get_fpscr() { return __builtin_arm_get_fpscr(); } 49 LIBC_INLINE static void set_fpscr(uint32_t val) { 50 __builtin_arm_set_fpscr(val); 51 } 52 53 LIBC_INLINE static int exception_enable_bits_to_macro(uint32_t status) { 54 return ((status & INVALID_ENABLE) ? FE_INVALID : 0) | 55 ((status & DIVBYZERO_ENABLE) ? FE_DIVBYZERO : 0) | 56 ((status & OVERFLOW_ENABLE) ? FE_OVERFLOW : 0) | 57 ((status & UNDERFLOW_ENABLE) ? FE_UNDERFLOW : 0) | 58 ((status & INEXACT_ENABLE) ? FE_INEXACT : 0); 59 } 60 61 LIBC_INLINE static uint32_t exception_macro_to_enable_bits(int except) { 62 return ((except & FE_INVALID) ? INVALID_ENABLE : 0) | 63 ((except & FE_DIVBYZERO) ? DIVBYZERO_ENABLE : 0) | 64 ((except & FE_OVERFLOW) ? OVERFLOW_ENABLE : 0) | 65 ((except & FE_UNDERFLOW) ? UNDERFLOW_ENABLE : 0) | 66 ((except & FE_INEXACT) ? INEXACT_ENABLE : 0); 67 } 68 69 LIBC_INLINE static uint32_t exception_macro_to_status_bits(int except) { 70 return ((except & FE_INVALID) ? INVALID_STATUS : 0) | 71 ((except & FE_DIVBYZERO) ? DIVBYZERO_STATUS : 0) | 72 ((except & FE_OVERFLOW) ? OVERFLOW_STATUS : 0) | 73 ((except & FE_UNDERFLOW) ? UNDERFLOW_STATUS : 0) | 74 ((except & FE_INEXACT) ? INEXACT_STATUS : 0); 75 } 76 77 LIBC_INLINE static uint32_t exception_status_bits_to_macro(int status) { 78 return ((status & INVALID_STATUS) ? FE_INVALID : 0) | 79 ((status & DIVBYZERO_STATUS) ? FE_DIVBYZERO : 0) | 80 ((status & OVERFLOW_STATUS) ? FE_OVERFLOW : 0) | 81 ((status & UNDERFLOW_STATUS) ? FE_UNDERFLOW : 0) | 82 ((status & INEXACT_STATUS) ? FE_INEXACT : 0); 83 } 84 }; 85 86 // Enables exceptions in |excepts| and returns the previously set exceptions. 87 LIBC_INLINE int enable_except(int excepts) { 88 uint32_t new_excepts = FEnv::exception_macro_to_enable_bits(excepts); 89 uint32_t fpscr = FEnv::get_fpscr(); 90 int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F; 91 fpscr |= (new_excepts << FEnv::ExceptionControlBitPosition); 92 FEnv::set_fpscr(fpscr); 93 return FEnv::exception_enable_bits_to_macro(old); 94 } 95 96 // Disables exceptions in |excepts| and returns the previously set exceptions. 97 LIBC_INLINE int disable_except(int excepts) { 98 uint32_t disable_bits = FEnv::exception_macro_to_enable_bits(excepts); 99 uint32_t fpscr = FEnv::get_fpscr(); 100 int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F; 101 fpscr &= ~(disable_bits << FEnv::ExceptionControlBitPosition); 102 FEnv::set_fpscr(fpscr); 103 return FEnv::exception_enable_bits_to_macro(old); 104 } 105 106 // Returns the currently enabled exceptions. 107 LIBC_INLINE int get_except() { 108 uint32_t fpscr = FEnv::get_fpscr(); 109 int enabled_excepts = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F; 110 return FEnv::exception_enable_bits_to_macro(enabled_excepts); 111 } 112 113 // Clears the exceptions in |excepts|. 114 LIBC_INLINE int clear_except(int excepts) { 115 uint32_t fpscr = FEnv::get_fpscr(); 116 uint32_t to_clear = FEnv::exception_macro_to_status_bits(excepts); 117 fpscr &= ~to_clear; 118 FEnv::set_fpscr(fpscr); 119 return 0; 120 } 121 122 // Returns the set of exceptions which are from the input set |excepts|. 123 LIBC_INLINE int test_except(int excepts) { 124 uint32_t to_test = FEnv::exception_macro_to_status_bits(excepts); 125 uint32_t fpscr = FEnv::get_fpscr(); 126 return FEnv::exception_status_bits_to_macro(fpscr & 0x9F & to_test); 127 } 128 129 // Set the exceptions in |excepts|. 130 LIBC_INLINE int set_except(int excepts) { 131 uint32_t fpscr = FEnv::get_fpscr(); 132 FEnv::set_fpscr(fpscr | FEnv::exception_macro_to_status_bits(excepts)); 133 return 0; 134 } 135 136 LIBC_INLINE int raise_except(int excepts) { 137 float zero = 0.0f; 138 float one = 1.0f; 139 float large_value = FPBits<float>::max_normal().get_val(); 140 float small_value = FPBits<float>::min_normal().get_val(); 141 auto divfunc = [](float a, float b) { 142 __asm__ __volatile__("flds s0, %0\n\t" 143 "flds s1, %1\n\t" 144 "fdivs s0, s0, s1\n\t" 145 : // No outputs 146 : "m"(a), "m"(b) 147 : "s0", "s1" /* s0 and s1 are clobbered */); 148 }; 149 150 uint32_t to_raise = FEnv::exception_macro_to_status_bits(excepts); 151 int result = 0; 152 153 if (to_raise & FEnv::INVALID_STATUS) { 154 divfunc(zero, zero); 155 uint32_t fpscr = FEnv::get_fpscr(); 156 if (!(fpscr & FEnv::INVALID_STATUS)) 157 result = -1; 158 } 159 if (to_raise & FEnv::DIVBYZERO_STATUS) { 160 divfunc(one, zero); 161 uint32_t fpscr = FEnv::get_fpscr(); 162 if (!(fpscr & FEnv::DIVBYZERO_STATUS)) 163 result = -1; 164 } 165 if (to_raise & FEnv::OVERFLOW_STATUS) { 166 divfunc(large_value, small_value); 167 uint32_t fpscr = FEnv::get_fpscr(); 168 if (!(fpscr & FEnv::OVERFLOW_STATUS)) 169 result = -1; 170 } 171 if (to_raise & FEnv::UNDERFLOW_STATUS) { 172 divfunc(small_value, large_value); 173 uint32_t fpscr = FEnv::get_fpscr(); 174 if (!(fpscr & FEnv::UNDERFLOW_STATUS)) 175 result = -1; 176 } 177 if (to_raise & FEnv::INEXACT_STATUS) { 178 float two = 2.0f; 179 float three = 3.0f; 180 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point 181 // format. 182 divfunc(two, three); 183 uint32_t fpscr = FEnv::get_fpscr(); 184 if (!(fpscr & FEnv::INEXACT_STATUS)) 185 result = -1; 186 } 187 return result; 188 } 189 190 LIBC_INLINE int get_round() { 191 uint32_t mode = (FEnv::get_fpscr() >> FEnv::RoundingControlBitPosition) & 0x3; 192 switch (mode) { 193 case FEnv::TONEAREST: 194 return FE_TONEAREST; 195 case FEnv::DOWNWARD: 196 return FE_DOWNWARD; 197 case FEnv::UPWARD: 198 return FE_UPWARD; 199 case FEnv::TOWARDZERO: 200 return FE_TOWARDZERO; 201 default: 202 return -1; // Error value. 203 } 204 return 0; 205 } 206 207 LIBC_INLINE int set_round(int mode) { 208 uint16_t bits; 209 switch (mode) { 210 case FE_TONEAREST: 211 bits = FEnv::TONEAREST; 212 break; 213 case FE_DOWNWARD: 214 bits = FEnv::DOWNWARD; 215 break; 216 case FE_UPWARD: 217 bits = FEnv::UPWARD; 218 break; 219 case FE_TOWARDZERO: 220 bits = FEnv::TOWARDZERO; 221 break; 222 default: 223 return 1; // To indicate failure 224 } 225 226 uint32_t fpscr = FEnv::get_fpscr(); 227 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition); 228 fpscr |= (bits << FEnv::RoundingControlBitPosition); 229 FEnv::set_fpscr(fpscr); 230 231 return 0; 232 } 233 234 LIBC_INLINE int get_env(fenv_t *envp) { 235 FEnv *state = reinterpret_cast<FEnv *>(envp); 236 state->fpscr = FEnv::get_fpscr(); 237 return 0; 238 } 239 240 LIBC_INLINE int set_env(const fenv_t *envp) { 241 if (envp == FE_DFL_ENV) { 242 uint32_t fpscr = FEnv::get_fpscr(); 243 // Default status implies: 244 // 1. Round to nearest rounding mode. 245 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition); 246 fpscr |= (FEnv::TONEAREST << FEnv::RoundingControlBitPosition); 247 // 2. All exceptions are disabled. 248 fpscr &= ~(0x3F << FEnv::ExceptionControlBitPosition); 249 // 3. All exceptions are cleared. There are two reserved bits 250 // at bit 5 and 6 so we just write one full byte (6 bits for 251 // the exceptions, and 2 reserved bits.) 252 fpscr &= ~(static_cast<uint32_t>(0xFF)); 253 254 FEnv::set_fpscr(fpscr); 255 return 0; 256 } 257 258 const FEnv *state = reinterpret_cast<const FEnv *>(envp); 259 FEnv::set_fpscr(state->fpscr); 260 return 0; 261 } 262 263 } // namespace fputil 264 } // namespace LIBC_NAMESPACE_DECL 265 266 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H 267