diff options
Diffstat (limited to 'libc')
122 files changed, 3281 insertions, 865 deletions
diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake index 2478fde..d85c393 100644 --- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake +++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake @@ -108,6 +108,10 @@ function(_get_compile_options_from_config output_var) list(APPEND config_options "-DLIBC_ERRNO_MODE=${LIBC_CONF_ERRNO_MODE}") endif() + if(LIBC_CONF_THREAD_MODE) + list(APPEND config_options "-DLIBC_THREAD_MODE=${LIBC_CONF_THREAD_MODE}") + endif() + set(${output_var} ${config_options} PARENT_SCOPE) endfunction(_get_compile_options_from_config) diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index c54d7d0..e24e2b9 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -218,6 +218,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.abort libc.src.stdlib.abs libc.src.stdlib.aligned_alloc + libc.src.stdlib.atexit libc.src.stdlib.atof libc.src.stdlib.atoi libc.src.stdlib.atol @@ -754,6 +755,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 80cd15e..44e9c3e 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -218,6 +218,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.abort libc.src.stdlib.abs libc.src.stdlib.aligned_alloc + libc.src.stdlib.atexit libc.src.stdlib.atof libc.src.stdlib.atoi libc.src.stdlib.atol @@ -757,6 +758,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json index 105e417..f01e508 100644 --- a/libc/config/baremetal/config.json +++ b/libc/config/baremetal/config.json @@ -4,6 +4,11 @@ "value": "LIBC_ERRNO_MODE_EXTERNAL" } }, + "threads": { + "LIBC_CONF_THREAD_MODE": { + "value": "LIBC_THREAD_MODE_SINGLE" + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FIXED_POINT": { "value": true diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index c9f8118..29cf322a 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -218,6 +218,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.abort libc.src.stdlib.abs libc.src.stdlib.aligned_alloc + libc.src.stdlib.atexit libc.src.stdlib.atof libc.src.stdlib.atoi libc.src.stdlib.atol @@ -757,6 +758,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints diff --git a/libc/config/config.json b/libc/config/config.json index d53b293..1b05469 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -5,6 +5,12 @@ "doc": "The implementation used for errno, acceptable values are LIBC_ERRNO_MODE_DEFAULT, LIBC_ERRNO_MODE_UNDEFINED, LIBC_ERRNO_MODE_THREAD_LOCAL, LIBC_ERRNO_MODE_SHARED, LIBC_ERRNO_MODE_EXTERNAL, LIBC_ERRNO_MODE_SYSTEM, and LIBC_ERRNO_MODE_SYSTEM_INLINE." } }, + "threads": { + "LIBC_CONF_THREAD_MODE": { + "value": "LIBC_THREAD_MODE_PLATFORM", + "doc": "The implementation used for Mutex, acceptable values are LIBC_THREAD_MODE_PLATFORM, LIBC_THREAD_MODE_SINGLE, and LIBC_THREAD_MODE_EXTERNAL." + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": false, diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt index 3bfdcdb..03e00a3 100644 --- a/libc/config/darwin/aarch64/entrypoints.txt +++ b/libc/config/darwin/aarch64/entrypoints.txt @@ -588,6 +588,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index c55b6aa..00cedab 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -231,6 +231,11 @@ set(TARGET_LIBM_ENTRYPOINTS #libc.src.math.truncl ) +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/config/gpu/amdgpu/config.json b/libc/config/gpu/amdgpu/config.json index 30ae10e..fa179b8 100644 --- a/libc/config/gpu/amdgpu/config.json +++ b/libc/config/gpu/amdgpu/config.json @@ -4,6 +4,11 @@ "value": "LIBC_ERRNO_MODE_SHARED" } }, + "threads": { + "LIBC_CONF_THREAD_MODE": { + "value": "LIBC_THREAD_MODE_SINGLE" + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": true diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt index 463727b..e39819d 100644 --- a/libc/config/gpu/amdgpu/entrypoints.txt +++ b/libc/config/gpu/amdgpu/entrypoints.txt @@ -612,6 +612,11 @@ if(LIBC_TYPES_HAS_FLOAT16) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/config/gpu/nvptx/config.json b/libc/config/gpu/nvptx/config.json index 30ae10e..fa179b8 100644 --- a/libc/config/gpu/nvptx/config.json +++ b/libc/config/gpu/nvptx/config.json @@ -4,6 +4,11 @@ "value": "LIBC_ERRNO_MODE_SHARED" } }, + "threads": { + "LIBC_CONF_THREAD_MODE": { + "value": "LIBC_THREAD_MODE_SINGLE" + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": true diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt index 13b77172..26e3b15 100644 --- a/libc/config/gpu/nvptx/entrypoints.txt +++ b/libc/config/gpu/nvptx/entrypoints.txt @@ -614,6 +614,11 @@ if(LIBC_TYPES_HAS_FLOAT16) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index b2abebe..d76cdc2 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -842,6 +842,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LLVM_LIBC_FULL_BUILD) list(APPEND TARGET_LIBC_ENTRYPOINTS # assert.h entrypoints diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 5865dc9..813c34d 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -458,6 +458,11 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.ufromfpxl ) +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 79077a5..190aef7 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -861,6 +861,11 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 5e8278e..ec41069 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -36,6 +36,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.poll.poll # sched.h entrypoints + libc.src.sched.getcpu libc.src.sched.sched_get_priority_max libc.src.sched.sched_get_priority_min libc.src.sched.sched_getaffinity @@ -892,6 +893,12 @@ if(LIBC_TYPES_HAS_FLOAT128) ) endif() + +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + if(LIBC_COMPILER_HAS_FIXED_POINT) list(APPEND TARGET_LIBM_ENTRYPOINTS # stdfix.h _Fract and _Accum entrypoints @@ -1050,6 +1057,9 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.pthread.pthread_join libc.src.pthread.pthread_key_create libc.src.pthread.pthread_key_delete + libc.src.pthread.pthread_barrier_init + libc.src.pthread.pthread_barrier_wait + libc.src.pthread.pthread_barrier_destroy libc.src.pthread.pthread_mutex_destroy libc.src.pthread.pthread_mutex_init libc.src.pthread.pthread_mutex_lock @@ -1267,6 +1277,9 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.wchar.mbsinit libc.src.wchar.mbrtowc libc.src.wchar.mbtowc + libc.src.wchar.mbstowcs + libc.src.wchar.mbsrtowcs + libc.src.wchar.mbsnrtowcs libc.src.wchar.wcrtomb libc.src.wchar.wctomb libc.src.wchar.wcstombs diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 1802729..3160d57 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -304,6 +304,11 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.truncl ) +list(APPEND TARGET_LIBM_ENTRYPOINTS + # bfloat16 entrypoints + libc.src.math.fabsbf16 +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst index 1094122..95c51b8 100644 --- a/libc/docs/configure.rst +++ b/libc/docs/configure.rst @@ -60,5 +60,7 @@ to learn about the defaults for your platform and target. * **"string" options** - ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled. - ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen. +* **"threads" options** + - ``LIBC_CONF_THREAD_MODE``: The implementation used for Mutex, acceptable values are LIBC_THREAD_MODE_PLATFORM, LIBC_THREAD_MODE_SINGLE, and LIBC_THREAD_MODE_EXTERNAL. * **"time" options** - ``LIBC_CONF_TIME_64BIT``: Force the size of time_t to 64 bits, even on platforms where compatibility considerations would otherwise make it 32-bit. diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt index 5fc25d0..f3f01c1 100644 --- a/libc/hdr/CMakeLists.txt +++ b/libc/hdr/CMakeLists.txt @@ -73,6 +73,15 @@ add_proxy_header_library( ) add_proxy_header_library( + pthread_macros + HDRS + pthread_macros.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-macros.pthread_macros + libc.include.pthread +) + +add_proxy_header_library( sched_macros HDRS sched_macros.h diff --git a/libc/hdr/pthread_macros.h b/libc/hdr/pthread_macros.h new file mode 100644 index 0000000..f913015 --- /dev/null +++ b/libc/hdr/pthread_macros.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread.h -------------------------------===// +// +// 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_HDR_PTHREAD_MACROS_H +#define LLVM_LIBC_HDR_PTHREAD_MACROS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-macros/pthread-macros.h" + +#else // Overlay mode + +#include <pthread.h> + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_PTHREAD_MACROS_H diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt index c212363..1c1f242 100644 --- a/libc/hdr/types/CMakeLists.txt +++ b/libc/hdr/types/CMakeLists.txt @@ -242,6 +242,22 @@ add_proxy_header_library( ) add_proxy_header_library( + pthread_barrier_t + HDRS + pthread_barrier_t.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.pthread_barrier_t +) + +add_proxy_header_library( + pthread_barrierattr_t + HDRS + pthread_barrierattr_t.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.pthread_barrierattr_t +) + +add_proxy_header_library( atexithandler_t HDRS atexithandler_t.h diff --git a/libc/hdr/types/pthread_barrier_t.h b/libc/hdr/types/pthread_barrier_t.h new file mode 100644 index 0000000..57bcdfc --- /dev/null +++ b/libc/hdr/types/pthread_barrier_t.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread_barrier_t.h ---------------------===// +// +// 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_HDR_TYPES_PTHREAD_BARRIER_T_H +#define LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIER_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/pthread_barrier_t.h" + +#else // Overlay mode + +#error "Cannot overlay pthread_barrier_t" + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIER_T_H diff --git a/libc/hdr/types/pthread_barrierattr_t.h b/libc/hdr/types/pthread_barrierattr_t.h new file mode 100644 index 0000000..d9d14c1 --- /dev/null +++ b/libc/hdr/types/pthread_barrierattr_t.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from pthread_barrierattr_t.h -----------------===// +// +// 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_HDR_TYPES_PTHREAD_BARRIERATTR_T_H +#define LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIERATTR_T_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/pthread_barrierattr_t.h" + +#else // Overlay mode + +#error "Cannot overlay pthread_barrierattr_t" + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_BARRIERATTR_T_H diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 120f385..74fcea0 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -392,6 +392,8 @@ add_header_macro( .llvm-libc-types.pthread_attr_t .llvm-libc-types.pthread_condattr_t .llvm-libc-types.pthread_key_t + .llvm-libc-types.pthread_barrier_t + .llvm-libc-types.pthread_barrierattr_t .llvm-libc-types.pthread_mutex_t .llvm-libc-types.pthread_mutexattr_t .llvm-libc-types.pthread_once_t diff --git a/libc/include/llvm-libc-macros/pthread-macros.h b/libc/include/llvm-libc-macros/pthread-macros.h index fcc6ef9..ce467b7 100644 --- a/libc/include/llvm-libc-macros/pthread-macros.h +++ b/libc/include/llvm-libc-macros/pthread-macros.h @@ -22,6 +22,8 @@ #define PTHREAD_MUTEX_STALLED 0 #define PTHREAD_MUTEX_ROBUST 1 +#define PTHREAD_BARRIER_SERIAL_THREAD -1 + #define PTHREAD_ONCE_INIT {0} #define PTHREAD_PROCESS_PRIVATE 0 diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 4ccdde6..451beae 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -10,6 +10,7 @@ add_header(__exec_envp_t HDR __exec_envp_t.h) add_header(__futex_word HDR __futex_word.h) add_header(pid_t HDR pid_t.h) add_header(__mutex_type HDR __mutex_type.h DEPENDS .__futex_word .pid_t) +add_header(__barrier_type HDR __barrier_type.h) add_header(__pthread_once_func_t HDR __pthread_once_func_t.h) add_header(__pthread_start_t HDR __pthread_start_t.h) add_header(__pthread_tss_dtor_t HDR __pthread_tss_dtor_t.h) @@ -53,6 +54,8 @@ add_header(pthread_condattr_t HDR pthread_condattr_t.h DEPENDS .clockid_t) add_header(pthread_key_t HDR pthread_key_t.h) add_header(pthread_mutex_t HDR pthread_mutex_t.h DEPENDS .__futex_word .__mutex_type) add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h) +add_header(pthread_barrier_t HDR pthread_barrier_t.h DEPENDS .__barrier_type) +add_header(pthread_barrierattr_t HDR pthread_barrierattr_t.h) add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word) add_header(pthread_rwlock_t HDR pthread_rwlock_t.h DEPENDS .__futex_word .pid_t) add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h) diff --git a/libc/include/llvm-libc-types/__barrier_type.h b/libc/include/llvm-libc-types/__barrier_type.h new file mode 100644 index 0000000..5971261 --- /dev/null +++ b/libc/include/llvm-libc-types/__barrier_type.h @@ -0,0 +1,21 @@ +//===-- Definition of __barrier_type type ---------------------------------===// +// +// 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_TYPES__BARRIER_TYPE_H +#define LLVM_LIBC_TYPES__BARRIER_TYPE_H + +typedef struct __attribute__((aligned(8 /* alignof (Barrier) */))) { + unsigned expected; + unsigned waiting; + bool blocking; + char entering[24 /* sizeof (CndVar) */]; + char exiting[24 /* sizeof (CndVar) */]; + char mutex[24 /* sizeof (Mutex) */]; +} __barrier_type; + +#endif // LLVM_LIBC_TYPES__BARRIER_TYPE_H diff --git a/libc/include/llvm-libc-types/pthread_barrier_t.h b/libc/include/llvm-libc-types/pthread_barrier_t.h new file mode 100644 index 0000000..86fbf7c --- /dev/null +++ b/libc/include/llvm-libc-types/pthread_barrier_t.h @@ -0,0 +1,15 @@ +//===-- Definition of pthread_barrier_t type --------------------------===// +// +// 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_TYPES_PTHREAD_BARRIER_T_H +#define LLVM_LIBC_TYPES_PTHREAD_BARRIER_T_H + +#include "__barrier_type.h" +typedef __barrier_type pthread_barrier_t; + +#endif // LLVM_LIBC_TYPES_PTHREAD_BARRIER_T_H diff --git a/libc/include/llvm-libc-types/pthread_barrierattr_t.h b/libc/include/llvm-libc-types/pthread_barrierattr_t.h new file mode 100644 index 0000000..064be5b --- /dev/null +++ b/libc/include/llvm-libc-types/pthread_barrierattr_t.h @@ -0,0 +1,16 @@ +//===-- Definition of pthread_barrierattr_t type --------------------------===// +// +// 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_TYPES_PTHREAD_BARRIERATTR_T_H +#define LLVM_LIBC_TYPES_PTHREAD_BARRIERATTR_T_H + +typedef struct { + bool pshared; +} pthread_barrierattr_t; + +#endif // LLVM_LIBC_TYPES_PTHREAD_BARRIERATTR_T_H diff --git a/libc/include/pthread.yaml b/libc/include/pthread.yaml index 5b27e68..8afce20 100644 --- a/libc/include/pthread.yaml +++ b/libc/include/pthread.yaml @@ -6,6 +6,8 @@ types: - type_name: pthread_once_t - type_name: pthread_mutex_t - type_name: pthread_mutexattr_t + - type_name: pthread_barrier_t + - type_name: pthread_barrierattr_t - type_name: pthread_key_t - type_name: pthread_condattr_t - type_name: __pthread_tss_dtor_t @@ -277,6 +279,26 @@ functions: arguments: - type: pthread_mutexattr_t *__restrict - type: int + - name: pthread_barrier_init + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t *__restrict + - type: const pthread_barrierattr_t *__restrict + - type: int + - name: pthread_barrier_wait + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t * + - name: pthread_barrier_destroy + standards: + - POSIX + return_type: int + arguments: + - type: pthread_barrier_t * - name: pthread_once standards: - POSIX diff --git a/libc/include/sched.yaml b/libc/include/sched.yaml index 57871f5..f14799d 100644 --- a/libc/include/sched.yaml +++ b/libc/include/sched.yaml @@ -18,6 +18,13 @@ functions: arguments: - type: size_t - type: const cpu_set_t * + - name: getcpu + standards: + - POSIX + return_type: int + arguments: + - type: unsigned int * + - type: unsigned int * - name: sched_get_priority_max standards: - POSIX diff --git a/libc/include/wchar.yaml b/libc/include/wchar.yaml index 6e1f595..8178091 100644 --- a/libc/include/wchar.yaml +++ b/libc/include/wchar.yaml @@ -53,6 +53,33 @@ functions: - type: wchar_t *__restrict - type: const char *__restrict - type: size_t + - name: mbsnrtowcs + standards: + - stdc + return_type: size_t + arguments: + - type: wchar_t *__restrict + - type: const char **__restrict + - type: size_t + - type: size_t + - type: mbstate_t *__restrict + - name: mbsrtowcs + standards: + - stdc + return_type: size_t + arguments: + - type: wchar_t *__restrict + - type: const char **__restrict + - type: size_t + - type: mbstate_t *__restrict + - name: mbstowcs + standards: + - stdc + return_type: size_t + arguments: + - type: wchar_t *__restrict + - type: const char *__restrict + - type: size_t - name: mbsinit standards: - stdc diff --git a/libc/shared/math.h b/libc/shared/math.h index e0f00f5..0605d91 100644 --- a/libc/shared/math.h +++ b/libc/shared/math.h @@ -21,6 +21,11 @@ #include "math/asinf.h" #include "math/asinf16.h" #include "math/asinhf.h" +#include "math/asinhf16.h" +#include "math/atan.h" +#include "math/atan2.h" +#include "math/atanf.h" +#include "math/atanf16.h" #include "math/erff.h" #include "math/exp.h" #include "math/exp10.h" diff --git a/libc/shared/math/asinf16.h b/libc/shared/math/asinf16.h index d545e26..af5b2ec 100644 --- a/libc/shared/math/asinf16.h +++ b/libc/shared/math/asinf16.h @@ -25,4 +25,4 @@ using math::asinf16; #endif // LIBC_TYPES_HAS_FLOAT16 -#endif // LLVM_LIBC_SHARED_MATH_ASINF_H +#endif // LLVM_LIBC_SHARED_MATH_ASINF16_H diff --git a/libc/shared/math/asinhf16.h b/libc/shared/math/asinhf16.h new file mode 100644 index 0000000..b8b007f --- /dev/null +++ b/libc/shared/math/asinhf16.h @@ -0,0 +1,28 @@ +//===-- Shared asinhf16 function --------------------------------*- 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_SHARED_MATH_ASINHF16_H +#define LLVM_LIBC_SHARED_MATH_ASINHF16_H + +#include "shared/libc_common.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "src/__support/math/asinhf16.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::asinhf16; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SHARED_MATH_ASINHF16_H diff --git a/libc/shared/math/atan.h b/libc/shared/math/atan.h new file mode 100644 index 0000000..b9ba89b --- /dev/null +++ b/libc/shared/math/atan.h @@ -0,0 +1,23 @@ +//===-- Shared atan function ------------------------------------*- 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_SHARED_MATH_ATAN_H +#define LLVM_LIBC_SHARED_MATH_ATAN_H + +#include "shared/libc_common.h" +#include "src/__support/math/atan.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::atan; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SHARED_MATH_ATAN_H diff --git a/libc/shared/math/atan2.h b/libc/shared/math/atan2.h new file mode 100644 index 0000000..8941108 --- /dev/null +++ b/libc/shared/math/atan2.h @@ -0,0 +1,23 @@ +//===-- Shared atan2 function -----------------------------------*- 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_SHARED_MATH_ATAN2_H +#define LLVM_LIBC_SHARED_MATH_ATAN2_H + +#include "shared/libc_common.h" +#include "src/__support/math/atan2.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::atan2; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SHARED_MATH_ATAN2_H diff --git a/libc/shared/math/atanf.h b/libc/shared/math/atanf.h new file mode 100644 index 0000000..858d727 --- /dev/null +++ b/libc/shared/math/atanf.h @@ -0,0 +1,23 @@ +//===-- Shared atanf function -----------------------------------*- 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_SHARED_MATH_ATANF_H +#define LLVM_LIBC_SHARED_MATH_ATANF_H + +#include "shared/libc_common.h" +#include "src/__support/math/atanf.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::atanf; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SHARED_MATH_ATANF_H diff --git a/libc/shared/math/atanf16.h b/libc/shared/math/atanf16.h new file mode 100644 index 0000000..f196907 --- /dev/null +++ b/libc/shared/math/atanf16.h @@ -0,0 +1,28 @@ +//===-- Shared atanf16 function ---------------------------------*- 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_SHARED_MATH_ATANF16_H +#define LLVM_LIBC_SHARED_MATH_ATANF16_H + +#include "shared/libc_common.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "src/__support/math/atanf16.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::atanf16; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SHARED_MATH_ATANF16_H diff --git a/libc/src/__support/FPUtil/cast.h b/libc/src/__support/FPUtil/cast.h index e6fad1b..e999ece 100644 --- a/libc/src/__support/FPUtil/cast.h +++ b/libc/src/__support/FPUtil/cast.h @@ -66,9 +66,9 @@ cast(InType x) { cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN); DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x); return xd.template as<OutType, /*ShouldSignalExceptions=*/true>(); + } else { + return static_cast<OutType>(x); } - - return static_cast<OutType>(x); } } // namespace LIBC_NAMESPACE::fputil diff --git a/libc/src/__support/GPU/allocator.cpp b/libc/src/__support/GPU/allocator.cpp index 2b78c4d..250bebd 100644 --- a/libc/src/__support/GPU/allocator.cpp +++ b/libc/src/__support/GPU/allocator.cpp @@ -156,7 +156,7 @@ static inline constexpr uint32_t get_start_index(uint32_t chunk_size) { // Returns the id of the lane below this one that acts as its leader. static inline uint32_t get_leader_id(uint64_t ballot, uint32_t id) { - uint64_t mask = id < BITS_IN_DWORD ? ~0ull << (id + 1) : 0; + uint64_t mask = id < BITS_IN_DWORD - 1 ? ~0ull << (id + 1) : 0; return BITS_IN_DWORD - cpp::countl_zero(ballot & ~mask) - 1; } @@ -266,38 +266,31 @@ struct Slab { // Randomly walks the bitfield until it finds a free bit. Allocations attempt // to put lanes right next to each other for better caching and convergence. - void *allocate(uint64_t lane_mask, uint64_t uniform) { + void *allocate(uint64_t uniform, uint32_t reserved) { uint32_t chunk_size = get_chunk_size(); uint32_t state = impl::entropy(); - // The uniform mask represents which lanes contain a uniform target pointer. - // We attempt to place these next to each other. - void *result = nullptr; - uint32_t after = ~0u; - uint32_t old_index = 0; - for (uint64_t mask = lane_mask; mask; - mask = gpu::ballot(lane_mask, !result)) { - if (result) - continue; - - // We try using any known empty bits from the previous attempt first. - uint32_t start = gpu::shuffle( - mask, cpp::countr_zero(uniform & mask), - ~after ? (old_index & ~(BITS_IN_WORD - 1)) + cpp::countr_zero(~after) - : __builtin_align_down(impl::xorshift32(state), BITS_IN_WORD)); + // Try to find the empty bit in the bitfield to finish the allocation. We + // start at the number of allocations as this is guaranteed to be available + // until the user starts freeing memory. + uint64_t lane_mask = gpu::get_lane_mask(); + uint32_t start = gpu::shuffle( + lane_mask, cpp::countr_zero(uniform & lane_mask), reserved); + for (;;) { + uint64_t lane_mask = gpu::get_lane_mask(); // Each lane tries to claim one bit in a single contiguous mask. - uint32_t id = impl::lane_count(uniform & mask, gpu::get_lane_id()); + uint32_t id = impl::lane_count(uniform & lane_mask, gpu::get_lane_id()); uint32_t index = (start + id) % usable_bits(chunk_size); uint32_t slot = index / BITS_IN_WORD; uint32_t bit = index % BITS_IN_WORD; // Get the mask of bits destined for the same slot and coalesce it. uint32_t leader = impl::get_leader_id( - uniform & gpu::ballot(mask, !id || index % BITS_IN_WORD == 0), + uniform & gpu::ballot(lane_mask, !id || index % BITS_IN_WORD == 0), gpu::get_lane_id()); - uint32_t length = cpp::popcount(uniform & mask) - - impl::lane_count(uniform & mask, leader); + uint32_t length = cpp::popcount(uniform & lane_mask) - + impl::lane_count(uniform & lane_mask, leader); uint32_t bitmask = static_cast<uint32_t>( (uint64_t(1) << cpp::min(length, BITS_IN_WORD)) - 1) @@ -307,18 +300,23 @@ struct Slab { if (gpu::get_lane_id() == leader) before = cpp::AtomicRef(get_bitfield()[slot]) .fetch_or(bitmask, cpp::MemoryOrder::RELAXED); - before = gpu::shuffle(mask, leader, before); - if (~before & (1 << bit)) - result = ptr_from_index(index, chunk_size); - else - sleep_briefly(); + before = gpu::shuffle(lane_mask, leader, before); + if (~before & (1 << bit)) { + cpp::atomic_thread_fence(cpp::MemoryOrder::ACQUIRE); + return ptr_from_index(index, chunk_size); + } - after = before | bitmask; - old_index = index; + // If the previous operation found an empty bit we move there, otherwise + // we generate new random index to start at. + uint32_t after = before | bitmask; + start = gpu::shuffle( + gpu::get_lane_mask(), + cpp::countr_zero(uniform & gpu::get_lane_mask()), + ~after ? __builtin_align_down(index, BITS_IN_WORD) + + cpp::countr_zero(~after) + : __builtin_align_down(impl::xorshift32(state), BITS_IN_WORD)); + sleep_briefly(); } - - cpp::atomic_thread_fence(cpp::MemoryOrder::ACQUIRE); - return result; } // Deallocates memory by resetting its corresponding bit in the bitfield. @@ -460,11 +458,13 @@ public: result->initialize(uniform); if (gpu::get_lane_id() == uint32_t(cpp::countr_zero(uniform))) finalize(result, cpp::popcount(uniform), count); + count = + gpu::shuffle(gpu::get_lane_mask(), cpp::countr_zero(uniform), count); } if (!impl::is_sentinel(count)) count = count - cpp::popcount(uniform) + - impl::lane_count(uniform, gpu::get_lane_id()) + 1; + impl::lane_count(uniform, gpu::get_lane_id()); return result; } @@ -505,7 +505,8 @@ static cpp::Atomic<uint32_t> indices[] = { #undef S // Tries to find a slab in the table that can support the given chunk size. -static Slab *find_slab(uint32_t chunk_size, uint64_t &uniform) { +static Slab *find_slab(uint32_t chunk_size, uint64_t &uniform, + uint32_t &reserved) { // We start at the index of the last successful allocation for this kind. uint32_t chunk_id = impl::get_chunk_id(chunk_size); uint32_t start = indices[chunk_id].load(cpp::MemoryOrder::RELAXED); @@ -518,7 +519,6 @@ static Slab *find_slab(uint32_t chunk_size, uint64_t &uniform) { if (!offset || slots[index].use_count() < Slab::available_chunks(chunk_size)) { uint64_t lane_mask = gpu::get_lane_mask(); - uint32_t reserved = 0; Slab *slab = slots[index].try_lock(lane_mask, uniform & lane_mask, reserved, chunk_size, index); @@ -536,13 +536,13 @@ static Slab *find_slab(uint32_t chunk_size, uint64_t &uniform) { // If we find a slab with a matching chunk size then we store the result. // Otherwise, we need to free the claimed lock and continue. In the case // of out-of-memory we receive a sentinel value and return a failure. - if (slab && reserved <= Slab::available_chunks(chunk_size) && + if (slab && reserved < Slab::available_chunks(chunk_size) && slab->get_chunk_size() == chunk_size) { if (index != start) indices[chunk_id].store(index, cpp::MemoryOrder::RELAXED); uniform = uniform & gpu::get_lane_mask(); return slab; - } else if (slab && (reserved > Slab::available_chunks(chunk_size) || + } else if (slab && (reserved >= Slab::available_chunks(chunk_size) || slab->get_chunk_size() != chunk_size)) { slots[index].unlock(gpu::get_lane_mask(), gpu::get_lane_mask() & uniform); @@ -578,12 +578,12 @@ void *allocate(uint64_t size) { // Try to find a slab for the rounded up chunk size and allocate from it. uint32_t chunk_size = impl::get_chunk_size(static_cast<uint32_t>(size)); uint64_t uniform = gpu::match_any(gpu::get_lane_mask(), chunk_size); - Slab *slab = find_slab(chunk_size, uniform); - if (!slab || impl::is_sentinel(reinterpret_cast<uintptr_t>(slab))) + uint32_t reserved = 0; + Slab *slab = find_slab(chunk_size, uniform, reserved); + if (!slab) return nullptr; - uint64_t lane_mask = gpu::get_lane_mask(); - void *ptr = slab->allocate(lane_mask, uniform); + void *ptr = slab->allocate(uniform, reserved); return ptr; } diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index 13f46a1..bbb07b6 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -155,6 +155,95 @@ add_header_library( ) add_header_library( + asinhf16 + HDRS + asinhf16.h + DEPENDS + .acoshf_utils + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.polyeval + libc.src.__support.FPUtil.cast + libc.src.__support.FPUtil.except_value_utils + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.rounding_mode + libc.src.__support.FPUtil.sqrt + libc.src.__support.macros.config + libc.src.__support.macros.optimization +) + +add_header_library( + atan_utils + HDRS + atan_utils.h + DEPENDS + libc.src.__support.integer_literals + libc.src.__support.FPUtil.double_double + libc.src.__support.FPUtil.dyadic_float + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.polyeval + libc.src.__support.macros.optimization +) + +add_header_library( + atan + HDRS + atan.h + DEPENDS + .atan_utils + libc.src.__support.FPUtil.double_double + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.macros.optimization +) + +add_header_library( + atan2 + HDRS + atan2.h + DEPENDS + .atan_utils + libc.src.__support.FPUtil.double_double + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.macros.optimization +) + +add_header_library( + atanf + HDRS + atanf.h + DEPENDS + .inv_trigf_utils + libc.src.__support.FPUtil.except_value_utils + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.FPUtil.polyeval + libc.src.__support.FPUtil.rounding_mode + libc.src.__support.macros.optimization +) + +add_header_library( + atanf16 + HDRS + atanf16.h + DEPENDS + libc.src.__support.FPUtil.cast + libc.src.__support.FPUtil.except_value_utils + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.polyeval + libc.src.__support.FPUtil.sqrt + libc.src.__support.macros.optimization +) + +add_header_library( asinf HDRS asinf.h diff --git a/libc/src/__support/math/asin_utils.h b/libc/src/__support/math/asin_utils.h index e0c9096..efe779c 100644 --- a/libc/src/__support/math/asin_utils.h +++ b/libc/src/__support/math/asin_utils.h @@ -45,7 +45,7 @@ static constexpr double ASIN_COEFFS[12] = { 0x1.2b5993bda1d9bp-6, -0x1.806aff270bf25p-7, 0x1.02614e5ed3936p-5, }; -LIBC_INLINE static constexpr double asin_eval(double u) { +LIBC_INLINE double asin_eval(double u) { double u2 = u * u; double c0 = fputil::multiply_add(u, ASIN_COEFFS[1], ASIN_COEFFS[0]); double c1 = fputil::multiply_add(u, ASIN_COEFFS[3], ASIN_COEFFS[2]); diff --git a/libc/src/__support/math/asinhf16.h b/libc/src/__support/math/asinhf16.h new file mode 100644 index 0000000..3c5171e --- /dev/null +++ b/libc/src/__support/math/asinhf16.h @@ -0,0 +1,121 @@ +//===-- Implementation header for asinhf16 ----------------------*- 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_MATH_ASINHF16_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ASINHF16_H + +#include "include/llvm-libc-macros/float16-macros.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "acoshf_utils.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/PolyEval.h" +#include "src/__support/FPUtil/cast.h" +#include "src/__support/FPUtil/except_value_utils.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/rounding_mode.h" +#include "src/__support/FPUtil/sqrt.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +LIBC_INLINE static constexpr float16 asinhf16(float16 x) { + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + constexpr size_t N_EXCEPTS = 8; + + constexpr fputil::ExceptValues<float16, N_EXCEPTS> ASINHF16_EXCEPTS{{ + // (input, RZ output, RU offset, RD offset, RN offset) + + // x = 0x1.da4p-2, asinhf16(x) = 0x1.ca8p-2 (RZ) + {0x3769, 0x372a, 1, 0, 1}, + // x = 0x1.d6cp-1, asinhf16(x) = 0x1.a58p-1 (RZ) + {0x3b5b, 0x3a96, 1, 0, 0}, + // x = 0x1.c7cp+3, asinhf16(x) = 0x1.accp+1 (RZ) + {0x4b1f, 0x42b3, 1, 0, 0}, + // x = 0x1.26cp+4, asinhf16(x) = 0x1.cd8p+1 (RZ) + {0x4c9b, 0x4336, 1, 0, 1}, + // x = -0x1.da4p-2, asinhf16(x) = -0x1.ca8p-2 (RZ) + {0xb769, 0xb72a, 0, 1, 1}, + // x = -0x1.d6cp-1, asinhf16(x) = -0x1.a58p-1 (RZ) + {0xbb5b, 0xba96, 0, 1, 0}, + // x = -0x1.c7cp+3, asinhf16(x) = -0x1.accp+1 (RZ) + {0xcb1f, 0xc2b3, 0, 1, 0}, + // x = -0x1.26cp+4, asinhf16(x) = -0x1.cd8p+1 (RZ) + {0xcc9b, 0xc336, 0, 1, 1}, + }}; +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + using namespace acoshf_internal; + using FPBits = fputil::FPBits<float16>; + FPBits xbits(x); + + uint16_t x_u = xbits.uintval(); + uint16_t x_abs = x_u & 0x7fff; + + if (LIBC_UNLIKELY(xbits.is_inf_or_nan())) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + + return x; + } + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + // Handle exceptional values + if (auto r = ASINHF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value())) + return r.value(); +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + float xf = x; + const float SIGN[2] = {1.0f, -1.0f}; + float x_sign = SIGN[x_u >> 15]; + + // |x| <= 0.25 + if (LIBC_UNLIKELY(x_abs <= 0x3400)) { + // when |x| < 0x1.718p-5, asinhf16(x) = x. Adjust by 1 ULP for certain + // rounding types. + if (LIBC_UNLIKELY(x_abs < 0x29c6)) { + int rounding = fputil::quick_get_round(); + if ((rounding == FE_UPWARD || rounding == FE_TOWARDZERO) && xf < 0) + return fputil::cast<float16>(xf + 0x1p-24f); + if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && xf > 0) + return fputil::cast<float16>(xf - 0x1p-24f); + return fputil::cast<float16>(xf); + } + + float x_sq = xf * xf; + // Generated by Sollya with: + // > P = fpminimax(asinh(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 2^-2]); + // The last coefficient 0x1.bd114ep-6f has been changed to 0x1.bd114ep-5f + // for better accuracy. + float p = fputil::polyeval(x_sq, 1.0f, -0x1.555552p-3f, 0x1.332f6ap-4f, + -0x1.6c53dep-5f, 0x1.bd114ep-5f); + + return fputil::cast<float16>(xf * p); + } + + // General case: asinh(x) = ln(x + sqrt(x^2 + 1)) + float sqrt_term = fputil::sqrt<float>(fputil::multiply_add(xf, xf, 1.0f)); + return fputil::cast<float16>( + x_sign * log_eval(fputil::multiply_add(xf, x_sign, sqrt_term))); +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ASINHF16_H diff --git a/libc/src/__support/math/atan.h b/libc/src/__support/math/atan.h new file mode 100644 index 0000000..62190b0 --- /dev/null +++ b/libc/src/__support/math/atan.h @@ -0,0 +1,189 @@ +//===-- Implementation header for atan --------------------------*- 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_MATH_ATAN_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ATAN_H + +#include "atan_utils.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/double_double.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/nearest_integer.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +// To compute atan(x), we divided it into the following cases: +// * |x| < 2^-26: +// Since |x| > atan(|x|) > |x| - |x|^3/3, and |x|^3/3 < ulp(x)/2, we simply +// return atan(x) = x - sign(x) * epsilon. +// * 2^-26 <= |x| < 1: +// We perform range reduction mod 2^-6 = 1/64 as follow: +// Let k = 2^(-6) * round(|x| * 2^6), then +// atan(x) = sign(x) * atan(|x|) +// = sign(x) * (atan(k) + atan((|x| - k) / (1 + |x|*k)). +// We store atan(k) in a look up table, and perform intermediate steps in +// double-double. +// * 1 < |x| < 2^53: +// First we perform the transformation y = 1/|x|: +// atan(x) = sign(x) * (pi/2 - atan(1/|x|)) +// = sign(x) * (pi/2 - atan(y)). +// Then we compute atan(y) using range reduction mod 2^-6 = 1/64 as the +// previous case: +// Let k = 2^(-6) * round(y * 2^6), then +// atan(y) = atan(k) + atan((y - k) / (1 + y*k)) +// = atan(k) + atan((1/|x| - k) / (1 + k/|x|) +// = atan(k) + atan((1 - k*|x|) / (|x| + k)). +// * |x| >= 2^53: +// Using the reciprocal transformation: +// atan(x) = sign(x) * (pi/2 - atan(1/|x|)). +// We have that: +// atan(1/|x|) <= 1/|x| <= 2^-53, +// which is smaller than ulp(pi/2) / 2. +// So we can return: +// atan(x) = sign(x) * (pi/2 - epsilon) + +LIBC_INLINE static constexpr double atan(double x) { + + using namespace atan_internal; + using FPBits = fputil::FPBits<double>; + + constexpr double IS_NEG[2] = {1.0, -1.0}; + constexpr DoubleDouble PI_OVER_2 = {0x1.1a62633145c07p-54, + 0x1.921fb54442d18p0}; + constexpr DoubleDouble MPI_OVER_2 = {-0x1.1a62633145c07p-54, + -0x1.921fb54442d18p0}; + + FPBits xbits(x); + bool x_sign = xbits.is_neg(); + xbits = xbits.abs(); + uint64_t x_abs = xbits.uintval(); + int x_exp = + static_cast<int>(x_abs >> FPBits::FRACTION_LEN) - FPBits::EXP_BIAS; + + // |x| < 1. + if (x_exp < 0) { + if (LIBC_UNLIKELY(x_exp < -26)) { +#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + return x; +#else + if (x == 0.0) + return x; + // |x| < 2^-26 + return fputil::multiply_add(-0x1.0p-54, x, x); +#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS + } + + double x_d = xbits.get_val(); + // k = 2^-6 * round(2^6 * |x|) + double k = fputil::nearest_integer(0x1.0p6 * x_d); + unsigned idx = static_cast<unsigned>(k); + k *= 0x1.0p-6; + + // numerator = |x| - k + DoubleDouble num, den; + num.lo = 0.0; + num.hi = x_d - k; + + // denominator = 1 - k * |x| + den.hi = fputil::multiply_add(x_d, k, 1.0); + DoubleDouble prod = fputil::exact_mult(x_d, k); + // Using Dekker's 2SUM algorithm to compute the lower part. + den.lo = ((1.0 - den.hi) + prod.hi) + prod.lo; + + // x_r = (|x| - k) / (1 + k * |x|) + DoubleDouble x_r = fputil::div(num, den); + + // Approximating atan(x_r) using Taylor polynomial. + DoubleDouble p = atan_eval(x_r); + + // atan(x) = sign(x) * (atan(k) + atan(x_r)) + // = sign(x) * (atan(k) + atan( (|x| - k) / (1 + k * |x|) )) +#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + return IS_NEG[x_sign] * (ATAN_I[idx].hi + (p.hi + (p.lo + ATAN_I[idx].lo))); +#else + + DoubleDouble c0 = fputil::exact_add(ATAN_I[idx].hi, p.hi); + double c1 = c0.lo + (ATAN_I[idx].lo + p.lo); + double r = IS_NEG[x_sign] * (c0.hi + c1); + + return r; +#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS + } + + // |x| >= 2^53 or x is NaN. + if (LIBC_UNLIKELY(x_exp >= 53)) { + // x is nan + if (xbits.is_nan()) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + return x; + } + // |x| >= 2^53 + // atan(x) ~ sign(x) * pi/2. + if (x_exp >= 53) +#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + return IS_NEG[x_sign] * PI_OVER_2.hi; +#else + return fputil::multiply_add(IS_NEG[x_sign], PI_OVER_2.hi, + IS_NEG[x_sign] * PI_OVER_2.lo); +#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS + } + + double x_d = xbits.get_val(); + double y = 1.0 / x_d; + + // k = 2^-6 * round(2^6 / |x|) + double k = fputil::nearest_integer(0x1.0p6 * y); + unsigned idx = static_cast<unsigned>(k); + k *= 0x1.0p-6; + + // denominator = |x| + k + DoubleDouble den = fputil::exact_add(x_d, k); + // numerator = 1 - k * |x| + DoubleDouble num; + num.hi = fputil::multiply_add(-x_d, k, 1.0); + DoubleDouble prod = fputil::exact_mult(x_d, k); + // Using Dekker's 2SUM algorithm to compute the lower part. + num.lo = ((1.0 - num.hi) - prod.hi) - prod.lo; + + // x_r = (1/|x| - k) / (1 - k/|x|) + // = (1 - k * |x|) / (|x| - k) + DoubleDouble x_r = fputil::div(num, den); + + // Approximating atan(x_r) using Taylor polynomial. + DoubleDouble p = atan_eval(x_r); + + // atan(x) = sign(x) * (pi/2 - atan(1/|x|)) + // = sign(x) * (pi/2 - atan(k) - atan(x_r)) + // = (-sign(x)) * (-pi/2 + atan(k) + atan((1 - k*|x|)/(|x| - k))) +#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + double lo_part = p.lo + ATAN_I[idx].lo + MPI_OVER_2.lo; + return IS_NEG[!x_sign] * (MPI_OVER_2.hi + ATAN_I[idx].hi + (p.hi + lo_part)); +#else + DoubleDouble c0 = fputil::exact_add(MPI_OVER_2.hi, ATAN_I[idx].hi); + DoubleDouble c1 = fputil::exact_add(c0.hi, p.hi); + double c2 = c1.lo + (c0.lo + p.lo) + (ATAN_I[idx].lo + MPI_OVER_2.lo); + + double r = IS_NEG[!x_sign] * (c1.hi + c2); + + return r; +#endif +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ATAN_H diff --git a/libc/src/__support/math/atan2.h b/libc/src/__support/math/atan2.h new file mode 100644 index 0000000..90ed926 --- /dev/null +++ b/libc/src/__support/math/atan2.h @@ -0,0 +1,209 @@ +//===-- Implementation header for atan2 -------------------------*- 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_MATH_ATAN2_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ATAN2_H + +#include "atan_utils.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/double_double.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/nearest_integer.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +// There are several range reduction steps we can take for atan2(y, x) as +// follow: + +// * Range reduction 1: signness +// atan2(y, x) will return a number between -PI and PI representing the angle +// forming by the 0x axis and the vector (x, y) on the 0xy-plane. +// In particular, we have that: +// atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant) +// = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant) +// = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant) +// = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant) +// Since atan function is odd, we can use the formula: +// atan(-u) = -atan(u) +// to adjust the above conditions a bit further: +// atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant) +// = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant) +// = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant) +// = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant) +// Which can be simplified to: +// atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0 +// = sign(y) * (pi - atan( |y|/|x| )) if x < 0 + +// * Range reduction 2: reciprocal +// Now that the argument inside atan is positive, we can use the formula: +// atan(1/x) = pi/2 - atan(x) +// to make the argument inside atan <= 1 as follow: +// atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x +// = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y| +// = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x +// = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y| + +// * Range reduction 3: look up table. +// After the previous two range reduction steps, we reduce the problem to +// compute atan(u) with 0 <= u <= 1, or to be precise: +// atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|). +// An accurate polynomial approximation for the whole [0, 1] input range will +// require a very large degree. To make it more efficient, we reduce the input +// range further by finding an integer idx such that: +// | n/d - idx/64 | <= 1/128. +// In particular, +// idx := round(2^6 * n/d) +// Then for the fast pass, we find a polynomial approximation for: +// atan( n/d ) ~ atan( idx/64 ) + (n/d - idx/64) * Q(n/d - idx/64) +// For the accurate pass, we use the addition formula: +// atan( n/d ) - atan( idx/64 ) = atan( (n/d - idx/64)/(1 + (n*idx)/(64*d)) ) +// = atan( (n - d*(idx/64))/(d + n*(idx/64)) ) +// And for the fast pass, we use degree-9 Taylor polynomial to compute the RHS: +// atan(u) ~ P(u) = u - u^3/3 + u^5/5 - u^7/7 + u^9/9 +// with absolute errors bounded by: +// |atan(u) - P(u)| < |u|^11 / 11 < 2^-80 +// and relative errors bounded by: +// |(atan(u) - P(u)) / P(u)| < u^10 / 11 < 2^-73. + +LIBC_INLINE static constexpr double atan2(double y, double x) { + using namespace atan_internal; + using FPBits = fputil::FPBits<double>; + + constexpr double IS_NEG[2] = {1.0, -1.0}; + constexpr DoubleDouble ZERO = {0.0, 0.0}; + constexpr DoubleDouble MZERO = {-0.0, -0.0}; + constexpr DoubleDouble PI = {0x1.1a62633145c07p-53, 0x1.921fb54442d18p+1}; + constexpr DoubleDouble MPI = {-0x1.1a62633145c07p-53, -0x1.921fb54442d18p+1}; + constexpr DoubleDouble PI_OVER_2 = {0x1.1a62633145c07p-54, + 0x1.921fb54442d18p0}; + constexpr DoubleDouble MPI_OVER_2 = {-0x1.1a62633145c07p-54, + -0x1.921fb54442d18p0}; + constexpr DoubleDouble PI_OVER_4 = {0x1.1a62633145c07p-55, + 0x1.921fb54442d18p-1}; + constexpr DoubleDouble THREE_PI_OVER_4 = {0x1.a79394c9e8a0ap-54, + 0x1.2d97c7f3321d2p+1}; + // Adjustment for constant term: + // CONST_ADJ[x_sign][y_sign][recip] + constexpr DoubleDouble CONST_ADJ[2][2][2] = { + {{ZERO, MPI_OVER_2}, {MZERO, MPI_OVER_2}}, + {{MPI, PI_OVER_2}, {MPI, PI_OVER_2}}}; + + FPBits x_bits(x), y_bits(y); + bool x_sign = x_bits.sign().is_neg(); + bool y_sign = y_bits.sign().is_neg(); + x_bits = x_bits.abs(); + y_bits = y_bits.abs(); + uint64_t x_abs = x_bits.uintval(); + uint64_t y_abs = y_bits.uintval(); + bool recip = x_abs < y_abs; + uint64_t min_abs = recip ? x_abs : y_abs; + uint64_t max_abs = !recip ? x_abs : y_abs; + unsigned min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); + unsigned max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); + + double num = FPBits(min_abs).get_val(); + double den = FPBits(max_abs).get_val(); + + // Check for exceptional cases, whether inputs are 0, inf, nan, or close to + // overflow, or close to underflow. + if (LIBC_UNLIKELY(max_exp > 0x7ffU - 128U || min_exp < 128U)) { + if (x_bits.is_nan() || y_bits.is_nan()) { + if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan()) + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + unsigned x_except = x == 0.0 ? 0 : (FPBits(x_abs).is_inf() ? 2 : 1); + unsigned y_except = y == 0.0 ? 0 : (FPBits(y_abs).is_inf() ? 2 : 1); + + // Exceptional cases: + // EXCEPT[y_except][x_except][x_is_neg] + // with x_except & y_except: + // 0: zero + // 1: finite, non-zero + // 2: infinity + constexpr DoubleDouble EXCEPTS[3][3][2] = { + {{ZERO, PI}, {ZERO, PI}, {ZERO, PI}}, + {{PI_OVER_2, PI_OVER_2}, {ZERO, ZERO}, {ZERO, PI}}, + {{PI_OVER_2, PI_OVER_2}, + {PI_OVER_2, PI_OVER_2}, + {PI_OVER_4, THREE_PI_OVER_4}}, + }; + + if ((x_except != 1) || (y_except != 1)) { + DoubleDouble r = EXCEPTS[y_except][x_except][x_sign]; + return fputil::multiply_add(IS_NEG[y_sign], r.hi, IS_NEG[y_sign] * r.lo); + } + bool scale_up = min_exp < 128U; + bool scale_down = max_exp > 0x7ffU - 128U; + // At least one input is denormal, multiply both numerator and denominator + // by some large enough power of 2 to normalize denormal inputs. + if (scale_up) { + num *= 0x1.0p64; + if (!scale_down) + den *= 0x1.0p64; + } else if (scale_down) { + den *= 0x1.0p-64; + if (!scale_up) + num *= 0x1.0p-64; + } + + min_abs = FPBits(num).uintval(); + max_abs = FPBits(den).uintval(); + min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); + max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); + } + + double final_sign = IS_NEG[(x_sign != y_sign) != recip]; + DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip]; + unsigned exp_diff = max_exp - min_exp; + // We have the following bound for normalized n and d: + // 2^(-exp_diff - 1) < n/d < 2^(-exp_diff + 1). + if (LIBC_UNLIKELY(exp_diff > 54)) { + return fputil::multiply_add(final_sign, const_term.hi, + final_sign * (const_term.lo + num / den)); + } + + double k = fputil::nearest_integer(64.0 * num / den); + unsigned idx = static_cast<unsigned>(k); + // k = idx / 64 + k *= 0x1.0p-6; + + // Range reduction: + // atan(n/d) - atan(k/64) = atan((n/d - k/64) / (1 + (n/d) * (k/64))) + // = atan((n - d * k/64)) / (d + n * k/64)) + DoubleDouble num_k = fputil::exact_mult(num, k); + DoubleDouble den_k = fputil::exact_mult(den, k); + + // num_dd = n - d * k + DoubleDouble num_dd = fputil::exact_add(num - den_k.hi, -den_k.lo); + // den_dd = d + n * k + DoubleDouble den_dd = fputil::exact_add(den, num_k.hi); + den_dd.lo += num_k.lo; + + // q = (n - d * k) / (d + n * k) + DoubleDouble q = fputil::div(num_dd, den_dd); + // p ~ atan(q) + DoubleDouble p = atan_eval(q); + + DoubleDouble r = fputil::add(const_term, fputil::add(ATAN_I[idx], p)); + r.hi *= final_sign; + r.lo *= final_sign; + + return r.hi + r.lo; +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ATAN2_H diff --git a/libc/src/math/generic/atan_utils.h b/libc/src/__support/math/atan_utils.h index 24c7271..9e8d7d6 100644 --- a/libc/src/math/generic/atan_utils.h +++ b/libc/src/__support/math/atan_utils.h @@ -18,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL { -namespace { +namespace atan_internal { using DoubleDouble = fputil::DoubleDouble; using Float128 = fputil::DyadicFloat<128>; @@ -29,7 +29,7 @@ using Float128 = fputil::DyadicFloat<128>; // b = round(atan(i/64) - a, D, RN); // print("{", b, ",", a, "},"); // }; -constexpr DoubleDouble ATAN_I[65] = { +static constexpr DoubleDouble ATAN_I[65] = { {0.0, 0.0}, {-0x1.220c39d4dff5p-61, 0x1.fff555bbb729bp-7}, {-0x1.5ec431444912cp-60, 0x1.ffd55bba97625p-6}, @@ -110,7 +110,8 @@ constexpr DoubleDouble ATAN_I[65] = { // + x_lo * (1 - x_hi^2 + x_hi^4) // Since p.lo is ~ x^3/3, the relative error from rounding is bounded by: // |(atan(x) - P(x))/atan(x)| < ulp(x^2) <= 2^(-14-52) = 2^-66. -[[maybe_unused]] DoubleDouble atan_eval(const DoubleDouble &x) { +[[maybe_unused]] LIBC_INLINE static DoubleDouble +atan_eval(const DoubleDouble &x) { DoubleDouble p; p.hi = x.hi; double x_hi_sq = x.hi * x.hi; @@ -142,7 +143,7 @@ constexpr DoubleDouble ATAN_I[65] = { // b = 2^ll + a; // print("{Sign::POS, ", 2^(ll - 128), ",", b, "},"); // }; -constexpr Float128 ATAN_I_F128[65] = { +static constexpr Float128 ATAN_I_F128[65] = { {Sign::POS, 0, 0_u128}, {Sign::POS, -134, 0xfffaaadd'db94d5bb'e78c5640'15f76048_u128}, {Sign::POS, -133, 0xffeaaddd'4bb12542'779d776d'da8c6214_u128}, @@ -215,7 +216,7 @@ constexpr Float128 ATAN_I_F128[65] = { // [0, 2^-7]); // > dirtyinfnorm(atan(x) - P, [0, 2^-7]); // 0x1.26016ad97f323875760f869684c0898d7b7bb8bep-122 -constexpr Float128 ATAN_POLY_F128[] = { +static constexpr Float128 ATAN_POLY_F128[] = { {Sign::NEG, -129, 0xaaaaaaaa'aaaaaaaa'aaaaaaa6'003c5d1d_u128}, {Sign::POS, -130, 0xcccccccc'cccccccc'cca00232'8776b063_u128}, {Sign::NEG, -130, 0x92492492'49249201'27f5268a'cb24aec0_u128}, @@ -225,7 +226,8 @@ constexpr Float128 ATAN_POLY_F128[] = { }; // Approximate atan for |x| <= 2^-7. -[[maybe_unused]] Float128 atan_eval(const Float128 &x) { +[[maybe_unused]] LIBC_INLINE static constexpr Float128 +atan_eval(const Float128 &x) { Float128 x_sq = fputil::quick_mul(x, x); Float128 x3 = fputil::quick_mul(x, x_sq); Float128 p = fputil::polyeval(x_sq, ATAN_POLY_F128[0], ATAN_POLY_F128[1], @@ -234,7 +236,7 @@ constexpr Float128 ATAN_POLY_F128[] = { return fputil::multiply_add(x3, p, x); } -} // anonymous namespace +} // namespace atan_internal } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/math/atanf.h b/libc/src/__support/math/atanf.h new file mode 100644 index 0000000..92799dc --- /dev/null +++ b/libc/src/__support/math/atanf.h @@ -0,0 +1,129 @@ +//===-- Implementation header for atanf -------------------------*- 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_MATH_ATANF_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ATANF_H + +#include "inv_trigf_utils.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/PolyEval.h" +#include "src/__support/FPUtil/except_value_utils.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/nearest_integer.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +LIBC_INLINE static constexpr float atanf(float x) { + using namespace inv_trigf_utils_internal; + using FPBits = typename fputil::FPBits<float>; + + constexpr double FINAL_SIGN[2] = {1.0, -1.0}; + constexpr double SIGNED_PI_OVER_2[2] = {0x1.921fb54442d18p0, + -0x1.921fb54442d18p0}; + + FPBits x_bits(x); + Sign sign = x_bits.sign(); + x_bits.set_sign(Sign::POS); + uint32_t x_abs = x_bits.uintval(); + + // x is inf or nan, |x| < 2^-4 or |x|= > 16. + if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U || x_abs >= 0x4180'0000U)) { + double x_d = static_cast<double>(x); + double const_term = 0.0; + if (LIBC_UNLIKELY(x_abs >= 0x4180'0000)) { + // atan(+-Inf) = +-pi/2. + if (x_bits.is_inf()) { + volatile double sign_pi_over_2 = SIGNED_PI_OVER_2[sign.is_neg()]; + return static_cast<float>(sign_pi_over_2); + } + if (x_bits.is_nan()) + return x; + // x >= 16 + x_d = -1.0 / x_d; + const_term = SIGNED_PI_OVER_2[sign.is_neg()]; + } + // 0 <= x < 1/16; + if (LIBC_UNLIKELY(x_bits.is_zero())) + return x; + // x <= 2^-12; + if (LIBC_UNLIKELY(x_abs < 0x3980'0000)) { +#if defined(LIBC_TARGET_CPU_HAS_FMA_FLOAT) + return fputil::multiply_add(x, -0x1.0p-25f, x); +#else + double x_d = static_cast<double>(x); + return static_cast<float>(fputil::multiply_add(x_d, -0x1.0p-25, x_d)); +#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT + } + // Use Taylor polynomial: + // atan(x) ~ x * (1 - x^2 / 3 + x^4 / 5 - x^6 / 7 + x^8 / 9 - x^10 / 11). + constexpr double ATAN_TAYLOR[6] = { + 0x1.0000000000000p+0, -0x1.5555555555555p-2, 0x1.999999999999ap-3, + -0x1.2492492492492p-3, 0x1.c71c71c71c71cp-4, -0x1.745d1745d1746p-4, + }; + double x2 = x_d * x_d; + double x4 = x2 * x2; + double c0 = fputil::multiply_add(x2, ATAN_TAYLOR[1], ATAN_TAYLOR[0]); + double c1 = fputil::multiply_add(x2, ATAN_TAYLOR[3], ATAN_TAYLOR[2]); + double c2 = fputil::multiply_add(x2, ATAN_TAYLOR[5], ATAN_TAYLOR[4]); + double p = fputil::polyeval(x4, c0, c1, c2); + double r = fputil::multiply_add(x_d, p, const_term); + return static_cast<float>(r); + } + + // Range reduction steps: + // 1) atan(x) = sign(x) * atan(|x|) + // 2) If |x| > 1, atan(|x|) = pi/2 - atan(1/|x|) + // 3) For 1/16 < x <= 1, we find k such that: |x - k/16| <= 1/32. + // 4) Then we use polynomial approximation: + // atan(x) ~ atan((k/16) + (x - (k/16)) * Q(x - k/16) + // = P(x - k/16) + double x_d = 0, const_term = 0, final_sign = 0; + int idx = 0; + + if (x_abs > 0x3f80'0000U) { + // |x| > 1, we need to invert x, so we will perform range reduction in + // double precision. + x_d = 1.0 / static_cast<double>(x_bits.get_val()); + double k_d = fputil::nearest_integer(x_d * 0x1.0p4); + x_d = fputil::multiply_add(k_d, -0x1.0p-4, x_d); + idx = static_cast<int>(k_d); + final_sign = FINAL_SIGN[sign.is_pos()]; + // Adjust constant term of the polynomial by +- pi/2. + const_term = fputil::multiply_add(final_sign, ATAN_COEFFS[idx][0], + SIGNED_PI_OVER_2[sign.is_neg()]); + } else { + // Exceptional value: + if (LIBC_UNLIKELY(x_abs == 0x3d8d'6b23U)) { // |x| = 0x1.1ad646p-4 + return sign.is_pos() ? fputil::round_result_slightly_down(0x1.1a6386p-4f) + : fputil::round_result_slightly_up(-0x1.1a6386p-4f); + } + // Perform range reduction in single precision. + float x_f = x_bits.get_val(); + float k_f = fputil::nearest_integer(x_f * 0x1.0p4f); + x_f = fputil::multiply_add(k_f, -0x1.0p-4f, x_f); + x_d = static_cast<double>(x_f); + idx = static_cast<int>(k_f); + final_sign = FINAL_SIGN[sign.is_neg()]; + const_term = final_sign * ATAN_COEFFS[idx][0]; + } + + double p = atan_eval(x_d, idx); + double r = fputil::multiply_add(final_sign * x_d, p, const_term); + + return static_cast<float>(r); +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ATANF_H diff --git a/libc/src/__support/math/atanf16.h b/libc/src/__support/math/atanf16.h new file mode 100644 index 0000000..f75d145 --- /dev/null +++ b/libc/src/__support/math/atanf16.h @@ -0,0 +1,119 @@ +//===-- Implementation header for atanf16 -----------------------*- 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_MATH_ATANF16_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_ATANF16_H + +#include "include/llvm-libc-macros/float16-macros.h" + +#ifdef LIBC_TYPES_HAS_FLOAT16 + +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/PolyEval.h" +#include "src/__support/FPUtil/cast.h" +#include "src/__support/FPUtil/except_value_utils.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/FPUtil/sqrt.h" +#include "src/__support/macros/optimization.h" + +namespace LIBC_NAMESPACE_DECL { + +namespace math { + +LIBC_INLINE static constexpr float16 atanf16(float16 x) { + // Generated by Solly using the following command: + // > round(pi/2, SG, RN); + constexpr float PI_2 = 0x1.921fb6p0; + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + constexpr size_t N_EXCEPTS = 6; + + constexpr fputil::ExceptValues<float16, N_EXCEPTS> ATANF16_EXCEPTS{{ + // (input, RZ output, RU offset, RD offset, RN offset) + {0x2745, 0x2744, 1, 0, 1}, + {0x3099, 0x3090, 1, 0, 1}, + {0x3c6c, 0x3aae, 1, 0, 1}, + {0x466e, 0x3daa, 1, 0, 1}, + {0x48ae, 0x3ddb, 1, 0, 0}, + {0x5619, 0x3e3d, 1, 0, 1}, + }}; +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + + using FPBits = fputil::FPBits<float16>; + FPBits xbits(x); + + uint16_t x_u = xbits.uintval(); + uint16_t x_abs = x_u & 0x7fff; + bool x_sign = x_u >> 15; + float sign = (x_sign ? -1.0 : 1.0); + + // |x| >= +/-inf + if (LIBC_UNLIKELY(x_abs >= 0x7c00)) { + if (xbits.is_nan()) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + return x; + } + + // atanf16(+/-inf) = +/-pi/2 + return fputil::cast<float16>(sign * PI_2); + } + + float xf = x; + float xsq = xf * xf; +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + // Handle exceptional values + if (auto r = ATANF16_EXCEPTS.lookup_odd(x_abs, x_sign); + LIBC_UNLIKELY(r.has_value())) + return r.value(); +#endif + + // |x| <= 0x1p0, |x| <= 1 + if (x_abs <= 0x3c00) { + // atanf16(+/-0) = +/-0 + if (LIBC_UNLIKELY(x_abs == 0)) + return x; + + // Degree-14 minimax odd polynomial of atan(x) generated by Sollya with: + // > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|], + // [0, 1]); + float result = fputil::polyeval( + xsq, 0x1.fffffcp-1f, -0x1.55519ep-2f, 0x1.98f6a8p-3f, -0x1.1f0a92p-3f, + 0x1.95b654p-4f, -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f); + return fputil::cast<float16>(xf * result); + } + + // If |x| > 1 + // y = atan(x) = sign(x) * atan(|x|) + // atan(|x|) = pi/2 - atan(1/|x|) + // Recall, 1/|x| < 1 + float x_inv_sq = 1.0f / xsq; + float x_inv = fputil::sqrt<float>(x_inv_sq); + + // Degree-14 minimax odd polynomial of atan(x) generated by Sollya with: + // > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|], + // [0, 1]); + float interm = + fputil::polyeval(x_inv_sq, 0x1.fffffcp-1f, -0x1.55519ep-2f, + 0x1.98f6a8p-3f, -0x1.1f0a92p-3f, 0x1.95b654p-4f, + -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f); + + return fputil::cast<float16>(sign * + fputil::multiply_add(x_inv, -interm, PI_2)); +} + +} // namespace math + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_HAS_FLOAT16 + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ATANF16_H diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt index b084346..f8a4493 100644 --- a/libc/src/__support/threads/CMakeLists.txt +++ b/libc/src/__support/threads/CMakeLists.txt @@ -42,6 +42,14 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.mutex) .mutex libc.src.__support.CPP.mutex ) +elseif(NOT (LIBC_CONF_THREAD_MODE STREQUAL LIBC_THREAD_MODE_PLATFORM)) + add_header_library( + mutex + HDRS + mutex.h + DEPENDS + .mutex_common + ) endif() add_header_library( diff --git a/libc/src/__support/threads/gpu/CMakeLists.txt b/libc/src/__support/threads/gpu/CMakeLists.txt deleted file mode 100644 index ea89feb..0000000 --- a/libc/src/__support/threads/gpu/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_header_library( - mutex - HDRS - mutex.h -) diff --git a/libc/src/__support/threads/gpu/mutex.h b/libc/src/__support/threads/gpu/mutex.h deleted file mode 100644 index c8c484e..0000000 --- a/libc/src/__support/threads/gpu/mutex.h +++ /dev/null @@ -1,32 +0,0 @@ -//===--- Implementation of a GPU mutex class --------------------*- 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_THREADS_GPU_MUTEX_H -#define LLVM_LIBC_SRC___SUPPORT_THREADS_GPU_MUTEX_H - -#include "src/__support/macros/attributes.h" -#include "src/__support/macros/config.h" -#include "src/__support/threads/mutex_common.h" - -namespace LIBC_NAMESPACE_DECL { - -/// Implementation of a simple passthrough mutex which guards nothing. A -/// complete Mutex locks in general cannot be implemented on the GPU. We simply -/// define the Mutex interface and require that only a single thread executes -/// code requiring a mutex lock. -struct Mutex { - LIBC_INLINE constexpr Mutex(bool, bool, bool, bool) {} - - LIBC_INLINE MutexError lock() { return MutexError::NONE; } - LIBC_INLINE MutexError unlock() { return MutexError::NONE; } - LIBC_INLINE MutexError reset() { return MutexError::NONE; } -}; - -} // namespace LIBC_NAMESPACE_DECL - -#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_GPU_MUTEX_H diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt index cbb7886..14aaad2 100644 --- a/libc/src/__support/threads/linux/CMakeLists.txt +++ b/libc/src/__support/threads/linux/CMakeLists.txt @@ -124,3 +124,14 @@ add_object_library( libc.src.__support.threads.linux.raw_mutex libc.src.__support.CPP.mutex ) + +add_object_library( + barrier + HDRS + barrier.h + SRCS + barrier.cpp + DEPENDS + libc.src.__support.threads.CndVar + libc.src.__support.threads.mutex +) diff --git a/libc/src/__support/threads/linux/barrier.cpp b/libc/src/__support/threads/linux/barrier.cpp new file mode 100644 index 0000000..cf7207b5 --- /dev/null +++ b/libc/src/__support/threads/linux/barrier.cpp @@ -0,0 +1,85 @@ +//===-- Implementation of Barrier class ------------- ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/threads/linux/barrier.h" +#include "hdr/errno_macros.h" +#include "src/__support/threads/CndVar.h" +#include "src/__support/threads/mutex.h" + +namespace LIBC_NAMESPACE_DECL { + +int Barrier::init(Barrier *b, + [[maybe_unused]] const pthread_barrierattr_t *attr, + unsigned count) { + LIBC_ASSERT(attr == nullptr); // TODO implement barrierattr + if (count == 0) + return EINVAL; + + b->expected = count; + b->waiting = 0; + b->blocking = true; + + int err; + err = CndVar::init(&b->entering); + if (err != 0) + return err; + + err = CndVar::init(&b->exiting); + if (err != 0) + return err; + + auto mutex_err = Mutex::init(&b->m, false, false, false, false); + if (mutex_err != MutexError::NONE) + return EAGAIN; + + return 0; +} + +int Barrier::wait() { + m.lock(); + + // if the barrier is emptying out threads, wait until it finishes + while (!blocking) + entering.wait(&m); + waiting++; + + if (waiting < expected) { + // block threads until waiting = expected + while (blocking) + exiting.wait(&m); + } else { + // this is the last thread to call wait(), so lets wake everyone up + blocking = false; + exiting.broadcast(); + } + waiting--; + + if (waiting == 0) { + // all threads have exited the barrier, let's let the ones waiting to enter + // continue + blocking = true; + entering.broadcast(); + m.unlock(); + + // POSIX dictates that the barrier should return a special value to just one + // thread, so we can arbitrarily choose this thread + return PTHREAD_BARRIER_SERIAL_THREAD; + } + m.unlock(); + + return 0; +} + +int Barrier::destroy(Barrier *b) { + CndVar::destroy(&b->entering); + CndVar::destroy(&b->exiting); + Mutex::destroy(&b->m); + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/threads/linux/barrier.h b/libc/src/__support/threads/linux/barrier.h new file mode 100644 index 0000000..f0655bf --- /dev/null +++ b/libc/src/__support/threads/linux/barrier.h @@ -0,0 +1,50 @@ +//===-- A platform independent abstraction layer for barriers --*- 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___SUPPORT_SRC_THREADS_LINUX_BARRIER_H +#define LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_BARRIER_H + +#include "hdr/pthread_macros.h" +#include "include/llvm-libc-types/pthread_barrier_t.h" +#include "include/llvm-libc-types/pthread_barrierattr_t.h" +#include "src/__support/threads/CndVar.h" +#include "src/__support/threads/mutex.h" + +namespace LIBC_NAMESPACE_DECL { + +// NOTE: if the size of this class changes, you must ensure that the size of +// pthread_barrier_t (found in include/llvm-libc/types/pthread_barrier_t.h) is +// the same size +class Barrier { +private: + unsigned expected; + unsigned waiting; + bool blocking; + CndVar entering; + CndVar exiting; + Mutex m; + +public: + static int init(Barrier *b, const pthread_barrierattr_t *attr, + unsigned count); + static int destroy(Barrier *b); + int wait(); +}; + +static_assert( + sizeof(Barrier) == sizeof(pthread_barrier_t), + "The public pthread_barrier_t type cannot accommodate the internal " + "barrier type."); + +static_assert(alignof(Barrier) == alignof(pthread_barrier_t), + "The public pthread_barrier_t type has a different alignment " + "than the internal barrier type."); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_BARRIER_H diff --git a/libc/src/__support/threads/mutex.h b/libc/src/__support/threads/mutex.h index 392b389..cbef0d0 100644 --- a/libc/src/__support/threads/mutex.h +++ b/libc/src/__support/threads/mutex.h @@ -9,10 +9,35 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_MUTEX_H #define LLVM_LIBC_SRC___SUPPORT_THREADS_MUTEX_H -#include "src/__support/macros/properties/architectures.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/config.h" + +// Uses the platform specific specialization +#define LIBC_THREAD_MODE_PLATFORM 0 + +// Mutex guards nothing, used in single-threaded implementations +#define LIBC_THREAD_MODE_SINGLE 1 + +// Vendor provides implementation +#define LIBC_THREAD_MODE_EXTERNAL 2 + +#if !defined(LIBC_THREAD_MODE) +#error LIBC_THREAD_MODE is undefined +#endif // LIBC_THREAD_MODE + +#if LIBC_THREAD_MODE != LIBC_THREAD_MODE_PLATFORM && \ + LIBC_THREAD_MODE != LIBC_THREAD_MODE_SINGLE && \ + LIBC_THREAD_MODE != LIBC_THREAD_MODE_EXTERNAL +#error LIBC_THREAD_MODE must be one of the following values: \ +LIBC_THREAD_MODE_PLATFORM, \ +LIBC_THREAD_MODE_SINGLE, \ +LIBC_THREAD_MODE_EXTERNAL. +#endif + +#if LIBC_THREAD_MODE == LIBC_THREAD_MODE_PLATFORM // Platform independent code will include this header file which pulls -// the platfrom specific specializations using platform macros. +// the platform specific specializations using platform macros. // // The platform specific specializations should define a class by name // Mutex with non-static methods having the following signature: @@ -39,8 +64,32 @@ #if defined(__linux__) #include "src/__support/threads/linux/mutex.h" -#elif defined(LIBC_TARGET_ARCH_IS_GPU) -#include "src/__support/threads/gpu/mutex.h" #endif // __linux__ +#elif LIBC_THREAD_MODE == LIBC_THREAD_MODE_SINGLE + +#include "src/__support/threads/mutex_common.h" + +namespace LIBC_NAMESPACE_DECL { + +/// Implementation of a simple passthrough mutex which guards nothing. A +/// complete Mutex locks in general cannot be implemented on the GPU, or on some +/// baremetal platforms. We simply define the Mutex interface and require that +/// only a single thread executes code requiring a mutex lock. +struct Mutex { + LIBC_INLINE constexpr Mutex(bool, bool, bool, bool) {} + + LIBC_INLINE MutexError lock() { return MutexError::NONE; } + LIBC_INLINE MutexError unlock() { return MutexError::NONE; } + LIBC_INLINE MutexError reset() { return MutexError::NONE; } +}; + +} // namespace LIBC_NAMESPACE_DECL + +#elif LIBC_THREAD_MODE == LIBC_THREAD_MODE_EXTERNAL + +// TODO: Implement the interfacing, if necessary, e.g. "extern struct Mutex;" + +#endif // LIBC_THREAD_MODE == LIBC_THREAD_MODE_PLATFORM + #endif // LLVM_LIBC_SRC___SUPPORT_THREADS_MUTEX_H diff --git a/libc/src/__support/wchar/CMakeLists.txt b/libc/src/__support/wchar/CMakeLists.txt index e363ad3..aed1d53 100644 --- a/libc/src/__support/wchar/CMakeLists.txt +++ b/libc/src/__support/wchar/CMakeLists.txt @@ -60,14 +60,31 @@ add_object_library( SRCS mbrtowc.cpp DEPENDS - libc.hdr.errno_macros - libc.hdr.types.wchar_t - libc.hdr.types.size_t - libc.src.__support.common - libc.src.__support.error_or - libc.src.__support.macros.config - .character_converter - .mbstate + libc.hdr.errno_macros + libc.hdr.types.wchar_t + libc.hdr.types.size_t + libc.src.__support.common + libc.src.__support.error_or + libc.src.__support.macros.config + .character_converter + .mbstate +) + +add_header_library( + mbsnrtowcs + HDRS + mbsnrtowcs.h + DEPENDS + libc.hdr.errno_macros + libc.hdr.types.wchar_t + libc.hdr.types.size_t + libc.src.__support.common + libc.src.__support.error_or + libc.src.__support.macros.config + libc.src.__support.macros.null_check + .character_converter + .mbstate + .string_converter ) add_header_library( diff --git a/libc/src/__support/wchar/mbsnrtowcs.h b/libc/src/__support/wchar/mbsnrtowcs.h new file mode 100644 index 0000000..54e3152 --- /dev/null +++ b/libc/src/__support/wchar/mbsnrtowcs.h @@ -0,0 +1,66 @@ +//===-- Implementation for mbsnrtowcs function ------------------*- 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_WCHAR_MBSNRTOWCS_H +#define LLVM_LIBC_SRC___SUPPORT_WCHAR_MBSNRTOWCS_H + +#include "hdr/errno_macros.h" +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/error_or.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/null_check.h" +#include "src/__support/wchar/character_converter.h" +#include "src/__support/wchar/mbstate.h" +#include "src/__support/wchar/string_converter.h" + +namespace LIBC_NAMESPACE_DECL { +namespace internal { + +LIBC_INLINE static ErrorOr<size_t> mbsnrtowcs(wchar_t *__restrict dst, + const char **__restrict src, + size_t nmc, size_t len, + mbstate *__restrict ps) { + LIBC_CRASH_ON_NULLPTR(src); + // Checking if mbstate is valid + CharacterConverter char_conv(ps); + if (!char_conv.isValidState()) + return Error(EINVAL); + + StringConverter<char8_t> str_conv(reinterpret_cast<const char8_t *>(*src), ps, + len, nmc); + size_t dst_idx = 0; + ErrorOr<char32_t> converted = str_conv.popUTF32(); + while (converted.has_value()) { + if (dst != nullptr) + dst[dst_idx] = converted.value(); + // null terminator should not be counted in return value + if (converted.value() == L'\0') { + if (dst != nullptr) + *src = nullptr; + return dst_idx; + } + dst_idx++; + converted = str_conv.popUTF32(); + } + + if (converted.error() == -1) { // if we hit conversion limit + if (dst != nullptr) + *src += str_conv.getSourceIndex(); + return dst_idx; + } + + return Error(converted.error()); +} + +} // namespace internal + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_WCHAR_MBSNRTOWCS_H diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 455ad34..0522e0e 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -189,6 +189,7 @@ add_math_entrypoint_object(fabsf) add_math_entrypoint_object(fabsl) add_math_entrypoint_object(fabsf16) add_math_entrypoint_object(fabsf128) +add_math_entrypoint_object(fabsbf16) add_math_entrypoint_object(fadd) add_math_entrypoint_object(faddl) diff --git a/libc/src/math/fabsbf16.h b/libc/src/math/fabsbf16.h new file mode 100644 index 0000000..4993668 --- /dev/null +++ b/libc/src/math/fabsbf16.h @@ -0,0 +1,21 @@ +//===-- Implementation header for fabsbf16 ----------------------*- 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_MATH_FABSBF16_H +#define LLVM_LIBC_SRC_MATH_FABSBF16_H + +#include "src/__support/macros/config.h" +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE_DECL { + +bfloat16 fabsbf16(bfloat16 x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_FABSBF16_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index f91feacb..6bcb1e2 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -698,6 +698,19 @@ add_entrypoint_object( ) add_entrypoint_object( + fabsbf16 + SRCS + fabsbf16.cpp + HDRS + ../fabsbf16.h + DEPENDS + libc.src.__support.FPUtil.basic_operations + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.macros.config + libc.src.__support.macros.properties.types +) + +add_entrypoint_object( fadd SRCS fadd.cpp @@ -3899,18 +3912,7 @@ add_entrypoint_object( HDRS ../asinhf16.h DEPENDS - .explogxf - libc.hdr.fenv_macros - libc.src.__support.FPUtil.cast - libc.src.__support.FPUtil.except_value_utils - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.polyeval - libc.src.__support.FPUtil.rounding_mode - libc.src.__support.FPUtil.sqrt - libc.src.__support.macros.optimization - libc.src.__support.macros.properties.types + libc.src.__support.math.asinhf16 ) add_entrypoint_object( @@ -4018,19 +4020,6 @@ add_entrypoint_object( libc.src.errno.errno ) -add_header_library( - atan_utils - HDRS - atan_utils.h - DEPENDS - libc.src.__support.integer_literals - libc.src.__support.FPUtil.double_double - libc.src.__support.FPUtil.dyadic_float - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.polyeval - libc.src.__support.macros.optimization -) - add_entrypoint_object( atanf SRCS @@ -4038,14 +4027,7 @@ add_entrypoint_object( HDRS ../atanf.h DEPENDS - libc.src.__support.FPUtil.except_value_utils - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.nearest_integer - libc.src.__support.FPUtil.polyeval - libc.src.__support.FPUtil.rounding_mode - libc.src.__support.macros.optimization - libc.src.__support.math.inv_trigf_utils + libc.src.__support.math.atanf ) add_entrypoint_object( @@ -4055,17 +4037,7 @@ add_entrypoint_object( HDRS ../atanf16.h DEPENDS - libc.hdr.errno_macros - libc.hdr.fenv_macros - libc.src.__support.FPUtil.cast - libc.src.__support.FPUtil.except_value_utils - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.polyeval - libc.src.__support.FPUtil.sqrt - libc.src.__support.macros.optimization - libc.src.__support.macros.properties.types + libc.src.__support.math.atanf16 ) add_entrypoint_object( @@ -4077,13 +4049,7 @@ add_entrypoint_object( COMPILE_OPTIONS -O3 DEPENDS - .atan_utils - libc.src.__support.FPUtil.double_double - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.nearest_integer - libc.src.__support.macros.optimization + libc.src.__support.math.atan ) add_entrypoint_object( @@ -4113,13 +4079,7 @@ add_entrypoint_object( HDRS ../atan2.h DEPENDS - .atan_utils - libc.src.__support.FPUtil.double_double - libc.src.__support.FPUtil.fenv_impl - libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.multiply_add - libc.src.__support.FPUtil.nearest_integer - libc.src.__support.macros.optimization + libc.src.__support.math.atan2 ) add_entrypoint_object( @@ -4129,7 +4089,7 @@ add_entrypoint_object( HDRS ../atan2l.h DEPENDS - .atan2 + libc.src.__support.math.atan2 ) add_entrypoint_object( @@ -4139,7 +4099,7 @@ add_entrypoint_object( HDRS ../atan2f128.h DEPENDS - .atan_utils + libc.src.__support.math.atan_utils libc.src.__support.integer_literals libc.src.__support.uint128 libc.src.__support.FPUtil.dyadic_float diff --git a/libc/src/math/generic/asinhf16.cpp b/libc/src/math/generic/asinhf16.cpp index 0a0b471..d517e63 100644 --- a/libc/src/math/generic/asinhf16.cpp +++ b/libc/src/math/generic/asinhf16.cpp @@ -7,102 +7,10 @@ //===----------------------------------------------------------------------===// #include "src/math/asinhf16.h" -#include "explogxf.h" -#include "hdr/fenv_macros.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/PolyEval.h" -#include "src/__support/FPUtil/cast.h" -#include "src/__support/FPUtil/except_value_utils.h" -#include "src/__support/FPUtil/multiply_add.h" -#include "src/__support/FPUtil/rounding_mode.h" -#include "src/__support/FPUtil/sqrt.h" -#include "src/__support/common.h" -#include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" +#include "src/__support/math/asinhf16.h" namespace LIBC_NAMESPACE_DECL { -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS -static constexpr size_t N_EXCEPTS = 8; - -static constexpr fputil::ExceptValues<float16, N_EXCEPTS> ASINHF16_EXCEPTS{{ - // (input, RZ output, RU offset, RD offset, RN offset) - - // x = 0x1.da4p-2, asinhf16(x) = 0x1.ca8p-2 (RZ) - {0x3769, 0x372a, 1, 0, 1}, - // x = 0x1.d6cp-1, asinhf16(x) = 0x1.a58p-1 (RZ) - {0x3b5b, 0x3a96, 1, 0, 0}, - // x = 0x1.c7cp+3, asinhf16(x) = 0x1.accp+1 (RZ) - {0x4b1f, 0x42b3, 1, 0, 0}, - // x = 0x1.26cp+4, asinhf16(x) = 0x1.cd8p+1 (RZ) - {0x4c9b, 0x4336, 1, 0, 1}, - // x = -0x1.da4p-2, asinhf16(x) = -0x1.ca8p-2 (RZ) - {0xb769, 0xb72a, 0, 1, 1}, - // x = -0x1.d6cp-1, asinhf16(x) = -0x1.a58p-1 (RZ) - {0xbb5b, 0xba96, 0, 1, 0}, - // x = -0x1.c7cp+3, asinhf16(x) = -0x1.accp+1 (RZ) - {0xcb1f, 0xc2b3, 0, 1, 0}, - // x = -0x1.26cp+4, asinhf16(x) = -0x1.cd8p+1 (RZ) - {0xcc9b, 0xc336, 0, 1, 1}, -}}; -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS - -LLVM_LIBC_FUNCTION(float16, asinhf16, (float16 x)) { - using namespace acoshf_internal; - using FPBits = fputil::FPBits<float16>; - FPBits xbits(x); - - uint16_t x_u = xbits.uintval(); - uint16_t x_abs = x_u & 0x7fff; - - if (LIBC_UNLIKELY(xbits.is_inf_or_nan())) { - if (xbits.is_signaling_nan()) { - fputil::raise_except_if_required(FE_INVALID); - return FPBits::quiet_nan().get_val(); - } - - return x; - } - -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - // Handle exceptional values - if (auto r = ASINHF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value())) - return r.value(); -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS - - float xf = x; - const float SIGN[2] = {1.0f, -1.0f}; - float x_sign = SIGN[x_u >> 15]; - - // |x| <= 0.25 - if (LIBC_UNLIKELY(x_abs <= 0x3400)) { - // when |x| < 0x1.718p-5, asinhf16(x) = x. Adjust by 1 ULP for certain - // rounding types. - if (LIBC_UNLIKELY(x_abs < 0x29c6)) { - int rounding = fputil::quick_get_round(); - if ((rounding == FE_UPWARD || rounding == FE_TOWARDZERO) && xf < 0) - return fputil::cast<float16>(xf + 0x1p-24f); - if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && xf > 0) - return fputil::cast<float16>(xf - 0x1p-24f); - return fputil::cast<float16>(xf); - } - - float x_sq = xf * xf; - // Generated by Sollya with: - // > P = fpminimax(asinh(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 2^-2]); - // The last coefficient 0x1.bd114ep-6f has been changed to 0x1.bd114ep-5f - // for better accuracy. - float p = fputil::polyeval(x_sq, 1.0f, -0x1.555552p-3f, 0x1.332f6ap-4f, - -0x1.6c53dep-5f, 0x1.bd114ep-5f); - - return fputil::cast<float16>(xf * p); - } - - // General case: asinh(x) = ln(x + sqrt(x^2 + 1)) - float sqrt_term = fputil::sqrt<float>(fputil::multiply_add(xf, xf, 1.0f)); - return fputil::cast<float16>( - x_sign * log_eval(fputil::multiply_add(xf, x_sign, sqrt_term))); -} +LLVM_LIBC_FUNCTION(float16, asinhf16, (float16 x)) { return math::asinhf16(x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/atan.cpp b/libc/src/math/generic/atan.cpp index cbca605..93bf2e1 100644 --- a/libc/src/math/generic/atan.cpp +++ b/libc/src/math/generic/atan.cpp @@ -7,173 +7,10 @@ //===----------------------------------------------------------------------===// #include "src/math/atan.h" -#include "atan_utils.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/double_double.h" -#include "src/__support/FPUtil/multiply_add.h" -#include "src/__support/FPUtil/nearest_integer.h" -#include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY +#include "src/__support/math/atan.h" namespace LIBC_NAMESPACE_DECL { -// To compute atan(x), we divided it into the following cases: -// * |x| < 2^-26: -// Since |x| > atan(|x|) > |x| - |x|^3/3, and |x|^3/3 < ulp(x)/2, we simply -// return atan(x) = x - sign(x) * epsilon. -// * 2^-26 <= |x| < 1: -// We perform range reduction mod 2^-6 = 1/64 as follow: -// Let k = 2^(-6) * round(|x| * 2^6), then -// atan(x) = sign(x) * atan(|x|) -// = sign(x) * (atan(k) + atan((|x| - k) / (1 + |x|*k)). -// We store atan(k) in a look up table, and perform intermediate steps in -// double-double. -// * 1 < |x| < 2^53: -// First we perform the transformation y = 1/|x|: -// atan(x) = sign(x) * (pi/2 - atan(1/|x|)) -// = sign(x) * (pi/2 - atan(y)). -// Then we compute atan(y) using range reduction mod 2^-6 = 1/64 as the -// previous case: -// Let k = 2^(-6) * round(y * 2^6), then -// atan(y) = atan(k) + atan((y - k) / (1 + y*k)) -// = atan(k) + atan((1/|x| - k) / (1 + k/|x|) -// = atan(k) + atan((1 - k*|x|) / (|x| + k)). -// * |x| >= 2^53: -// Using the reciprocal transformation: -// atan(x) = sign(x) * (pi/2 - atan(1/|x|)). -// We have that: -// atan(1/|x|) <= 1/|x| <= 2^-53, -// which is smaller than ulp(pi/2) / 2. -// So we can return: -// atan(x) = sign(x) * (pi/2 - epsilon) - -LLVM_LIBC_FUNCTION(double, atan, (double x)) { - using FPBits = fputil::FPBits<double>; - - constexpr double IS_NEG[2] = {1.0, -1.0}; - constexpr DoubleDouble PI_OVER_2 = {0x1.1a62633145c07p-54, - 0x1.921fb54442d18p0}; - constexpr DoubleDouble MPI_OVER_2 = {-0x1.1a62633145c07p-54, - -0x1.921fb54442d18p0}; - - FPBits xbits(x); - bool x_sign = xbits.is_neg(); - xbits = xbits.abs(); - uint64_t x_abs = xbits.uintval(); - int x_exp = - static_cast<int>(x_abs >> FPBits::FRACTION_LEN) - FPBits::EXP_BIAS; - - // |x| < 1. - if (x_exp < 0) { - if (LIBC_UNLIKELY(x_exp < -26)) { -#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - return x; -#else - if (x == 0.0) - return x; - // |x| < 2^-26 - return fputil::multiply_add(-0x1.0p-54, x, x); -#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS - } - - double x_d = xbits.get_val(); - // k = 2^-6 * round(2^6 * |x|) - double k = fputil::nearest_integer(0x1.0p6 * x_d); - unsigned idx = static_cast<unsigned>(k); - k *= 0x1.0p-6; - - // numerator = |x| - k - DoubleDouble num, den; - num.lo = 0.0; - num.hi = x_d - k; - - // denominator = 1 - k * |x| - den.hi = fputil::multiply_add(x_d, k, 1.0); - DoubleDouble prod = fputil::exact_mult(x_d, k); - // Using Dekker's 2SUM algorithm to compute the lower part. - den.lo = ((1.0 - den.hi) + prod.hi) + prod.lo; - - // x_r = (|x| - k) / (1 + k * |x|) - DoubleDouble x_r = fputil::div(num, den); - - // Approximating atan(x_r) using Taylor polynomial. - DoubleDouble p = atan_eval(x_r); - - // atan(x) = sign(x) * (atan(k) + atan(x_r)) - // = sign(x) * (atan(k) + atan( (|x| - k) / (1 + k * |x|) )) -#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - return IS_NEG[x_sign] * (ATAN_I[idx].hi + (p.hi + (p.lo + ATAN_I[idx].lo))); -#else - - DoubleDouble c0 = fputil::exact_add(ATAN_I[idx].hi, p.hi); - double c1 = c0.lo + (ATAN_I[idx].lo + p.lo); - double r = IS_NEG[x_sign] * (c0.hi + c1); - - return r; -#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS - } - - // |x| >= 2^53 or x is NaN. - if (LIBC_UNLIKELY(x_exp >= 53)) { - // x is nan - if (xbits.is_nan()) { - if (xbits.is_signaling_nan()) { - fputil::raise_except_if_required(FE_INVALID); - return FPBits::quiet_nan().get_val(); - } - return x; - } - // |x| >= 2^53 - // atan(x) ~ sign(x) * pi/2. - if (x_exp >= 53) -#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - return IS_NEG[x_sign] * PI_OVER_2.hi; -#else - return fputil::multiply_add(IS_NEG[x_sign], PI_OVER_2.hi, - IS_NEG[x_sign] * PI_OVER_2.lo); -#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS - } - - double x_d = xbits.get_val(); - double y = 1.0 / x_d; - - // k = 2^-6 * round(2^6 / |x|) - double k = fputil::nearest_integer(0x1.0p6 * y); - unsigned idx = static_cast<unsigned>(k); - k *= 0x1.0p-6; - - // denominator = |x| + k - DoubleDouble den = fputil::exact_add(x_d, k); - // numerator = 1 - k * |x| - DoubleDouble num; - num.hi = fputil::multiply_add(-x_d, k, 1.0); - DoubleDouble prod = fputil::exact_mult(x_d, k); - // Using Dekker's 2SUM algorithm to compute the lower part. - num.lo = ((1.0 - num.hi) - prod.hi) - prod.lo; - - // x_r = (1/|x| - k) / (1 - k/|x|) - // = (1 - k * |x|) / (|x| - k) - DoubleDouble x_r = fputil::div(num, den); - - // Approximating atan(x_r) using Taylor polynomial. - DoubleDouble p = atan_eval(x_r); - - // atan(x) = sign(x) * (pi/2 - atan(1/|x|)) - // = sign(x) * (pi/2 - atan(k) - atan(x_r)) - // = (-sign(x)) * (-pi/2 + atan(k) + atan((1 - k*|x|)/(|x| - k))) -#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - double lo_part = p.lo + ATAN_I[idx].lo + MPI_OVER_2.lo; - return IS_NEG[!x_sign] * (MPI_OVER_2.hi + ATAN_I[idx].hi + (p.hi + lo_part)); -#else - DoubleDouble c0 = fputil::exact_add(MPI_OVER_2.hi, ATAN_I[idx].hi); - DoubleDouble c1 = fputil::exact_add(c0.hi, p.hi); - double c2 = c1.lo + (c0.lo + p.lo) + (ATAN_I[idx].lo + MPI_OVER_2.lo); - - double r = IS_NEG[!x_sign] * (c1.hi + c2); - - return r; -#endif -} +LLVM_LIBC_FUNCTION(double, atan, (double x)) { return math::atan(x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/atan2.cpp b/libc/src/math/generic/atan2.cpp index aa770de..4aaa63d 100644 --- a/libc/src/math/generic/atan2.cpp +++ b/libc/src/math/generic/atan2.cpp @@ -7,194 +7,12 @@ //===----------------------------------------------------------------------===// #include "src/math/atan2.h" -#include "atan_utils.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/double_double.h" -#include "src/__support/FPUtil/multiply_add.h" -#include "src/__support/FPUtil/nearest_integer.h" -#include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY +#include "src/__support/math/atan2.h" namespace LIBC_NAMESPACE_DECL { -// There are several range reduction steps we can take for atan2(y, x) as -// follow: - -// * Range reduction 1: signness -// atan2(y, x) will return a number between -PI and PI representing the angle -// forming by the 0x axis and the vector (x, y) on the 0xy-plane. -// In particular, we have that: -// atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant) -// = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant) -// = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant) -// = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant) -// Since atan function is odd, we can use the formula: -// atan(-u) = -atan(u) -// to adjust the above conditions a bit further: -// atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant) -// = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant) -// = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant) -// = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant) -// Which can be simplified to: -// atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0 -// = sign(y) * (pi - atan( |y|/|x| )) if x < 0 - -// * Range reduction 2: reciprocal -// Now that the argument inside atan is positive, we can use the formula: -// atan(1/x) = pi/2 - atan(x) -// to make the argument inside atan <= 1 as follow: -// atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x -// = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y| -// = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x -// = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y| - -// * Range reduction 3: look up table. -// After the previous two range reduction steps, we reduce the problem to -// compute atan(u) with 0 <= u <= 1, or to be precise: -// atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|). -// An accurate polynomial approximation for the whole [0, 1] input range will -// require a very large degree. To make it more efficient, we reduce the input -// range further by finding an integer idx such that: -// | n/d - idx/64 | <= 1/128. -// In particular, -// idx := round(2^6 * n/d) -// Then for the fast pass, we find a polynomial approximation for: -// atan( n/d ) ~ atan( idx/64 ) + (n/d - idx/64) * Q(n/d - idx/64) -// For the accurate pass, we use the addition formula: -// atan( n/d ) - atan( idx/64 ) = atan( (n/d - idx/64)/(1 + (n*idx)/(64*d)) ) -// = atan( (n - d*(idx/64))/(d + n*(idx/64)) ) -// And for the fast pass, we use degree-9 Taylor polynomial to compute the RHS: -// atan(u) ~ P(u) = u - u^3/3 + u^5/5 - u^7/7 + u^9/9 -// with absolute errors bounded by: -// |atan(u) - P(u)| < |u|^11 / 11 < 2^-80 -// and relative errors bounded by: -// |(atan(u) - P(u)) / P(u)| < u^10 / 11 < 2^-73. - LLVM_LIBC_FUNCTION(double, atan2, (double y, double x)) { - using FPBits = fputil::FPBits<double>; - - constexpr double IS_NEG[2] = {1.0, -1.0}; - constexpr DoubleDouble ZERO = {0.0, 0.0}; - constexpr DoubleDouble MZERO = {-0.0, -0.0}; - constexpr DoubleDouble PI = {0x1.1a62633145c07p-53, 0x1.921fb54442d18p+1}; - constexpr DoubleDouble MPI = {-0x1.1a62633145c07p-53, -0x1.921fb54442d18p+1}; - constexpr DoubleDouble PI_OVER_2 = {0x1.1a62633145c07p-54, - 0x1.921fb54442d18p0}; - constexpr DoubleDouble MPI_OVER_2 = {-0x1.1a62633145c07p-54, - -0x1.921fb54442d18p0}; - constexpr DoubleDouble PI_OVER_4 = {0x1.1a62633145c07p-55, - 0x1.921fb54442d18p-1}; - constexpr DoubleDouble THREE_PI_OVER_4 = {0x1.a79394c9e8a0ap-54, - 0x1.2d97c7f3321d2p+1}; - // Adjustment for constant term: - // CONST_ADJ[x_sign][y_sign][recip] - constexpr DoubleDouble CONST_ADJ[2][2][2] = { - {{ZERO, MPI_OVER_2}, {MZERO, MPI_OVER_2}}, - {{MPI, PI_OVER_2}, {MPI, PI_OVER_2}}}; - - FPBits x_bits(x), y_bits(y); - bool x_sign = x_bits.sign().is_neg(); - bool y_sign = y_bits.sign().is_neg(); - x_bits = x_bits.abs(); - y_bits = y_bits.abs(); - uint64_t x_abs = x_bits.uintval(); - uint64_t y_abs = y_bits.uintval(); - bool recip = x_abs < y_abs; - uint64_t min_abs = recip ? x_abs : y_abs; - uint64_t max_abs = !recip ? x_abs : y_abs; - unsigned min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); - unsigned max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); - - double num = FPBits(min_abs).get_val(); - double den = FPBits(max_abs).get_val(); - - // Check for exceptional cases, whether inputs are 0, inf, nan, or close to - // overflow, or close to underflow. - if (LIBC_UNLIKELY(max_exp > 0x7ffU - 128U || min_exp < 128U)) { - if (x_bits.is_nan() || y_bits.is_nan()) { - if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan()) - fputil::raise_except_if_required(FE_INVALID); - return FPBits::quiet_nan().get_val(); - } - unsigned x_except = x == 0.0 ? 0 : (FPBits(x_abs).is_inf() ? 2 : 1); - unsigned y_except = y == 0.0 ? 0 : (FPBits(y_abs).is_inf() ? 2 : 1); - - // Exceptional cases: - // EXCEPT[y_except][x_except][x_is_neg] - // with x_except & y_except: - // 0: zero - // 1: finite, non-zero - // 2: infinity - constexpr DoubleDouble EXCEPTS[3][3][2] = { - {{ZERO, PI}, {ZERO, PI}, {ZERO, PI}}, - {{PI_OVER_2, PI_OVER_2}, {ZERO, ZERO}, {ZERO, PI}}, - {{PI_OVER_2, PI_OVER_2}, - {PI_OVER_2, PI_OVER_2}, - {PI_OVER_4, THREE_PI_OVER_4}}, - }; - - if ((x_except != 1) || (y_except != 1)) { - DoubleDouble r = EXCEPTS[y_except][x_except][x_sign]; - return fputil::multiply_add(IS_NEG[y_sign], r.hi, IS_NEG[y_sign] * r.lo); - } - bool scale_up = min_exp < 128U; - bool scale_down = max_exp > 0x7ffU - 128U; - // At least one input is denormal, multiply both numerator and denominator - // by some large enough power of 2 to normalize denormal inputs. - if (scale_up) { - num *= 0x1.0p64; - if (!scale_down) - den *= 0x1.0p64; - } else if (scale_down) { - den *= 0x1.0p-64; - if (!scale_up) - num *= 0x1.0p-64; - } - - min_abs = FPBits(num).uintval(); - max_abs = FPBits(den).uintval(); - min_exp = static_cast<unsigned>(min_abs >> FPBits::FRACTION_LEN); - max_exp = static_cast<unsigned>(max_abs >> FPBits::FRACTION_LEN); - } - - double final_sign = IS_NEG[(x_sign != y_sign) != recip]; - DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip]; - unsigned exp_diff = max_exp - min_exp; - // We have the following bound for normalized n and d: - // 2^(-exp_diff - 1) < n/d < 2^(-exp_diff + 1). - if (LIBC_UNLIKELY(exp_diff > 54)) { - return fputil::multiply_add(final_sign, const_term.hi, - final_sign * (const_term.lo + num / den)); - } - - double k = fputil::nearest_integer(64.0 * num / den); - unsigned idx = static_cast<unsigned>(k); - // k = idx / 64 - k *= 0x1.0p-6; - - // Range reduction: - // atan(n/d) - atan(k/64) = atan((n/d - k/64) / (1 + (n/d) * (k/64))) - // = atan((n - d * k/64)) / (d + n * k/64)) - DoubleDouble num_k = fputil::exact_mult(num, k); - DoubleDouble den_k = fputil::exact_mult(den, k); - - // num_dd = n - d * k - DoubleDouble num_dd = fputil::exact_add(num - den_k.hi, -den_k.lo); - // den_dd = d + n * k - DoubleDouble den_dd = fputil::exact_add(den, num_k.hi); - den_dd.lo += num_k.lo; - - // q = (n - d * k) / (d + n * k) - DoubleDouble q = fputil::div(num_dd, den_dd); - // p ~ atan(q) - DoubleDouble p = atan_eval(q); - - DoubleDouble r = fputil::add(const_term, fputil::add(ATAN_I[idx], p)); - r.hi *= final_sign; - r.lo *= final_sign; - - return r.hi + r.lo; + return math::atan2(y, x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/atan2f128.cpp b/libc/src/math/generic/atan2f128.cpp index a3aba0b..8838d94 100644 --- a/libc/src/math/generic/atan2f128.cpp +++ b/libc/src/math/generic/atan2f128.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "src/math/atan2f128.h" -#include "atan_utils.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/dyadic_float.h" #include "src/__support/FPUtil/multiply_add.h" @@ -16,6 +15,7 @@ #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/__support/macros/properties/types.h" +#include "src/__support/math/atan_utils.h" #include "src/__support/uint128.h" namespace LIBC_NAMESPACE_DECL { @@ -103,6 +103,7 @@ static constexpr Float128 CONST_ADJ[2][2][2] = { // |(atan(u) - P(u)) / P(u)| < 2^-114. LLVM_LIBC_FUNCTION(float128, atan2f128, (float128 y, float128 x)) { + using namespace atan_internal; using FPBits = fputil::FPBits<float128>; using Float128 = fputil::DyadicFloat<128>; diff --git a/libc/src/math/generic/atan2l.cpp b/libc/src/math/generic/atan2l.cpp index 47a2e985..a7824c6 100644 --- a/libc/src/math/generic/atan2l.cpp +++ b/libc/src/math/generic/atan2l.cpp @@ -9,7 +9,7 @@ #include "src/math/atan2l.h" #include "src/__support/common.h" #include "src/__support/macros/properties/types.h" -#include "src/math/atan2.h" +#include "src/__support/math/atan2.h" namespace LIBC_NAMESPACE_DECL { @@ -17,7 +17,7 @@ namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(long double, atan2l, (long double y, long double x)) { #if defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) return static_cast<long double>( - atan2(static_cast<double>(y), static_cast<double>(x))); + math::atan2(static_cast<double>(y), static_cast<double>(x))); #else #error "Extended precision is not yet supported" #endif diff --git a/libc/src/math/generic/atanf.cpp b/libc/src/math/generic/atanf.cpp index 22f962e..acd32f0 100644 --- a/libc/src/math/generic/atanf.cpp +++ b/libc/src/math/generic/atanf.cpp @@ -7,116 +7,10 @@ //===----------------------------------------------------------------------===// #include "src/math/atanf.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/PolyEval.h" -#include "src/__support/FPUtil/except_value_utils.h" -#include "src/__support/FPUtil/multiply_add.h" -#include "src/__support/FPUtil/nearest_integer.h" -#include "src/__support/FPUtil/rounding_mode.h" -#include "src/__support/macros/config.h" -#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY -#include "src/__support/math/inv_trigf_utils.h" +#include "src/__support/math/atanf.h" namespace LIBC_NAMESPACE_DECL { -LLVM_LIBC_FUNCTION(float, atanf, (float x)) { - using namespace inv_trigf_utils_internal; - using FPBits = typename fputil::FPBits<float>; - - constexpr double FINAL_SIGN[2] = {1.0, -1.0}; - constexpr double SIGNED_PI_OVER_2[2] = {0x1.921fb54442d18p0, - -0x1.921fb54442d18p0}; - - FPBits x_bits(x); - Sign sign = x_bits.sign(); - x_bits.set_sign(Sign::POS); - uint32_t x_abs = x_bits.uintval(); - - // x is inf or nan, |x| < 2^-4 or |x|= > 16. - if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U || x_abs >= 0x4180'0000U)) { - double x_d = static_cast<double>(x); - double const_term = 0.0; - if (LIBC_UNLIKELY(x_abs >= 0x4180'0000)) { - // atan(+-Inf) = +-pi/2. - if (x_bits.is_inf()) { - volatile double sign_pi_over_2 = SIGNED_PI_OVER_2[sign.is_neg()]; - return static_cast<float>(sign_pi_over_2); - } - if (x_bits.is_nan()) - return x; - // x >= 16 - x_d = -1.0 / x_d; - const_term = SIGNED_PI_OVER_2[sign.is_neg()]; - } - // 0 <= x < 1/16; - if (LIBC_UNLIKELY(x_bits.is_zero())) - return x; - // x <= 2^-12; - if (LIBC_UNLIKELY(x_abs < 0x3980'0000)) { -#if defined(LIBC_TARGET_CPU_HAS_FMA_FLOAT) - return fputil::multiply_add(x, -0x1.0p-25f, x); -#else - double x_d = static_cast<double>(x); - return static_cast<float>(fputil::multiply_add(x_d, -0x1.0p-25, x_d)); -#endif // LIBC_TARGET_CPU_HAS_FMA_FLOAT - } - // Use Taylor polynomial: - // atan(x) ~ x * (1 - x^2 / 3 + x^4 / 5 - x^6 / 7 + x^8 / 9 - x^10 / 11). - constexpr double ATAN_TAYLOR[6] = { - 0x1.0000000000000p+0, -0x1.5555555555555p-2, 0x1.999999999999ap-3, - -0x1.2492492492492p-3, 0x1.c71c71c71c71cp-4, -0x1.745d1745d1746p-4, - }; - double x2 = x_d * x_d; - double x4 = x2 * x2; - double c0 = fputil::multiply_add(x2, ATAN_TAYLOR[1], ATAN_TAYLOR[0]); - double c1 = fputil::multiply_add(x2, ATAN_TAYLOR[3], ATAN_TAYLOR[2]); - double c2 = fputil::multiply_add(x2, ATAN_TAYLOR[5], ATAN_TAYLOR[4]); - double p = fputil::polyeval(x4, c0, c1, c2); - double r = fputil::multiply_add(x_d, p, const_term); - return static_cast<float>(r); - } - - // Range reduction steps: - // 1) atan(x) = sign(x) * atan(|x|) - // 2) If |x| > 1, atan(|x|) = pi/2 - atan(1/|x|) - // 3) For 1/16 < x <= 1, we find k such that: |x - k/16| <= 1/32. - // 4) Then we use polynomial approximation: - // atan(x) ~ atan((k/16) + (x - (k/16)) * Q(x - k/16) - // = P(x - k/16) - double x_d, const_term, final_sign; - int idx; - - if (x_abs > 0x3f80'0000U) { - // |x| > 1, we need to invert x, so we will perform range reduction in - // double precision. - x_d = 1.0 / static_cast<double>(x_bits.get_val()); - double k_d = fputil::nearest_integer(x_d * 0x1.0p4); - x_d = fputil::multiply_add(k_d, -0x1.0p-4, x_d); - idx = static_cast<int>(k_d); - final_sign = FINAL_SIGN[sign.is_pos()]; - // Adjust constant term of the polynomial by +- pi/2. - const_term = fputil::multiply_add(final_sign, ATAN_COEFFS[idx][0], - SIGNED_PI_OVER_2[sign.is_neg()]); - } else { - // Exceptional value: - if (LIBC_UNLIKELY(x_abs == 0x3d8d'6b23U)) { // |x| = 0x1.1ad646p-4 - return sign.is_pos() ? fputil::round_result_slightly_down(0x1.1a6386p-4f) - : fputil::round_result_slightly_up(-0x1.1a6386p-4f); - } - // Perform range reduction in single precision. - float x_f = x_bits.get_val(); - float k_f = fputil::nearest_integer(x_f * 0x1.0p4f); - x_f = fputil::multiply_add(k_f, -0x1.0p-4f, x_f); - x_d = static_cast<double>(x_f); - idx = static_cast<int>(k_f); - final_sign = FINAL_SIGN[sign.is_neg()]; - const_term = final_sign * ATAN_COEFFS[idx][0]; - } - - double p = atan_eval(x_d, idx); - double r = fputil::multiply_add(final_sign * x_d, p, const_term); - - return static_cast<float>(r); -} +LLVM_LIBC_FUNCTION(float, atanf, (float x)) { return math::atanf(x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/atanf16.cpp b/libc/src/math/generic/atanf16.cpp index 9b6ec65..7191c42 100644 --- a/libc/src/math/generic/atanf16.cpp +++ b/libc/src/math/generic/atanf16.cpp @@ -7,101 +7,10 @@ //===----------------------------------------------------------------------===// #include "src/math/atanf16.h" -#include "hdr/errno_macros.h" -#include "hdr/fenv_macros.h" -#include "src/__support/FPUtil/FEnvImpl.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/FPUtil/PolyEval.h" -#include "src/__support/FPUtil/cast.h" -#include "src/__support/FPUtil/except_value_utils.h" -#include "src/__support/FPUtil/multiply_add.h" -#include "src/__support/FPUtil/sqrt.h" -#include "src/__support/macros/optimization.h" +#include "src/__support/math/atanf16.h" namespace LIBC_NAMESPACE_DECL { -// Generated by Solly using the following command: -// > round(pi/2, SG, RN); -static constexpr float PI_2 = 0x1.921fb6p0; - -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS -static constexpr size_t N_EXCEPTS = 6; - -static constexpr fputil::ExceptValues<float16, N_EXCEPTS> ATANF16_EXCEPTS{{ - // (input, RZ output, RU offset, RD offset, RN offset) - {0x2745, 0x2744, 1, 0, 1}, - {0x3099, 0x3090, 1, 0, 1}, - {0x3c6c, 0x3aae, 1, 0, 1}, - {0x466e, 0x3daa, 1, 0, 1}, - {0x48ae, 0x3ddb, 1, 0, 0}, - {0x5619, 0x3e3d, 1, 0, 1}, -}}; -#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS - -LLVM_LIBC_FUNCTION(float16, atanf16, (float16 x)) { - using FPBits = fputil::FPBits<float16>; - FPBits xbits(x); - - uint16_t x_u = xbits.uintval(); - uint16_t x_abs = x_u & 0x7fff; - bool x_sign = x_u >> 15; - float sign = (x_sign ? -1.0 : 1.0); - - // |x| >= +/-inf - if (LIBC_UNLIKELY(x_abs >= 0x7c00)) { - if (xbits.is_nan()) { - if (xbits.is_signaling_nan()) { - fputil::raise_except_if_required(FE_INVALID); - return FPBits::quiet_nan().get_val(); - } - return x; - } - - // atanf16(+/-inf) = +/-pi/2 - return fputil::cast<float16>(sign * PI_2); - } - - float xf = x; - float xsq = xf * xf; -#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS - // Handle exceptional values - if (auto r = ATANF16_EXCEPTS.lookup_odd(x_abs, x_sign); - LIBC_UNLIKELY(r.has_value())) - return r.value(); -#endif - - // |x| <= 0x1p0, |x| <= 1 - if (x_abs <= 0x3c00) { - // atanf16(+/-0) = +/-0 - if (LIBC_UNLIKELY(x_abs == 0)) - return x; - - // Degree-14 minimax odd polynomial of atan(x) generated by Sollya with: - // > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|], - // [0, 1]); - float result = fputil::polyeval( - xsq, 0x1.fffffcp-1f, -0x1.55519ep-2f, 0x1.98f6a8p-3f, -0x1.1f0a92p-3f, - 0x1.95b654p-4f, -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f); - return fputil::cast<float16>(xf * result); - } - - // If |x| > 1 - // y = atan(x) = sign(x) * atan(|x|) - // atan(|x|) = pi/2 - atan(1/|x|) - // Recall, 1/|x| < 1 - float x_inv_sq = 1.0f / xsq; - float x_inv = fputil::sqrt<float>(x_inv_sq); - - // Degree-14 minimax odd polynomial of atan(x) generated by Sollya with: - // > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|], - // [0, 1]); - float interm = - fputil::polyeval(x_inv_sq, 0x1.fffffcp-1f, -0x1.55519ep-2f, - 0x1.98f6a8p-3f, -0x1.1f0a92p-3f, 0x1.95b654p-4f, - -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f); - - return fputil::cast<float16>(sign * - fputil::multiply_add(x_inv, -interm, PI_2)); -} +LLVM_LIBC_FUNCTION(float16, atanf16, (float16 x)) { return math::atanf16(x); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/generic/fabsbf16.cpp b/libc/src/math/generic/fabsbf16.cpp new file mode 100644 index 0000000..ea39719 --- /dev/null +++ b/libc/src/math/generic/fabsbf16.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of fabsbf16 function -------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/math/fabsbf16.h" + +#include "src/__support/FPUtil/BasicOperations.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(bfloat16, fabsbf16, (bfloat16 x)) { return fputil::abs(x); } + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt index c5db6fa..fe31e6a 100644 --- a/libc/src/pthread/CMakeLists.txt +++ b/libc/src/pthread/CMakeLists.txt @@ -273,6 +273,40 @@ add_entrypoint_object( ) add_entrypoint_object( + pthread_barrier_init + SRCS + pthread_barrier_init.cpp + HDRS + pthread_barrier_init.h + DEPENDS + libc.src.errno.errno + libc.include.pthread + libc.src.__support.threads.linux.barrier +) + +add_entrypoint_object( + pthread_barrier_destroy + SRCS + pthread_barrier_destroy.cpp + HDRS + pthread_barrier_destroy.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.linux.barrier +) + +add_entrypoint_object( + pthread_barrier_wait + SRCS + pthread_barrier_wait.cpp + HDRS + pthread_barrier_wait.h + DEPENDS + libc.include.pthread + libc.src.__support.threads.linux.barrier +) + +add_entrypoint_object( pthread_mutex_init SRCS pthread_mutex_init.cpp diff --git a/libc/src/pthread/pthread_barrier_destroy.cpp b/libc/src/pthread/pthread_barrier_destroy.cpp new file mode 100644 index 0000000..82de8f2 --- /dev/null +++ b/libc/src/pthread/pthread_barrier_destroy.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of the pthread_barrier_destroy function ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_barrier_destroy.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/linux/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_destroy, (pthread_barrier_t * b)) { + return Barrier::destroy(reinterpret_cast<Barrier *>(b)); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_destroy.h b/libc/src/pthread/pthread_barrier_destroy.h new file mode 100644 index 0000000..e27552c --- /dev/null +++ b/libc/src/pthread/pthread_barrier_destroy.h @@ -0,0 +1,21 @@ +//===-- Implementation header for pthread_barrier_destroy --------*- 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_PTHREAD_PTHREAD_BARRIER_DESTROY_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_DESTROY_H + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_destroy(pthread_barrier_t *b); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_DESTROY_H diff --git a/libc/src/pthread/pthread_barrier_init.cpp b/libc/src/pthread/pthread_barrier_init.cpp new file mode 100644 index 0000000..2e92238 --- /dev/null +++ b/libc/src/pthread/pthread_barrier_init.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of the pthread_barrier_init function ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_barrier_init.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "hdr/types/pthread_barrierattr_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/linux/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_init, + (pthread_barrier_t * b, + const pthread_barrierattr_t *__restrict attr, + unsigned count)) { + return Barrier::init(reinterpret_cast<Barrier *>(b), attr, count); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_init.h b/libc/src/pthread/pthread_barrier_init.h new file mode 100644 index 0000000..bb17f3f --- /dev/null +++ b/libc/src/pthread/pthread_barrier_init.h @@ -0,0 +1,24 @@ +//===-- Implementation header for pthread_barrier_init ----------*- 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_PTHREAD_PTHREAD_BARRIER_INIT_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_INIT_H + +#include "hdr/types/pthread_barrier_t.h" +#include "hdr/types/pthread_barrierattr_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_init(pthread_barrier_t *b, + const pthread_barrierattr_t *__restrict attr, + unsigned count); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_INIT_H diff --git a/libc/src/pthread/pthread_barrier_wait.cpp b/libc/src/pthread/pthread_barrier_wait.cpp new file mode 100644 index 0000000..dbd1333 --- /dev/null +++ b/libc/src/pthread/pthread_barrier_wait.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of the pthread_barrier_wait function ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "pthread_barrier_wait.h" + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/threads/linux/barrier.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_barrier_wait, (pthread_barrier_t * b)) { + return reinterpret_cast<Barrier *>(b)->wait(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pthread/pthread_barrier_wait.h b/libc/src/pthread/pthread_barrier_wait.h new file mode 100644 index 0000000..16ddc06 --- /dev/null +++ b/libc/src/pthread/pthread_barrier_wait.h @@ -0,0 +1,21 @@ +//===-- Implementation header for pthread_barrier_wait ----------*- 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_PTHREAD_PTHREAD_BARRIER_WAIT_H +#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_WAIT_H + +#include "hdr/types/pthread_barrier_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_barrier_wait(pthread_barrier_t *b); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_BARRIER_WAIT_H diff --git a/libc/src/sched/CMakeLists.txt b/libc/src/sched/CMakeLists.txt index e6c37d3..d1d1de0 100644 --- a/libc/src/sched/CMakeLists.txt +++ b/libc/src/sched/CMakeLists.txt @@ -3,6 +3,13 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() add_entrypoint_object( + getcpu + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.getcpu +) + +add_entrypoint_object( sched_getaffinity ALIAS DEPENDS diff --git a/libc/src/sched/getcpu.h b/libc/src/sched/getcpu.h new file mode 100644 index 0000000..4c90e64 --- /dev/null +++ b/libc/src/sched/getcpu.h @@ -0,0 +1,20 @@ +//===-- Implementation header for getcpu ------------------------*- 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_SCHED_GETCPU_H +#define LLVM_LIBC_SRC_SCHED_GETCPU_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int getcpu(unsigned int *cpu, unsigned int *node); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SCHED_GETCPU_H diff --git a/libc/src/sched/linux/CMakeLists.txt b/libc/src/sched/linux/CMakeLists.txt index e690e76..bb50002 100644 --- a/libc/src/sched/linux/CMakeLists.txt +++ b/libc/src/sched/linux/CMakeLists.txt @@ -1,4 +1,16 @@ add_entrypoint_object( + getcpu + SRCS + getcpu.cpp + HDRS + ../getcpu.h + DEPENDS + libc.include.sched + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( sched_getaffinity SRCS sched_getaffinity.cpp @@ -6,7 +18,9 @@ add_entrypoint_object( ../sched_getaffinity.h DEPENDS libc.hdr.stdint_proxy - libc.include.sched + libc.hdr.types.cpu_set_t + libc.hdr.types.pid_t + libc.hdr.types.size_t libc.src.__support.OSUtil.osutil libc.src.errno.errno ) @@ -18,7 +32,9 @@ add_entrypoint_object( HDRS ../sched_setaffinity.h DEPENDS - libc.include.sched + libc.hdr.types.cpu_set_t + libc.hdr.types.pid_t + libc.hdr.types.size_t libc.src.__support.OSUtil.osutil libc.src.errno.errno ) @@ -30,7 +46,8 @@ add_entrypoint_object( HDRS ../sched_getcpucount.h DEPENDS - libc.include.sched + libc.hdr.types.cpu_set_t + libc.hdr.types.size_t ) add_entrypoint_object( @@ -94,7 +111,7 @@ add_entrypoint_object( HDRS ../sched_getscheduler.h DEPENDS - libc.include.sched + libc.hdr.types.pid_t libc.include.sys_syscall libc.src.__support.OSUtil.osutil libc.src.errno.errno @@ -131,8 +148,9 @@ add_entrypoint_object( HDRS ../sched_rr_get_interval.h DEPENDS + libc.hdr.types.pid_t + libc.hdr.types.struct_timespec libc.include.sys_syscall - libc.include.sched libc.src.__support.OSUtil.osutil libc.src.errno.errno ) diff --git a/libc/src/sched/linux/getcpu.cpp b/libc/src/sched/linux/getcpu.cpp new file mode 100644 index 0000000..a34b693 --- /dev/null +++ b/libc/src/sched/linux/getcpu.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of getcpu ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/sched/getcpu.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" + +#include <sys/syscall.h> // For syscall numbers. + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, getcpu, (unsigned int *cpu, unsigned int *node)) { + int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_getcpu, cpu, node, nullptr); + if (ret < 0) { + libc_errno = -ret; + return -1; + } + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sched/linux/sched_getaffinity.cpp b/libc/src/sched/linux/sched_getaffinity.cpp index 4a5e91a..d652f7f7 100644 --- a/libc/src/sched/linux/sched_getaffinity.cpp +++ b/libc/src/sched/linux/sched_getaffinity.cpp @@ -14,7 +14,9 @@ #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" -#include <sched.h> +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" +#include "hdr/types/size_t.h" #include <sys/syscall.h> // For syscall numbers. namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/linux/sched_getcpucount.cpp b/libc/src/sched/linux/sched_getcpucount.cpp index 7ae166e..dcc2338 100644 --- a/libc/src/sched/linux/sched_getcpucount.cpp +++ b/libc/src/sched/linux/sched_getcpucount.cpp @@ -12,7 +12,8 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#include <sched.h> +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/size_t.h" #include <stddef.h> namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/linux/sched_getscheduler.cpp b/libc/src/sched/linux/sched_getscheduler.cpp index d8e0296..10625f2 100644 --- a/libc/src/sched/linux/sched_getscheduler.cpp +++ b/libc/src/sched/linux/sched_getscheduler.cpp @@ -13,6 +13,7 @@ #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" +#include "hdr/types/pid_t.h" #include <sys/syscall.h> // For syscall numbers. namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/linux/sched_rr_get_interval.cpp b/libc/src/sched/linux/sched_rr_get_interval.cpp index 5668d596b..eecbaa4 100644 --- a/libc/src/sched/linux/sched_rr_get_interval.cpp +++ b/libc/src/sched/linux/sched_rr_get_interval.cpp @@ -13,6 +13,8 @@ #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" +#include "hdr/types/pid_t.h" +#include "hdr/types/struct_timespec.h" #include <sys/syscall.h> // For syscall numbers. #ifdef SYS_sched_rr_get_interval_time64 diff --git a/libc/src/sched/linux/sched_setaffinity.cpp b/libc/src/sched/linux/sched_setaffinity.cpp index 93e930d..3c7ed91 100644 --- a/libc/src/sched/linux/sched_setaffinity.cpp +++ b/libc/src/sched/linux/sched_setaffinity.cpp @@ -13,7 +13,9 @@ #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" -#include <sched.h> +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" +#include "hdr/types/size_t.h" #include <sys/syscall.h> // For syscall numbers. namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/sched_getaffinity.h b/libc/src/sched/sched_getaffinity.h index 52ec5bc..8623089 100644 --- a/libc/src/sched/sched_getaffinity.h +++ b/libc/src/sched/sched_getaffinity.h @@ -10,7 +10,10 @@ #define LLVM_LIBC_SRC_SCHED_SCHED_GETAFFINITY_H #include "src/__support/macros/config.h" -#include <sched.h> + +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" +#include "hdr/types/size_t.h" namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/sched_getcpucount.h b/libc/src/sched/sched_getcpucount.h index 8f35301..0667d8c 100644 --- a/libc/src/sched/sched_getcpucount.h +++ b/libc/src/sched/sched_getcpucount.h @@ -10,7 +10,8 @@ #define LLVM_LIBC_SRC_SCHED_SCHED_GETCPUCOUNT_H #include "src/__support/macros/config.h" -#include <sched.h> + +#include "hdr/types/cpu_set_t.h" #include <stddef.h> namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/sched_getscheduler.h b/libc/src/sched/sched_getscheduler.h index d29e902..6407dbf 100644 --- a/libc/src/sched/sched_getscheduler.h +++ b/libc/src/sched/sched_getscheduler.h @@ -10,7 +10,8 @@ #define LLVM_LIBC_SRC_SCHED_SCHED_GETSCHEDULER_H #include "src/__support/macros/config.h" -#include <sched.h> + +#include "hdr/types/pid_t.h" namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/sched_rr_get_interval.h b/libc/src/sched/sched_rr_get_interval.h index ff09329..4195c14 100644 --- a/libc/src/sched/sched_rr_get_interval.h +++ b/libc/src/sched/sched_rr_get_interval.h @@ -10,7 +10,9 @@ #define LLVM_LIBC_SRC_SCHED_SCHED_RR_GET_INTERVAL_H #include "src/__support/macros/config.h" -#include <sched.h> + +#include "hdr/types/pid_t.h" +#include "hdr/types/struct_timespec.h" namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/sched/sched_setaffinity.h b/libc/src/sched/sched_setaffinity.h index cb2303d..f6739ab 100644 --- a/libc/src/sched/sched_setaffinity.h +++ b/libc/src/sched/sched_setaffinity.h @@ -10,7 +10,10 @@ #define LLVM_LIBC_SRC_SCHED_SCHED_SETAFFINITY_H #include "src/__support/macros/config.h" -#include <sched.h> + +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" +#include "hdr/types/size_t.h" namespace LIBC_NAMESPACE_DECL { diff --git a/libc/src/wchar/CMakeLists.txt b/libc/src/wchar/CMakeLists.txt index 49f4a1b..9ba0a06 100644 --- a/libc/src/wchar/CMakeLists.txt +++ b/libc/src/wchar/CMakeLists.txt @@ -185,6 +185,55 @@ add_entrypoint_object( ) add_entrypoint_object( + mbstowcs + SRCS + mbstowcs.cpp + HDRS + mbstowcs.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.wchar_t + libc.src.__support.common + libc.src.__support.macros.config + libc.src.__support.macros.null_check + libc.src.__support.libc_errno + libc.src.__support.wchar.mbstate + libc.src.__support.wchar.mbsnrtowcs +) + +add_entrypoint_object( + mbsrtowcs + SRCS + mbsrtowcs.cpp + HDRS + mbsrtowcs.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.wchar_t + libc.src.__support.common + libc.src.__support.macros.config + libc.src.__support.libc_errno + libc.src.__support.wchar.mbstate + libc.src.__support.wchar.mbsnrtowcs +) + +add_entrypoint_object( + mbsnrtowcs + SRCS + mbsnrtowcs.cpp + HDRS + mbsnrtowcs.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.wchar_t + libc.src.__support.common + libc.src.__support.macros.config + libc.src.__support.libc_errno + libc.src.__support.wchar.mbstate + libc.src.__support.wchar.mbsnrtowcs +) + +add_entrypoint_object( wcstombs SRCS wcstombs.cpp diff --git a/libc/src/wchar/mbsnrtowcs.cpp b/libc/src/wchar/mbsnrtowcs.cpp new file mode 100644 index 0000000..28e0ff3 --- /dev/null +++ b/libc/src/wchar/mbsnrtowcs.cpp @@ -0,0 +1,39 @@ +//===-- Implementation of mbsnrtowcs --------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/wchar/mbsnrtowcs.h" + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/__support/wchar/mbsnrtowcs.h" +#include "src/__support/wchar/mbstate.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(size_t, mbsnrtowcs, + (wchar_t *__restrict dst, const char **__restrict src, + size_t nmc, size_t len, mbstate_t *__restrict ps)) { + static internal::mbstate internal_mbstate; + // If destination is null, ignore len + len = dst == nullptr ? SIZE_MAX : len; + auto ret = internal::mbsnrtowcs( + dst, src, nmc, len, + ps == nullptr ? &internal_mbstate + : reinterpret_cast<internal::mbstate *>(ps)); + if (!ret.has_value()) { + // Encoding failure + libc_errno = ret.error(); + return -1; + } + return ret.value(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/mbsnrtowcs.h b/libc/src/wchar/mbsnrtowcs.h new file mode 100644 index 0000000..0d66b95 --- /dev/null +++ b/libc/src/wchar/mbsnrtowcs.h @@ -0,0 +1,24 @@ +//===-- Implementation header for mbsnrtowcs ------------------------------===// +// +// 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_WCHAR_MBSNRTOWCS_H +#define LLVM_LIBC_SRC_WCHAR_MBSNRTOWCS_H + +#include "hdr/types/mbstate_t.h" +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +size_t mbsnrtowcs(wchar_t *__restrict dst, const char **__restrict src, + size_t nmc, size_t len, mbstate_t *__restrict ps); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_MBSNRTOWCS_H diff --git a/libc/src/wchar/mbsrtowcs.cpp b/libc/src/wchar/mbsrtowcs.cpp new file mode 100644 index 0000000..82ca25a --- /dev/null +++ b/libc/src/wchar/mbsrtowcs.cpp @@ -0,0 +1,39 @@ +//===-- Implementation of mbsrtowcs ---------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/wchar/mbsrtowcs.h" + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/__support/wchar/mbsnrtowcs.h" +#include "src/__support/wchar/mbstate.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(size_t, mbsrtowcs, + (wchar_t *__restrict dst, const char **__restrict src, + size_t len, mbstate_t *__restrict ps)) { + static internal::mbstate internal_mbstate; + // If destination is null, ignore len + len = dst == nullptr ? SIZE_MAX : len; + auto ret = internal::mbsnrtowcs( + dst, src, SIZE_MAX, len, + ps == nullptr ? &internal_mbstate + : reinterpret_cast<internal::mbstate *>(ps)); + if (!ret.has_value()) { + // Encoding failure + libc_errno = ret.error(); + return -1; + } + return ret.value(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/mbsrtowcs.h b/libc/src/wchar/mbsrtowcs.h new file mode 100644 index 0000000..f8d4cc2 --- /dev/null +++ b/libc/src/wchar/mbsrtowcs.h @@ -0,0 +1,24 @@ +//===-- Implementation header for mbsrtowcs -------------------------------===// +// +// 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_WCHAR_MBSRTOWCS_H +#define LLVM_LIBC_SRC_WCHAR_MBSRTOWCS_H + +#include "hdr/types/mbstate_t.h" +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +size_t mbsrtowcs(wchar_t *__restrict dst, const char **__restrict src, + size_t len, mbstate_t *__restrict ps); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_MBSRTOWCS_H diff --git a/libc/src/wchar/mbstowcs.cpp b/libc/src/wchar/mbstowcs.cpp new file mode 100644 index 0000000..43e953c --- /dev/null +++ b/libc/src/wchar/mbstowcs.cpp @@ -0,0 +1,40 @@ +//===-- Implementation of mbstowcs ----------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/wchar/mbstowcs.h" + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/null_check.h" +#include "src/__support/wchar/mbsnrtowcs.h" +#include "src/__support/wchar/mbstate.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(size_t, mbstowcs, + (wchar_t *__restrict pwcs, const char *__restrict s, + size_t n)) { + LIBC_CRASH_ON_NULLPTR(s); + // If destination is null, ignore n + n = pwcs == nullptr ? SIZE_MAX : n; + static internal::mbstate internal_mbstate; + const char *temp = s; + auto ret = internal::mbsnrtowcs(pwcs, &temp, SIZE_MAX, n, &internal_mbstate); + + if (!ret.has_value()) { + // Encoding failure + libc_errno = ret.error(); + return -1; + } + return ret.value(); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/wchar/mbstowcs.h b/libc/src/wchar/mbstowcs.h new file mode 100644 index 0000000..7d08a83 --- /dev/null +++ b/libc/src/wchar/mbstowcs.h @@ -0,0 +1,22 @@ +//===-- Implementation header for mbstowcs --------------------------------===// +// +// 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_WCHAR_MBSTOWCS_H +#define LLVM_LIBC_SRC_WCHAR_MBSTOWCS_H + +#include "hdr/types/size_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +size_t mbstowcs(wchar_t *__restrict pwcs, const char *__restrict s, size_t n); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_WCHAR_MBSTOWCS_H diff --git a/libc/startup/baremetal/CMakeLists.txt b/libc/startup/baremetal/CMakeLists.txt index 276fe33..e361000 100644 --- a/libc/startup/baremetal/CMakeLists.txt +++ b/libc/startup/baremetal/CMakeLists.txt @@ -1,3 +1,34 @@ +# TODO: Use generic "add_startup_object" https://github.com/llvm/llvm-project/issues/133156 +function(add_startup_object name) + cmake_parse_arguments( + "ADD_STARTUP_OBJECT" + "ALIAS" # Option argument + "SRC" # Single value arguments + "DEPENDS;COMPILE_OPTIONS" # Multi value arguments + ${ARGN} + ) + + get_fq_target_name(${name} fq_target_name) + if(ADD_STARTUP_OBJECT_ALIAS) + get_fq_deps_list(fq_dep_list ${ADD_STARTUP_OBJECT_DEPENDS}) + add_library(${fq_target_name} ALIAS ${fq_dep_list}) + return() + endif() + + add_object_library( + ${name} + SRCS ${ADD_STARTUP_OBJECT_SRC} + COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS} + ${ADD_STARTUP_OBJECT_UNPARSED_ARGUMENTS} + DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS} + ) + set_target_properties( + ${fq_target_name} + PROPERTIES + OUTPUT_NAME ${name}.o + ) +endfunction() + add_entrypoint_object( init SRCS @@ -5,6 +36,8 @@ add_entrypoint_object( DEPENDS libc.hdr.stdint_proxy libc.src.__support.common + HDRS + init.h ) add_entrypoint_object( @@ -14,4 +47,29 @@ add_entrypoint_object( DEPENDS libc.hdr.stdint_proxy libc.src.__support.common + HDRS + fini.h ) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE}) + add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) +else() + message(WARNING "Cannot build 'crt1.o' for ${LIBC_TARGET_ARCHITECTURE} yet.") + return() +endif() + +add_startup_object( + crt1 + ALIAS + DEPENDS + .${LIBC_TARGET_ARCHITECTURE}.crt1 +) + +add_custom_target(libc-startup) + +set(fq_target_name libc.startup.baremetal.${LIBC_TARGET_ARCHITECTURE}.crt1) +add_dependencies(libc-startup ${fq_target_name}) +install(FILES $<TARGET_OBJECTS:${fq_target_name}> + DESTINATION ${LIBC_INSTALL_LIBRARY_DIR} + RENAME $<TARGET_PROPERTY:${fq_target_name},OUTPUT_NAME> + COMPONENT libc) diff --git a/libc/startup/baremetal/arm/CMakeLists.txt b/libc/startup/baremetal/arm/CMakeLists.txt new file mode 100644 index 0000000..f75bd89 --- /dev/null +++ b/libc/startup/baremetal/arm/CMakeLists.txt @@ -0,0 +1,16 @@ +add_startup_object( + crt1 + SRC + start.cpp + DEPENDS + libc.src.stdlib.atexit + libc.src.stdlib.exit + libc.src.string.memcpy + libc.src.string.memset + libc.startup.baremetal.init + libc.startup.baremetal.fini + COMPILE_OPTIONS + -ffreestanding # To avoid compiler warnings about calling the main function. + -fno-builtin + -Wno-global-constructors # To allow vector table initialization +) diff --git a/libc/startup/baremetal/arm/start.cpp b/libc/startup/baremetal/arm/start.cpp new file mode 100644 index 0000000..123efc4 --- /dev/null +++ b/libc/startup/baremetal/arm/start.cpp @@ -0,0 +1,92 @@ +//===-- Implementation of crt for arm -------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/macros/config.h" +#include "src/stdlib/atexit.h" +#include "src/stdlib/exit.h" +#include "src/string/memcpy.h" +#include "src/string/memset.h" +#include "startup/baremetal/fini.h" +#include "startup/baremetal/init.h" + +#include <stdint.h> + +extern "C" { +int main(int argc, char **argv); +void _start(); + +// Semihosting library initialisation if applicable. Required for printf, etc. +[[gnu::weak]] void _platform_init() {} + +// These symbols are provided by the linker. The exact names are not defined by +// a standard. +extern uintptr_t __stack; +extern uintptr_t __data_source[]; +extern uintptr_t __data_start[]; +extern uintptr_t __data_size[]; +extern uintptr_t __bss_start[]; +extern uintptr_t __bss_size[]; + +// Based on +// https://developer.arm.com/documentation/107565/0101/Use-case-examples/Generic-Information/What-is-inside-a-program-image-/Vector-table +void NMI_Handler() {} +void HardFault_Handler() { LIBC_NAMESPACE::exit(1); } +void MemManage_Handler() { LIBC_NAMESPACE::exit(1); } +void BusFault_Handler() { LIBC_NAMESPACE::exit(1); } +void UsageFault_Handler() { LIBC_NAMESPACE::exit(1); } +void SVC_Handler() {} +void DebugMon_Handler() {} +void PendSV_Handler() {} +void SysTick_Handler() {} + +// Architecturally the bottom 7 bits of VTOR are zero, meaning the vector table +// has to be 128-byte aligned, however an implementation can require more bits +// to be zero and Cortex-M23 can require up to 10, so 1024-byte align the vector +// table. +using HandlerType = void (*)(void); +const HandlerType vector_table[] + __attribute__((section(".vectors"), aligned(1024), used)) = { + (HandlerType)&__stack, // SP + _start, // Reset + NMI_Handler, // NMI Handler + HardFault_Handler, // Hard Fault Handlerß + MemManage_Handler, // MPU Fault Han`dler + BusFault_Handler, // Bus Fault Handler + UsageFault_Handler, // Usage Fault Handler + 0, // Reserved + 0, // Reserved + 0, // Reserved + 0, // Reserved + SVC_Handler, // SVC Handler + DebugMon_Handler, // Debug Monitor Handler + 0, // Reserved + PendSV_Handler, // PendSV Handler + SysTick_Handler, // SysTick Handler + // Unused +}; +} // extern "C" + +namespace LIBC_NAMESPACE_DECL { +[[noreturn]] void do_start() { + // FIXME: set up the QEMU test environment + + // Perform the equivalent of scatterloading + LIBC_NAMESPACE::memcpy(__data_start, __data_source, (uintptr_t)__data_size); + LIBC_NAMESPACE::memset(__bss_start, '\0', (uintptr_t)__bss_size); + __libc_init_array(); + + _platform_init(); + LIBC_NAMESPACE::atexit(&__libc_fini_array); + LIBC_NAMESPACE::exit(main(0, 0)); +} +} // namespace LIBC_NAMESPACE_DECL + +extern "C" void _start() { + asm volatile("mov sp, %0" : : "r"(&__stack)); + asm volatile("bl %0" : : "X"(LIBC_NAMESPACE::do_start)); +} diff --git a/libc/startup/baremetal/fini.cpp b/libc/startup/baremetal/fini.cpp index 64af842..66714e2 100644 --- a/libc/startup/baremetal/fini.cpp +++ b/libc/startup/baremetal/fini.cpp @@ -6,17 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "hdr/stdint_proxy.h" +#include "startup/baremetal/fini.h" + #include "src/__support/macros/config.h" #include <stddef.h> namespace LIBC_NAMESPACE_DECL { -extern "C" { -extern uintptr_t __fini_array_start[]; -extern uintptr_t __fini_array_end[]; -} - using FiniCallback = void(void); extern "C" void __libc_fini_array(void) { diff --git a/libc/startup/baremetal/fini.h b/libc/startup/baremetal/fini.h new file mode 100644 index 0000000..74e9601 --- /dev/null +++ b/libc/startup/baremetal/fini.h @@ -0,0 +1,16 @@ +//===-- Implementation header of __libc_fini_array ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/stdint_proxy.h" + +extern "C" { +extern uintptr_t __fini_array_start[]; +extern uintptr_t __fini_array_end[]; + +void __libc_fini_array(void); +} // extern "C" diff --git a/libc/startup/baremetal/init.cpp b/libc/startup/baremetal/init.cpp index 995609c..89065fd 100644 --- a/libc/startup/baremetal/init.cpp +++ b/libc/startup/baremetal/init.cpp @@ -6,19 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "hdr/stdint_proxy.h" +#include "startup/baremetal/init.h" + #include "src/__support/macros/config.h" #include <stddef.h> namespace LIBC_NAMESPACE_DECL { -extern "C" { -extern uintptr_t __preinit_array_start[]; -extern uintptr_t __preinit_array_end[]; -extern uintptr_t __init_array_start[]; -extern uintptr_t __init_array_end[]; -} - using InitCallback = void(void); extern "C" void __libc_init_array(void) { diff --git a/libc/startup/baremetal/init.h b/libc/startup/baremetal/init.h new file mode 100644 index 0000000..6b545db3 --- /dev/null +++ b/libc/startup/baremetal/init.h @@ -0,0 +1,18 @@ +//===-- Implementation header of __libc_init_array ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/stdint_proxy.h" + +extern "C" { +extern uintptr_t __preinit_array_start[]; +extern uintptr_t __preinit_array_end[]; +extern uintptr_t __init_array_start[]; +extern uintptr_t __init_array_end[]; + +void __libc_init_array(void); +} // extern "C" diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt index 0bdd99c..251b009 100644 --- a/libc/test/integration/src/pthread/CMakeLists.txt +++ b/libc/test/integration/src/pthread/CMakeLists.txt @@ -19,6 +19,23 @@ add_integration_test( ) add_integration_test( + pthread_barrier_test + SUITE + libc-pthread-integration-tests + SRCS + pthread_barrier_test.cpp + DEPENDS + libc.include.pthread + libc.src.errno.errno + libc.src.pthread.pthread_barrier_destroy + libc.src.pthread.pthread_barrier_wait + libc.src.pthread.pthread_barrier_init + libc.src.pthread.pthread_create + libc.src.pthread.pthread_join + libc.src.stdio.printf +) + +add_integration_test( pthread_rwlock_test SUITE libc-pthread-integration-tests diff --git a/libc/test/integration/src/pthread/pthread_barrier_test.cpp b/libc/test/integration/src/pthread/pthread_barrier_test.cpp new file mode 100644 index 0000000..c8e1104 --- /dev/null +++ b/libc/test/integration/src/pthread/pthread_barrier_test.cpp @@ -0,0 +1,117 @@ +//===-- Tests for pthread_barrier_t ---------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/pthread/pthread_barrier_destroy.h" +#include "src/pthread/pthread_barrier_init.h" +#include "src/pthread/pthread_barrier_wait.h" + +#include "src/__support/CPP/atomic.h" +#include "src/pthread/pthread_create.h" +#include "src/pthread/pthread_join.h" +#include "src/pthread/pthread_mutex_destroy.h" +#include "src/pthread/pthread_mutex_init.h" +#include "src/pthread/pthread_mutex_lock.h" +#include "src/pthread/pthread_mutex_unlock.h" +#include "src/string/memset.h" + +#include "test/IntegrationTest/test.h" + +#include <pthread.h> + +pthread_barrier_t barrier; +LIBC_NAMESPACE::cpp::Atomic<int> counter; + +void *increment_counter_and_wait(void *args) { + counter.fetch_add(1); + return reinterpret_cast<void *>( + LIBC_NAMESPACE::pthread_barrier_wait(&barrier)); +} + +void single_use_barrier_test(int num_threads) { + counter.set(0); + // create n - 1 ADDITIONAL threads since the current thread will also wait at + // the barrier + pthread_t threads[num_threads - 1]; + LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t)); + ASSERT_EQ( + LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, num_threads), 0); + + for (int i = 0; i < num_threads - 1; ++i) + LIBC_NAMESPACE::pthread_create(&threads[i], nullptr, + increment_counter_and_wait, nullptr); + + uintptr_t return_val_sum = + reinterpret_cast<uintptr_t>(increment_counter_and_wait(nullptr)); + ASSERT_EQ(counter.load(), num_threads); + + // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value + for (int i = 0; i < num_threads - 1; ++i) { + void *ret; + LIBC_NAMESPACE::pthread_join(threads[i], &ret); + if (reinterpret_cast<uintptr_t>(ret) == + static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)) { + return_val_sum += reinterpret_cast<uintptr_t>(ret); + } else { + ASSERT_EQ(ret, 0); + } + } + ASSERT_EQ(return_val_sum, + static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)); + + LIBC_NAMESPACE::pthread_barrier_destroy(&barrier); +} + +void reused_barrier_test() { + counter.set(0); + const int NUM_THREADS = 30; + const int REPEAT = 20; + pthread_t threads[NUM_THREADS - 1]; // subtract 1 for main thread + LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t)); + ASSERT_EQ( + LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, NUM_THREADS), 0); + + for (int i = 0; i < REPEAT; ++i) { + for (int j = 0; j < NUM_THREADS - 1; ++j) + LIBC_NAMESPACE::pthread_create(&threads[j], nullptr, + increment_counter_and_wait, nullptr); + + uintptr_t return_val_sum = + reinterpret_cast<uintptr_t>(increment_counter_and_wait(nullptr)); + ASSERT_EQ(counter.load(), NUM_THREADS * (i + 1)); + + // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value + for (int i = 0; i < NUM_THREADS - 1; ++i) { + void *ret; + LIBC_NAMESPACE::pthread_join(threads[i], &ret); + if (reinterpret_cast<uintptr_t>(ret) == + static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)) { + return_val_sum += reinterpret_cast<uintptr_t>(ret); + } else { + ASSERT_EQ(ret, 0); + } + } + ASSERT_EQ(return_val_sum, + static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)); + } + + LIBC_NAMESPACE::pthread_barrier_destroy(&barrier); +} + +void *barrier_wait(void *in) { + return reinterpret_cast<void *>( + LIBC_NAMESPACE::pthread_barrier_wait(&barrier)); +} + +TEST_MAIN() { + // don't create any additional threads; only use main thread + single_use_barrier_test(1); + + single_use_barrier_test(30); + reused_barrier_test(); + return 0; +} diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt index 77f3617..4ed32d4 100644 --- a/libc/test/shared/CMakeLists.txt +++ b/libc/test/shared/CMakeLists.txt @@ -17,6 +17,11 @@ add_fp_unittest( libc.src.__support.math.asinf libc.src.__support.math.asinf16 libc.src.__support.math.asinhf + libc.src.__support.math.asinhf16 + libc.src.__support.math.atan + libc.src.__support.math.atan2 + libc.src.__support.math.atanf + libc.src.__support.math.atanf16 libc.src.__support.math.erff libc.src.__support.math.exp libc.src.__support.math.exp10 diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp index 2e4450a..cd72df4 100644 --- a/libc/test/shared/shared_math_test.cpp +++ b/libc/test/shared/shared_math_test.cpp @@ -18,6 +18,8 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) { EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::acoshf16(1.0f16)); EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::acospif16(1.0f16)); EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinf16(0.0f16)); + EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinhf16(0.0f16)); + EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::atanf16(0.0f16)); EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::exp10f16(0.0f16)); @@ -43,6 +45,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat) { EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::acoshf(1.0f)); EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::asinf(0.0f)); EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::asinhf(0.0f)); + EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::atanf(0.0f)); EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::erff(0.0f)); EXPECT_FP_EQ(0x1p+0f, LIBC_NAMESPACE::shared::exp10f(0.0f)); EXPECT_FP_EQ(0x1p+0f, LIBC_NAMESPACE::shared::expf(0.0f)); @@ -58,6 +61,8 @@ TEST(LlvmLibcSharedMathTest, AllFloat) { TEST(LlvmLibcSharedMathTest, AllDouble) { EXPECT_FP_EQ(0x1.921fb54442d18p+0, LIBC_NAMESPACE::shared::acos(0.0)); EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::asin(0.0)); + EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::atan(0.0)); + EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::atan2(0.0, 0.0)); EXPECT_FP_EQ(0x1p+0, LIBC_NAMESPACE::shared::exp(0.0)); EXPECT_FP_EQ(0x1p+0, LIBC_NAMESPACE::shared::exp10(0.0)); } diff --git a/libc/test/src/__support/FPUtil/comparison_operations_test.cpp b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp index 04a3321..05b8f68 100644 --- a/libc/test/src/__support/FPUtil/comparison_operations_test.cpp +++ b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp @@ -25,28 +25,15 @@ template <typename T> class ComparisonOperationsTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { DECLARE_SPECIAL_CONSTANTS(T) - // TODO: Make these constexpr once quick_get_round is made constexpr. - T normal1; - T neg_normal1; - T normal2; - T small; - T neg_small; - T large; - T neg_large; + static constexpr T normal1 = T(3.14); + static constexpr T neg_normal1 = T(-3.14); + static constexpr T normal2 = T(2.71); + static constexpr T small = T(0.1); + static constexpr T neg_small = T(-0.1); + static constexpr T large = T(10000.0); + static constexpr T neg_large = T(-10000.0); public: - void SetUp() override { - with_fenv_preserved([this]() { - normal1 = T(3.14); - neg_normal1 = T(-3.14); - normal2 = T(2.71); - small = T(0.1); - neg_small = T(-0.1); - large = T(10000.0); - neg_large = T(-10000.0); - }); - } - void test_equals() { EXPECT_TRUE(equals(neg_zero, neg_zero)); EXPECT_TRUE(equals(zero, neg_zero)); diff --git a/libc/test/src/math/generic/CMakeLists.txt b/libc/test/src/math/generic/CMakeLists.txt index 1fe7801..a9d54d6 100644 --- a/libc/test/src/math/generic/CMakeLists.txt +++ b/libc/test/src/math/generic/CMakeLists.txt @@ -30,4 +30,3 @@ add_fp_unittest( DEPENDS libc.src.math.generic.ceill ) - diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index ec4c09c..40b7a342 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -221,6 +221,19 @@ add_fp_unittest( ) add_fp_unittest( + fabsbf16_test + SUITE + libc-math-smoke-tests + SRCS + fabsbf16_test.cpp + HDRS + FAbsTest.h + DEPENDS + libc.src.__support.FPUtil.bfloat16 + libc.src.math.fabsbf16 +) + +add_fp_unittest( fadd_test SUITE libc-math-smoke-tests diff --git a/libc/test/src/math/smoke/fabsbf16_test.cpp b/libc/test/src/math/smoke/fabsbf16_test.cpp new file mode 100644 index 0000000..611050a --- /dev/null +++ b/libc/test/src/math/smoke/fabsbf16_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for fabsbf16 --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "FAbsTest.h" + +#include "src/__support/FPUtil/bfloat16.h" +#include "src/math/fabsbf16.h" + +LIST_FABS_TESTS(bfloat16, LIBC_NAMESPACE::fabsbf16) diff --git a/libc/test/src/sched/CMakeLists.txt b/libc/test/src/sched/CMakeLists.txt index 9dda4ea..f6151d0 100644 --- a/libc/test/src/sched/CMakeLists.txt +++ b/libc/test/src/sched/CMakeLists.txt @@ -7,7 +7,8 @@ add_libc_unittest( SRCS affinity_test.cpp DEPENDS - libc.include.sched + libc.hdr.types.cpu_set_t + libc.hdr.types.pid_t libc.include.sys_syscall libc.src.__support.OSUtil.osutil libc.src.errno.errno @@ -34,13 +35,26 @@ add_libc_unittest( SRCS get_priority_test.cpp DEPENDS - libc.include.sched + libc.hdr.sched_macros libc.src.errno.errno libc.src.sched.sched_get_priority_min libc.src.sched.sched_get_priority_max ) add_libc_unittest( + getcpu_test + SUITE + libc_sched_unittests + SRCS + getcpu_test.cpp + DEPENDS + libc.include.sched + libc.src.errno.errno + libc.src.sched.getcpu + libc.test.UnitTest.ErrnoCheckingTest +) + +add_libc_unittest( scheduler_test SUITE libc_sched_unittests @@ -65,7 +79,7 @@ add_libc_unittest( SRCS sched_rr_get_interval_test.cpp DEPENDS - libc.include.sched + libc.hdr.types.struct_timespec libc.src.errno.errno libc.src.sched.sched_getscheduler libc.src.sched.sched_setscheduler @@ -81,7 +95,9 @@ add_libc_unittest( SRCS cpu_count_test.cpp DEPENDS - libc.include.sched + libc.hdr.sched_macros + libc.hdr.types.cpu_set_t + libc.hdr.types.pid_t libc.include.sys_syscall libc.src.__support.OSUtil.osutil libc.src.errno.errno diff --git a/libc/test/src/sched/affinity_test.cpp b/libc/test/src/sched/affinity_test.cpp index b77f22f..1c8599b 100644 --- a/libc/test/src/sched/affinity_test.cpp +++ b/libc/test/src/sched/affinity_test.cpp @@ -12,7 +12,8 @@ #include "src/sched/sched_setaffinity.h" #include "test/UnitTest/ErrnoSetterMatcher.h" -#include <sched.h> +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" #include <sys/syscall.h> TEST(LlvmLibcSchedAffinityTest, SmokeTest) { diff --git a/libc/test/src/sched/cpu_count_test.cpp b/libc/test/src/sched/cpu_count_test.cpp index 919f147..06e4fff 100644 --- a/libc/test/src/sched/cpu_count_test.cpp +++ b/libc/test/src/sched/cpu_count_test.cpp @@ -12,8 +12,9 @@ #include "src/sched/sched_getcpucount.h" #include "test/UnitTest/ErrnoSetterMatcher.h" -#include <sched.h> -#include <sys/syscall.h> +#include "hdr/sched_macros.h" +#include "hdr/types/cpu_set_t.h" +#include "hdr/types/pid_t.h" TEST(LlvmLibcSchedCpuCountTest, SmokeTest) { cpu_set_t mask; diff --git a/libc/test/src/sched/get_priority_test.cpp b/libc/test/src/sched/get_priority_test.cpp index bb41dc0b..bf4fca8 100644 --- a/libc/test/src/sched/get_priority_test.cpp +++ b/libc/test/src/sched/get_priority_test.cpp @@ -11,7 +11,7 @@ #include "src/sched/sched_get_priority_min.h" #include "test/UnitTest/Test.h" -#include <sched.h> +#include "hdr/sched_macros.h" TEST(LlvmLibcSchedGetPriorityTest, HandleBadPolicyTest) { diff --git a/libc/test/src/sched/getcpu_test.cpp b/libc/test/src/sched/getcpu_test.cpp new file mode 100644 index 0000000..fc4ada8 --- /dev/null +++ b/libc/test/src/sched/getcpu_test.cpp @@ -0,0 +1,30 @@ +//===-- Unittests for getcpu ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/libc_errno.h" +#include "src/sched/getcpu.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" + +using LlvmLibcSchedGetCpuTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +TEST_F(LlvmLibcSchedGetCpuTest, SmokeTest) { + unsigned int current_cpu; + unsigned int current_node; + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; + ASSERT_THAT(LIBC_NAMESPACE::getcpu(¤t_cpu, ¤t_node), Succeeds(0)); +} + +TEST_F(LlvmLibcSchedGetCpuTest, BadPointer) { + unsigned int current_cpu; + unsigned int *current_node = reinterpret_cast<unsigned int *>(-1); + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(LIBC_NAMESPACE::getcpu(¤t_cpu, current_node), + Fails(EFAULT)); +} diff --git a/libc/test/src/sched/sched_rr_get_interval_test.cpp b/libc/test/src/sched/sched_rr_get_interval_test.cpp index a0fe5ed..272cf86 100644 --- a/libc/test/src/sched/sched_rr_get_interval_test.cpp +++ b/libc/test/src/sched/sched_rr_get_interval_test.cpp @@ -14,7 +14,7 @@ #include "src/unistd/getuid.h" #include "test/UnitTest/Test.h" -#include <sched.h> +#include "hdr/types/struct_timespec.h" TEST(LlvmLibcSchedRRGetIntervalTest, SmokeTest) { libc_errno = 0; diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt index fad89dc..d1a0684 100644 --- a/libc/test/src/wchar/CMakeLists.txt +++ b/libc/test/src/wchar/CMakeLists.txt @@ -76,6 +76,19 @@ add_libc_test( ) add_libc_test( + mbstowcs_test + SUITE + libc_wchar_unittests + SRCS + mbstowcs_test.cpp + DEPENDS + libc.src.__support.libc_errno + libc.src.wchar.mbstowcs + libc.hdr.types.wchar_t + libc.test.UnitTest.ErrnoCheckingTest +) + +add_libc_test( mblen_test SUITE libc_wchar_unittests @@ -88,6 +101,22 @@ add_libc_test( ) add_libc_test( + mbsrtowcs_test + SUITE + libc_wchar_unittests + SRCS + mbsrtowcs_test.cpp + DEPENDS + libc.src.__support.libc_errno + libc.src.__support.wchar.mbstate + libc.src.string.memset + libc.src.wchar.mbsrtowcs + libc.hdr.types.mbstate_t + libc.hdr.types.wchar_t + libc.test.UnitTest.ErrnoCheckingTest +) + +add_libc_test( mbrlen_test SUITE libc_wchar_unittests @@ -97,7 +126,23 @@ add_libc_test( libc.src.__support.libc_errno libc.src.__support.wchar.mbstate libc.src.string.memset - libc.src.wchar.mbrlen + libc.src.wchar.mbsrlen + libc.hdr.types.mbstate_t + libc.hdr.types.wchar_t + libc.test.UnitTest.ErrnoCheckingTest +) + +add_libc_test( + mbsnrtowcs_test + SUITE + libc_wchar_unittests + SRCS + mbsnrtowcs_test.cpp + DEPENDS + libc.src.__support.libc_errno + libc.src.__support.wchar.mbstate + libc.src.string.memset + libc.src.wchar.mbsnrtowcs libc.hdr.types.mbstate_t libc.hdr.types.wchar_t libc.test.UnitTest.ErrnoCheckingTest diff --git a/libc/test/src/wchar/mbsnrtowcs_test.cpp b/libc/test/src/wchar/mbsnrtowcs_test.cpp new file mode 100644 index 0000000..a3de68f --- /dev/null +++ b/libc/test/src/wchar/mbsnrtowcs_test.cpp @@ -0,0 +1,212 @@ +//===-- Unittests for mbsetowcs -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/types/mbstate_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/null_check.h" +#include "src/__support/wchar/mbstate.h" +#include "src/string/memset.h" +#include "src/wchar/mbsnrtowcs.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcMBSNRToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +TEST_F(LlvmLibcMBSNRToWCSTest, OneByteOneChar) { + const char *ch = "A"; + const char *original = ch; + wchar_t dest[2]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &ch, 1, 1, &mb); + ASSERT_EQ(static_cast<char>(*dest), 'A'); + ASSERT_EQ(static_cast<int>(n), 1); + // Should point to null terminator now + ASSERT_EQ(ch, original + 1); + ASSERT_ERRNO_SUCCESS(); + + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &ch, 1, 1, &mb); + ASSERT_EQ(static_cast<char>(dest[1]), '\0'); + // Should not include null terminator + ASSERT_EQ(static_cast<int>(n), 0); + // Should now be a nullptr + ASSERT_EQ(ch, nullptr); + ASSERT_ERRNO_SUCCESS(); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, FourByteOneChar) { + const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹 + const char *original = src; + wchar_t dest[2]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + // Not enough bytes for the full character + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 3, 2, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 0); + ASSERT_EQ(src, original + 3); + // Needs 2 more bytes (last byte of cat + null terminator) + n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 2, 2, &mb); + ASSERT_ERRNO_SUCCESS(); + // Does not include null terminator + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_EQ(src, nullptr); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_TRUE(dest[1] == L'\0'); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, MixedNumberOfBytes) { + // 'A', sigma symbol 'Σ', recycling symbol 'â™»', laughing cat emoji '😹' + const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + + // Read 'A' + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 1, 1, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<char>(dest[0]), 'A'); + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_EQ(src, original + 1); + + // Read sigma 'Σ' + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &src, 2, 1, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(dest[1]), 931); + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_EQ(src, original + 3); + + // Read recycling 'â™»' + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 2, 5, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 0); + ASSERT_EQ(src, original + 5); + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 1, 1, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_EQ(src, original + 6); + ASSERT_EQ(static_cast<int>(dest[2]), 9851); + + // Read laughing cat emoji '😹' + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 3, &src, 4, 5, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_EQ(src, original + 10); + ASSERT_EQ(static_cast<int>(dest[3]), 128569); + + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 4, &src, 4, 4, nullptr); + ASSERT_TRUE(dest[4] == L'\0'); + ASSERT_ERRNO_SUCCESS(); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 0); + // Should now be a nullptr + ASSERT_EQ(src, nullptr); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, ReadLessThanStringLength) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'}; + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 100, 3, nullptr); + ASSERT_ERRNO_SUCCESS(); + // Should have read 3 emojis + ASSERT_EQ(static_cast<int>(n), 3); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_EQ(static_cast<int>(dest[2]), 128569); + ASSERT_TRUE(dest[3] == L'd'); + ASSERT_TRUE(dest[4] == L'e'); + // Read three laughing cat emojis, 12 bytes + ASSERT_EQ(src, original + 12); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, InvalidFirstByte) { + // 0x80 is invalid first byte of mb character + const char *src = + "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 88, 88, nullptr); + // Should return error and set errno + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); + // Should not update pointer + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, InvalidMiddleByte) { + // The 7th byte is invalid for a 4 byte character + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + // Successfully read one character and first byte of the second character + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 5, 88, &mb); + ASSERT_EQ(static_cast<int>(n), 1); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(src, original + 5); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &src, 5, 88, &mb); + // Should return error, set errno, and not update the pointer + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); + ASSERT_EQ(src, original + 5); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, NullDestination) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + size_t n = LIBC_NAMESPACE::mbsnrtowcs(nullptr, &src, 88, 88, nullptr); + ASSERT_ERRNO_SUCCESS(); + // Null destination should ignore len and read till end of string + ASSERT_EQ(static_cast<int>(n), 4); + // It should also not change the src pointer + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSNRToWCSTest, ErrnoChecks) { + // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it) + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + // First two bytes are valid --> should not set errno + size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 80, 2, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 2); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_EQ(src, original + 8); + + // Trying to read the 3rd byte should set errno + n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 4, 2, &mb); + ASSERT_ERRNO_EQ(EILSEQ); + ASSERT_EQ(static_cast<int>(n), -1); + // Should not move the pointer + ASSERT_EQ(src, original + 8); +} + +#if defined(LIBC_ADD_NULL_CHECKS) +TEST(LlvmLibcMBSNRToWCSTest, NullptrCrash) { + // Passing in a nullptr should crash the program. + EXPECT_DEATH( + [] { LIBC_NAMESPACE::mbsnrtowcs(nullptr, nullptr, 1, 1, nullptr); }, + WITH_SIGNAL(-1)); +} +#endif // LIBC_ADD_NULL_CHECKS diff --git a/libc/test/src/wchar/mbsrtowcs_test.cpp b/libc/test/src/wchar/mbsrtowcs_test.cpp new file mode 100644 index 0000000..59efc0d --- /dev/null +++ b/libc/test/src/wchar/mbsrtowcs_test.cpp @@ -0,0 +1,185 @@ +//===-- Unittests for mbsetowcs -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/types/mbstate_t.h" +#include "hdr/types/wchar_t.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/null_check.h" +#include "src/__support/wchar/mbstate.h" +#include "src/string/memset.h" +#include "src/wchar/mbsrtowcs.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcMBSRToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +TEST_F(LlvmLibcMBSRToWCSTest, OneByteOneChar) { + const char *ch = "A"; + const char *original = ch; + wchar_t dest[2]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &ch, 1, &mb); + ASSERT_EQ(static_cast<char>(*dest), 'A'); + ASSERT_EQ(static_cast<int>(n), 1); + // Should point to null terminator now + ASSERT_EQ(ch, original + 1); + ASSERT_ERRNO_SUCCESS(); + + n = LIBC_NAMESPACE::mbsrtowcs(dest + 1, &ch, 1, &mb); + ASSERT_EQ(static_cast<char>(dest[1]), '\0'); + // Should not include null terminator + ASSERT_EQ(static_cast<int>(n), 0); + // Should now be a nullptr + ASSERT_EQ(ch, nullptr); + ASSERT_ERRNO_SUCCESS(); +} + +TEST_F(LlvmLibcMBSRToWCSTest, FourByteOneChar) { + const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹 + wchar_t dest[2]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_TRUE(dest[1] == L'\0'); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 1); + // Should now be a nullptr + ASSERT_EQ(src, nullptr); +} + +TEST_F(LlvmLibcMBSRToWCSTest, MultiByteTwoCharacters) { + // Two laughing cat emojis "😹😹" + const char *src = "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + wchar_t dest[3]; + mbstate_t mb; + LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t)); + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, &mb); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_TRUE(dest[2] == L'\0'); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 2); + // Should now be a nullptr + ASSERT_EQ(src, nullptr); +} + +TEST_F(LlvmLibcMBSRToWCSTest, MixedNumberOfBytes) { + // 'A', sigma symbol 'Σ', recycling symbol 'â™»', laughing cat emoji '😹' + const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 4, nullptr); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<char>(dest[0]), 'A'); + ASSERT_EQ(static_cast<int>(dest[1]), 931); + ASSERT_EQ(static_cast<int>(dest[2]), 9851); + ASSERT_EQ(static_cast<int>(dest[3]), 128569); + // Should point to null terminator (byte at 10th index) + ASSERT_EQ(src, original + 10); + ASSERT_EQ(static_cast<int>(n), 4); + n = LIBC_NAMESPACE::mbsrtowcs(dest + 4, &src, 4, nullptr); + ASSERT_TRUE(dest[4] == L'\0'); + ASSERT_ERRNO_SUCCESS(); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 0); + // Should now be a nullptr + ASSERT_EQ(src, nullptr); +} + +TEST_F(LlvmLibcMBSRToWCSTest, ReadLessThanStringLength) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'}; + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, nullptr); + ASSERT_ERRNO_SUCCESS(); + // Should have read 3 emojis + ASSERT_EQ(static_cast<int>(n), 3); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_EQ(static_cast<int>(dest[2]), 128569); + ASSERT_TRUE(dest[3] == L'd'); + ASSERT_TRUE(dest[4] == L'e'); + // Read three laughing cat emojis, 12 bytes + ASSERT_EQ(src, original + 12); +} + +TEST_F(LlvmLibcMBSRToWCSTest, InvalidFirstByte) { + // 0x80 is invalid first byte of mb character + const char *src = + "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, nullptr); + // Should return error and set errno + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); + // Should not update pointer + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSRToWCSTest, InvalidMiddleByte) { + // The 7th byte is invalid for a 4 byte character + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 5, nullptr); + // Should return error, set errno, and not update the pointer + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSRToWCSTest, NullDestination) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + size_t n = LIBC_NAMESPACE::mbsrtowcs(nullptr, &src, 2, nullptr); + ASSERT_ERRNO_SUCCESS(); + // Null destination should ignore len and read till end of string + ASSERT_EQ(static_cast<int>(n), 4); + // It should also not change the src pointer + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSRToWCSTest, ErrnoChecks) { + // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it) + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + // First two bytes are valid --> should not set errno + size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, nullptr); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 2); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_EQ(src, original + 8); + + // Trying to read the 3rd byte should set errno + n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, nullptr); + ASSERT_ERRNO_EQ(EILSEQ); + ASSERT_EQ(static_cast<int>(n), -1); + // Should not move the pointer + ASSERT_EQ(src, original + 8); +} + +#if defined(LIBC_ADD_NULL_CHECKS) +TEST(LlvmLibcMBSRToWCSTest, NullptrCrash) { + // Passing in a nullptr should crash the program. + EXPECT_DEATH([] { LIBC_NAMESPACE::mbsrtowcs(nullptr, nullptr, 1, nullptr); }, + WITH_SIGNAL(-1)); +} +#endif // LIBC_ADD_NULL_CHECKS diff --git a/libc/test/src/wchar/mbstowcs_test.cpp b/libc/test/src/wchar/mbstowcs_test.cpp new file mode 100644 index 0000000..f0396e0 --- /dev/null +++ b/libc/test/src/wchar/mbstowcs_test.cpp @@ -0,0 +1,171 @@ +//===-- Unittests for mbstowcs --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/types/wchar_t.h" +#include "src/__support/libc_errno.h" +#include "src/__support/macros/null_check.h" +#include "src/wchar/mbstowcs.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcMBSToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; + +TEST_F(LlvmLibcMBSToWCSTest, OneByteOneChar) { + const char *ch = "A"; + const char *original = ch; + wchar_t dest[2]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, ch, 1); + ASSERT_EQ(static_cast<char>(*dest), 'A'); + ASSERT_EQ(static_cast<int>(n), 1); + // Making sure the pointer is not getting updated + ASSERT_EQ(ch, original); + ASSERT_ERRNO_SUCCESS(); + + n = LIBC_NAMESPACE::mbstowcs(dest + 1, ch + 1, 1); + ASSERT_EQ(static_cast<char>(dest[1]), '\0'); + // Should not include null terminator + ASSERT_EQ(static_cast<int>(n), 0); + // Making sure the pointer is not getting updated + ASSERT_EQ(ch, original); + ASSERT_ERRNO_SUCCESS(); +} + +TEST_F(LlvmLibcMBSToWCSTest, FourByteOneChar) { + const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹 + const char *original = src; + wchar_t dest[2]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 2); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_TRUE(dest[1] == L'\0'); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 1); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, MultiByteTwoCharacters) { + // Two laughing cat emojis "😹😹" + const char *src = "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_TRUE(dest[2] == L'\0'); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 2); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, MixedNumberOfBytes) { + // 'A', sigma symbol 'Σ', recycling symbol 'â™»', laughing cat emoji '😹' + const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 5); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<char>(dest[0]), 'A'); + ASSERT_EQ(static_cast<int>(dest[1]), 931); + ASSERT_EQ(static_cast<int>(dest[2]), 9851); + ASSERT_EQ(static_cast<int>(dest[3]), 128569); + ASSERT_TRUE(dest[4] == L'\0'); + // Should not count null terminator in number + ASSERT_EQ(static_cast<int>(n), 4); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, ReadLessThanStringLength) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'}; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3); + ASSERT_ERRNO_SUCCESS(); + // Should have read 3 emojis + ASSERT_EQ(static_cast<int>(n), 3); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + ASSERT_EQ(static_cast<int>(dest[2]), 128569); + ASSERT_TRUE(dest[3] == L'd'); + ASSERT_TRUE(dest[4] == L'e'); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, InvalidFirstByte) { + // 0x80 is invalid first byte of mb character + const char *src = + "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3); + // Should return error and set errno + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); +} + +TEST_F(LlvmLibcMBSToWCSTest, InvalidMiddleByte) { + // The 7th byte is invalid for a 4 byte character + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[3]; + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 5); + // Should return error and set errno + ASSERT_EQ(static_cast<int>(n), -1); + ASSERT_ERRNO_EQ(EILSEQ); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, NullDestination) { + // Four laughing cat emojis "😹😹😹😹" + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + size_t n = LIBC_NAMESPACE::mbstowcs(nullptr, src, 2); + ASSERT_ERRNO_SUCCESS(); + // Null destination should ignore len and read till end of string + ASSERT_EQ(static_cast<int>(n), 4); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +TEST_F(LlvmLibcMBSToWCSTest, ErrnoChecks) { + // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it) + const char *src = + "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9"; + const char *original = src; + wchar_t dest[5]; + // First two bytes are valid --> should not set errno + size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 2); + ASSERT_ERRNO_SUCCESS(); + ASSERT_EQ(static_cast<int>(n), 2); + ASSERT_EQ(static_cast<int>(dest[0]), 128569); + ASSERT_EQ(static_cast<int>(dest[1]), 128569); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); + // Trying to read the 3rd byte should set errno + n = LIBC_NAMESPACE::mbstowcs(dest, src + 2, 2); + ASSERT_ERRNO_EQ(EILSEQ); + ASSERT_EQ(static_cast<int>(n), -1); + // Making sure the pointer is not getting updated + ASSERT_EQ(src, original); +} + +#if defined(LIBC_ADD_NULL_CHECKS) +TEST(LlvmLibcMBSToWCSTest, NullptrCrash) { + // Passing in a nullptr should crash the program. + EXPECT_DEATH([] { LIBC_NAMESPACE::mbstowcs(nullptr, nullptr, 1); }, + WITH_SIGNAL(-1)); +} +#endif // LIBC_ADD_NULL_CHECKS |