[debug] Use poison instead of undef to set a killed dbg.assign address [NFC] (#119760)
[llvm-project.git] / libc / src / __support / FPUtil / arm / FEnvImpl.h
blobaaf37c0a045af771e1a4a4c383a800f57d78ed0b
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 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
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>
19 namespace LIBC_NAMESPACE_DECL {
20 namespace fputil {
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;
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;
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;
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;
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);
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);
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);
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);
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);
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);
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);
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);
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;
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);
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;
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 */);
150 uint32_t to_raise = FEnv::exception_macro_to_status_bits(excepts);
151 int result = 0;
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;
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;
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;
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;
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;
187 return result;
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.
204 return 0;
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
226 uint32_t fpscr = FEnv::get_fpscr();
227 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
228 fpscr |= (bits << FEnv::RoundingControlBitPosition);
229 FEnv::set_fpscr(fpscr);
231 return 0;
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;
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));
254 FEnv::set_fpscr(fpscr);
255 return 0;
258 const FEnv *state = reinterpret_cast<const FEnv *>(envp);
259 FEnv::set_fpscr(state->fpscr);
260 return 0;
263 } // namespace fputil
264 } // namespace LIBC_NAMESPACE_DECL
266 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H