xref: /llvm-project/libc/src/__support/FPUtil/aarch64/FEnvImpl.h (revision f91a5fee53651d39eb9a610fcc25fb68915c64dc)
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 
55   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 
63   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 
71   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 
80   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 
89   static uint32_t getStatusWord() {
90 #ifdef __clang__
91     return __arm_rsr("fpsr");
92 #else
93     return __builtin_aarch64_get_fpsr();
94 #endif
95   }
96 
97   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 
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(oldExcepts);
114 }
115 
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(oldExcepts);
124 }
125 
126 LIBC_INLINE int get_except() {
127   uint32_t controlWord = FEnv::getControlWord();
128   int enabledExcepts =
129       (controlWord >> FEnv::ExceptionControlFlagsBitPosition) & 0x1F;
130   return FEnv::exceptionStatusToMacro(enabledExcepts);
131 }
132 
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 
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 
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 
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 
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 
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 &= ~(0x3 << FEnv::RoundingControlBitPosition);
254   controlWord |= (bitValue << FEnv::RoundingControlBitPosition);
255   FEnv::writeControlWord(controlWord);
256 
257   return 0;
258 }
259 
260 LIBC_INLINE int get_env(fenv_t *envp) {
261   FEnv::FPState *state = reinterpret_cast<FEnv::FPState *>(envp);
262   state->ControlWord = FEnv::getControlWord();
263   state->StatusWord = FEnv::getStatusWord();
264   return 0;
265 }
266 
267 LIBC_INLINE int set_env(const fenv_t *envp) {
268   if (envp == FE_DFL_ENV) {
269     // Default status and control words bits are all zeros so we just
270     // write zeros.
271     FEnv::writeStatusWord(0);
272     FEnv::writeControlWord(0);
273     return 0;
274   }
275   const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp);
276   FEnv::writeControlWord(state->ControlWord);
277   FEnv::writeStatusWord(state->StatusWord);
278   return 0;
279 }
280 } // namespace fputil
281 } // namespace LIBC_NAMESPACE_DECL
282 
283 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_H
284