diff options
author | Hendiadyoin1 <leon.a@serenityos.org> | 2022-04-08 17:02:46 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-05-07 20:27:05 +0200 |
commit | 6d9bc87903d07fac1f59ab680d4d95560719a398 (patch) | |
tree | e99ee2ed3a4bdd84bcac2a3b733e3d07117fcfd8 | |
parent | 9aa4958234f7ebe011b4c07df0d737332c51e438 (diff) | |
download | serenity-6d9bc87903d07fac1f59ab680d4d95560719a398.zip |
AK: Add a cpp-y, more fine grained version of fenv.h: FPControl.h
This allows direct inlining and hides away some assembly and
bit-fiddling when manipulating the floating point environment.
This only implements the x87/SSE versions, as of now.
-rw-r--r-- | AK/FPControl.h | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/AK/FPControl.h b/AK/FPControl.h new file mode 100644 index 0000000000..6639338aba --- /dev/null +++ b/AK/FPControl.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022, Leon Albrecht <leon.a@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Types.h> + +// FIXME: Add equivalent datastructures for aarch64 +VALIDATE_IS_X86(); + +namespace AK { + +enum class RoundingMode : u8 { + NEAREST = 0b00, + DOWN = 0b01, + UP = 0b10, + TRUNC = 0b11 +}; + +union X87ControlWord { + u16 cw; + struct { + u16 mask_invalid : 1; // IM + u16 mask_denorm : 1; // DM + u16 mask_zero_div : 1; // ZM + u16 mask_overflow : 1; // OM + u16 mask_underflow : 1; // UM + u16 mask_precision : 1; // PM + u16 : 2; // unused + u16 precision : 2; // PC + RoundingMode rounding_control : 2; // RC + u16 infinity_control : 1; // X + u16 : 3; // unused + }; +}; +static_assert(sizeof(X87ControlWord) == sizeof(u16)); + +union MXCSR { + u32 mxcsr; + struct { + u32 invalid_operation_flag : 1; // IE + u32 denormal_operation_flag : 1; // DE + u32 divide_by_zero_flag : 1; // ZE + u32 overflow_flag : 1; // OE + u32 underflow_flag : 1; // UE + u32 precision_flag : 1; // PE + u32 denormals_are_zero : 1; // DAZ + u32 invalid_operation_mask : 1; // IM + u32 denormal_operation_mask : 1; // DM + u32 divide_by_zero_mask : 1; // ZM + u32 overflow_mask : 1; // OM + u32 underflow_mask : 1; // UM + u32 precision_mask : 1; // PM + RoundingMode rounding_control : 2; // RC + u32 flush_to_zero : 1; // FTZ + u32 __reserved : 16; + }; +}; +static_assert(sizeof(MXCSR) == sizeof(u32)); + +ALWAYS_INLINE X87ControlWord get_cw_x87() +{ + X87ControlWord control_word; + asm("fnstcw %0" + : "=m"(control_word)); + return control_word; +} +ALWAYS_INLINE void set_cw_x87(X87ControlWord control_word) +{ + asm("fldcw %0" ::"m"(control_word)); +} + +ALWAYS_INLINE MXCSR get_mxcsr() +{ + MXCSR mxcsr; + asm("stmxcsr %0" + : "=m"(mxcsr)); + return mxcsr; +} +ALWAYS_INLINE void set_mxcsr(MXCSR mxcsr) +{ + asm("ldmxcsr %0" ::"m"(mxcsr)); +} + +class X87RoundingModeScope { +public: + X87RoundingModeScope(RoundingMode rounding_mode) + { + m_cw = get_cw_x87(); + auto cw = m_cw; + cw.rounding_control = rounding_mode; + set_cw_x87(cw); + } + ~X87RoundingModeScope() + { + set_cw_x87(m_cw); + } + +private: + X87ControlWord m_cw; +}; + +class SSERoundingModeScope { +public: + SSERoundingModeScope(RoundingMode rounding_mode) + { + m_mxcsr = get_mxcsr(); + auto mxcsr = m_mxcsr; + mxcsr.rounding_control = rounding_mode; + set_mxcsr(mxcsr); + } + ~SSERoundingModeScope() + { + set_mxcsr(m_mxcsr); + } + +private: + MXCSR m_mxcsr; +}; + +} |