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 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
11
12 #include "hdr/fenv_macros.h"
13 #include "hdr/types/fenv_t.h"
14 #include "src/__support/FPUtil/FPBits.h"
15 #include "src/__support/macros/attributes.h" // For LIBC_INLINE
16 #include <stdint.h>
17
18 namespace LIBC_NAMESPACE {
19 namespace fputil {
20
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;
27
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;
32
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;
39
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;
46
get_fpscrFEnv47 LIBC_INLINE static uint32_t get_fpscr() { return __builtin_arm_get_fpscr(); }
set_fpscrFEnv48 LIBC_INLINE static void set_fpscr(uint32_t val) {
49 __builtin_arm_set_fpscr(val);
50 }
51
exception_enable_bits_to_macroFEnv52 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);
58 }
59
exception_macro_to_enable_bitsFEnv60 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);
66 }
67
exception_macro_to_status_bitsFEnv68 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);
74 }
75
exception_status_bits_to_macroFEnv76 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);
82 }
83 };
84
85 // Enables exceptions in |excepts| and returns the previously set exceptions.
enable_except(int excepts)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);
93 }
94
95 // Disables exceptions in |excepts| and returns the previously set exceptions.
disable_except(int excepts)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);
103 }
104
105 // Returns the currently enabled exceptions.
get_except()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);
110 }
111
112 // Clears the exceptions in |excepts|.
clear_except(int 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;
119 }
120
121 // Returns the set of exceptions which are from the input set |excepts|.
test_except(int 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);
126 }
127
128 // Set the exceptions in |excepts|.
set_except(int 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;
133 }
134
raise_except(int excepts)135 LIBC_INLINE int raise_except(int excepts) {
136 float zero = 0.0f;
137 float one = 1.0f;
138 float large_value = FPBits<float>::max_normal().get_val();
139 float small_value = FPBits<float>::min_normal().get_val();
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 */);
147 };
148
149 uint32_t to_raise = FEnv::exception_macro_to_status_bits(excepts);
150 int result = 0;
151
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;
157 }
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;
163 }
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;
169 }
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;
175 }
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;
185 }
186 return result;
187 }
188
get_round()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.
202 }
203 return 0;
204 }
205
set_round(int mode)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
223 }
224
225 uint32_t fpscr = FEnv::get_fpscr();
226 fpscr &= ~(0x3 << FEnv::RoundingControlBitPosition);
227 fpscr |= (bits << FEnv::RoundingControlBitPosition);
228 FEnv::set_fpscr(fpscr);
229
230 return 0;
231 }
232
get_env(fenv_t * envp)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;
237 }
238
set_env(const fenv_t * envp)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));
252
253 FEnv::set_fpscr(fpscr);
254 return 0;
255 }
256
257 const FEnv *state = reinterpret_cast<const FEnv *>(envp);
258 FEnv::set_fpscr(state->fpscr);
259 return 0;
260 }
261
262 } // namespace fputil
263 } // namespace LIBC_NAMESPACE
264
265 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_ARM_FENVIMPL_H
266