1 //===-- aarch64 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_AARCH64_FENVIMPL_H 10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_H 11 12 #include "src/__support/macros/attributes.h" // LIBC_INLINE 13 #include "src/__support/macros/config.h" 14 #include "src/__support/macros/properties/architectures.h" 15 16 #if !defined(LIBC_TARGET_ARCH_IS_AARCH64) || defined(__APPLE__) 17 #error "Invalid include" 18 #endif 19 20 #include <arm_acle.h> 21 #include <stdint.h> 22 23 #include "hdr/fenv_macros.h" 24 #include "hdr/types/fenv_t.h" 25 #include "src/__support/FPUtil/FPBits.h" 26 27 namespace LIBC_NAMESPACE_DECL { 28 namespace fputil { 29 struct FEnv { 30 struct FPState { 31 uint32_t ControlWord; 32 uint32_t StatusWord; 33 }; 34 35 static_assert( 36 sizeof(fenv_t) == sizeof(FPState), 37 "Internal floating point state does not match the public fenv_t type."); 38 39 static constexpr uint32_t TONEAREST = 0x0; 40 static constexpr uint32_t UPWARD = 0x1; 41 static constexpr uint32_t DOWNWARD = 0x2; 42 static constexpr uint32_t TOWARDZERO = 0x3; 43 44 static constexpr uint32_t INVALID_F = 0x1; 45 static constexpr uint32_t DIVBYZERO_F = 0x2; 46 static constexpr uint32_t OVERFLOW_F = 0x4; 47 static constexpr uint32_t UNDERFLOW_F = 0x8; 48 static constexpr uint32_t INEXACT_F = 0x10; 49 50 // Zero-th bit is the first bit. 51 static constexpr uint32_t RoundingControlBitPosition = 22; 52 static constexpr uint32_t ExceptionStatusFlagsBitPosition = 0; 53 static constexpr uint32_t ExceptionControlFlagsBitPosition = 8; 54 55 LIBC_INLINE static uint32_t getStatusValueForExcept(int excepts) { 56 return ((excepts & FE_INVALID) ? INVALID_F : 0) | 57 ((excepts & FE_DIVBYZERO) ? DIVBYZERO_F : 0) | 58 ((excepts & FE_OVERFLOW) ? OVERFLOW_F : 0) | 59 ((excepts & FE_UNDERFLOW) ? UNDERFLOW_F : 0) | 60 ((excepts & FE_INEXACT) ? INEXACT_F : 0); 61 } 62 63 LIBC_INLINE static int exceptionStatusToMacro(uint32_t status) { 64 return ((status & INVALID_F) ? FE_INVALID : 0) | 65 ((status & DIVBYZERO_F) ? FE_DIVBYZERO : 0) | 66 ((status & OVERFLOW_F) ? FE_OVERFLOW : 0) | 67 ((status & UNDERFLOW_F) ? FE_UNDERFLOW : 0) | 68 ((status & INEXACT_F) ? FE_INEXACT : 0); 69 } 70 71 static uint32_t getControlWord() { 72 #ifdef __clang__ 73 // GCC does not currently support __arm_rsr. 74 return __arm_rsr("fpcr"); 75 #else 76 return __builtin_aarch64_get_fpcr(); 77 #endif 78 } 79 80 static void writeControlWord(uint32_t fpcr) { 81 #ifdef __clang__ 82 // GCC does not currently support __arm_wsr. 83 __arm_wsr("fpcr", fpcr); 84 #else 85 __builtin_aarch64_set_fpcr(fpcr); 86 #endif 87 } 88 89 static uint32_t getStatusWord() { 90 #ifdef __clang__ 91 return __arm_rsr("fpsr"); 92 #else 93 return __builtin_aarch64_get_fpsr(); 94 #endif 95 } 96 97 static void writeStatusWord(uint32_t fpsr) { 98 #ifdef __clang__ 99 __arm_wsr("fpsr", fpsr); 100 #else 101 __builtin_aarch64_set_fpsr(fpsr); 102 #endif 103 } 104 }; 105 106 LIBC_INLINE int enable_except(int excepts) { 107 uint32_t newExcepts = FEnv::getStatusValueForExcept(excepts); 108 uint32_t controlWord = FEnv::getControlWord(); 109 int oldExcepts = 110 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F; 111 controlWord |= (newExcepts << FEnv::ExceptionControlFlagsBitPosition); 112 FEnv::writeControlWord(controlWord); 113 return FEnv::exceptionStatusToMacro(oldExcepts); 114 } 115 116 LIBC_INLINE int disable_except(int excepts) { 117 uint32_t disabledExcepts = FEnv::getStatusValueForExcept(excepts); 118 uint32_t controlWord = FEnv::getControlWord(); 119 int oldExcepts = 120 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F; 121 controlWord &= ~(disabledExcepts << FEnv::ExceptionControlFlagsBitPosition); 122 FEnv::writeControlWord(controlWord); 123 return FEnv::exceptionStatusToMacro(oldExcepts); 124 } 125 126 LIBC_INLINE int get_except() { 127 uint32_t controlWord = FEnv::getControlWord(); 128 int enabledExcepts = 129 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F; 130 return FEnv::exceptionStatusToMacro(enabledExcepts); 131 } 132 133 LIBC_INLINE int clear_except(int excepts) { 134 uint32_t statusWord = FEnv::getStatusWord(); 135 uint32_t toClear = FEnv::getStatusValueForExcept(excepts); 136 statusWord &= ~(toClear << FEnv::ExceptionStatusFlagsBitPosition); 137 FEnv::writeStatusWord(statusWord); 138 return 0; 139 } 140 141 LIBC_INLINE int test_except(int excepts) { 142 uint32_t toTest = FEnv::getStatusValueForExcept(excepts); 143 uint32_t statusWord = FEnv::getStatusWord(); 144 return FEnv::exceptionStatusToMacro( 145 (statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & toTest); 146 } 147 148 LIBC_INLINE int set_except(int excepts) { 149 uint32_t statusWord = FEnv::getStatusWord(); 150 uint32_t statusValue = FEnv::getStatusValueForExcept(excepts); 151 statusWord |= (statusValue << FEnv::ExceptionStatusFlagsBitPosition); 152 FEnv::writeStatusWord(statusWord); 153 return 0; 154 } 155 156 LIBC_INLINE int raise_except(int excepts) { 157 float zero = 0.0f; 158 float one = 1.0f; 159 float largeValue = FPBits<float>::max_normal().get_val(); 160 float smallValue = FPBits<float>::min_normal().get_val(); 161 auto divfunc = [](float a, float b) { 162 __asm__ __volatile__("ldr s0, %0\n\t" 163 "ldr s1, %1\n\t" 164 "fdiv s0, s0, s1\n\t" 165 : // No outputs 166 : "m"(a), "m"(b) 167 : "s0", "s1" /* s0 and s1 are clobbered */); 168 }; 169 170 uint32_t toRaise = FEnv::getStatusValueForExcept(excepts); 171 int result = 0; 172 173 if (toRaise & FEnv::INVALID_F) { 174 divfunc(zero, zero); 175 uint32_t statusWord = FEnv::getStatusWord(); 176 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & 177 FEnv::INVALID_F)) 178 result = -1; 179 } 180 181 if (toRaise & FEnv::DIVBYZERO_F) { 182 divfunc(one, zero); 183 uint32_t statusWord = FEnv::getStatusWord(); 184 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & 185 FEnv::DIVBYZERO_F)) 186 result = -1; 187 } 188 if (toRaise & FEnv::OVERFLOW_F) { 189 divfunc(largeValue, smallValue); 190 uint32_t statusWord = FEnv::getStatusWord(); 191 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & 192 FEnv::OVERFLOW_F)) 193 result = -1; 194 } 195 if (toRaise & FEnv::UNDERFLOW_F) { 196 divfunc(smallValue, largeValue); 197 uint32_t statusWord = FEnv::getStatusWord(); 198 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & 199 FEnv::UNDERFLOW_F)) 200 result = -1; 201 } 202 if (toRaise & FEnv::INEXACT_F) { 203 float two = 2.0f; 204 float three = 3.0f; 205 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point 206 // format. 207 divfunc(two, three); 208 uint32_t statusWord = FEnv::getStatusWord(); 209 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & 210 FEnv::INEXACT_F)) 211 result = -1; 212 } 213 return result; 214 } 215 216 LIBC_INLINE int get_round() { 217 uint32_t roundingMode = 218 (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition) & 0x3; 219 switch (roundingMode) { 220 case FEnv::TONEAREST: 221 return FE_TONEAREST; 222 case FEnv::DOWNWARD: 223 return FE_DOWNWARD; 224 case FEnv::UPWARD: 225 return FE_UPWARD; 226 case FEnv::TOWARDZERO: 227 return FE_TOWARDZERO; 228 default: 229 return -1; // Error value. 230 } 231 } 232 233 LIBC_INLINE int set_round(int mode) { 234 uint16_t bitValue; 235 switch (mode) { 236 case FE_TONEAREST: 237 bitValue = FEnv::TONEAREST; 238 break; 239 case FE_DOWNWARD: 240 bitValue = FEnv::DOWNWARD; 241 break; 242 case FE_UPWARD: 243 bitValue = FEnv::UPWARD; 244 break; 245 case FE_TOWARDZERO: 246 bitValue = FEnv::TOWARDZERO; 247 break; 248 default: 249 return 1; // To indicate failure 250 } 251 252 uint32_t controlWord = FEnv::getControlWord(); 253 controlWord &= ~(0x3 << FEnv::RoundingControlBitPosition); 254 controlWord |= (bitValue << FEnv::RoundingControlBitPosition); 255 FEnv::writeControlWord(controlWord); 256 257 return 0; 258 } 259 260 LIBC_INLINE int get_env(fenv_t *envp) { 261 FEnv::FPState *state = reinterpret_cast<FEnv::FPState *>(envp); 262 state->ControlWord = FEnv::getControlWord(); 263 state->StatusWord = FEnv::getStatusWord(); 264 return 0; 265 } 266 267 LIBC_INLINE int set_env(const fenv_t *envp) { 268 if (envp == FE_DFL_ENV) { 269 // Default status and control words bits are all zeros so we just 270 // write zeros. 271 FEnv::writeStatusWord(0); 272 FEnv::writeControlWord(0); 273 return 0; 274 } 275 const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp); 276 FEnv::writeControlWord(state->ControlWord); 277 FEnv::writeStatusWord(state->StatusWord); 278 return 0; 279 } 280 } // namespace fputil 281 } // namespace LIBC_NAMESPACE_DECL 282 283 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_H 284