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