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/properties/architectures.h"
15 #if !defined(LIBC_TARGET_ARCH_IS_AARCH64) || defined(__APPLE__)
16 #error "Invalid include"
23 #include "src/__support/FPUtil/FPBits.h"
25 namespace __llvm_libc
{
35 sizeof(fenv_t
) == sizeof(FPState
),
36 "Internal floating point state does not match the public fenv_t type.");
38 static constexpr uint32_t TONEAREST
= 0x0;
39 static constexpr uint32_t UPWARD
= 0x1;
40 static constexpr uint32_t DOWNWARD
= 0x2;
41 static constexpr uint32_t TOWARDZERO
= 0x3;
43 static constexpr uint32_t INVALID
= 0x1;
44 static constexpr uint32_t DIVBYZERO
= 0x2;
45 static constexpr uint32_t OVERFLOW
= 0x4;
46 static constexpr uint32_t UNDERFLOW
= 0x8;
47 static constexpr uint32_t INEXACT
= 0x10;
49 // Zero-th bit is the first bit.
50 static constexpr uint32_t RoundingControlBitPosition
= 22;
51 static constexpr uint32_t ExceptionStatusFlagsBitPosition
= 0;
52 static constexpr uint32_t ExceptionControlFlagsBitPosition
= 8;
54 LIBC_INLINE
static uint32_t getStatusValueForExcept(int excepts
) {
55 return (excepts
& FE_INVALID
? INVALID
: 0) |
56 (excepts
& FE_DIVBYZERO
? DIVBYZERO
: 0) |
57 (excepts
& FE_OVERFLOW
? OVERFLOW
: 0) |
58 (excepts
& FE_UNDERFLOW
? UNDERFLOW
: 0) |
59 (excepts
& FE_INEXACT
? INEXACT
: 0);
62 LIBC_INLINE
static int exceptionStatusToMacro(uint32_t status
) {
63 return (status
& INVALID
? FE_INVALID
: 0) |
64 (status
& DIVBYZERO
? FE_DIVBYZERO
: 0) |
65 (status
& OVERFLOW
? FE_OVERFLOW
: 0) |
66 (status
& UNDERFLOW
? FE_UNDERFLOW
: 0) |
67 (status
& INEXACT
? FE_INEXACT
: 0);
70 static uint32_t getControlWord() {
72 // GCC does not currently support __arm_rsr.
73 return __arm_rsr("fpcr");
75 return __builtin_aarch64_get_fpcr();
79 static void writeControlWord(uint32_t fpcr
) {
81 // GCC does not currently support __arm_wsr.
82 __arm_wsr("fpcr", fpcr
);
84 __builtin_aarch64_set_fpcr(fpcr
);
88 static uint32_t getStatusWord() {
90 return __arm_rsr("fpsr");
92 return __builtin_aarch64_get_fpsr();
96 static void writeStatusWord(uint32_t fpsr
) {
98 __arm_wsr("fpsr", fpsr
);
100 __builtin_aarch64_set_fpsr(fpsr
);
105 LIBC_INLINE
int enable_except(int excepts
) {
106 uint32_t newExcepts
= FEnv::getStatusValueForExcept(excepts
);
107 uint32_t controlWord
= FEnv::getControlWord();
109 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
110 controlWord
|= (newExcepts
<< FEnv::ExceptionControlFlagsBitPosition
);
111 FEnv::writeControlWord(controlWord
);
112 return FEnv::exceptionStatusToMacro(oldExcepts
);
115 LIBC_INLINE
int disable_except(int excepts
) {
116 uint32_t disabledExcepts
= FEnv::getStatusValueForExcept(excepts
);
117 uint32_t controlWord
= FEnv::getControlWord();
119 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
120 controlWord
&= ~(disabledExcepts
<< FEnv::ExceptionControlFlagsBitPosition
);
121 FEnv::writeControlWord(controlWord
);
122 return FEnv::exceptionStatusToMacro(oldExcepts
);
125 LIBC_INLINE
int get_except() {
126 uint32_t controlWord
= FEnv::getControlWord();
128 (controlWord
>> FEnv::ExceptionControlFlagsBitPosition
) & 0x1F;
129 return FEnv::exceptionStatusToMacro(enabledExcepts
);
132 LIBC_INLINE
int clear_except(int excepts
) {
133 uint32_t statusWord
= FEnv::getStatusWord();
134 uint32_t toClear
= FEnv::getStatusValueForExcept(excepts
);
135 statusWord
&= ~(toClear
<< FEnv::ExceptionStatusFlagsBitPosition
);
136 FEnv::writeStatusWord(statusWord
);
140 LIBC_INLINE
int test_except(int excepts
) {
141 uint32_t toTest
= FEnv::getStatusValueForExcept(excepts
);
142 uint32_t statusWord
= FEnv::getStatusWord();
143 return FEnv::exceptionStatusToMacro(
144 (statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) & toTest
);
147 LIBC_INLINE
int set_except(int excepts
) {
148 uint32_t statusWord
= FEnv::getStatusWord();
149 uint32_t statusValue
= FEnv::getStatusValueForExcept(excepts
);
150 statusWord
|= (statusValue
<< FEnv::ExceptionStatusFlagsBitPosition
);
151 FEnv::writeStatusWord(statusWord
);
155 LIBC_INLINE
int raise_except(int excepts
) {
158 float largeValue
= float(FPBits
<float>(FPBits
<float>::MAX_NORMAL
));
159 float smallValue
= float(FPBits
<float>(FPBits
<float>::MIN_NORMAL
));
160 auto divfunc
= [](float a
, float b
) {
161 __asm__
__volatile__("ldr s0, %0\n\t"
163 "fdiv s0, s0, s1\n\t"
166 : "s0", "s1" /* s0 and s1 are clobbered */);
169 uint32_t toRaise
= FEnv::getStatusValueForExcept(excepts
);
172 if (toRaise
& FEnv::INVALID
) {
174 uint32_t statusWord
= FEnv::getStatusWord();
175 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
180 if (toRaise
& FEnv::DIVBYZERO
) {
182 uint32_t statusWord
= FEnv::getStatusWord();
183 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
187 if (toRaise
& FEnv::OVERFLOW
) {
188 divfunc(largeValue
, smallValue
);
189 uint32_t statusWord
= FEnv::getStatusWord();
190 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
194 if (toRaise
& FEnv::UNDERFLOW
) {
195 divfunc(smallValue
, largeValue
);
196 uint32_t statusWord
= FEnv::getStatusWord();
197 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
201 if (toRaise
& FEnv::INEXACT
) {
204 // 2.0 / 3.0 cannot be represented exactly in any radix 2 floating point
207 uint32_t statusWord
= FEnv::getStatusWord();
208 if (!((statusWord
>> FEnv::ExceptionStatusFlagsBitPosition
) &
215 LIBC_INLINE
int get_round() {
216 uint32_t roundingMode
=
217 (FEnv::getControlWord() >> FEnv::RoundingControlBitPosition
) & 0x3;
218 switch (roundingMode
) {
219 case FEnv::TONEAREST
:
225 case FEnv::TOWARDZERO
:
226 return FE_TOWARDZERO
;
228 return -1; // Error value.
232 LIBC_INLINE
int set_round(int mode
) {
236 bitValue
= FEnv::TONEAREST
;
239 bitValue
= FEnv::DOWNWARD
;
242 bitValue
= FEnv::UPWARD
;
245 bitValue
= FEnv::TOWARDZERO
;
248 return 1; // To indicate failure
251 uint32_t controlWord
= FEnv::getControlWord();
252 controlWord
&= ~(0x3 << FEnv::RoundingControlBitPosition
);
253 controlWord
|= (bitValue
<< FEnv::RoundingControlBitPosition
);
254 FEnv::writeControlWord(controlWord
);
259 LIBC_INLINE
int get_env(fenv_t
*envp
) {
260 FEnv::FPState
*state
= reinterpret_cast<FEnv::FPState
*>(envp
);
261 state
->ControlWord
= FEnv::getControlWord();
262 state
->StatusWord
= FEnv::getStatusWord();
266 LIBC_INLINE
int set_env(const fenv_t
*envp
) {
267 if (envp
== FE_DFL_ENV
) {
268 // Default status and control words bits are all zeros so we just
270 FEnv::writeStatusWord(0);
271 FEnv::writeControlWord(0);
274 const FEnv::FPState
*state
= reinterpret_cast<const FEnv::FPState
*>(envp
);
275 FEnv::writeControlWord(state
->ControlWord
);
276 FEnv::writeStatusWord(state
->StatusWord
);
280 } // namespace fputil
281 } // namespace __llvm_libc
283 #endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_AARCH64_FENVIMPL_H