diff --git a/Userland/DynamicLoader/CMakeLists.txt b/Userland/DynamicLoader/CMakeLists.txt index 469054966e..1e7ab92ba1 100644 --- a/Userland/DynamicLoader/CMakeLists.txt +++ b/Userland/DynamicLoader/CMakeLists.txt @@ -11,6 +11,7 @@ set(ARCH_FOLDER "${SERENITY_ARCH}") file(GLOB LIBC_SOURCES3 "../Libraries/LibC/arch/${ARCH_FOLDER}/*.S") set(ELF_SOURCES ${ELF_SOURCES} "../Libraries/LibELF/Arch/${ARCH_FOLDER}/entry.S" "../Libraries/LibELF/Arch/${ARCH_FOLDER}/plt_trampoline.S") +set(LIBC_SOURCES3 ${LIBC_SOURCES3} "../Libraries/LibC/arch/${ARCH_FOLDER}/fenv.cpp") if ("${SERENITY_ARCH}" STREQUAL "x86_64") set(LIBC_SOURCES3 ${LIBC_SOURCES3} "../Libraries/LibC/arch/x86_64/memset.cpp") elseif ("${SERENITY_ARCH}" STREQUAL "aarch64") diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt index 501e58bf31..6be7091b56 100644 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ b/Userland/Libraries/LibC/CMakeLists.txt @@ -99,17 +99,19 @@ endforeach() file(GLOB ELF_SOURCES CONFIGURE_DEPENDS "../LibELF/*.cpp") if ("${SERENITY_ARCH}" STREQUAL "aarch64") + set(LIBC_SOURCES ${LIBC_SOURCES} "arch/aarch64/fenv.cpp") set(ASM_SOURCES "arch/aarch64/setjmp.S") set(ELF_SOURCES ${ELF_SOURCES} ../LibELF/Arch/aarch64/entry.S ../LibELF/Arch/aarch64/plt_trampoline.S ../LibELF/Arch/aarch64/tls.S) set(CRTI_SOURCE "arch/aarch64/crti.S") set(CRTN_SOURCE "arch/aarch64/crtn.S") elseif ("${SERENITY_ARCH}" STREQUAL "x86_64") - set(LIBC_SOURCES ${LIBC_SOURCES} "arch/x86_64/memset.cpp") + set(LIBC_SOURCES ${LIBC_SOURCES} "arch/x86_64/memset.cpp" "arch/x86_64/fenv.cpp") set(ASM_SOURCES "arch/x86_64/setjmp.S" "arch/x86_64/memset.S") set(ELF_SOURCES ${ELF_SOURCES} ../LibELF/Arch/x86_64/entry.S ../LibELF/Arch/x86_64/plt_trampoline.S) set(CRTI_SOURCE "arch/x86_64/crti.S") set(CRTN_SOURCE "arch/x86_64/crtn.S") elseif ("${SERENITY_ARCH}" STREQUAL "riscv64") + set(LIBC_SOURCES ${LIBC_SOURCES} "arch/riscv64/fenv.cpp") set(ASM_SOURCES "arch/riscv64/setjmp.S") set(ELF_SOURCES ${ELF_SOURCES} ../LibELF/Arch/riscv64/entry.S ../LibELF/Arch/riscv64/plt_trampoline.S) set(CRTI_SOURCE "arch/riscv64/crti.S") diff --git a/Userland/Libraries/LibC/arch/aarch64/fenv.cpp b/Userland/Libraries/LibC/arch/aarch64/fenv.cpp new file mode 100644 index 0000000000..286fc738a6 --- /dev/null +++ b/Userland/Libraries/LibC/arch/aarch64/fenv.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Tom Finet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +extern "C" { + +int fegetenv(fenv_t* env) +{ + if (!env) + return 1; + + (void)env; + TODO_AARCH64(); + return 0; +} + +int fesetenv(fenv_t const* env) +{ + if (!env) + return 1; + + (void)env; + TODO_AARCH64(); + return 0; +} + +int feholdexcept(fenv_t* env) +{ + fegetenv(env); + + fenv_t current_env; + fegetenv(¤t_env); + + (void)env; + TODO_AARCH64(); + + fesetenv(¤t_env); + return 0; +} + +int fesetexceptflag(fexcept_t const* except, int exceptions) +{ + if (!except) + return 1; + + fenv_t current_env; + fegetenv(¤t_env); + + exceptions &= FE_ALL_EXCEPT; + + (void)exceptions; + (void)except; + TODO_AARCH64(); + + fesetenv(¤t_env); + return 0; +} + +int fegetround() +{ + TODO_AARCH64(); +} + +int fesetround(int rounding_mode) +{ + if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO) + return 1; + + TODO_AARCH64(); + return 0; +} + +int feclearexcept(int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fenv_t current_env; + fegetenv(¤t_env); + + (void)exceptions; + TODO_AARCH64(); + + fesetenv(¤t_env); + return 0; +} + +int fetestexcept(int exceptions) +{ + (void)exceptions; + TODO_AARCH64(); +} + +int feraiseexcept(int exceptions) +{ + fenv_t env; + fegetenv(&env); + + exceptions &= FE_ALL_EXCEPT; + + (void)exceptions; + TODO_AARCH64(); +} +} diff --git a/Userland/Libraries/LibC/arch/riscv64/fenv.cpp b/Userland/Libraries/LibC/arch/riscv64/fenv.cpp new file mode 100644 index 0000000000..c406c4ed1c --- /dev/null +++ b/Userland/Libraries/LibC/arch/riscv64/fenv.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, Tom Finet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +extern "C" { + +int fegetenv(fenv_t* env) +{ + if (!env) + return 1; + + (void)env; + TODO_RISCV64(); + return 0; +} + +int fesetenv(fenv_t const* env) +{ + if (!env) + return 1; + + (void)env; + TODO_RISCV64(); + return 0; +} + +int feholdexcept(fenv_t* env) +{ + fegetenv(env); + + fenv_t current_env; + fegetenv(¤t_env); + + (void)env; + TODO_RISCV64(); + + fesetenv(¤t_env); + return 0; +} + +int fesetexceptflag(fexcept_t const* except, int exceptions) +{ + if (!except) + return 1; + + fenv_t current_env; + fegetenv(¤t_env); + + exceptions &= FE_ALL_EXCEPT; + + (void)exceptions; + (void)except; + TODO_RISCV64(); + + fesetenv(¤t_env); + return 0; +} + +int fegetround() +{ + TODO_RISCV64(); +} + +int fesetround(int rounding_mode) +{ + if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO) + return 1; + + TODO_RISCV64(); + return 0; +} + +int feclearexcept(int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fenv_t current_env; + fegetenv(¤t_env); + + (void)exceptions; + TODO_RISCV64(); + + fesetenv(¤t_env); + return 0; +} + +int fetestexcept(int exceptions) +{ + (void)exceptions; + TODO_RISCV64(); +} + +int feraiseexcept(int exceptions) +{ + fenv_t env; + fegetenv(&env); + + exceptions &= FE_ALL_EXCEPT; + + (void)exceptions; + TODO_RISCV64(); +} +} diff --git a/Userland/Libraries/LibC/arch/x86_64/fenv.cpp b/Userland/Libraries/LibC/arch/x86_64/fenv.cpp new file mode 100644 index 0000000000..53ed376cf6 --- /dev/null +++ b/Userland/Libraries/LibC/arch/x86_64/fenv.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021-2024, MiČ›ca Dumitru + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +static u16 read_status_register() +{ + u16 status_register; + asm volatile("fnstsw %0" + : "=m"(status_register)); + return status_register; +} + +static u16 read_control_word() +{ + u16 control_word; + asm volatile("fnstcw %0" + : "=m"(control_word)); + return control_word; +} + +static void set_control_word(u16 new_control_word) +{ + asm volatile("fldcw %0" ::"m"(new_control_word)); +} + +static u32 read_mxcsr() +{ + u32 mxcsr; + asm volatile("stmxcsr %0" + : "=m"(mxcsr)); + return mxcsr; +} + +static void set_mxcsr(u32 new_mxcsr) +{ + asm volatile("ldmxcsr %0" ::"m"(new_mxcsr)); +} + +static constexpr u32 default_mxcsr_value = 0x1f80; + +extern "C" { + +int fegetenv(fenv_t* env) +{ + if (!env) + return 1; + + asm volatile("fnstenv %0" + : "=m"(env->__x87_fpu_env)::"memory"); + + env->__mxcsr = read_mxcsr(); + return 0; +} + +int fesetenv(fenv_t const* env) +{ + if (!env) + return 1; + + if (env == FE_DFL_ENV) { + asm volatile("finit"); + set_mxcsr(default_mxcsr_value); + return 0; + } + + asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env) + : "memory"); + + set_mxcsr(env->__mxcsr); + return 0; +} + +int feholdexcept(fenv_t* env) +{ + fegetenv(env); + + fenv_t current_env; + fegetenv(¤t_env); + + current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT; + current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit + current_env.__x87_fpu_env.__control_word &= FE_ALL_EXCEPT; // Masking these bits stops the corresponding exceptions from being generated according to the Intel Programmer's Manual + + fesetenv(¤t_env); + return 0; +} + +int fesetexceptflag(fexcept_t const* except, int exceptions) +{ + if (!except) + return 1; + + fenv_t current_env; + fegetenv(¤t_env); + + exceptions &= FE_ALL_EXCEPT; + current_env.__x87_fpu_env.__status_word &= exceptions; + current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised + + fesetenv(¤t_env); + return 0; +} + +int fegetround() +{ + // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same + return (read_status_register() >> 10) & 3; +} + +int fesetround(int rounding_mode) +{ + if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO) + return 1; + + auto control_word = read_control_word(); + + control_word &= ~(3 << 10); + control_word |= rounding_mode << 10; + + set_control_word(control_word); + + auto mxcsr = read_mxcsr(); + + mxcsr &= ~(3 << 13); + mxcsr |= rounding_mode << 13; + + set_mxcsr(mxcsr); + return 0; +} + +int feclearexcept(int exceptions) +{ + exceptions &= FE_ALL_EXCEPT; + + fenv_t current_env; + fegetenv(¤t_env); + + current_env.__x87_fpu_env.__status_word &= ~exceptions; + current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit + + fesetenv(¤t_env); + return 0; +} + +int fetestexcept(int exceptions) +{ + u16 status_register = read_status_register() & FE_ALL_EXCEPT; + exceptions &= FE_ALL_EXCEPT; + + return status_register & exceptions; +} + +int feraiseexcept(int exceptions) +{ + fenv_t env; + fegetenv(&env); + + exceptions &= FE_ALL_EXCEPT; + + // While the order in which the exceptions is raised is unspecified, FE_OVERFLOW and FE_UNDERFLOW must be raised before FE_INEXACT, so handle that case in this branch + if (exceptions & FE_INEXACT) { + env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT); + fesetenv(&env); + asm volatile("fwait"); // "raise" the exception by performing a floating point operation + + fegetenv(&env); + env.__x87_fpu_env.__status_word &= FE_INEXACT; + fesetenv(&env); + asm volatile("fwait"); + + return 0; + } + + env.__x87_fpu_env.__status_word &= exceptions; + fesetenv(&env); + asm volatile("fwait"); + + return 0; +} +} diff --git a/Userland/Libraries/LibC/fenv.cpp b/Userland/Libraries/LibC/fenv.cpp index c6063dc6b5..12e7782b36 100644 --- a/Userland/Libraries/LibC/fenv.cpp +++ b/Userland/Libraries/LibC/fenv.cpp @@ -10,124 +10,8 @@ // This is the size of the floating point environment image in protected mode static_assert(sizeof(__x87_floating_point_environment) == 28); -#if ARCH(X86_64) -static u16 read_status_register() -{ - u16 status_register; - asm volatile("fnstsw %0" - : "=m"(status_register)); - return status_register; -} - -static u16 read_control_word() -{ - u16 control_word; - asm volatile("fnstcw %0" - : "=m"(control_word)); - return control_word; -} - -static void set_control_word(u16 new_control_word) -{ - asm volatile("fldcw %0" ::"m"(new_control_word)); -} - -static u32 read_mxcsr() -{ - u32 mxcsr; - asm volatile("stmxcsr %0" - : "=m"(mxcsr)); - return mxcsr; -} - -static void set_mxcsr(u32 new_mxcsr) -{ - asm volatile("ldmxcsr %0" ::"m"(new_mxcsr)); -} - -static constexpr u32 default_mxcsr_value = 0x1f80; -#endif - extern "C" { -int fegetenv(fenv_t* env) -{ - if (!env) - return 1; - -#if ARCH(AARCH64) - (void)env; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)env; - TODO_RISCV64(); -#elif ARCH(X86_64) - asm volatile("fnstenv %0" - : "=m"(env->__x87_fpu_env)::"memory"); - - env->__mxcsr = read_mxcsr(); -#else -# error Unknown architecture -#endif - - return 0; -} - -int fesetenv(fenv_t const* env) -{ - if (!env) - return 1; - -#if ARCH(AARCH64) - (void)env; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)env; - TODO_RISCV64(); -#elif ARCH(X86_64) - if (env == FE_DFL_ENV) { - asm volatile("finit"); - set_mxcsr(default_mxcsr_value); - return 0; - } - - asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env) - : "memory"); - - set_mxcsr(env->__mxcsr); -#else -# error Unknown architecture -#endif - - return 0; -} - -int feholdexcept(fenv_t* env) -{ - fegetenv(env); - - fenv_t current_env; - fegetenv(¤t_env); - -#if ARCH(AARCH64) - (void)env; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)env; - TODO_RISCV64(); -#elif ARCH(X86_64) - current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit - current_env.__x87_fpu_env.__control_word &= FE_ALL_EXCEPT; // Masking these bits stops the corresponding exceptions from being generated according to the Intel Programmer's Manual -#else -# error Unknown architecture -#endif - - fesetenv(¤t_env); - - return 0; -} - int feupdateenv(fenv_t const* env) { auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT); @@ -145,155 +29,4 @@ int fegetexceptflag(fexcept_t* except, int exceptions) *except = (uint16_t)fetestexcept(exceptions); return 0; } -int fesetexceptflag(fexcept_t const* except, int exceptions) -{ - if (!except) - return 1; - - fenv_t current_env; - fegetenv(¤t_env); - - exceptions &= FE_ALL_EXCEPT; -#if ARCH(AARCH64) - (void)exceptions; - (void)except; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)exceptions; - (void)except; - TODO_RISCV64(); -#elif ARCH(X86_64) - current_env.__x87_fpu_env.__status_word &= exceptions; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised -#else -# error Unknown architecture -#endif - - fesetenv(¤t_env); - return 0; -} - -int fegetround() -{ -#if ARCH(AARCH64) - TODO_AARCH64(); -#elif ARCH(RISCV64) - TODO_RISCV64(); -#elif ARCH(X86_64) - // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same - return (read_status_register() >> 10) & 3; -#else -# error Unknown architecture -#endif -} - -int fesetround(int rounding_mode) -{ - if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO) - return 1; - -#if ARCH(AARCH64) - TODO_AARCH64(); -#elif ARCH(RISCV64) - TODO_RISCV64(); -#elif ARCH(X86_64) - auto control_word = read_control_word(); - - control_word &= ~(3 << 10); - control_word |= rounding_mode << 10; - - set_control_word(control_word); - - auto mxcsr = read_mxcsr(); - - mxcsr &= ~(3 << 13); - mxcsr |= rounding_mode << 13; - - set_mxcsr(mxcsr); -#else -# error Unknown architecture -#endif - - return 0; -} - -int feclearexcept(int exceptions) -{ - exceptions &= FE_ALL_EXCEPT; - - fenv_t current_env; - fegetenv(¤t_env); - -#if ARCH(AARCH64) - (void)exceptions; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)exceptions; - TODO_RISCV64(); -#elif ARCH(X86_64) - current_env.__x87_fpu_env.__status_word &= ~exceptions; - current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit -#else -# error Unknown architecture -#endif - - fesetenv(¤t_env); - return 0; -} - -int fetestexcept(int exceptions) -{ -#if ARCH(AARCH64) - (void)exceptions; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)exceptions; - TODO_RISCV64(); -#elif ARCH(X86_64) - u16 status_register = read_status_register() & FE_ALL_EXCEPT; - exceptions &= FE_ALL_EXCEPT; - - return status_register & exceptions; -#else -# error Unknown architecture -#endif -} - -int feraiseexcept(int exceptions) -{ - fenv_t env; - fegetenv(&env); - - exceptions &= FE_ALL_EXCEPT; - -#if ARCH(AARCH64) - (void)exceptions; - TODO_AARCH64(); -#elif ARCH(RISCV64) - (void)exceptions; - TODO_RISCV64(); -#elif ARCH(X86_64) - // While the order in which the exceptions is raised is unspecified, FE_OVERFLOW and FE_UNDERFLOW must be raised before FE_INEXACT, so handle that case in this branch - if (exceptions & FE_INEXACT) { - env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT); - fesetenv(&env); - asm volatile("fwait"); // "raise" the exception by performing a floating point operation - - fegetenv(&env); - env.__x87_fpu_env.__status_word &= FE_INEXACT; - fesetenv(&env); - asm volatile("fwait"); - - return 0; - } - - env.__x87_fpu_env.__status_word &= exceptions; - fesetenv(&env); - asm volatile("fwait"); -#else -# error Unknown architecture -#endif - - return 0; -} }