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