xref: /freebsd/contrib/llvm-project/libc/src/__support/FPUtil/aarch64/fenv_darwin_impl.h (revision bb722a7d0f1642bff6487f943ad0427799a6e5bf)
1 //===- darwin-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_FENV_DARWIN_IMPL_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENV_DARWIN_IMPL_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 
30 struct FEnv {
31   struct FPState {
32     uint64_t StatusWord;
33     uint64_t ControlWord;
34   };
35 
36   static_assert(
37       sizeof(fenv_t) == sizeof(FPState),
38       "Internal floating point state does not match the public fenv_t type.");
39 
40   static constexpr uint32_t TONEAREST = 0x0;
41   static constexpr uint32_t UPWARD = 0x1;
42   static constexpr uint32_t DOWNWARD = 0x2;
43   static constexpr uint32_t TOWARDZERO = 0x3;
44 
45   // These will be the exception flags we use for exception values normalized
46   // from both status word and control word.
47   // We add EX_ prefix to the names since macOS <math.h> defines OVERFLOW and
48   // UNDERFLOW macros.
49   static constexpr uint32_t EX_INVALID = 0x1;
50   static constexpr uint32_t EX_DIVBYZERO = 0x2;
51   static constexpr uint32_t EX_OVERFLOW = 0x4;
52   static constexpr uint32_t EX_UNDERFLOW = 0x8;
53   static constexpr uint32_t EX_INEXACT = 0x10;
54   // __APPLE__ ARM64 has an extra flag that is raised when a denormal is flushed
55   // to zero.
56   static constexpr uint32_t EX_FLUSHTOZERO = 0x20;
57 
58   // Zero-th bit is the first bit.
59   static constexpr uint32_t ROUNDING_CONTROL_BIT_POSITION = 22;
60 
61   // In addition to the 5 floating point exceptions, macOS on arm64 defines
62   // another floating point exception: FE_FLUSHTOZERO, which is controlled by
63   // __fpcr_flush_to_zero bit in the FPCR register.  This control bit is
64   // located in a different place from FE_FLUSHTOZERO status bit relative to
65   // the other exceptions.
exception_value_from_statusFEnv66   LIBC_INLINE static uint32_t exception_value_from_status(uint32_t status) {
67     return ((status & FE_INVALID) ? EX_INVALID : 0) |
68            ((status & FE_DIVBYZERO) ? EX_DIVBYZERO : 0) |
69            ((status & FE_OVERFLOW) ? EX_OVERFLOW : 0) |
70            ((status & FE_UNDERFLOW) ? EX_UNDERFLOW : 0) |
71            ((status & FE_INEXACT) ? EX_INEXACT : 0) |
72            ((status & FE_FLUSHTOZERO) ? EX_FLUSHTOZERO : 0);
73   }
74 
exception_value_from_controlFEnv75   LIBC_INLINE static uint32_t exception_value_from_control(uint32_t control) {
76     return ((control & __fpcr_trap_invalid) ? EX_INVALID : 0) |
77            ((control & __fpcr_trap_divbyzero) ? EX_DIVBYZERO : 0) |
78            ((control & __fpcr_trap_overflow) ? EX_OVERFLOW : 0) |
79            ((control & __fpcr_trap_underflow) ? EX_UNDERFLOW : 0) |
80            ((control & __fpcr_trap_inexact) ? EX_INEXACT : 0) |
81            ((control & __fpcr_flush_to_zero) ? EX_FLUSHTOZERO : 0);
82   }
83 
exception_value_to_statusFEnv84   LIBC_INLINE static uint32_t exception_value_to_status(uint32_t excepts) {
85     return ((excepts & EX_INVALID) ? FE_INVALID : 0) |
86            ((excepts & EX_DIVBYZERO) ? FE_DIVBYZERO : 0) |
87            ((excepts & EX_OVERFLOW) ? FE_OVERFLOW : 0) |
88            ((excepts & EX_UNDERFLOW) ? FE_UNDERFLOW : 0) |
89            ((excepts & EX_INEXACT) ? FE_INEXACT : 0) |
90            ((excepts & EX_FLUSHTOZERO) ? FE_FLUSHTOZERO : 0);
91   }
92 
exception_value_to_controlFEnv93   LIBC_INLINE static uint32_t exception_value_to_control(uint32_t excepts) {
94     return ((excepts & EX_INVALID) ? __fpcr_trap_invalid : 0) |
95            ((excepts & EX_DIVBYZERO) ? __fpcr_trap_divbyzero : 0) |
96            ((excepts & EX_OVERFLOW) ? __fpcr_trap_overflow : 0) |
97            ((excepts & EX_UNDERFLOW) ? __fpcr_trap_underflow : 0) |
98            ((excepts & EX_INEXACT) ? __fpcr_trap_inexact : 0) |
99            ((excepts & EX_FLUSHTOZERO) ? __fpcr_flush_to_zero : 0);
100   }
101 
get_control_wordFEnv102   LIBC_INLINE static uint32_t get_control_word() { return __arm_rsr("fpcr"); }
103 
set_control_wordFEnv104   LIBC_INLINE static void set_control_word(uint32_t fpcr) {
105     __arm_wsr("fpcr", fpcr);
106   }
107 
get_status_wordFEnv108   LIBC_INLINE static uint32_t get_status_word() { return __arm_rsr("fpsr"); }
109 
set_status_wordFEnv110   LIBC_INLINE static void set_status_word(uint32_t fpsr) {
111     __arm_wsr("fpsr", fpsr);
112   }
113 };
114 
enable_except(int excepts)115 LIBC_INLINE int enable_except(int excepts) {
116   uint32_t new_excepts =
117       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
118   uint32_t control_word = FEnv::get_control_word();
119   uint32_t old_excepts = FEnv::exception_value_from_control(control_word);
120   if (new_excepts != old_excepts) {
121     control_word |= FEnv::exception_value_to_control(new_excepts);
122     FEnv::set_control_word(control_word);
123   }
124   return static_cast<int>(FEnv::exception_value_to_status(old_excepts));
125 }
126 
disable_except(int excepts)127 LIBC_INLINE int disable_except(int excepts) {
128   uint32_t disabled_excepts =
129       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
130   uint32_t control_word = FEnv::get_control_word();
131   uint32_t old_excepts = FEnv::exception_value_from_control(control_word);
132   control_word &= ~FEnv::exception_value_to_control(disabled_excepts);
133   FEnv::set_control_word(control_word);
134   return static_cast<int>(FEnv::exception_value_to_status(old_excepts));
135 }
136 
get_except()137 LIBC_INLINE int get_except() {
138   uint32_t control_word = FEnv::get_control_word();
139   uint32_t enabled_excepts = FEnv::exception_value_from_control(control_word);
140   return static_cast<int>(FEnv::exception_value_to_status(enabled_excepts));
141 }
142 
clear_except(int excepts)143 LIBC_INLINE int clear_except(int excepts) {
144   uint32_t status_word = FEnv::get_status_word();
145   uint32_t except_value =
146       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
147   status_word &= ~FEnv::exception_value_to_status(except_value);
148   FEnv::set_status_word(status_word);
149   return 0;
150 }
151 
test_except(int excepts)152 LIBC_INLINE int test_except(int excepts) {
153   uint32_t statusWord = FEnv::get_status_word();
154   uint32_t ex_value =
155       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
156   return static_cast<int>(statusWord &
157                           FEnv::exception_value_to_status(ex_value));
158 }
159 
set_except(int excepts)160 LIBC_INLINE int set_except(int excepts) {
161   uint32_t status_word = FEnv::get_status_word();
162   uint32_t new_exceptions =
163       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
164   status_word |= FEnv::exception_value_to_status(new_exceptions);
165   FEnv::set_status_word(status_word);
166   return 0;
167 }
168 
raise_except(int excepts)169 LIBC_INLINE int raise_except(int excepts) {
170   float zero = 0.0f;
171   float one = 1.0f;
172   float large_value = FPBits<float>::max_normal().get_val();
173   float small_value = FPBits<float>::min_normal().get_val();
174   auto divfunc = [](float a, float b) {
175     __asm__ __volatile__("ldr  s0, %0\n\t"
176                          "ldr  s1, %1\n\t"
177                          "fdiv s0, s0, s1\n\t"
178                          : // No outputs
179                          : "m"(a), "m"(b)
180                          : "s0", "s1" /* s0 and s1 are clobbered */);
181   };
182 
183   uint32_t to_raise =
184       FEnv::exception_value_from_status(static_cast<uint32_t>(excepts));
185   int result = 0;
186 
187   if (to_raise & FEnv::EX_INVALID) {
188     divfunc(zero, zero);
189     uint32_t status_word = FEnv::get_status_word();
190     if (!(FEnv::exception_value_from_status(status_word) & FEnv::EX_INVALID))
191       result = -1;
192   }
193 
194   if (to_raise & FEnv::EX_DIVBYZERO) {
195     divfunc(one, zero);
196     uint32_t status_word = FEnv::get_status_word();
197     if (!(FEnv::exception_value_from_status(status_word) & FEnv::EX_DIVBYZERO))
198       result = -1;
199   }
200   if (to_raise & FEnv::EX_OVERFLOW) {
201     divfunc(large_value, small_value);
202     uint32_t status_word = FEnv::get_status_word();
203     if (!(FEnv::exception_value_from_status(status_word) & FEnv::EX_OVERFLOW))
204       result = -1;
205   }
206   if (to_raise & FEnv::EX_UNDERFLOW) {
207     divfunc(small_value, large_value);
208     uint32_t status_word = FEnv::get_status_word();
209     if (!(FEnv::exception_value_from_status(status_word) & FEnv::EX_UNDERFLOW))
210       result = -1;
211   }
212   if (to_raise & FEnv::EX_INEXACT) {
213     float two = 2.0f;
214     float three = 3.0f;
215     // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
216     // format.
217     divfunc(two, three);
218     uint32_t status_word = FEnv::get_status_word();
219     if (!(FEnv::exception_value_from_status(status_word) & FEnv::EX_INEXACT))
220       result = -1;
221   }
222   if (to_raise & FEnv::EX_FLUSHTOZERO) {
223     // TODO: raise the flush to zero floating point exception.
224     result = -1;
225   }
226   return result;
227 }
228 
get_round()229 LIBC_INLINE int get_round() {
230   uint32_t rounding_mode =
231       (FEnv::get_control_word() >> FEnv::ROUNDING_CONTROL_BIT_POSITION) & 0x3;
232   switch (rounding_mode) {
233   case FEnv::TONEAREST:
234     return FE_TONEAREST;
235   case FEnv::DOWNWARD:
236     return FE_DOWNWARD;
237   case FEnv::UPWARD:
238     return FE_UPWARD;
239   case FEnv::TOWARDZERO:
240     return FE_TOWARDZERO;
241   default:
242     return -1; // Error value.
243   }
244 }
245 
set_round(int mode)246 LIBC_INLINE int set_round(int mode) {
247   uint32_t bit_value;
248   switch (mode) {
249   case FE_TONEAREST:
250     bit_value = FEnv::TONEAREST;
251     break;
252   case FE_DOWNWARD:
253     bit_value = FEnv::DOWNWARD;
254     break;
255   case FE_UPWARD:
256     bit_value = FEnv::UPWARD;
257     break;
258   case FE_TOWARDZERO:
259     bit_value = FEnv::TOWARDZERO;
260     break;
261   default:
262     return 1; // To indicate failure
263   }
264 
265   uint32_t control_word = FEnv::get_control_word();
266   control_word &= ~(0x3u << FEnv::ROUNDING_CONTROL_BIT_POSITION);
267   control_word |= (bit_value << FEnv::ROUNDING_CONTROL_BIT_POSITION);
268   FEnv::set_control_word(control_word);
269 
270   return 0;
271 }
272 
get_env(fenv_t * envp)273 LIBC_INLINE int get_env(fenv_t *envp) {
274   FEnv::FPState *state = reinterpret_cast<FEnv::FPState *>(envp);
275   state->ControlWord = FEnv::get_control_word();
276   state->StatusWord = FEnv::get_status_word();
277   return 0;
278 }
279 
set_env(const fenv_t * envp)280 LIBC_INLINE int set_env(const fenv_t *envp) {
281   if (envp == FE_DFL_ENV) {
282     // Default status and control words bits are all zeros so we just
283     // write zeros.
284     FEnv::set_status_word(0);
285     FEnv::set_control_word(0);
286     return 0;
287   }
288   const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp);
289   FEnv::set_control_word(static_cast<uint32_t>(state->ControlWord));
290   FEnv::set_status_word(static_cast<uint32_t>(state->StatusWord));
291   return 0;
292 }
293 
294 } // namespace fputil
295 } // namespace LIBC_NAMESPACE_DECL
296 
297 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENV_DARWIN_IMPL_H
298