[libc][NFC] Move aligned access implementations to separate header
[llvm-project.git] / libc / src / __support / FPUtil / arm / FEnvImpl.h
blob02535ecbca1991844552bf56f3b0d91ec0ce808b
1 //===-- arm 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_ARM_FENVIMPL_H
10 #define LLVM_LIBC_SRC_SUPPORT_FPUTIL_ARM_FENVIMPL_H
12 #include "src/__support/FPUtil/FPBits.h"
13 #include "src/__support/macros/config.h" // For LIBC_INLINE
15 #include <fenv.h>
16 #include <stdint.h>
18 namespace __llvm_libc {
19 namespace fputil {
21 struct FEnv {
22 // Arm floating point state is all stored in a single 32-bit register named
23 // fpscr.
24 uint32_t fpscr;
25 static constexpr uint32_t RoundingControlBitPosition = 22;
26 static constexpr uint32_t ExceptionControlBitPosition = 8;
28 static constexpr uint32_t TONEAREST = 0x0;
29 static constexpr uint32_t UPWARD = 0x1;
30 static constexpr uint32_t DOWNWARD = 0x2;
31 static constexpr uint32_t TOWARDZERO = 0x3;
33 static constexpr uint32_t INVALID_ENABLE = 0x1;
34 static constexpr uint32_t DIVBYZERO_ENABLE = 0x2;
35 static constexpr uint32_t OVERFLOW_ENABLE = 0x4;
36 static constexpr uint32_t UNDERFLOW_ENABLE = 0x8;
37 static constexpr uint32_t INEXACT_ENABLE = 0x10;
38 static constexpr uint32_t DENORMAL_ENABLE = 0x20;
40 static constexpr uint32_t INVALID_STATUS = 0x1;
41 static constexpr uint32_t DIVBYZERO_STATUS = 0x2;
42 static constexpr uint32_t OVERFLOW_STATUS = 0x4;
43 static constexpr uint32_t UNDERFLOW_STATUS = 0x8;
44 static constexpr uint32_t INEXACT_STATUS = 0x10;
45 static constexpr uint32_t DENORMAL_STATUS = 0x80;
47 LIBC_INLINE static uint32_t get_fpscr() { return __builtin_arm_get_fpscr(); }
48 LIBC_INLINE static void set_fpscr(uint32_t val) {
49 __builtin_arm_set_fpscr(val);
52 LIBC_INLINE static int exception_enable_bits_to_macro(uint32_t status) {
53 return (status & INVALID_ENABLE ? FE_INVALID : 0) |
54 (status & DIVBYZERO_ENABLE ? FE_DIVBYZERO : 0) |
55 (status & OVERFLOW_ENABLE ? FE_OVERFLOW : 0) |
56 (status & UNDERFLOW_ENABLE ? FE_UNDERFLOW : 0) |
57 (status & INEXACT_ENABLE ? FE_INEXACT : 0);
60 LIBC_INLINE static uint32_t exception_macro_to_enable_bits(int except) {
61 return (except & FE_INVALID ? INVALID_ENABLE : 0) |
62 (except & FE_DIVBYZERO ? DIVBYZERO_ENABLE : 0) |
63 (except & FE_OVERFLOW ? OVERFLOW_ENABLE : 0) |
64 (except & FE_UNDERFLOW ? UNDERFLOW_ENABLE : 0) |
65 (except & FE_INEXACT ? INEXACT_ENABLE : 0);
68 LIBC_INLINE static uint32_t exception_macro_to_status_bits(int except) {
69 return (except & FE_INVALID ? INVALID_STATUS : 0) |
70 (except & FE_DIVBYZERO ? DIVBYZERO_STATUS : 0) |
71 (except & FE_OVERFLOW ? OVERFLOW_STATUS : 0) |
72 (except & FE_UNDERFLOW ? UNDERFLOW_STATUS : 0) |
73 (except & FE_INEXACT ? INEXACT_STATUS : 0);
76 LIBC_INLINE static uint32_t exception_status_bits_to_macro(int status) {
77 return (status & INVALID_STATUS ? FE_INVALID : 0) |
78 (status & DIVBYZERO_STATUS ? FE_DIVBYZERO : 0) |
79 (status & OVERFLOW_STATUS ? FE_OVERFLOW : 0) |
80 (status & UNDERFLOW_STATUS ? FE_UNDERFLOW : 0) |
81 (status & INEXACT_STATUS ? FE_INEXACT : 0);
85 // Enables exceptions in |excepts| and returns the previously set exceptions.
86 LIBC_INLINE int enable_except(int excepts) {
87 uint32_t new_excepts = FEnv::exception_macro_to_enable_bits(excepts);
88 uint32_t fpscr = FEnv::get_fpscr();
89 int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
90 fpscr |= (new_excepts << FEnv::ExceptionControlBitPosition);
91 FEnv::set_fpscr(fpscr);
92 return FEnv::exception_enable_bits_to_macro(old);
95 // Disables exceptions in |excepts| and returns the previously set exceptions.
96 LIBC_INLINE int disable_except(int excepts) {
97 uint32_t disable_bits = FEnv::exception_macro_to_enable_bits(excepts);
98 uint32_t fpscr = FEnv::get_fpscr();
99 int old = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
100 fpscr &= ~(disable_bits << FEnv::ExceptionControlBitPosition);
101 FEnv::set_fpscr(fpscr);
102 return FEnv::exception_enable_bits_to_macro(old);
105 // Returns the currently enabled exceptions.
106 LIBC_INLINE int get_except() {
107 uint32_t fpscr = FEnv::get_fpscr();
108 int enabled_excepts = (fpscr >> FEnv::ExceptionControlBitPosition) & 0x3F;
109 return FEnv::exception_enable_bits_to_macro(enabled_excepts);
112 // Clears the exceptions in |excepts|.
113 LIBC_INLINE int clear_except(int excepts) {
114 uint32_t fpscr = FEnv::get_fpscr();
115 uint32_t to_clear = FEnv::exception_macro_to_status_bits(excepts);
116 fpscr &= ~to_clear;
117 FEnv::set_fpscr(fpscr);
118 return 0;
121 // Returns the set of exceptions which are from the input set |excepts|.
122 LIBC_INLINE int test_except(int excepts) {
123 uint32_t to_test = FEnv::exception_macro_to_status_bits(excepts);
124 uint32_t fpscr = FEnv::get_fpscr();
125 return FEnv::exception_status_bits_to_macro(fpscr & 0x9F & to_test);
128 // Set the exceptions in |excepts|.
129 LIBC_INLINE int set_except(int excepts) {
130 uint32_t fpscr = FEnv::get_fpscr();
131 FEnv::set_fpscr(fpscr | FEnv::exception_macro_to_status_bits(excepts));
132 return 0;
135 LIBC_INLINE int raise_except(int excepts) {
136 float zero = 0.0f;
137 float one = 1.0f;
138 float large_value = float(FPBits<float>(FPBits<float>::MAX_NORMAL));
139 float small_value = float(FPBits<float>(FPBits<float>::MIN_NORMAL));
140 auto divfunc = [](float a, float b) {
141 __asm__ __volatile__("flds s0, %0\n\t"
142 "flds s1, %1\n\t"
143 "fdivs s0, s0, s1\n\t"
144 : // No outputs
145 : "m"(a), "m"(b)
146 : "s0", "s1" /* s0 and s1 are clobbered */);
149 uint32_t to_raise = FEnv::exception_macro_to_status_bits(excepts);
150 int result = 0;
152 if (to_raise & FEnv::INVALID_STATUS) {
153 divfunc(zero, zero);
154 uint32_t fpscr = FEnv::get_fpscr();
155 if (!(fpscr & FEnv::INVALID_STATUS))
156 result = -1;
158 if (to_raise & FEnv::DIVBYZERO_STATUS) {
159 divfunc(one, zero);
160 uint32_t fpscr = FEnv::get_fpscr();
161 if (!(fpscr & FEnv::DIVBYZERO_STATUS))
162 result = -1;
164 if (to_raise & FEnv::OVERFLOW_STATUS) {
165 divfunc(large_value, small_value);
166 uint32_t fpscr = FEnv::get_fpscr();
167 if (!(fpscr & FEnv::OVERFLOW_STATUS))
168 result = -1;
170 if (to_raise & FEnv::UNDERFLOW_STATUS) {
171 divfunc(small_value, large_value);
172 uint32_t fpscr = FEnv::get_fpscr();
173 if (!(fpscr & FEnv::UNDERFLOW_STATUS))
174 result = -1;
176 if (to_raise & FEnv::INEXACT_STATUS) {
177 float two = 2.0f;
178 float three = 3.0f;
179 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
180 // format.
181 divfunc(two, three);
182 uint32_t fpscr = FEnv::get_fpscr();
183 if (!(fpscr & FEnv::INEXACT_STATUS))
184 result = -1;
186 return result;
189 LIBC_INLINE int get_round() {
190 uint32_t mode = (FEnv::get_fpscr() >> FEnv::RoundingControlBitPosition) & 0x3;
191 switch (mode) {
192 case FEnv::TONEAREST:
193 return FE_TONEAREST;
194 case FEnv::DOWNWARD:
195 return FE_DOWNWARD;
196 case FEnv::UPWARD:
197 return FE_UPWARD;
198 case FEnv::TOWARDZERO:
199 return FE_TOWARDZERO;
200 default:
201 return -1; // Error value.
203 return 0;
206 LIBC_INLINE int set_round(int mode) {
207 uint16_t bits;
208 switch (mode) {
209 case FE_TONEAREST:
210 bits = FEnv::TONEAREST;
211 break;
212 case FE_DOWNWARD:
213 bits = FEnv::DOWNWARD;
214 break;
215 case FE_UPWARD:
216 bits = FEnv::UPWARD;
217 break;
218 case FE_TOWARDZERO:
219 bits = FEnv::TOWARDZERO;
220 break;
221 default:
222 return 1; // To indicate failure
225 uint32_t fpscr = FEnv::get_fpscr();
226 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
227 fpscr |= (bits << FEnv::RoundingControlBitPosition);
228 FEnv::set_fpscr(fpscr);
230 return 0;
233 LIBC_INLINE int get_env(fenv_t *envp) {
234 FEnv *state = reinterpret_cast<FEnv *>(envp);
235 state->fpscr = FEnv::get_fpscr();
236 return 0;
239 LIBC_INLINE int set_env(const fenv_t *envp) {
240 if (envp == FE_DFL_ENV) {
241 uint32_t fpscr = FEnv::get_fpscr();
242 // Default status implies:
243 // 1. Round to nearest rounding mode.
244 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
245 fpscr |= (FEnv::TONEAREST << FEnv::RoundingControlBitPosition);
246 // 2. All exceptions are disabled.
247 fpscr &= ~(0x3F << FEnv::ExceptionControlBitPosition);
248 // 3. All exceptions are cleared. There are two reserved bits
249 // at bit 5 and 6 so we just write one full byte (6 bits for
250 // the exceptions, and 2 reserved bits.)
251 fpscr &= ~(static_cast<uint32_t>(0xFF));
253 FEnv::set_fpscr(fpscr);
254 return 0;
257 const FEnv *state = reinterpret_cast<const FEnv *>(envp);
258 FEnv::set_fpscr(state->fpscr);
259 return 0;
262 } // namespace fputil
263 } // namespace __llvm_libc
265 #endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_ARM_FENVIMPL_H