1 //===-- aarch64 floating point env manipulation functions -------*- C++ -*-===//
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
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/config.h"
14 #include "src/__support/macros/properties/architectures.h"
16 #if !defined(LIBC_TARGET_ARCH_IS_AARCH64) || defined(__APPLE__)
17 #error "Invalid include"
23 #include "hdr/fenv_macros.h"
24 #include "hdr/types/fenv_t.h"
25 #include "src/__support/FPUtil/FPBits.h"
27 namespace LIBC_NAMESPACE_DECL
{
36 sizeof(fenv_t
) == sizeof(FPState
),
37 "Internal floating point state does not match the public fenv_t type.");
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;
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;
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;
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);
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);
71 static uint32_t getControlWord() {
73 // GCC does not currently support __arm_rsr.
74 return __arm_rsr("fpcr");
76 return __builtin_aarch64_get_fpcr();
80 static void writeControlWord(uint32_t fpcr
) {
82 // GCC does not currently support __arm_wsr.
83 __arm_wsr("fpcr", fpcr
);
85 __builtin_aarch64_set_fpcr(fpcr
);
89 static uint32_t getStatusWord() {
91 return __arm_rsr("fpsr");
93 return __builtin_aarch64_get_fpsr();
97 static void writeStatusWord(uint32_t fpsr
) {
99 __arm_wsr("fpsr", fpsr
);
101 __builtin_aarch64_set_fpsr(fpsr
);
106 LIBC_INLINE
int enable_except(int excepts
) {
107 uint32_t newExcepts
= FEnv::getStatusValueForExcept(excepts
);
108 uint32_t controlWord
= FEnv::getControlWord();
110 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
111 controlWord
|= (newExcepts
<< FEnv::ExceptionControlFlagsBitPosition
);
112 FEnv::writeControlWord(controlWord
);
113 return FEnv::exceptionStatusToMacro(oldExcepts
);
116 LIBC_INLINE
int disable_except(int excepts
) {
117 uint32_t disabledExcepts
= FEnv::getStatusValueForExcept(excepts
);
118 uint32_t controlWord
= FEnv::getControlWord();
120 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
121 controlWord
&= ~(disabledExcepts
<< FEnv::ExceptionControlFlagsBitPosition
);
122 FEnv::writeControlWord(controlWord
);
123 return FEnv::exceptionStatusToMacro(oldExcepts
);
126 LIBC_INLINE
int get_except() {
127 uint32_t controlWord
= FEnv::getControlWord();
129 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
130 return FEnv::exceptionStatusToMacro(enabledExcepts
);
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
);
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
);
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
);
156 LIBC_INLINE
int raise_except(int excepts
) {
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"
164 "fdiv s0, s0, s1\n\t"
167 : "s0", "s1" /* s0 and s1 are clobbered */);
170 uint32_t toRaise
= FEnv::getStatusValueForExcept(excepts
);
173 if (toRaise
& FEnv::INVALID_F
) {
175 uint32_t statusWord
= FEnv::getStatusWord();
176 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
181 if (toRaise
& FEnv::DIVBYZERO_F
) {
183 uint32_t statusWord
= FEnv::getStatusWord();
184 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
188 if (toRaise
& FEnv::OVERFLOW_F
) {
189 divfunc(largeValue
, smallValue
);
190 uint32_t statusWord
= FEnv::getStatusWord();
191 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
195 if (toRaise
& FEnv::UNDERFLOW_F
) {
196 divfunc(smallValue
, largeValue
);
197 uint32_t statusWord
= FEnv::getStatusWord();
198 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
202 if (toRaise
& FEnv::INEXACT_F
) {
205 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
208 uint32_t statusWord
= FEnv::getStatusWord();
209 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
216 LIBC_INLINE
int get_round() {
217 uint32_t roundingMode
=
218 (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition
) & 0x3;
219 switch (roundingMode
) {
220 case FEnv::TONEAREST
:
226 case FEnv::TOWARDZERO
:
227 return FE_TOWARDZERO
;
229 return -1; // Error value.
233 LIBC_INLINE
int set_round(int mode
) {
237 bitValue
= FEnv::TONEAREST
;
240 bitValue
= FEnv::DOWNWARD
;
243 bitValue
= FEnv::UPWARD
;
246 bitValue
= FEnv::TOWARDZERO
;
249 return 1; // To indicate failure
252 uint32_t controlWord
= FEnv::getControlWord();
253 controlWord
&= ~(0x3 << FEnv::RoundingControlBitPosition
);
254 controlWord
|= (bitValue
<< FEnv::RoundingControlBitPosition
);
255 FEnv::writeControlWord(controlWord
);
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();
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
271 FEnv::writeStatusWord(0);
272 FEnv::writeControlWord(0);
275 const FEnv::FPState
*state
= reinterpret_cast<const FEnv::FPState
*>(envp
);
276 FEnv::writeControlWord(state
->ControlWord
);
277 FEnv::writeStatusWord(state
->StatusWord
);
280 } // namespace fputil
281 } // namespace LIBC_NAMESPACE_DECL
283 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_AARCH64_FENVIMPL_H