diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -189,6 +189,22 @@ ) set(TARGET_LIBM_ENTRYPOINTS + # fenv.h entrypoints + libc.src.fenv.feclearexcept + libc.src.fenv.fedisableexcept + libc.src.fenv.feenableexcept + libc.src.fenv.fegetenv + libc.src.fenv.fegetexcept + libc.src.fenv.fegetexceptflag + libc.src.fenv.fegetround + libc.src.fenv.feholdexcept + libc.src.fenv.fesetenv + libc.src.fenv.fesetexceptflag + libc.src.fenv.fesetround + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.fenv.feupdateenv + # math.h entrypoints libc.src.math.fabs libc.src.math.fabsf diff --git a/libc/src/__support/FPUtil/FEnvImpl.h b/libc/src/__support/FPUtil/FEnvImpl.h --- a/libc/src/__support/FPUtil/FEnvImpl.h +++ b/libc/src/__support/FPUtil/FEnvImpl.h @@ -30,6 +30,8 @@ #include "x86_64/FEnvImpl.h" #elif defined(LIBC_TARGET_ARCH_IS_ARM) #include "arm/FEnvImpl.h" +#elif defined(LIBC_TARGET_ARCH_IS_RISCV64) +#include "riscv64/FEnvImpl.h" #else namespace __llvm_libc::fputil { diff --git a/libc/src/__support/FPUtil/riscv64/FEnvImpl.h b/libc/src/__support/FPUtil/riscv64/FEnvImpl.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/FPUtil/riscv64/FEnvImpl.h @@ -0,0 +1,180 @@ +//===-- riscv64 floating point env manipulation functions -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_FPUTIL_RISCV64_FENVIMPL_H +#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_RISCV64_FENVIMPL_H + +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/macros/attributes.h" // For LIBC_INLINE_ASM +#include "src/__support/macros/config.h" // For LIBC_INLINE + +#include +#include + +namespace __llvm_libc { +namespace fputil { + +struct FEnv { + // We will ignore RMM and DYN rounding modes. + static constexpr uint32_t TONEAREST = 0x0; + static constexpr uint32_t TOWARDZERO = 0x1; + static constexpr uint32_t DOWNWARD = 0x2; + static constexpr uint32_t UPWARD = 0x3; + + // These are the bit locations of the corresponding exceptions in fcsr. + static constexpr uint32_t INEXACT = 0x1; + static constexpr uint32_t UNDERFLOW = 0x2; + static constexpr uint32_t OVERFLOW = 0x4; + static constexpr uint32_t DIVBYZERO = 0x8; + static constexpr uint32_t INVALID = 0x10; + + LIBC_INLINE static uint32_t get_frm() { + unsigned int rm; + LIBC_INLINE_ASM("frrm %0\n\t" : "=r"(rm)); + return rm; + } + + LIBC_INLINE static void set_frm(uint32_t rm) { + LIBC_INLINE_ASM("fsrm %0, %0\n\t" : "+r"(rm)); + } + + LIBC_INLINE static uint32_t get_fflags() { + unsigned int flags; + LIBC_INLINE_ASM("frflags %0\n\t" : "=r"(flags)); + return flags; + } + + LIBC_INLINE static void set_fflags(uint32_t flags) { + LIBC_INLINE_ASM("fsflags %0, %0\n\t" : "+r"(flags)); + } + + LIBC_INLINE static uint32_t get_fcsr() { + unsigned int fcsr; + LIBC_INLINE_ASM("frcsr %0\n\t" : "=r"(fcsr)); + return fcsr; + } + + LIBC_INLINE static void set_fcsr(uint32_t fcsr) { + LIBC_INLINE_ASM("fscsr %0, %0\n\t" : "+r"(fcsr)); + } + + LIBC_INLINE static int exception_bits_to_macro(uint32_t status) { + return (status & INVALID ? FE_INVALID : 0) | + (status & DIVBYZERO ? FE_DIVBYZERO : 0) | + (status & OVERFLOW ? FE_OVERFLOW : 0) | + (status & UNDERFLOW ? FE_UNDERFLOW : 0) | + (status & INEXACT ? FE_INEXACT : 0); + } + + LIBC_INLINE static uint32_t exception_macro_to_bits(int except) { + return (except & FE_INVALID ? INVALID : 0) | + (except & FE_DIVBYZERO ? DIVBYZERO : 0) | + (except & FE_OVERFLOW ? OVERFLOW : 0) | + (except & FE_UNDERFLOW ? UNDERFLOW : 0) | + (except & FE_INEXACT ? INEXACT : 0); + } +}; + +// Since RISCV does not have exception enable bits, we will just return +// the failure indicator. +LIBC_INLINE int enable_except(int) { return -1; } + +// Always succeed. +LIBC_INLINE int disable_except(int) { return 0; } + +// Always return "no exceptions enabled". +LIBC_INLINE int get_except() { return 0; } + +LIBC_INLINE int clear_except(int excepts) { + uint32_t flags = FEnv::get_fflags(); + uint32_t to_clear = FEnv::exception_macro_to_bits(excepts); + flags &= ~to_clear; + FEnv::set_fflags(flags); + return 0; +} + +LIBC_INLINE int test_except(int excepts) { + uint32_t to_test = FEnv::exception_macro_to_bits(excepts); + uint32_t flags = FEnv::get_fflags(); + return FEnv::exception_bits_to_macro(flags & to_test); +} + +LIBC_INLINE int set_except(int excepts) { + uint32_t flags = FEnv::get_fflags(); + FEnv::set_fflags(flags | FEnv::exception_macro_to_bits(excepts)); + return 0; +} + +LIBC_INLINE int raise_except(int excepts) { + // Since there are no traps, we just set the exception flags. + uint32_t flags = FEnv::get_fflags(); + FEnv::set_fflags(flags | FEnv::exception_macro_to_bits(excepts)); + return 0; +} + +LIBC_INLINE int get_round() { + uint32_t rm = FEnv::get_frm(); + switch (rm) { + case FEnv::TONEAREST: + return FE_TONEAREST; + case FEnv::DOWNWARD: + return FE_DOWNWARD; + case FEnv::UPWARD: + return FE_UPWARD; + case FEnv::TOWARDZERO: + return FE_TOWARDZERO; + default: + return -1; // Error value. + } + return 0; +} + +LIBC_INLINE int set_round(int mode) { + uint32_t rm; + switch (mode) { + case FE_TONEAREST: + rm = FEnv::TONEAREST; + break; + case FE_DOWNWARD: + rm = FEnv::DOWNWARD; + break; + case FE_UPWARD: + rm = FEnv::UPWARD; + break; + case FE_TOWARDZERO: + rm = FEnv::TOWARDZERO; + break; + default: + return -1; // To indicate failure + } + FEnv::set_frm(rm); + return 0; +} + +LIBC_INLINE int get_env(fenv_t *envp) { + uint32_t *state = reinterpret_cast(envp); + *state = FEnv::get_fcsr(); + return 0; +} + +LIBC_INLINE int set_env(const fenv_t *envp) { + if (envp == FE_DFL_ENV) { + FEnv::set_frm(FEnv::TONEAREST); + FEnv::set_fflags(0); + return 0; + } + uint32_t status = *reinterpret_cast(envp); + // We have to do the masking to preserve the reserved bits. + FEnv::set_fcsr((status & 0xFF) | (FEnv::get_fcsr() & 0xFFFFFF00)); + return 0; +} + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_RISCV64_FENVIMPL_H diff --git a/libc/test/src/fenv/enabled_exceptions_test.cpp b/libc/test/src/fenv/enabled_exceptions_test.cpp --- a/libc/test/src/fenv/enabled_exceptions_test.cpp +++ b/libc/test/src/fenv/enabled_exceptions_test.cpp @@ -21,7 +21,7 @@ // This test enables an exception and verifies that raising that exception // triggers SIGFPE. TEST(LlvmLibcExceptionStatusTest, RaiseAndCrash) { -#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) || defined(LIBC_TARGET_ARCH_IS_RISCV64) // Few Arm HW implementations do not trap exceptions. We skip this test // completely on such HW. // @@ -33,7 +33,7 @@ __llvm_libc::fputil::enable_except(FE_DIVBYZERO); if (__llvm_libc::fputil::get_except() == 0) return; -#endif // defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#endif // Architectures where exception trapping is not supported // TODO: Install a floating point exception handler and verify that the // the expected exception was raised. One will have to longjmp back from diff --git a/libc/test/src/fenv/feenableexcept_test.cpp b/libc/test/src/fenv/feenableexcept_test.cpp --- a/libc/test/src/fenv/feenableexcept_test.cpp +++ b/libc/test/src/fenv/feenableexcept_test.cpp @@ -16,7 +16,7 @@ #include TEST(LlvmLibcFEnvTest, EnableTest) { -#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) || defined(LIBC_TARGET_ARCH_IS_RISCV64) // Few Arm HW implementations do not trap exceptions. We skip this test // completely on such HW. // @@ -28,7 +28,7 @@ __llvm_libc::feenableexcept(FE_DIVBYZERO); if (__llvm_libc::fegetexcept() == 0) return; -#endif // defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#endif // Architectures where exception trapping is not supported int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW, FE_UNDERFLOW}; diff --git a/libc/test/src/fenv/feholdexcept_test.cpp b/libc/test/src/fenv/feholdexcept_test.cpp --- a/libc/test/src/fenv/feholdexcept_test.cpp +++ b/libc/test/src/fenv/feholdexcept_test.cpp @@ -16,7 +16,7 @@ #include TEST(LlvmLibcFEnvTest, RaiseAndCrash) { -#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#if defined(LIBC_TARGET_ARCH_IS_ANY_ARM) || defined(LIBC_TARGET_ARCH_IS_RISCV64) // Few Arm HW implementations do not trap exceptions. We skip this test // completely on such HW. // @@ -28,7 +28,7 @@ __llvm_libc::fputil::enable_except(FE_DIVBYZERO); if (__llvm_libc::fputil::get_except() == 0) return; -#endif // defined(LIBC_TARGET_ARCH_IS_ANY_ARM) +#endif // Architectures where exception trapping is not supported int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW, FE_UNDERFLOW};