[NFC][Py Reformat] Added more commits to .git-blame-ignore-revs
[llvm-project.git] / libc / src / __support / FPUtil / aarch64 / FEnvImpl.h
blob783d3f3f56eb4f4b3a80ffcb37177d4bee35bb17
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 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENVIMPL_H
10 #define LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENVIMPL_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"
17 #endif
19 #include <arm_acle.h>
20 #include <fenv.h>
21 #include <stdint.h>
23 #include "src/__support/FPUtil/FPBits.h"
25 namespace __llvm_libc {
26 namespace fputil {
28 struct FEnv {
29 struct FPState {
30 uint32_t ControlWord;
31 uint32_t StatusWord;
34 static_assert(
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 static constexpr uint32_t INVALID = 0x1;
44 static constexpr uint32_t DIVBYZERO = 0x2;
45 static constexpr uint32_t OVERFLOW = 0x4;
46 static constexpr uint32_t UNDERFLOW = 0x8;
47 static constexpr uint32_t INEXACT = 0x10;
49 // Zero-th bit is the first bit.
50 static constexpr uint32_t RoundingControlBitPosition = 22;
51 static constexpr uint32_t ExceptionStatusFlagsBitPosition = 0;
52 static constexpr uint32_t ExceptionControlFlagsBitPosition = 8;
54 LIBC_INLINE static uint32_t getStatusValueForExcept(int excepts) {
55 return (excepts & FE_INVALID ? INVALID : 0) |
56 (excepts & FE_DIVBYZERO ? DIVBYZERO : 0) |
57 (excepts & FE_OVERFLOW ? OVERFLOW : 0) |
58 (excepts & FE_UNDERFLOW ? UNDERFLOW : 0) |
59 (excepts & FE_INEXACT ? INEXACT : 0);
62 LIBC_INLINE static int exceptionStatusToMacro(uint32_t status) {
63 return (status & INVALID ? FE_INVALID : 0) |
64 (status & DIVBYZERO ? FE_DIVBYZERO : 0) |
65 (status & OVERFLOW ? FE_OVERFLOW : 0) |
66 (status & UNDERFLOW ? FE_UNDERFLOW : 0) |
67 (status & INEXACT ? FE_INEXACT : 0);
70 static uint32_t getControlWord() {
71 #ifdef __clang__
72 // GCC does not currently support __arm_rsr.
73 return __arm_rsr("fpcr");
74 #else
75 return __builtin_aarch64_get_fpcr();
76 #endif
79 static void writeControlWord(uint32_t fpcr) {
80 #ifdef __clang__
81 // GCC does not currently support __arm_wsr.
82 __arm_wsr("fpcr", fpcr);
83 #else
84 __builtin_aarch64_set_fpcr(fpcr);
85 #endif
88 static uint32_t getStatusWord() {
89 #ifdef __clang__
90 return __arm_rsr("fpsr");
91 #else
92 return __builtin_aarch64_get_fpsr();
93 #endif
96 static void writeStatusWord(uint32_t fpsr) {
97 #ifdef __clang__
98 __arm_wsr("fpsr", fpsr);
99 #else
100 __builtin_aarch64_set_fpsr(fpsr);
101 #endif
105 LIBC_INLINE int enable_except(int excepts) {
106 uint32_t newExcepts = FEnv::getStatusValueForExcept(excepts);
107 uint32_t controlWord = FEnv::getControlWord();
108 int oldExcepts =
109 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
110 controlWord |= (newExcepts << FEnv::ExceptionControlFlagsBitPosition);
111 FEnv::writeControlWord(controlWord);
112 return FEnv::exceptionStatusToMacro(oldExcepts);
115 LIBC_INLINE int disable_except(int excepts) {
116 uint32_t disabledExcepts = FEnv::getStatusValueForExcept(excepts);
117 uint32_t controlWord = FEnv::getControlWord();
118 int oldExcepts =
119 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
120 controlWord &= ~(disabledExcepts << FEnv::ExceptionControlFlagsBitPosition);
121 FEnv::writeControlWord(controlWord);
122 return FEnv::exceptionStatusToMacro(oldExcepts);
125 LIBC_INLINE int get_except() {
126 uint32_t controlWord = FEnv::getControlWord();
127 int enabledExcepts =
128 (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
129 return FEnv::exceptionStatusToMacro(enabledExcepts);
132 LIBC_INLINE int clear_except(int excepts) {
133 uint32_t statusWord = FEnv::getStatusWord();
134 uint32_t toClear = FEnv::getStatusValueForExcept(excepts);
135 statusWord &= ~(toClear << FEnv::ExceptionStatusFlagsBitPosition);
136 FEnv::writeStatusWord(statusWord);
137 return 0;
140 LIBC_INLINE int test_except(int excepts) {
141 uint32_t toTest = FEnv::getStatusValueForExcept(excepts);
142 uint32_t statusWord = FEnv::getStatusWord();
143 return FEnv::exceptionStatusToMacro(
144 (statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & toTest);
147 LIBC_INLINE int set_except(int excepts) {
148 uint32_t statusWord = FEnv::getStatusWord();
149 uint32_t statusValue = FEnv::getStatusValueForExcept(excepts);
150 statusWord |= (statusValue << FEnv::ExceptionStatusFlagsBitPosition);
151 FEnv::writeStatusWord(statusWord);
152 return 0;
155 LIBC_INLINE int raise_except(int excepts) {
156 float zero = 0.0f;
157 float one = 1.0f;
158 float largeValue = float(FPBits<float>(FPBits<float>::MAX_NORMAL));
159 float smallValue = float(FPBits<float>(FPBits<float>::MIN_NORMAL));
160 auto divfunc = [](float a, float b) {
161 __asm__ __volatile__("ldr s0, %0\n\t"
162 "ldr s1, %1\n\t"
163 "fdiv s0, s0, s1\n\t"
164 : // No outputs
165 : "m"(a), "m"(b)
166 : "s0", "s1" /* s0 and s1 are clobbered */);
169 uint32_t toRaise = FEnv::getStatusValueForExcept(excepts);
170 int result = 0;
172 if (toRaise & FEnv::INVALID) {
173 divfunc(zero, zero);
174 uint32_t statusWord = FEnv::getStatusWord();
175 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
176 FEnv::INVALID))
177 result = -1;
180 if (toRaise & FEnv::DIVBYZERO) {
181 divfunc(one, zero);
182 uint32_t statusWord = FEnv::getStatusWord();
183 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
184 FEnv::DIVBYZERO))
185 result = -1;
187 if (toRaise & FEnv::OVERFLOW) {
188 divfunc(largeValue, smallValue);
189 uint32_t statusWord = FEnv::getStatusWord();
190 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
191 FEnv::OVERFLOW))
192 result = -1;
194 if (toRaise & FEnv::UNDERFLOW) {
195 divfunc(smallValue, largeValue);
196 uint32_t statusWord = FEnv::getStatusWord();
197 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
198 FEnv::UNDERFLOW))
199 result = -1;
201 if (toRaise & FEnv::INEXACT) {
202 float two = 2.0f;
203 float three = 3.0f;
204 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
205 // format.
206 divfunc(two, three);
207 uint32_t statusWord = FEnv::getStatusWord();
208 if (!((statusWord >> FEnv::ExceptionStatusFlagsBitPosition) &
209 FEnv::INEXACT))
210 result = -1;
212 return result;
215 LIBC_INLINE int get_round() {
216 uint32_t roundingMode =
217 (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition) & 0x3;
218 switch (roundingMode) {
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.
232 LIBC_INLINE int set_round(int mode) {
233 uint16_t bitValue;
234 switch (mode) {
235 case FE_TONEAREST:
236 bitValue = FEnv::TONEAREST;
237 break;
238 case FE_DOWNWARD:
239 bitValue = FEnv::DOWNWARD;
240 break;
241 case FE_UPWARD:
242 bitValue = FEnv::UPWARD;
243 break;
244 case FE_TOWARDZERO:
245 bitValue = FEnv::TOWARDZERO;
246 break;
247 default:
248 return 1; // To indicate failure
251 uint32_t controlWord = FEnv::getControlWord();
252 controlWord &= ~(0x3 << FEnv::RoundingControlBitPosition);
253 controlWord |= (bitValue << FEnv::RoundingControlBitPosition);
254 FEnv::writeControlWord(controlWord);
256 return 0;
259 LIBC_INLINE int get_env(fenv_t *envp) {
260 FEnv::FPState *state = reinterpret_cast<FEnv::FPState *>(envp);
261 state->ControlWord = FEnv::getControlWord();
262 state->StatusWord = FEnv::getStatusWord();
263 return 0;
266 LIBC_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::writeStatusWord(0);
271 FEnv::writeControlWord(0);
272 return 0;
274 const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp);
275 FEnv::writeControlWord(state->ControlWord);
276 FEnv::writeStatusWord(state->StatusWord);
277 return 0;
280 } // namespace fputil
281 } // namespace __llvm_libc
283 #endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENVIMPL_H