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