diff --git a/libc/cmake/modules/LLVMLibCArchitectures.cmake b/libc/cmake/modules/LLVMLibCArchitectures.cmake --- a/libc/cmake/modules/LLVMLibCArchitectures.cmake +++ b/libc/cmake/modules/LLVMLibCArchitectures.cmake @@ -55,6 +55,8 @@ set(target_arch "x86_64") elseif(target_arch MATCHES "^(powerpc|ppc)") set(target_arch "power") + elseif(target_arch MATCHES "^riscv32") + set(target_arch "riscv32") elseif(target_arch MATCHES "^riscv64") set(target_arch "riscv64") else() @@ -148,6 +150,8 @@ set(LIBC_TARGET_ARCHITECTURE_IS_AARCH64 TRUE) elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64") set(LIBC_TARGET_ARCHITECTURE_IS_X86 TRUE) +elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "riscv32") + set(LIBC_TARGET_ARCHITECTURE_IS_RISCV32 TRUE) elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "riscv64") set(LIBC_TARGET_ARCHITECTURE_IS_RISCV64 TRUE) else() diff --git a/libc/config/baremetal/riscv32/entrypoints.txt b/libc/config/baremetal/riscv32/entrypoints.txt new file mode 100644 --- /dev/null +++ b/libc/config/baremetal/riscv32/entrypoints.txt @@ -0,0 +1,120 @@ +set(TARGET_LIBC_ENTRYPOINTS + # ctype.h entrypoints + libc.src.ctype.isalnum + libc.src.ctype.isalpha + libc.src.ctype.isascii + libc.src.ctype.isblank + libc.src.ctype.iscntrl + libc.src.ctype.isdigit + libc.src.ctype.isgraph + libc.src.ctype.islower + libc.src.ctype.isprint + libc.src.ctype.ispunct + libc.src.ctype.isspace + libc.src.ctype.isupper + libc.src.ctype.isxdigit + libc.src.ctype.toascii + libc.src.ctype.tolower + libc.src.ctype.toupper + + # errno.h entrypoints + libc.src.errno.errno + + # string.h entrypoints + libc.src.string.bcmp + libc.src.string.bcopy + libc.src.string.bzero + libc.src.string.memccpy + libc.src.string.memchr + libc.src.string.memcmp + libc.src.string.memcpy + libc.src.string.memmem + libc.src.string.memmove + libc.src.string.mempcpy + libc.src.string.memrchr + libc.src.string.memset + libc.src.string.stpcpy + libc.src.string.stpncpy + libc.src.string.strcasecmp + libc.src.string.strcasestr + libc.src.string.strcat + libc.src.string.strchr + libc.src.string.strchrnul + libc.src.string.strcmp + libc.src.string.strcpy + libc.src.string.strcspn + libc.src.string.strlcat + libc.src.string.strlcpy + libc.src.string.strlen + libc.src.string.strncasecmp + libc.src.string.strncat + libc.src.string.strncmp + libc.src.string.strncpy + libc.src.string.strnlen + libc.src.string.strpbrk + libc.src.string.strrchr + libc.src.string.strspn + libc.src.string.strstr + libc.src.string.strtok + libc.src.string.strtok_r + + # inttypes.h entrypoints + libc.src.inttypes.imaxabs + libc.src.inttypes.imaxdiv + libc.src.inttypes.strtoimax + libc.src.inttypes.strtoumax + + # stdlib.h entrypoints + libc.src.stdlib.abs + libc.src.stdlib.atoi + libc.src.stdlib.atol + libc.src.stdlib.atoll + libc.src.stdlib.bsearch + libc.src.stdlib.div + libc.src.stdlib.labs + libc.src.stdlib.ldiv + libc.src.stdlib.llabs + libc.src.stdlib.lldiv + libc.src.stdlib.qsort + libc.src.stdlib.strtol + libc.src.stdlib.strtoll + libc.src.stdlib.strtoul + libc.src.stdlib.strtoull +) + +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 + libc.src.math.fabsl + libc.src.math.fdim + libc.src.math.fdimf + libc.src.math.fdiml + libc.src.math.fmax + libc.src.math.fmaxf + libc.src.math.fmaxl + libc.src.math.fmin + libc.src.math.fminf + libc.src.math.fminl +) + +set(TARGET_LLVMLIBC_ENTRYPOINTS + ${TARGET_LIBC_ENTRYPOINTS} + ${TARGET_LIBM_ENTRYPOINTS} +) diff --git a/libc/config/baremetal/riscv32/headers.txt b/libc/config/baremetal/riscv32/headers.txt new file mode 100644 --- /dev/null +++ b/libc/config/baremetal/riscv32/headers.txt @@ -0,0 +1,10 @@ +set(TARGET_PUBLIC_HEADERS + libc.include.ctype + libc.include.fenv + libc.include.errno + libc.include.inttypes + libc.include.math + libc.include.stdlib + libc.include.string + libc.include.strings +) 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_RISCV32) +#include "riscv32/FEnvImpl.h" #elif defined(LIBC_TARGET_ARCH_IS_RISCV64) #include "riscv64/FEnvImpl.h" #else diff --git a/libc/src/__support/FPUtil/riscv32/FEnvImpl.h b/libc/src/__support/FPUtil/riscv32/FEnvImpl.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/FPUtil/riscv32/FEnvImpl.h @@ -0,0 +1,180 @@ +//===-- riscv32 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_RISCV32_FENVIMPL_H +#define LLVM_LIBC_SRC_SUPPORT_FPUTIL_RISCV32_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_RISCV32_FENVIMPL_H