1 //===- darwin-aarch64 floating point env manipulation functions -*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENV_DARWIN_IMPL_H
10 #define LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENV_DARWIN_IMPL_H
12 #include "src/__support/macros/attributes.h" // LIBC_INLINE
13 #include "src/__support/macros/properties/architectures.h"
15 #if !defined(LIBC_TARGET_ARCH_IS_AARCH64) || !defined(__APPLE__)
16 #error "Invalid include"
23 #include "src/__support/FPUtil/FPBits.h"
25 namespace __llvm_libc
{
35 sizeof(fenv_t
) == sizeof(FPState
),
36 "Internal floating point state does not match the public fenv_t type.");
38 static constexpr uint32_t TONEAREST
= 0x0;
39 static constexpr uint32_t UPWARD
= 0x1;
40 static constexpr uint32_t DOWNWARD
= 0x2;
41 static constexpr uint32_t TOWARDZERO
= 0x3;
43 // These will be the exception flags we use for exception values normalized
44 // from both status word and control word.
45 // We add EX_ prefix to the names since macOS <math.h> defines OVERFLOW and
47 static constexpr uint32_t EX_INVALID
= 0x1;
48 static constexpr uint32_t EX_DIVBYZERO
= 0x2;
49 static constexpr uint32_t EX_OVERFLOW
= 0x4;
50 static constexpr uint32_t EX_UNDERFLOW
= 0x8;
51 static constexpr uint32_t EX_INEXACT
= 0x10;
52 // __APPLE__ ARM64 has an extra flag that is raised when a denormal is flushed
54 static constexpr uint32_t EX_FLUSHTOZERO
= 0x20;
56 // Zero-th bit is the first bit.
57 static constexpr uint32_t ROUNDING_CONTROL_BIT_POSITION
= 22;
59 // In addition to the 5 floating point exceptions, macOS on arm64 defines
60 // another floating point exception: FE_FLUSHTOZERO, which is controlled by
61 // __fpcr_flush_to_zero bit in the FPCR register. This control bit is
62 // located in a different place from FE_FLUSHTOZERO status bit relative to
63 // the other exceptions.
64 LIBC_INLINE
static uint32_t exception_value_from_status(int status
) {
65 return (status
& FE_INVALID
? EX_INVALID
: 0) |
66 (status
& FE_DIVBYZERO
? EX_DIVBYZERO
: 0) |
67 (status
& FE_OVERFLOW
? EX_OVERFLOW
: 0) |
68 (status
& FE_UNDERFLOW
? EX_UNDERFLOW
: 0) |
69 (status
& FE_INEXACT
? EX_INEXACT
: 0) |
70 (status
& FE_FLUSHTOZERO
? EX_FLUSHTOZERO
: 0);
73 LIBC_INLINE
static uint32_t exception_value_from_control(int control
) {
74 return (control
& __fpcr_trap_invalid
? EX_INVALID
: 0) |
75 (control
& __fpcr_trap_divbyzero
? EX_DIVBYZERO
: 0) |
76 (control
& __fpcr_trap_overflow
? EX_OVERFLOW
: 0) |
77 (control
& __fpcr_trap_underflow
? EX_UNDERFLOW
: 0) |
78 (control
& __fpcr_trap_inexact
? EX_INEXACT
: 0) |
79 (control
& __fpcr_flush_to_zero
? EX_FLUSHTOZERO
: 0);
82 LIBC_INLINE
static int exception_value_to_status(uint32_t excepts
) {
83 return (excepts
& EX_INVALID
? FE_INVALID
: 0) |
84 (excepts
& EX_DIVBYZERO
? FE_DIVBYZERO
: 0) |
85 (excepts
& EX_OVERFLOW
? FE_OVERFLOW
: 0) |
86 (excepts
& EX_UNDERFLOW
? FE_UNDERFLOW
: 0) |
87 (excepts
& EX_INEXACT
? FE_INEXACT
: 0) |
88 (excepts
& EX_FLUSHTOZERO
? FE_FLUSHTOZERO
: 0);
91 LIBC_INLINE
static int exception_value_to_control(uint32_t excepts
) {
92 return (excepts
& EX_INVALID
? __fpcr_trap_invalid
: 0) |
93 (excepts
& EX_DIVBYZERO
? __fpcr_trap_divbyzero
: 0) |
94 (excepts
& EX_OVERFLOW
? __fpcr_trap_overflow
: 0) |
95 (excepts
& EX_UNDERFLOW
? __fpcr_trap_underflow
: 0) |
96 (excepts
& EX_INEXACT
? __fpcr_trap_inexact
: 0) |
97 (excepts
& EX_FLUSHTOZERO
? __fpcr_flush_to_zero
: 0);
100 LIBC_INLINE
static uint32_t get_control_word() { return __arm_rsr("fpcr"); }
102 LIBC_INLINE
static void set_control_word(uint32_t fpcr
) {
103 __arm_wsr("fpcr", fpcr
);
106 LIBC_INLINE
static uint32_t get_status_word() { return __arm_rsr("fpsr"); }
108 LIBC_INLINE
static void set_status_word(uint32_t fpsr
) {
109 __arm_wsr("fpsr", fpsr
);
113 LIBC_INLINE
int enable_except(int excepts
) {
114 uint32_t new_excepts
= FEnv::exception_value_from_status(excepts
);
115 uint32_t control_word
= FEnv::get_control_word();
116 uint32_t old_excepts
= FEnv::exception_value_from_control(control_word
);
117 if (new_excepts
!= old_excepts
) {
118 control_word
|= FEnv::exception_value_to_control(new_excepts
);
119 FEnv::set_control_word(control_word
);
121 return FEnv::exception_value_to_status(old_excepts
);
124 LIBC_INLINE
int disable_except(int excepts
) {
125 uint32_t disabled_excepts
= FEnv::exception_value_from_status(excepts
);
126 uint32_t control_word
= FEnv::get_control_word();
127 uint32_t old_excepts
= FEnv::exception_value_from_control(control_word
);
128 control_word
&= ~FEnv::exception_value_to_control(disabled_excepts
);
129 FEnv::set_control_word(control_word
);
130 return FEnv::exception_value_to_status(old_excepts
);
133 LIBC_INLINE
int get_except() {
134 uint32_t control_word
= FEnv::get_control_word();
135 uint32_t enabled_excepts
= FEnv::exception_value_from_control(control_word
);
136 return FEnv::exception_value_to_status(enabled_excepts
);
139 LIBC_INLINE
int clear_except(int excepts
) {
140 uint32_t status_word
= FEnv::get_status_word();
141 uint32_t except_value
= FEnv::exception_value_from_status(excepts
);
142 status_word
&= ~FEnv::exception_value_to_status(except_value
);
143 FEnv::set_status_word(status_word
);
147 LIBC_INLINE
int test_except(int excepts
) {
148 uint32_t statusWord
= FEnv::get_status_word();
149 uint32_t ex_value
= FEnv::exception_value_from_status(excepts
);
150 return statusWord
& FEnv::exception_value_to_status(ex_value
);
153 LIBC_INLINE
int set_except(int excepts
) {
154 uint32_t status_word
= FEnv::get_status_word();
155 uint32_t new_exceptions
= FEnv::exception_value_from_status(excepts
);
156 status_word
|= FEnv::exception_value_to_status(new_exceptions
);
157 FEnv::set_status_word(status_word
);
161 LIBC_INLINE
int raise_except(int excepts
) {
164 float large_value
= float(FPBits
<float>(FPBits
<float>::MAX_NORMAL
));
165 float small_value
= float(FPBits
<float>(FPBits
<float>::MIN_NORMAL
));
166 auto divfunc
= [](float a
, float b
) {
167 __asm__
__volatile__("ldr s0, %0\n\t"
169 "fdiv s0, s0, s1\n\t"
172 : "s0", "s1" /* s0 and s1 are clobbered */);
175 uint32_t to_raise
= FEnv::exception_value_from_status(excepts
);
178 if (to_raise
& FEnv::EX_INVALID
) {
180 uint32_t status_word
= FEnv::get_status_word();
181 if (!(FEnv::exception_value_from_status(status_word
) & FEnv::EX_INVALID
))
185 if (to_raise
& FEnv::EX_DIVBYZERO
) {
187 uint32_t status_word
= FEnv::get_status_word();
188 if (!(FEnv::exception_value_from_status(status_word
) & FEnv::EX_DIVBYZERO
))
191 if (to_raise
& FEnv::EX_OVERFLOW
) {
192 divfunc(large_value
, small_value
);
193 uint32_t status_word
= FEnv::get_status_word();
194 if (!(FEnv::exception_value_from_status(status_word
) & FEnv::EX_OVERFLOW
))
197 if (to_raise
& FEnv::EX_UNDERFLOW
) {
198 divfunc(small_value
, large_value
);
199 uint32_t status_word
= FEnv::get_status_word();
200 if (!(FEnv::exception_value_from_status(status_word
) & FEnv::EX_UNDERFLOW
))
203 if (to_raise
& FEnv::EX_INEXACT
) {
206 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
209 uint32_t status_word
= FEnv::get_status_word();
210 if (!(FEnv::exception_value_from_status(status_word
) & FEnv::EX_INEXACT
))
213 if (to_raise
& FEnv::EX_FLUSHTOZERO
) {
214 // TODO: raise the flush to zero floating point exception.
220 LIBC_INLINE
int get_round() {
221 uint32_t rounding_mode
=
222 (FEnv::get_control_word() >> FEnv::ROUNDING_CONTROL_BIT_POSITION
) & 0x3;
223 switch (rounding_mode
) {
224 case FEnv::TONEAREST
:
230 case FEnv::TOWARDZERO
:
231 return FE_TOWARDZERO
;
233 return -1; // Error value.
237 LIBC_INLINE
int set_round(int mode
) {
241 bit_value
= FEnv::TONEAREST
;
244 bit_value
= FEnv::DOWNWARD
;
247 bit_value
= FEnv::UPWARD
;
250 bit_value
= FEnv::TOWARDZERO
;
253 return 1; // To indicate failure
256 uint32_t control_word
= FEnv::get_control_word();
257 control_word
&= ~(0x3 << FEnv::ROUNDING_CONTROL_BIT_POSITION
);
258 control_word
|= (bit_value
<< FEnv::ROUNDING_CONTROL_BIT_POSITION
);
259 FEnv::set_control_word(control_word
);
264 LIBC_INLINE
int get_env(fenv_t
*envp
) {
265 FEnv::FPState
*state
= reinterpret_cast<FEnv::FPState
*>(envp
);
266 state
->ControlWord
= FEnv::get_control_word();
267 state
->StatusWord
= FEnv::get_status_word();
271 LIBC_INLINE
int set_env(const fenv_t
*envp
) {
272 if (envp
== FE_DFL_ENV
) {
273 // Default status and control words bits are all zeros so we just
275 FEnv::set_status_word(0);
276 FEnv::set_control_word(0);
279 const FEnv::FPState
*state
= reinterpret_cast<const FEnv::FPState
*>(envp
);
280 FEnv::set_control_word(state
->ControlWord
);
281 FEnv::set_status_word(state
->StatusWord
);
285 } // namespace fputil
286 } // namespace __llvm_libc
288 #endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENV_DARWIN_IMPL_H