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