xref: /aosp_15_r20/external/llvm-libc/src/__support/FPUtil/arm/FEnvImpl.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 
get_fpscrFEnv48   LIBC_INLINE static uint32_t get_fpscr() { return __builtin_arm_get_fpscr(); }
set_fpscrFEnv49   LIBC_INLINE static void set_fpscr(uint32_t val) {
50     __builtin_arm_set_fpscr(val);
51   }
52 
exception_enable_bits_to_macroFEnv53   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 
exception_macro_to_enable_bitsFEnv61   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 
exception_macro_to_status_bitsFEnv69   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 
exception_status_bits_to_macroFEnv77   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.
enable_except(int excepts)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.
disable_except(int excepts)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.
get_except()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|.
clear_except(int 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|.
test_except(int 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|.
set_except(int 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 
raise_except(int excepts)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 
get_round()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 
set_round(int mode)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 
get_env(fenv_t * envp)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 
set_env(const fenv_t * envp)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