1 //===-- 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_FENVIMPL_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_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 struct FEnv {
30 struct FPState {
31 uint32_t ControlWord;
32 uint32_t StatusWord;
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 static constexpr uint32_t INVALID_F = 0x1;
45 static constexpr uint32_t DIVBYZERO_F = 0x2;
46 static constexpr uint32_t OVERFLOW_F = 0x4;
47 static constexpr uint32_t UNDERFLOW_F = 0x8;
48 static constexpr uint32_t INEXACT_F = 0x10;
49
50 // Zero-th bit is the first bit.
51 static constexpr uint32_t RoundingControlBitPosition = 22;
52 static constexpr uint32_t ExceptionStatusFlagsBitPosition = 0;
53 static constexpr uint32_t ExceptionControlFlagsBitPosition = 8;
54
getStatusValueForExceptFEnv55 LIBC_INLINE static uint32_t getStatusValueForExcept(int excepts) {
56 return ((excepts & FE_INVALID) ? INVALID_F : 0) |
57 ((excepts & FE_DIVBYZERO) ? DIVBYZERO_F : 0) |
58 ((excepts & FE_OVERFLOW) ? OVERFLOW_F : 0) |
59 ((excepts & FE_UNDERFLOW) ? UNDERFLOW_F : 0) |
60 ((excepts & FE_INEXACT) ? INEXACT_F : 0);
61 }
62
exceptionStatusToMacroFEnv63 LIBC_INLINE static int exceptionStatusToMacro(uint32_t status) {
64 return ((status & INVALID_F) ? FE_INVALID : 0) |
65 ((status & DIVBYZERO_F) ? FE_DIVBYZERO : 0) |
66 ((status & OVERFLOW_F) ? FE_OVERFLOW : 0) |
67 ((status & UNDERFLOW_F) ? FE_UNDERFLOW : 0) |
68 ((status & INEXACT_F) ? FE_INEXACT : 0);
69 }
70
getControlWordFEnv71 static uint32_t getControlWord() {
72 #ifdef __clang__
73 // GCC does not currently support __arm_rsr.
74 return __arm_rsr("fpcr");
75 #else
76 return __builtin_aarch64_get_fpcr();
77 #endif
78 }
79
writeControlWordFEnv80 static void writeControlWord(uint32_t fpcr) {
81 #ifdef __clang__
82 // GCC does not currently support __arm_wsr.
83 __arm_wsr("fpcr", fpcr);
84 #else
85 __builtin_aarch64_set_fpcr(fpcr);
86 #endif
87 }
88
getStatusWordFEnv89 static uint32_t getStatusWord() {
90 #ifdef __clang__
91 return __arm_rsr("fpsr");
92 #else
93 return __builtin_aarch64_get_fpsr();
94 #endif
95 }
96
writeStatusWordFEnv97 static void writeStatusWord(uint32_t fpsr) {
98 #ifdef __clang__
99 __arm_wsr("fpsr", fpsr);
100 #else
101 __builtin_aarch64_set_fpsr(fpsr);
102 #endif
103 }
104 };
105
enable_except(int excepts)106 LIBC_INLINE int enable_except(int excepts) {
107 uint32_t newExcepts = FEnv::getStatusValueForExcept(excepts);
108 uint32_t controlWord = FEnv::getControlWord();
109 int oldExcepts =
110 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
111 controlWord |= (newExcepts << FEnv::ExceptionControlFlagsBitPosition);
112 FEnv::writeControlWord(controlWord);
113 return FEnv::exceptionStatusToMacro(static_cast<uint32_t>(oldExcepts));
114 }
115
disable_except(int excepts)116 LIBC_INLINE int disable_except(int excepts) {
117 uint32_t disabledExcepts = FEnv::getStatusValueForExcept(excepts);
118 uint32_t controlWord = FEnv::getControlWord();
119 int oldExcepts =
120 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
121 controlWord &= ~(disabledExcepts << FEnv::ExceptionControlFlagsBitPosition);
122 FEnv::writeControlWord(controlWord);
123 return FEnv::exceptionStatusToMacro(static_cast<uint32_t>(oldExcepts));
124 }
125
get_except()126 LIBC_INLINE int get_except() {
127 uint32_t controlWord = FEnv::getControlWord();
128 uint32_t enabledExcepts =
129 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
130 return FEnv::exceptionStatusToMacro(enabledExcepts);
131 }
132
clear_except(int excepts)133 LIBC_INLINE int clear_except(int excepts) {
134 uint32_t statusWord = FEnv::getStatusWord();
135 uint32_t toClear = FEnv::getStatusValueForExcept(excepts);
136 statusWord &= ~(toClear << FEnv::ExceptionStatusFlagsBitPosition);
137 FEnv::writeStatusWord(statusWord);
138 return 0;
139 }
140
test_except(int excepts)141 LIBC_INLINE int test_except(int excepts) {
142 uint32_t toTest = FEnv::getStatusValueForExcept(excepts);
143 uint32_t statusWord = FEnv::getStatusWord();
144 return FEnv::exceptionStatusToMacro(
145 (statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & toTest);
146 }
147
set_except(int excepts)148 LIBC_INLINE int set_except(int excepts) {
149 uint32_t statusWord = FEnv::getStatusWord();
150 uint32_t statusValue = FEnv::getStatusValueForExcept(excepts);
151 statusWord |= (statusValue << FEnv::ExceptionStatusFlagsBitPosition);
152 FEnv::writeStatusWord(statusWord);
153 return 0;
154 }
155
raise_except(int excepts)156 LIBC_INLINE int raise_except(int excepts) {
157 float zero = 0.0f;
158 float one = 1.0f;
159 float largeValue = FPBits<float>::max_normal().get_val();
160 float smallValue = FPBits<float>::min_normal().get_val();
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 toRaise = FEnv::getStatusValueForExcept(excepts);
171 int result = 0;
172
173 if (toRaise & FEnv::INVALID_F) {
174 divfunc(zero, zero);
175 uint32_t statusWord = FEnv::getStatusWord();
176 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
177 FEnv::INVALID_F))
178 result = -1;
179 }
180
181 if (toRaise & FEnv::DIVBYZERO_F) {
182 divfunc(one, zero);
183 uint32_t statusWord = FEnv::getStatusWord();
184 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
185 FEnv::DIVBYZERO_F))
186 result = -1;
187 }
188 if (toRaise & FEnv::OVERFLOW_F) {
189 divfunc(largeValue, smallValue);
190 uint32_t statusWord = FEnv::getStatusWord();
191 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
192 FEnv::OVERFLOW_F))
193 result = -1;
194 }
195 if (toRaise & FEnv::UNDERFLOW_F) {
196 divfunc(smallValue, largeValue);
197 uint32_t statusWord = FEnv::getStatusWord();
198 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
199 FEnv::UNDERFLOW_F))
200 result = -1;
201 }
202 if (toRaise & FEnv::INEXACT_F) {
203 float two = 2.0f;
204 float three = 3.0f;
205 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
206 // format.
207 divfunc(two, three);
208 uint32_t statusWord = FEnv::getStatusWord();
209 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
210 FEnv::INEXACT_F))
211 result = -1;
212 }
213 return result;
214 }
215
get_round()216 LIBC_INLINE int get_round() {
217 uint32_t roundingMode =
218 (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition) & 0x3;
219 switch (roundingMode) {
220 case FEnv::TONEAREST:
221 return FE_TONEAREST;
222 case FEnv::DOWNWARD:
223 return FE_DOWNWARD;
224 case FEnv::UPWARD:
225 return FE_UPWARD;
226 case FEnv::TOWARDZERO:
227 return FE_TOWARDZERO;
228 default:
229 return -1; // Error value.
230 }
231 }
232
set_round(int mode)233 LIBC_INLINE int set_round(int mode) {
234 uint16_t bitValue;
235 switch (mode) {
236 case FE_TONEAREST:
237 bitValue = FEnv::TONEAREST;
238 break;
239 case FE_DOWNWARD:
240 bitValue = FEnv::DOWNWARD;
241 break;
242 case FE_UPWARD:
243 bitValue = FEnv::UPWARD;
244 break;
245 case FE_TOWARDZERO:
246 bitValue = FEnv::TOWARDZERO;
247 break;
248 default:
249 return 1; // To indicate failure
250 }
251
252 uint32_t controlWord = FEnv::getControlWord();
253 controlWord &=
254 static_cast<uint32_t>(~(0x3 << FEnv::RoundingControlBitPosition));
255 controlWord |=
256 static_cast<uint32_t>(bitValue << FEnv::RoundingControlBitPosition);
257 FEnv::writeControlWord(controlWord);
258
259 return 0;
260 }
261
get_env(fenv_t * envp)262 LIBC_INLINE int get_env(fenv_t *envp) {
263 FEnv::FPState *state = reinterpret_cast<FEnv::FPState *>(envp);
264 state->ControlWord = FEnv::getControlWord();
265 state->StatusWord = FEnv::getStatusWord();
266 return 0;
267 }
268
set_env(const fenv_t * envp)269 LIBC_INLINE int set_env(const fenv_t *envp) {
270 if (envp == FE_DFL_ENV) {
271 // Default status and control words bits are all zeros so we just
272 // write zeros.
273 FEnv::writeStatusWord(0);
274 FEnv::writeControlWord(0);
275 return 0;
276 }
277 const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp);
278 FEnv::writeControlWord(state->ControlWord);
279 FEnv::writeStatusWord(state->StatusWord);
280 return 0;
281 }
282 } // namespace fputil
283 } // namespace LIBC_NAMESPACE_DECL
284
285 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_H
286