diff options
Diffstat (limited to 'libc')
56 files changed, 948 insertions, 358 deletions
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 4824684..8bf6c44 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -735,6 +735,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.rintf16 libc.src.math.roundevenf16 libc.src.math.roundf16 + libc.src.math.rsqrtf libc.src.math.rsqrtf16 libc.src.math.scalblnf16 libc.src.math.scalbnf16 diff --git a/libc/config/linux/app.h b/libc/config/linux/app.h index f3d11da..11ac6ee 100644 --- a/libc/config/linux/app.h +++ b/libc/config/linux/app.h @@ -35,17 +35,6 @@ struct TLSImage { uintptr_t align; }; -// Linux manpage on `proc(5)` says that the aux vector is an array of -// unsigned long pairs. -// (see: https://man7.org/linux/man-pages/man5/proc.5.html) -using AuxEntryType = unsigned long; -// Using the naming convention from `proc(5)`. -// TODO: Would be nice to use the aux entry structure from elf.h when available. -struct AuxEntry { - AuxEntryType id; - AuxEntryType value; -}; - struct Args { uintptr_t argc; @@ -70,9 +59,6 @@ struct AppProperties { // Environment data. uintptr_t *env_ptr; - - // Auxiliary vector data. - AuxEntry *auxv_ptr; }; [[gnu::weak]] extern AppProperties app; diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 5f407e8..dffccba 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -750,6 +750,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.rintf16 libc.src.math.roundevenf16 libc.src.math.roundf16 + libc.src.math.rsqrtf libc.src.math.rsqrtf16 libc.src.math.scalblnf16 libc.src.math.scalbnf16 @@ -1022,6 +1023,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT) libc.src.stdfix.idivulr libc.src.stdfix.idivuk libc.src.stdfix.idivulk + libc.src.stdfix.rdivi ) endif() diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 87b78a33..b4ab073 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -786,6 +786,7 @@ if(LIBC_TYPES_HAS_FLOAT16) libc.src.math.rintf16 libc.src.math.roundevenf16 libc.src.math.roundf16 + libc.src.math.rsqrtf libc.src.math.rsqrtf16 libc.src.math.scalblnf16 libc.src.math.scalbnf16 @@ -1058,6 +1059,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT) libc.src.stdfix.idivulr libc.src.stdfix.idivuk libc.src.stdfix.idivulk + libc.src.stdfix.rdivi ) endif() diff --git a/libc/docs/headers/math/index.rst b/libc/docs/headers/math/index.rst index 51bf238..51bb17f 100644 --- a/libc/docs/headers/math/index.rst +++ b/libc/docs/headers/math/index.rst @@ -343,7 +343,7 @@ Higher Math Functions +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ | rootn | | | | | | | 7.12.7.8 | F.10.4.8 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ -| rsqrt | | | | |check| | | | 7.12.7.9 | F.10.4.9 | +| rsqrt | |check| | | | |check| | | | 7.12.7.9 | F.10.4.9 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ | sin | |check| | |check| | | |check| | | | 7.12.4.6 | F.10.1.6 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/docs/headers/stdfix.rst b/libc/docs/headers/stdfix.rst index a2f5e94..e75b9d8 100644 --- a/libc/docs/headers/stdfix.rst +++ b/libc/docs/headers/stdfix.rst @@ -81,7 +81,7 @@ The following functions are included in the ISO/IEC TR 18037:2008 standard. +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+ | muli | | | | | | | | | | | | | +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+ -| \*divi | | | | | | | | | | | | | +| \*divi | | | | |check| | | | | | | | | | +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+ | round | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | |check| | +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+ diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 70da983..5f506c4 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -39,6 +39,8 @@ add_header(float_t HDR float_t.h) add_header(gid_t HDR gid_t.h) add_header(uid_t HDR uid_t.h) add_header(imaxdiv_t HDR imaxdiv_t.h) +add_header(in_addr_t HDR in_addr_t.h) +add_header(in_addr HDR in_addr.h DEPENDS .in_addr_t) add_header(ino_t HDR ino_t.h) add_header(mbstate_t HDR mbstate_t.h) add_header(mode_t HDR mode_t.h) diff --git a/libc/include/llvm-libc-types/in_addr.h b/libc/include/llvm-libc-types/in_addr.h new file mode 100644 index 0000000..ab42142 --- /dev/null +++ b/libc/include/llvm-libc-types/in_addr.h @@ -0,0 +1,18 @@ +//===-- Definition of in_addr 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_IN_ADDR_H +#define LLVM_LIBC_TYPES_IN_ADDR_H + +#include "in_addr_t.h" + +typedef struct { + in_addr_t s_addr; +} in_addr; + +#endif // LLVM_LIBC_TYPES_IN_ADDR_H diff --git a/libc/include/llvm-libc-types/in_addr_t.h b/libc/include/llvm-libc-types/in_addr_t.h new file mode 100644 index 0000000..334b073 --- /dev/null +++ b/libc/include/llvm-libc-types/in_addr_t.h @@ -0,0 +1,14 @@ +//===-- Definition of in_addr_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_IN_ADDR_T_H +#define LLVM_LIBC_TYPES_IN_ADDR_T_H + +typedef __UINT32_TYPE__ in_addr_t; + +#endif // LLVM_LIBC_TYPES_IN_ADDR_T_H diff --git a/libc/include/math.yaml b/libc/include/math.yaml index 6c800a0..afd3ae3 100644 --- a/libc/include/math.yaml +++ b/libc/include/math.yaml @@ -2349,6 +2349,12 @@ functions: return_type: long double arguments: - type: long double + - name: rsqrtf + standards: + - stdc + return_type: float + arguments: + - type: float - name: rsqrtf16 standards: - stdc diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml index 5b38512..451330c 100644 --- a/libc/include/stdfix.yaml +++ b/libc/include/stdfix.yaml @@ -544,3 +544,11 @@ functions: arguments: - type: unsigned long accum guard: LIBC_COMPILER_HAS_FIXED_POINT + - name: rdivi + standards: + - stdc_ext + return_type: fract + arguments: + - type: int + - type: int + guard: LIBC_COMPILER_HAS_FIXED_POINT diff --git a/libc/shared/math.h b/libc/shared/math.h index 1262fa6..82b9250 100644 --- a/libc/shared/math.h +++ b/libc/shared/math.h @@ -57,7 +57,7 @@ #include "math/ldexpf.h" #include "math/ldexpf128.h" #include "math/ldexpf16.h" - +#include "math/rsqrtf.h" #include "math/rsqrtf16.h" #endif // LLVM_LIBC_SHARED_MATH_H diff --git a/libc/shared/math/rsqrtf.h b/libc/shared/math/rsqrtf.h new file mode 100644 index 0000000..32d0a61 --- /dev/null +++ b/libc/shared/math/rsqrtf.h @@ -0,0 +1,23 @@ +//===-- Shared rsqrtf 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_RSQRTF_H +#define LLVM_LIBC_SHARED_MATH_RSQRTF_H + +#include "shared/libc_common.h" +#include "src/__support/math/rsqrtf.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using math::rsqrtf; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SHARED_MATH_RSQRTF_H diff --git a/libc/shared/math/rsqrtf16.h b/libc/shared/math/rsqrtf16.h index 54c7499..8dbf065 100644 --- a/libc/shared/math/rsqrtf16.h +++ b/libc/shared/math/rsqrtf16.h @@ -1,4 +1,4 @@ -//===-- Shared rsqrtf16 function -------------------------------*- C++ -*-===// +//===-- Shared rsqrtf16 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. diff --git a/libc/src/__support/File/linux/lseekImpl.h b/libc/src/__support/File/linux/lseekImpl.h index c22a6c5..47df99a 100644 --- a/libc/src/__support/File/linux/lseekImpl.h +++ b/libc/src/__support/File/linux/lseekImpl.h @@ -25,8 +25,9 @@ namespace internal { LIBC_INLINE ErrorOr<off_t> lseekimpl(int fd, off_t offset, int whence) { off_t result; #ifdef SYS_lseek - int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_lseek, fd, offset, whence); - result = ret; + result = LIBC_NAMESPACE::syscall_impl<off_t>(SYS_lseek, fd, offset, whence); + if (result < 0) + return Error(-static_cast<int>(result)); #elif defined(SYS_llseek) || defined(SYS__llseek) static_assert(sizeof(size_t) == 4, "size_t must be 32 bits."); #ifdef SYS_llseek @@ -37,11 +38,11 @@ LIBC_INLINE ErrorOr<off_t> lseekimpl(int fd, off_t offset, int whence) { off_t offset_64 = offset; int ret = LIBC_NAMESPACE::syscall_impl<int>( LLSEEK_SYSCALL_NO, fd, offset_64 >> 32, offset_64, &result, whence); + if (ret < 0) + return Error(-ret); #else #error "lseek, llseek and _llseek syscalls not available." #endif - if (ret < 0) - return Error(-ret); return result; } diff --git a/libc/src/__support/OSUtil/linux/CMakeLists.txt b/libc/src/__support/OSUtil/linux/CMakeLists.txt index f303e54..0a60377 100644 --- a/libc/src/__support/OSUtil/linux/CMakeLists.txt +++ b/libc/src/__support/OSUtil/linux/CMakeLists.txt @@ -25,6 +25,18 @@ add_object_library( ) add_header_library( + auxv + HDRS + auxv.h + DEPENDS + libc.hdr.fcntl_macros + libc.src.__support.OSUtil.osutil + libc.src.__support.common + libc.src.__support.CPP.optional + libc.src.__support.threads.callonce +) + +add_header_library( getrandom HDRS getrandom.h @@ -58,11 +70,10 @@ add_object_library( libc.src.__support.CPP.string_view libc.src.__support.threads.callonce libc.src.__support.threads.linux.futex_word_type + libc.src.__support.OSUtil.linux.auxv libc.hdr.types.struct_timeval libc.hdr.types.struct_timespec libc.hdr.types.clockid_t libc.hdr.types.time_t libc.hdr.link_macros - libc.src.errno.errno - libc.src.sys.auxv.getauxval ) diff --git a/libc/src/__support/OSUtil/linux/auxv.h b/libc/src/__support/OSUtil/linux/auxv.h new file mode 100644 index 0000000..894868a --- /dev/null +++ b/libc/src/__support/OSUtil/linux/auxv.h @@ -0,0 +1,158 @@ +//===------------- Linux AUXV Header --------------------------*- 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_OSUTIL_LINUX_AUXV_H +#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H + +#include "hdr/fcntl_macros.h" // For open flags +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/common.h" +#include "src/__support/threads/callonce.h" + +#include <linux/auxvec.h> // For AT_ macros +#include <linux/mman.h> // For mmap flags +#include <linux/prctl.h> // For prctl +#include <sys/syscall.h> // For syscall numbers + +namespace LIBC_NAMESPACE_DECL { + +namespace auxv { +struct Entry { + unsigned long type; // Entry type + unsigned long val; // Integer value +}; + +class Vector { + LIBC_INLINE_VAR static constexpr Entry END = {AT_NULL, AT_NULL}; + LIBC_INLINE_VAR static const Entry *entries = &END; + LIBC_INLINE_VAR static CallOnceFlag init_flag = callonce_impl::NOT_CALLED; + LIBC_INLINE_VAR constexpr static size_t FALLBACK_AUXV_ENTRIES = 64; + + LIBC_INLINE static void fallback_initialize_unsync(); + LIBC_INLINE static const Entry *get_entries() { + if (LIBC_LIKELY(entries != &END)) + return entries; + callonce(&init_flag, fallback_initialize_unsync); + return entries; + } + +public: + class Iterator { + const Entry *current; + + public: + LIBC_INLINE explicit Iterator(const Entry *entry) : current(entry) {} + LIBC_INLINE Iterator &operator++() { + ++current; + return *this; + } + LIBC_INLINE const Entry &operator*() const { return *current; } + LIBC_INLINE bool operator!=(const Iterator &other) const { + return current->type != other.current->type; + } + LIBC_INLINE bool operator==(const Iterator &other) const { + return current->type == other.current->type; + } + }; + using iterator = Iterator; + LIBC_INLINE static Iterator begin() { return Iterator(get_entries()); } + LIBC_INLINE static Iterator end() { return Iterator(&END); } + LIBC_INLINE static void initialize_unsafe(const Entry *auxv); +}; + +// Initializes the auxv entries. +// This function is intended to be called once inside crt0. +LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) { + init_flag = callonce_impl::FINISH; + entries = auxv; +} + +// When CRT0 does not setup the global array, this function is called. +// As its name suggests, this function is not thread-safe and should be +// backed by a callonce guard. +// This initialize routine will do a mmap to allocate a memory region. +// Since auxv tends to live throughout the program lifetime, we do not +// munmap it. +[[gnu::cold]] +LIBC_INLINE void Vector::fallback_initialize_unsync() { + constexpr size_t AUXV_MMAP_SIZE = FALLBACK_AUXV_ENTRIES * sizeof(Entry); +#ifdef SYS_mmap2 + constexpr int MMAP_SYSNO = SYS_mmap2; +#else + constexpr int MMAP_SYSNO = SYS_mmap; +#endif + long mmap_ret = syscall_impl<long>(MMAP_SYSNO, nullptr, AUXV_MMAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + // We do not proceed if mmap fails. + if (mmap_ret <= 0) + return; + + // Initialize the auxv array with AT_NULL entries. + Entry *vector = reinterpret_cast<Entry *>(mmap_ret); + for (size_t i = 0; i < FALLBACK_AUXV_ENTRIES; ++i) { + vector[i].type = AT_NULL; + vector[i].val = AT_NULL; + } + size_t avaiable_size = AUXV_MMAP_SIZE - sizeof(Entry); + +// Attempt 1: use PRCTL to get the auxv. +// We guarantee that the vector is always padded with AT_NULL entries. +#ifdef PR_GET_AUXV + long prctl_ret = syscall_impl<long>(SYS_prctl, PR_GET_AUXV, + reinterpret_cast<unsigned long>(vector), + avaiable_size, 0, 0); + if (prctl_ret >= 0) { + entries = vector; + return; + } +#endif + + // Attempt 2: read /proc/self/auxv. +#ifdef SYS_openat + int fd = syscall_impl<int>(SYS_openat, AT_FDCWD, "/proc/self/auxv", + O_RDONLY | O_CLOEXEC); +#else + int fd = syscall_impl<int>(SYS_open, "/proc/self/auxv", O_RDONLY | O_CLOEXEC); +#endif + if (fd < 0) { + syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE); + return; + } + uint8_t *cursor = reinterpret_cast<uint8_t *>(vector); + bool has_error = false; + while (avaiable_size != 0) { + long bytes_read = syscall_impl<long>(SYS_read, fd, cursor, avaiable_size); + if (bytes_read <= 0) { + if (bytes_read == -EINTR) + continue; + has_error = bytes_read < 0; + break; + } + avaiable_size -= bytes_read; + cursor += bytes_read; + } + syscall_impl<long>(SYS_close, fd); + if (has_error) { + syscall_impl<long>(SYS_munmap, vector, AUXV_MMAP_SIZE); + return; + } + entries = vector; +} + +LIBC_INLINE cpp::optional<unsigned long> get(unsigned long type) { + Vector auxvec; + for (const auto &entry : auxvec) + if (entry.type == type) + return entry.val; + return cpp::nullopt; +} +} // namespace auxv +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_AUXV_H diff --git a/libc/src/__support/OSUtil/linux/vdso.cpp b/libc/src/__support/OSUtil/linux/vdso.cpp index e4e53c3..d6fd3f3 100644 --- a/libc/src/__support/OSUtil/linux/vdso.cpp +++ b/libc/src/__support/OSUtil/linux/vdso.cpp @@ -11,10 +11,9 @@ #include "src/__support/CPP/array.h" #include "src/__support/CPP/optional.h" #include "src/__support/CPP/string_view.h" -#include "src/__support/libc_errno.h" +#include "src/__support/OSUtil/linux/auxv.h" #include "src/__support/threads/callonce.h" #include "src/__support/threads/linux/futex_word.h" -#include "src/sys/auxv/getauxval.h" #include <linux/auxvec.h> // TODO: This is a temporary workaround to avoid including elf.h @@ -189,17 +188,13 @@ void Symbol::initialize_vdso_global_cache() { for (auto &i : global_cache) i = nullptr; - // get the address of the VDSO, protect errno since getauxval may change - // it - int errno_backup = libc_errno; - uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR); + cpp::optional<unsigned long> auxv_res = auxv::get(AT_SYSINFO_EHDR); + uintptr_t vdso_ehdr_addr = auxv_res ? static_cast<uintptr_t>(*auxv_res) : 0; // Get the memory address of the vDSO ELF header. auto vdso_ehdr = reinterpret_cast<ElfW(Ehdr) *>(vdso_ehdr_addr); // leave the table unpopulated if we don't have vDSO - if (vdso_ehdr == nullptr) { - libc_errno = errno_backup; + if (vdso_ehdr == nullptr) return; - } // locate the section header inside the elf using the section header // offset diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h index 00c6119b..25221d9 100644 --- a/libc/src/__support/fixed_point/fx_bits.h +++ b/libc/src/__support/fixed_point/fx_bits.h @@ -10,9 +10,11 @@ #define LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H #include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/CPP/algorithm.h" #include "src/__support/CPP/bit.h" #include "src/__support/CPP/limits.h" // numeric_limits #include "src/__support/CPP/type_traits.h" +#include "src/__support/libc_assert.h" #include "src/__support/macros/attributes.h" // LIBC_INLINE #include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL #include "src/__support/macros/null_check.h" // LIBC_CRASH_ON_VALUE @@ -21,6 +23,8 @@ #include "fx_rep.h" +#include <stdio.h> + #ifdef LIBC_COMPILER_HAS_FIXED_POINT namespace LIBC_NAMESPACE_DECL { @@ -224,6 +228,113 @@ idiv(T x, T y) { return static_cast<XType>(result); } +LIBC_INLINE long accum nrstep(long accum d, long accum x0) { + auto v = x0 * (2.lk - (d * x0)); + return v; +} + +// Divide the two integers and return a fixed_point value +// +// For reference, see: +// https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division +// https://stackoverflow.com/a/9231996 + +template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) { + // If the value of the second operand of the / operator is zero, the + // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16 + LIBC_CRASH_ON_VALUE(d, 0); + + if (LIBC_UNLIKELY(n == 0)) { + return FXRep<XType>::ZERO(); + } + auto is_power_of_two = [](int n) { return (n > 0) && ((n & (n - 1)) == 0); }; + long accum max_val = static_cast<long accum>(FXRep<XType>::MAX()); + long accum min_val = static_cast<long accum>(FXRep<XType>::MIN()); + + if (is_power_of_two(cpp::abs(d))) { + int k = cpp::countr_zero<uint32_t>(static_cast<uint32_t>(cpp::abs(d))); + constexpr int F = FXRep<XType>::FRACTION_LEN; + int64_t scaled_n = static_cast<int64_t>(n) << F; + int64_t res64 = scaled_n >> k; + constexpr int TOTAL_BITS = sizeof(XType) * 8; + const int64_t max_limit = (1LL << (TOTAL_BITS - 1)) - 1; + const int64_t min_limit = -(1LL << (TOTAL_BITS - 1)); + if (res64 > max_limit) { + return FXRep<XType>::MAX(); + } else if (res64 < min_limit) { + return FXRep<XType>::MIN(); + } + long accum res_accum = + static_cast<long accum>(res64) / static_cast<long accum>(1 << F); + res_accum = (d < 0) ? static_cast<long accum>(-1) * res_accum : res_accum; + if (res_accum > max_val) { + return FXRep<XType>::MAX(); + } else if (res_accum < min_val) { + return FXRep<XType>::MIN(); + } + return static_cast<XType>(res_accum); + } + + bool result_is_negative = ((n < 0) != (d < 0)); + int64_t n64 = static_cast<int64_t>(n); + int64_t d64 = static_cast<int64_t>(d); + + uint64_t nv = static_cast<uint64_t>(n64 < 0 ? -n64 : n64); + uint64_t dv = static_cast<uint64_t>(d64 < 0 ? -d64 : d64); + + if (d == INT_MIN) { + nv <<= 1; + dv >>= 1; + } + + uint32_t clz = cpp::countl_zero<uint32_t>(static_cast<uint32_t>(dv)) - 1; + uint64_t scaled_val = dv << clz; + // Scale denominator to be in the range of [0.5,1] + FXBits<long accum> d_scaled{scaled_val}; + uint64_t scaled_val_n = nv << clz; + // Scale the numerator as much as the denominator to maintain correctness of + // the original equation + FXBits<long accum> n_scaled{scaled_val_n}; + long accum n_scaled_val = n_scaled.get_val(); + long accum d_scaled_val = d_scaled.get_val(); + // x0 = (48/17) - (32/17) * d_n + long accum a = 0x2.d89d89d8p0lk; // 48/17 = 2.8235294... + long accum b = 0x1.e1e1e1e1p0lk; // 32/17 = 1.8823529... + // Error of the initial approximation, as derived + // from the wikipedia article is + // E0 = 1/17 = 0.059 (5.9%) + long accum initial_approx = a - (b * d_scaled_val); + // Since, 0.5 <= d_scaled_val <= 1.0, 0.9412 <= initial_approx <= 1.88235 + LIBC_ASSERT((initial_approx >= 0x0.78793dd9p0lk) && + (initial_approx <= 0x1.f0f0d845p0lk)); + // Each newton-raphson iteration will square the error, due + // to quadratic convergence. So, + // E1 = (0.059)^2 = 0.0034 + long accum val = nrstep(d_scaled_val, initial_approx); + if constexpr (FXRep<XType>::FRACTION_LEN > 8) { + // E2 = 0.0000121 + val = nrstep(d_scaled_val, val); + if constexpr (FXRep<XType>::FRACTION_LEN > 16) { + // E3 = 1.468e−10 + val = nrstep(d_scaled_val, val); + } + } + long accum res = n_scaled_val * val; + + if (result_is_negative) { + res *= static_cast<long accum>(-1); + } + + // Per clause 7.18a.6.1, saturate values on overflow + if (res > max_val) { + return FXRep<XType>::MAX(); + } else if (res < min_val) { + return FXRep<XType>::MIN(); + } else { + return static_cast<XType>(res); + } +} + } // namespace fixed_point } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt index 203ebb4..61253de 100644 --- a/libc/src/__support/math/CMakeLists.txt +++ b/libc/src/__support/math/CMakeLists.txt @@ -109,22 +109,6 @@ add_header_library( libc.src.__support.macros.properties.types ) - -add_header_library( - rsqrtf16 - HDRS - rsqrtf16.h - DEPENDS - libc.src.__support.FPUtil.cast - 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.manipulation_functions - libc.src.__support.macros.optimization - libc.src.__support.macros.properties.types -) - add_header_library( asin_utils HDRS @@ -867,6 +851,34 @@ add_header_library( ) add_header_library( + rsqrtf + HDRS + rsqrtf.h + DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.__support.FPUtil.cast + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.sqrt + libc.src.__support.macros.optimization +) + +add_header_library( + rsqrtf16 + HDRS + rsqrtf16.h + DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.__support.FPUtil.cast + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.sqrt + libc.src.__support.macros.optimization +) + +add_header_library( sincos_eval HDRS sincos_eval.h diff --git a/libc/src/__support/math/rsqrtf.h b/libc/src/__support/math/rsqrtf.h new file mode 100644 index 0000000..5da1e73 --- /dev/null +++ b/libc/src/__support/math/rsqrtf.h @@ -0,0 +1,71 @@ +//===-- Implementation header for rsqrtf ------------------------*- 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_RSQRTF_H +#define LLVM_LIBC_SRC___SUPPORT_MATH_RSQRTF_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/cast.h" +#include "src/__support/FPUtil/sqrt.h" +#include "src/__support/macros/optimization.h" + +namespace LIBC_NAMESPACE_DECL { +namespace math { + +LIBC_INLINE static constexpr float rsqrtf(float x) { + using FPBits = fputil::FPBits<float>; + FPBits xbits(x); + + uint32_t x_u = xbits.uintval(); + uint32_t x_abs = x_u & 0x7fff'ffffU; + + constexpr uint32_t INF_BITS = FPBits::inf().uintval(); + + // x is 0, inf/nan, or negative. + if (LIBC_UNLIKELY(x_u == 0 || x_u >= INF_BITS)) { + // x is NaN + if (x_abs > INF_BITS) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + return x; + } + + // |x| = 0 + if (x_abs == 0) { + fputil::raise_except_if_required(FE_DIVBYZERO); + fputil::set_errno_if_required(ERANGE); + return FPBits::inf(xbits.sign()).get_val(); + } + + // -inf <= x < 0 + if (x_u > 0x7fff'ffffU) { + fputil::raise_except_if_required(FE_INVALID); + fputil::set_errno_if_required(EDOM); + return FPBits::quiet_nan().get_val(); + } + + // x = +inf => rsqrt(x) = 0 + return FPBits::zero(xbits.sign()).get_val(); + } + + // TODO: add float based approximation when + // LIBC_TARGET_CPU_HAS_FPU_DOUBLE is not defined + double result = 1.0 / fputil::sqrt<double>(fputil::cast<double>(x)); + + return fputil::cast<float>(result); +} + +} // namespace math +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_MATH_RSQRTF_H diff --git a/libc/src/__support/threads/callonce.h b/libc/src/__support/threads/callonce.h index 5392722..0ccbcf9 100644 --- a/libc/src/__support/threads/callonce.h +++ b/libc/src/__support/threads/callonce.h @@ -23,14 +23,8 @@ #endif namespace LIBC_NAMESPACE_DECL { - -// Common definitions -using CallOnceCallback = void(void); -namespace callonce_impl { -int callonce_slowpath(CallOnceFlag *flag, CallOnceCallback *callback); -} // namespace callonce_impl - -LIBC_INLINE int callonce(CallOnceFlag *flag, CallOnceCallback *callback) { +template <class CallOnceCallback> +LIBC_INLINE int callonce(CallOnceFlag *flag, CallOnceCallback callback) { if (LIBC_LIKELY(callonce_impl::callonce_fastpath(flag))) return 0; diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt index 14aaad2..39d2c6f 100644 --- a/libc/src/__support/threads/linux/CMakeLists.txt +++ b/libc/src/__support/threads/linux/CMakeLists.txt @@ -97,10 +97,8 @@ add_object_library( # value other than 0 is dangerous. We know. ) -add_object_library( +add_header_library( callonce - SRCS - callonce.cpp HDRS ../callonce.h callonce.h diff --git a/libc/src/__support/threads/linux/callonce.cpp b/libc/src/__support/threads/linux/callonce.cpp deleted file mode 100644 index c6e5f2a..0000000 --- a/libc/src/__support/threads/linux/callonce.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//===-- Linux implementation of the callonce 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/__support/threads/callonce.h" -#include "src/__support/macros/config.h" -#include "src/__support/threads/linux/callonce.h" -#include "src/__support/threads/linux/futex_utils.h" - -namespace LIBC_NAMESPACE_DECL { -namespace callonce_impl { -int callonce_slowpath(CallOnceFlag *flag, CallOnceCallback *func) { - auto *futex_word = reinterpret_cast<Futex *>(flag); - - FutexWordType not_called = NOT_CALLED; - - // The call_once call can return only after the called function |func| - // returns. So, we use futexes to synchronize calls with the same flag value. - if (futex_word->compare_exchange_strong(not_called, START)) { - func(); - auto status = futex_word->exchange(FINISH); - if (status == WAITING) - futex_word->notify_all(); - return 0; - } - - FutexWordType status = START; - if (futex_word->compare_exchange_strong(status, WAITING) || - status == WAITING) { - futex_word->wait(WAITING); - } - - return 0; -} -} // namespace callonce_impl -} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/__support/threads/linux/callonce.h b/libc/src/__support/threads/linux/callonce.h index b3ea3a5..70872b6 100644 --- a/libc/src/__support/threads/linux/callonce.h +++ b/libc/src/__support/threads/linux/callonce.h @@ -26,6 +26,31 @@ static constexpr FutexWordType FINISH = 0x33; LIBC_INLINE bool callonce_fastpath(CallOnceFlag *flag) { return flag->load(cpp::MemoryOrder::RELAXED) == FINISH; } + +template <class CallOnceCallback> +[[gnu::noinline, gnu::cold]] int callonce_slowpath(CallOnceFlag *flag, + CallOnceCallback callback) { + + auto *futex_word = reinterpret_cast<Futex *>(flag); + + FutexWordType not_called = NOT_CALLED; + + // The call_once call can return only after the called function |func| + // returns. So, we use futexes to synchronize calls with the same flag value. + if (futex_word->compare_exchange_strong(not_called, START)) { + callback(); + auto status = futex_word->exchange(FINISH); + if (status == WAITING) + futex_word->notify_all(); + return 0; + } + + FutexWordType status = START; + if (futex_word->compare_exchange_strong(status, WAITING) || status == WAITING) + futex_word->wait(WAITING); + + return 0; +} } // namespace callonce_impl } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 3c7e99f..e37e22f 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -517,6 +517,7 @@ add_math_entrypoint_object(roundevenf16) add_math_entrypoint_object(roundevenf128) add_math_entrypoint_object(roundevenbf16) +add_math_entrypoint_object(rsqrtf) add_math_entrypoint_object(rsqrtf16) add_math_entrypoint_object(scalbln) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 5738fe8..55f4aaf 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -989,6 +989,16 @@ add_entrypoint_object( ) add_entrypoint_object( + rsqrtf + SRCS + rsqrtf.cpp + HDRS + ../rsqrtf.h + DEPENDS + libc.src.__support.math.rsqrtf +) + +add_entrypoint_object( rsqrtf16 SRCS rsqrtf16.cpp diff --git a/libc/src/math/generic/rsqrtf.cpp b/libc/src/math/generic/rsqrtf.cpp new file mode 100644 index 0000000..6a38fa7 --- /dev/null +++ b/libc/src/math/generic/rsqrtf.cpp @@ -0,0 +1,16 @@ +//===-- Single-precision rsqrt 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/rsqrtf.h" +#include "src/__support/math/rsqrtf.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(float, rsqrtf, (float x)) { return math::rsqrtf(x); } + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/rsqrtf.h b/libc/src/math/rsqrtf.h new file mode 100644 index 0000000..bc55904 --- /dev/null +++ b/libc/src/math/rsqrtf.h @@ -0,0 +1,20 @@ +//===-- Implementation header for rsqrtf ------------------------*- 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_RSQRTF_H +#define LLVM_LIBC_SRC_MATH_RSQRTF_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +float rsqrtf(float x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_RSQRTF_H diff --git a/libc/src/pthread/pthread_once.cpp b/libc/src/pthread/pthread_once.cpp index d78644a..109f70c 100644 --- a/libc/src/pthread/pthread_once.cpp +++ b/libc/src/pthread/pthread_once.cpp @@ -17,8 +17,7 @@ namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(int, pthread_once, (pthread_once_t * flag, __pthread_once_func_t func)) { - return callonce(reinterpret_cast<CallOnceFlag *>(flag), - reinterpret_cast<CallOnceCallback *>(func)); + return callonce(reinterpret_cast<CallOnceFlag *>(flag), func); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt index 843111e..3cbabd1 100644 --- a/libc/src/stdfix/CMakeLists.txt +++ b/libc/src/stdfix/CMakeLists.txt @@ -89,6 +89,20 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk) ) endforeach() +foreach(suffix IN ITEMS r) + add_entrypoint_object( + ${suffix}divi + HDRS + ${suffix}divi.h + SRCS + ${suffix}divi.cpp + COMPILE_OPTIONS + ${libc_opt_high_flag} + DEPENDS + libc.src.__support.fixed_point.fx_bits + ) +endforeach() + add_entrypoint_object( uhksqrtus HDRS diff --git a/libc/src/stdfix/rdivi.cpp b/libc/src/stdfix/rdivi.cpp new file mode 100644 index 0000000..7021a8b --- /dev/null +++ b/libc/src/stdfix/rdivi.cpp @@ -0,0 +1,21 @@ +//===-- Implementation of rdivi 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 "rdivi.h" +#include "include/llvm-libc-macros/stdfix-macros.h" // fract +#include "src/__support/common.h" // LLVM_LIBC_FUNCTION +#include "src/__support/fixed_point/fx_bits.h" // fixed_point +#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(fract, rdivi, (int a, int b)) { + return fixed_point::divi<fract>(a, b); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdfix/rdivi.h b/libc/src/stdfix/rdivi.h new file mode 100644 index 0000000..aeda1ee --- /dev/null +++ b/libc/src/stdfix/rdivi.h @@ -0,0 +1,21 @@ +//===-- Implementation header for rdivi ------------------------*- 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_STDFIX_RDIVI_H +#define LLVM_LIBC_SRC_STDFIX_RDIVI_H + +#include "include/llvm-libc-macros/stdfix-macros.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +fract rdivi(int a, int b); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_STDFIX_RDIVI_H diff --git a/libc/src/sys/auxv/linux/CMakeLists.txt b/libc/src/sys/auxv/linux/CMakeLists.txt index 4884184c..39cfb7b 100644 --- a/libc/src/sys/auxv/linux/CMakeLists.txt +++ b/libc/src/sys/auxv/linux/CMakeLists.txt @@ -5,15 +5,6 @@ add_entrypoint_object( HDRS ../getauxval.h DEPENDS - libc.src.sys.prctl.prctl - libc.src.sys.mman.mmap - libc.src.sys.mman.munmap - libc.src.__support.threads.callonce - libc.src.__support.common - libc.src.errno.errno - libc.config.app_h - libc.src.fcntl.open - libc.src.unistd.read - libc.src.unistd.close - libc.include.sys_auxv + libc.src.__support.OSUtil.linux.auxv + libc.src.__support.libc_errno ) diff --git a/libc/src/sys/auxv/linux/getauxval.cpp b/libc/src/sys/auxv/linux/getauxval.cpp index b50c584..52d5a98 100644 --- a/libc/src/sys/auxv/linux/getauxval.cpp +++ b/libc/src/sys/auxv/linux/getauxval.cpp @@ -7,227 +7,14 @@ //===----------------------------------------------------------------------===// #include "src/sys/auxv/getauxval.h" -#include "config/app.h" -#include "hdr/fcntl_macros.h" -#include "src/__support/OSUtil/fcntl.h" -#include "src/__support/common.h" +#include "src/__support/OSUtil/linux/auxv.h" #include "src/__support/libc_errno.h" -#include "src/__support/macros/config.h" -#include <linux/auxvec.h> - -// for guarded initialization -#include "src/__support/threads/callonce.h" -#include "src/__support/threads/linux/futex_word.h" - -// ----------------------------------------------------------------------------- -// TODO: This file should not include other public libc functions. Calling other -// public libc functions is an antipattern within LLVM-libc. This needs to be -// cleaned up. DO NOT COPY THIS. -// ----------------------------------------------------------------------------- - -// for mallocing the global auxv -#include "src/sys/mman/mmap.h" -#include "src/sys/mman/munmap.h" - -// for reading /proc/self/auxv -#include "src/sys/prctl/prctl.h" -#include "src/unistd/read.h" - -// getauxval will work either with or without __cxa_atexit support. -// In order to detect if __cxa_atexit is supported, we define a weak symbol. -// We prefer __cxa_atexit as it is always defined as a C symbol whileas atexit -// may not be created via objcopy yet. Also, for glibc, atexit is provided via -// libc_nonshared.a rather than libc.so. So, it is may not be made ready for -// overlay builds. -extern "C" [[gnu::weak]] int __cxa_atexit(void (*callback)(void *), - void *payload, void *); namespace LIBC_NAMESPACE_DECL { - -constexpr static size_t MAX_AUXV_ENTRIES = 64; - -// Helper to recover or set errno -class AuxvErrnoGuard { -public: - AuxvErrnoGuard() : saved(libc_errno), failure(false) {} - ~AuxvErrnoGuard() { libc_errno = failure ? ENOENT : saved; } - void mark_failure() { failure = true; } - -private: - int saved; - bool failure; -}; - -// Helper to manage the memory -static AuxEntry *auxv = nullptr; - -class AuxvMMapGuard { -public: - constexpr static size_t AUXV_MMAP_SIZE = sizeof(AuxEntry) * MAX_AUXV_ENTRIES; - - AuxvMMapGuard() - : ptr(LIBC_NAMESPACE::mmap(nullptr, AUXV_MMAP_SIZE, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) {} - ~AuxvMMapGuard() { - if (ptr != MAP_FAILED) - LIBC_NAMESPACE::munmap(ptr, AUXV_MMAP_SIZE); - } - void submit_to_global() { - // atexit may fail, we do not set it to global in that case. - int ret = __cxa_atexit( - [](void *) { - LIBC_NAMESPACE::munmap(auxv, AUXV_MMAP_SIZE); - auxv = nullptr; - }, - nullptr, nullptr); - - if (ret != 0) - return; - - auxv = reinterpret_cast<AuxEntry *>(ptr); - ptr = MAP_FAILED; - } - bool allocated() const { return ptr != MAP_FAILED; } - void *get() const { return ptr; } - -private: - void *ptr; -}; - -class AuxvFdGuard { -public: - AuxvFdGuard() { - auto result = internal::open("/proc/self/auxv", O_RDONLY | O_CLOEXEC); - if (!result.has_value()) - fd = -1; - - fd = result.value(); - } - ~AuxvFdGuard() { - if (fd != -1) - internal::close(fd); - } - bool valid() const { return fd != -1; } - int get() const { return fd; } - -private: - int fd; -}; - -static void initialize_auxv_once(void) { - // If we cannot get atexit, we cannot register the cleanup function. - if (&__cxa_atexit == nullptr) - return; - - AuxvMMapGuard mmap_guard; - if (!mmap_guard.allocated()) - return; - auto *ptr = reinterpret_cast<AuxEntry *>(mmap_guard.get()); - - // We get one less than the max size to make sure the search always - // terminates. MMAP private pages are zeroed out already. - size_t available_size = AuxvMMapGuard::AUXV_MMAP_SIZE - sizeof(AuxEntryType); - // PR_GET_AUXV is only available on Linux kernel 6.1 and above. If this is not - // defined, we direcly fall back to reading /proc/self/auxv. In case the libc - // is compiled and run on separate kernels, we also check the return value of - // prctl. -#ifdef PR_GET_AUXV - int ret = prctl(PR_GET_AUXV, reinterpret_cast<unsigned long>(ptr), - available_size, 0, 0); - if (ret >= 0) { - mmap_guard.submit_to_global(); - return; - } -#endif - AuxvFdGuard fd_guard; - if (!fd_guard.valid()) - return; - auto *buf = reinterpret_cast<char *>(ptr); - libc_errno = 0; - bool error_detected = false; - // Read until we use up all the available space or we finish reading the file. - while (available_size != 0) { - ssize_t bytes_read = - LIBC_NAMESPACE::read(fd_guard.get(), buf, available_size); - if (bytes_read <= 0) { - if (libc_errno == EINTR) - continue; - // Now, we either have an non-recoverable error or we have reached the end - // of the file. Mark `error_detected` accordingly. - if (bytes_read == -1) - error_detected = true; - break; - } - buf += bytes_read; - available_size -= bytes_read; - } - // If we get out of the loop without an error, the auxv is ready. - if (!error_detected) - mmap_guard.submit_to_global(); -} - -static AuxEntry read_entry(int fd) { - AuxEntry buf; - size_t size = sizeof(AuxEntry); - char *ptr = reinterpret_cast<char *>(&buf); - while (size > 0) { - ssize_t ret = LIBC_NAMESPACE::read(fd, ptr, size); - if (ret < 0) { - if (libc_errno == EINTR) - continue; - // Error detected, return AT_NULL - buf.id = AT_NULL; - buf.value = AT_NULL; - break; - } - ptr += ret; - size -= ret; - } - return buf; -} - LLVM_LIBC_FUNCTION(unsigned long, getauxval, (unsigned long id)) { - // Fast path when libc is loaded by its own initialization code. In this case, - // app.auxv_ptr is already set to the auxv passed on the initial stack of the - // process. - AuxvErrnoGuard errno_guard; - - auto search_auxv = [&errno_guard](AuxEntry *auxv, - unsigned long id) -> AuxEntryType { - for (auto *ptr = auxv; ptr->id != AT_NULL; ptr++) - if (ptr->id == id) - return ptr->value; - - errno_guard.mark_failure(); - return AT_NULL; - }; - - // App is a weak symbol that is only defined if libc is linked to its own - // initialization routine. We need to check if it is null. - if (&app != nullptr) - return search_auxv(app.auxv_ptr, id); - - static FutexWordType once_flag; - LIBC_NAMESPACE::callonce(reinterpret_cast<CallOnceFlag *>(&once_flag), - initialize_auxv_once); - if (auxv != nullptr) - return search_auxv(auxv, id); - - // Fallback to use read without mmap - AuxvFdGuard fd_guard; - if (fd_guard.valid()) { - while (true) { - AuxEntry buf = read_entry(fd_guard.get()); - if (buf.id == AT_NULL) - break; - if (buf.id == id) - return buf.value; - } - } - - // cannot find the entry after all methods, mark failure and return 0 - errno_guard.mark_failure(); - return AT_NULL; + if (cpp::optional<unsigned long> val = auxv::get(id)) + return *val; + libc_errno = ENOENT; + return 0; } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/threads/call_once.cpp b/libc/src/threads/call_once.cpp index 8466cd62..b60a20d 100644 --- a/libc/src/threads/call_once.cpp +++ b/libc/src/threads/call_once.cpp @@ -17,8 +17,7 @@ namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(void, call_once, (once_flag * flag, __call_once_func_t func)) { - callonce(reinterpret_cast<CallOnceFlag *>(flag), - reinterpret_cast<CallOnceCallback *>(func)); + callonce(reinterpret_cast<CallOnceFlag *>(flag), func); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt index dff6ba2..4eb3c7d 100644 --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -563,8 +563,8 @@ add_entrypoint_object( DEPENDS libc.include.unistd libc.include.sys_auxv - libc.src.errno.errno - libc.src.sys.auxv.getauxval + libc.src.__support.libc_errno + libc.src.__support.OSUtil.linux.auxv ) add_entrypoint_object( diff --git a/libc/src/unistd/linux/sysconf.cpp b/libc/src/unistd/linux/sysconf.cpp index 03f224b..979e178 100644 --- a/libc/src/unistd/linux/sysconf.cpp +++ b/libc/src/unistd/linux/sysconf.cpp @@ -11,17 +11,20 @@ #include "src/__support/common.h" #include "hdr/unistd_macros.h" +#include "src/__support/OSUtil/linux/auxv.h" #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" -#include "src/sys/auxv/getauxval.h" -#include <sys/auxv.h> namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(long, sysconf, (int name)) { long ret = 0; - if (name == _SC_PAGESIZE) - return static_cast<long>(getauxval(AT_PAGESZ)); + if (name == _SC_PAGESIZE) { + cpp::optional<unsigned long> page_size = auxv::get(AT_PAGESZ); + if (page_size) + return static_cast<long>(*page_size); + ret = -1; + } // TODO: Complete the rest of the sysconf options. if (ret < 0) { diff --git a/libc/startup/gpu/amdgpu/start.cpp b/libc/startup/gpu/amdgpu/start.cpp index 8bd0c3a..48f095d 100644 --- a/libc/startup/gpu/amdgpu/start.cpp +++ b/libc/startup/gpu/amdgpu/start.cpp @@ -14,12 +14,16 @@ #include "src/stdlib/exit.h" extern "C" int main(int argc, char **argv, char **envp); +extern "C" void __cxa_finalize(void *dso); namespace LIBC_NAMESPACE_DECL { // FIXME: Factor this out into common logic so we don't need to stub it here. void teardown_main_tls() {} +// FIXME: Touch this symbol to force this to be linked in statically. +volatile void *dummy = &LIBC_NAMESPACE::rpc::client; + DataEnvironment app; extern "C" uintptr_t __init_array_start[]; @@ -68,9 +72,8 @@ _start(int argc, char **argv, char **envp, int *ret) { extern "C" [[gnu::visibility("protected"), clang::amdgpu_kernel, clang::amdgpu_flat_work_group_size(1, 1), clang::amdgpu_max_num_work_groups(1)]] void -_end(int retval) { - // Only a single thread should call `exit` here, the rest should gracefully - // return from the kernel. This is so only one thread calls the destructors - // registred with 'atexit' above. - LIBC_NAMESPACE::exit(retval); +_end() { + // Only a single thread should call the destructors registred with 'atexit'. + // The loader utility will handle the actual exit and return code cleanly. + __cxa_finalize(nullptr); } diff --git a/libc/startup/gpu/nvptx/start.cpp b/libc/startup/gpu/nvptx/start.cpp index bc529b3..ce8f5bbb 100644 --- a/libc/startup/gpu/nvptx/start.cpp +++ b/libc/startup/gpu/nvptx/start.cpp @@ -14,6 +14,7 @@ #include "src/stdlib/exit.h" extern "C" int main(int argc, char **argv, char **envp); +extern "C" void __cxa_finalize(void *dso); namespace LIBC_NAMESPACE_DECL { @@ -22,6 +23,9 @@ DataEnvironment app; // FIXME: Factor this out into common logic so we don't need to stub it here. void teardown_main_tls() {} +// FIXME: Touch this symbol to force this to be linked in statically. +volatile void *dummy = &LIBC_NAMESPACE::rpc::client; + extern "C" { // Nvidia's 'nvlink' linker does not provide these symbols. We instead need // to manually create them and update the globals in the loader implememtation. @@ -70,9 +74,8 @@ _start(int argc, char **argv, char **envp, int *ret) { __atomic_fetch_or(ret, main(argc, argv, envp), __ATOMIC_RELAXED); } -extern "C" [[gnu::visibility("protected"), clang::nvptx_kernel]] void -_end(int retval) { - // To finis the execution we invoke all the callbacks registered via 'atexit' - // and then exit with the appropriate return value. - LIBC_NAMESPACE::exit(retval); +extern "C" [[gnu::visibility("protected"), clang::nvptx_kernel]] void _end() { + // Only a single thread should call the destructors registred with 'atexit'. + // The loader utility will handle the actual exit and return code cleanly. + __cxa_finalize(nullptr); } diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt index 7af1819..df2c4b9 100644 --- a/libc/startup/linux/CMakeLists.txt +++ b/libc/startup/linux/CMakeLists.txt @@ -105,6 +105,7 @@ add_object_library( libc.src.stdlib.exit libc.src.stdlib.atexit libc.src.unistd.environ + libc.src.__support.OSUtil.linux.auxv COMPILE_OPTIONS -ffreestanding # To avoid compiler warnings about calling the main function. -fno-builtin # avoid emit unexpected calls diff --git a/libc/startup/linux/do_start.cpp b/libc/startup/linux/do_start.cpp index 94c4ec7..a67bf18 100644 --- a/libc/startup/linux/do_start.cpp +++ b/libc/startup/linux/do_start.cpp @@ -9,6 +9,7 @@ #include "config/linux/app.h" #include "hdr/stdint_proxy.h" #include "include/llvm-libc-macros/link-macros.h" +#include "src/__support/OSUtil/linux/auxv.h" #include "src/__support/OSUtil/syscall.h" #include "src/__support/macros/config.h" #include "src/__support/threads/thread.h" @@ -88,17 +89,19 @@ void teardown_main_tls() { cleanup_tls(tls.addr, tls.size); } // denoted by an AT_NULL entry. ElfW(Phdr) *program_hdr_table = nullptr; uintptr_t program_hdr_count = 0; - app.auxv_ptr = reinterpret_cast<AuxEntry *>(env_end_marker + 1); - for (auto *aux_entry = app.auxv_ptr; aux_entry->id != AT_NULL; ++aux_entry) { - switch (aux_entry->id) { + auxv::Vector::initialize_unsafe( + reinterpret_cast<const auxv::Entry *>(env_end_marker + 1)); + auxv::Vector auxvec; + for (const auto &aux_entry : auxvec) { + switch (aux_entry.type) { case AT_PHDR: - program_hdr_table = reinterpret_cast<ElfW(Phdr) *>(aux_entry->value); + program_hdr_table = reinterpret_cast<ElfW(Phdr) *>(aux_entry.val); break; case AT_PHNUM: - program_hdr_count = aux_entry->value; + program_hdr_count = aux_entry.val; break; case AT_PAGESZ: - app.page_size = aux_entry->value; + app.page_size = aux_entry.val; break; default: break; // TODO: Read other useful entries from the aux vector. diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt index dbc3889c..f341d3f 100644 --- a/libc/test/shared/CMakeLists.txt +++ b/libc/test/shared/CMakeLists.txt @@ -53,6 +53,6 @@ add_fp_unittest( libc.src.__support.math.ldexpf libc.src.__support.math.ldexpf128 libc.src.__support.math.ldexpf16 + libc.src.__support.math.rsqrtf libc.src.__support.math.rsqrtf16 - ) diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp index d118d96..477b7ec 100644 --- a/libc/test/shared/shared_math_test.cpp +++ b/libc/test/shared/shared_math_test.cpp @@ -70,6 +70,8 @@ TEST(LlvmLibcSharedMathTest, AllFloat) { ASSERT_FP_EQ(float(8 << 5), LIBC_NAMESPACE::shared::ldexpf(8.0f, 5)); ASSERT_FP_EQ(float(-1 * (8 << 5)), LIBC_NAMESPACE::shared::ldexpf(-8.0f, 5)); + + EXPECT_FP_EQ(0x1p+0f, LIBC_NAMESPACE::shared::rsqrtf(1.0f)); } TEST(LlvmLibcSharedMathTest, AllDouble) { diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 2d2d528..b3f54ab 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -1679,6 +1679,17 @@ add_fp_unittest( ) add_fp_unittest( + rsqrtf_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + rsqrtf_test.cpp + DEPENDS + libc.src.math.rsqrtf +) + +add_fp_unittest( rsqrtf16_test NEED_MPFR SUITE diff --git a/libc/test/src/math/RsqrtTest.h b/libc/test/src/math/RsqrtTest.h new file mode 100644 index 0000000..d11d708 --- /dev/null +++ b/libc/test/src/math/RsqrtTest.h @@ -0,0 +1,74 @@ +//===-- Utility class to test rsqrt[f|l] ------------------------*- 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_TEST_SRC_MATH_RSQRTTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_RSQRTTEST_H + +#include "test/UnitTest/FEnvSafeTest.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +template <typename OutType, typename InType = OutType> +class RsqrtTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { + + DECLARE_SPECIAL_CONSTANTS(InType) + + static constexpr StorageType HIDDEN_BIT = + StorageType(1) << LIBC_NAMESPACE::fputil::FPBits<InType>::FRACTION_LEN; + +public: + using RsqrtFunc = OutType (*)(InType); + + // Subnormal inputs: probe both power-of-two mantissas and an even sampling + // across the subnormal range. + void test_denormal_values(RsqrtFunc func) { + // Powers of two in the subnormal mantissa space. + for (StorageType mant = 1; mant < HIDDEN_BIT; mant <<= 1) { + FPBits denormal(zero); + denormal.set_mantissa(mant); + InType x = denormal.get_val(); + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Rsqrt, x, func(x), 0.5); + } + + // Even sampling across all subnormals. + constexpr StorageType COUNT = 200'001; + constexpr StorageType STEP = HIDDEN_BIT / COUNT; + for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { + InType x = FPBits(i).get_val(); + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Rsqrt, x, func(x), 0.5); + } + } + + // Positive normal range sampling: skip NaNs and negative values. + void test_normal_range(RsqrtFunc func) { + constexpr StorageType COUNT = 200'001; + constexpr StorageType STEP = STORAGE_MAX / COUNT; + for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { + FPBits x_bits(v); + InType x = x_bits.get_val(); + if (x_bits.is_nan() || x_bits.is_neg()) + continue; + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Rsqrt, x, func(x), 0.5); + } + } +}; + +#define LIST_RSQRT_TESTS(T, func) \ + using LlvmLibcRsqrtTest = RsqrtTest<T, T>; \ + TEST_F(LlvmLibcRsqrtTest, DenormalValues) { test_denormal_values(&func); } \ + TEST_F(LlvmLibcRsqrtTest, NormalRange) { test_normal_range(&func); } + +#define LIST_NARROWING_RSQRT_TESTS(OutType, InType, func) \ + using LlvmLibcRsqrtTest = RsqrtTest<OutType, InType>; \ + TEST_F(LlvmLibcRsqrtTest, DenormalValues) { test_denormal_values(&func); } \ + TEST_F(LlvmLibcRsqrtTest, NormalRange) { test_normal_range(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_RSQRTTEST_H diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index 07c36f424..1583ab6 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -27,6 +27,21 @@ add_fp_unittest( ) add_fp_unittest( + rsqrtf_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + rsqrtf_test.cpp + DEPENDS + .exhaustive_test + libc.src.math.rsqrtf + LINK_LIBRARIES + -lpthread +) + +add_fp_unittest( sinf_test NO_RUN_POSTBUILD NEED_MPFR diff --git a/libc/test/src/math/exhaustive/rsqrtf_test.cpp b/libc/test/src/math/exhaustive/rsqrtf_test.cpp new file mode 100644 index 0000000..803c9b4 --- /dev/null +++ b/libc/test/src/math/exhaustive/rsqrtf_test.cpp @@ -0,0 +1,27 @@ +//===-- Exhaustive test for rsqrtf ----------------------------------------===// +// +// 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 "exhaustive_test.h" +#include "src/math/rsqrtf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +using LlvmLibcRsqrtfTest = LIBC_NAMESPACE::testing::FPTest<float>; + +using LlvmLibcRsqrtfExhaustiveTest = + LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Rsqrt, + LIBC_NAMESPACE::rsqrtf>; + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +// Range: [0, Inf] +static constexpr uint32_t POS_START = 0x0000'0000U; +static constexpr uint32_t POS_STOP = 0x7f80'0000U; + +TEST_F(LlvmLibcRsqrtfExhaustiveTest, PositiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP); +} diff --git a/libc/test/src/math/rsqrtf_test.cpp b/libc/test/src/math/rsqrtf_test.cpp new file mode 100644 index 0000000..4e476c0 --- /dev/null +++ b/libc/test/src/math/rsqrtf_test.cpp @@ -0,0 +1,12 @@ +//===-- Unittests for rsqrtf ----------------------------------------------===// +// 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 "RsqrtTest.h" + +#include "src/math/rsqrtf.h" + +LIST_RSQRT_TESTS(float, LIBC_NAMESPACE::rsqrtf) diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 42a97ba..5afd3a9 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -3503,14 +3503,27 @@ add_fp_unittest( ) add_fp_unittest( + rsqrtf_test + SUITE + libc-math-smoke-tests + SRCS + rsqrtf_test.cpp + DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros + libc.src.math.rsqrtf +) + +add_fp_unittest( rsqrtf16_test SUITE libc-math-smoke-tests SRCS rsqrtf16_test.cpp DEPENDS + libc.hdr.errno_macros + libc.hdr.fenv_macros libc.src.math.rsqrtf16 - libc.hdr.errno_macros ) add_fp_unittest( diff --git a/libc/test/src/math/smoke/rsqrtf16_test.cpp b/libc/test/src/math/smoke/rsqrtf16_test.cpp index e103e73..13d9a33 100644 --- a/libc/test/src/math/smoke/rsqrtf16_test.cpp +++ b/libc/test/src/math/smoke/rsqrtf16_test.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "hdr/errno_macros.h" +#include "hdr/fenv_macros.h" #include "src/__support/FPUtil/cast.h" #include "src/__support/macros/properties/architectures.h" #include "src/math/rsqrtf16.h" diff --git a/libc/test/src/math/smoke/rsqrtf_test.cpp b/libc/test/src/math/smoke/rsqrtf_test.cpp new file mode 100644 index 0000000..d71b82c --- /dev/null +++ b/libc/test/src/math/smoke/rsqrtf_test.cpp @@ -0,0 +1,41 @@ +//===-- Unittests for rsqrtf --------------------------------------------===// +// +// 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/errno_macros.h" +#include "hdr/fenv_macros.h" +#include "src/math/rsqrtf.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcRsqrtfTest = LIBC_NAMESPACE::testing::FPTest<float>; + +TEST_F(LlvmLibcRsqrtfTest, SpecialNumbers) { + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::rsqrtf(aNaN)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::rsqrtf(sNaN), FE_INVALID); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(inf, LIBC_NAMESPACE::rsqrtf(zero)); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ(neg_inf, LIBC_NAMESPACE::rsqrtf(neg_zero)); + EXPECT_MATH_ERRNO(ERANGE); + + EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::rsqrtf(1.0f)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::rsqrtf(inf)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::rsqrtf(neg_inf)); + EXPECT_MATH_ERRNO(EDOM); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::rsqrtf(-2.0f)); + EXPECT_MATH_ERRNO(EDOM); +} diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt index e2b4bc1..7415222 100644 --- a/libc/test/src/stdfix/CMakeLists.txt +++ b/libc/test/src/stdfix/CMakeLists.txt @@ -120,6 +120,22 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk) ) endforeach() +foreach(suffix IN ITEMS r) + add_libc_test( + ${suffix}divi_test + SUITE + libc-stdfix-tests + HDRS + DivITest.h + SRCS + ${suffix}divi_test.cpp + DEPENDS + libc.src.stdfix.${suffix}divi + libc.src.__support.fixed_point.fx_bits + libc.hdr.signal_macros + ) +endforeach() + add_libc_test( uhksqrtus_test SUITE diff --git a/libc/test/src/stdfix/DivITest.h b/libc/test/src/stdfix/DivITest.h new file mode 100644 index 0000000..ad15637 --- /dev/null +++ b/libc/test/src/stdfix/DivITest.h @@ -0,0 +1,74 @@ +//===-- Utility class to test fxdivi functions ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/type_traits.h" +#include "src/__support/fixed_point/fx_bits.h" +#include "src/__support/fixed_point/fx_rep.h" +#include "test/UnitTest/Test.h" + +template <typename XType> XType get_epsilon() = delete; +template <> fract get_epsilon() { return FRACT_EPSILON; } +template <> unsigned fract get_epsilon() { return UFRACT_EPSILON; } +template <> long fract get_epsilon() { return LFRACT_EPSILON; } + +template <typename XType> +class DivITest : public LIBC_NAMESPACE::testing::Test { + using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<XType>; + using FXBits = LIBC_NAMESPACE::fixed_point::FXBits<XType>; + +public: + typedef XType (*DivIFunc)(int, int); + + void testBasic(DivIFunc func) { + XType epsilon = get_epsilon<XType>(); + EXPECT_LT((func(2, 3) - 0.666656494140625r), epsilon); + EXPECT_LT((func(3, 4) - 0.75r), epsilon); + EXPECT_LT((func(1043, 2764) - 0.3773516643r), epsilon); + EXPECT_LT((func(60000, 720293) - 0.08329943509r), epsilon); + + EXPECT_EQ(func(128, 256), 0.5r); + EXPECT_EQ(func(1, 2), 0.5r); + EXPECT_EQ(func(1, 4), 0.25r); + EXPECT_EQ(func(1, 8), 0.125r); + EXPECT_EQ(func(1, 16), 0.0625r); + + EXPECT_EQ(func(-1, 2), -0.5r); + EXPECT_EQ(func(1, -4), -0.25r); + EXPECT_EQ(func(-1, 8), -0.125r); + EXPECT_EQ(func(1, -16), -0.0625r); + } + + void testSpecial(DivIFunc func) { + XType epsilon = get_epsilon<XType>(); + EXPECT_EQ(func(0, 10), 0.r); + EXPECT_EQ(func(0, -10), 0.r); + EXPECT_EQ(func(-(1 << FRACT_FBIT), 1 << FRACT_FBIT), FRACT_MIN); + EXPECT_EQ(func((1 << FRACT_FBIT) - 1, 1 << FRACT_FBIT), FRACT_MAX); + // From Section 7.18a.6.1, functions returning a fixed-point value, the + // return value is saturated on overflow. + EXPECT_EQ(func(INT_MAX, INT_MAX), FRACT_MAX); + EXPECT_LT(func(INT_MAX - 1, INT_MAX) - 0.99999999r, epsilon); + EXPECT_EQ(func(INT_MIN, INT_MAX), FRACT_MIN); + // Expecting 0 here as fract is not precise enough to + // handle 1/INT_MAX + EXPECT_LT(func(1, INT_MAX) - 0.r, epsilon); + // This results in 1.1739, which should be saturated to FRACT_MAX + EXPECT_EQ(func(27, 23), FRACT_MAX); + + EXPECT_EQ(func(INT_MIN, 1), FRACT_MIN); + EXPECT_LT(func(1, INT_MIN) - 0.r, epsilon); + + EXPECT_EQ(func(INT_MIN, INT_MIN), 1.r); + } +}; + +#define LIST_DIVI_TESTS(Name, XType, func) \ + using LlvmLibc##Name##diviTest = DivITest<XType>; \ + TEST_F(LlvmLibc##Name##diviTest, Basic) { testBasic(&func); } \ + TEST_F(LlvmLibc##Name##diviTest, Special) { testSpecial(&func); } \ + static_assert(true, "Require semicolon.") diff --git a/libc/test/src/stdfix/rdivi_test.cpp b/libc/test/src/stdfix/rdivi_test.cpp new file mode 100644 index 0000000..10ab366 --- /dev/null +++ b/libc/test/src/stdfix/rdivi_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for rdivi -----------------------------------------------===// +// +// 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 "DivITest.h" + +#include "llvm-libc-macros/stdfix-macros.h" // fract +#include "src/stdfix/rdivi.h" + +LIST_DIVI_TESTS(r, fract, LIBC_NAMESPACE::rdivi); diff --git a/libc/utils/buildbot/Dockerfile b/libc/utils/buildbot/Dockerfile index 8c497be..bbca8df 100644 --- a/libc/utils/buildbot/Dockerfile +++ b/libc/utils/buildbot/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:10 +FROM docker.io/debian:10 # Installing dependencies. RUN dpkg --add-architecture i386 |