diff options
Diffstat (limited to 'libc')
-rw-r--r-- | libc/config/linux/riscv/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/config/linux/x86_64/entrypoints.txt | 1 | ||||
-rw-r--r-- | libc/docs/headers/stdfix.rst | 2 | ||||
-rw-r--r-- | libc/include/stdfix.yaml | 8 | ||||
-rw-r--r-- | libc/src/__support/File/linux/lseekImpl.h | 9 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/linux/CMakeLists.txt | 3 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/linux/auxv.h | 7 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/linux/vdso.cpp | 13 | ||||
-rw-r--r-- | libc/src/__support/fixed_point/fx_bits.h | 111 | ||||
-rw-r--r-- | libc/src/stdfix/CMakeLists.txt | 14 | ||||
-rw-r--r-- | libc/src/stdfix/rdivi.cpp | 21 | ||||
-rw-r--r-- | libc/src/stdfix/rdivi.h | 21 | ||||
-rw-r--r-- | libc/src/unistd/linux/CMakeLists.txt | 4 | ||||
-rw-r--r-- | libc/src/unistd/linux/sysconf.cpp | 11 | ||||
-rw-r--r-- | libc/startup/gpu/amdgpu/start.cpp | 13 | ||||
-rw-r--r-- | libc/startup/gpu/nvptx/start.cpp | 13 | ||||
-rw-r--r-- | libc/test/src/stdfix/CMakeLists.txt | 16 | ||||
-rw-r--r-- | libc/test/src/stdfix/DivITest.h | 74 | ||||
-rw-r--r-- | libc/test/src/stdfix/rdivi_test.cpp | 14 | ||||
-rw-r--r-- | libc/utils/buildbot/Dockerfile | 2 |
20 files changed, 324 insertions, 34 deletions
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 5f407e8..99c9d3d 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -1022,6 +1022,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..82a83a2 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1058,6 +1058,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/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/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/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 f6377ca..0a60377 100644 --- a/libc/src/__support/OSUtil/linux/CMakeLists.txt +++ b/libc/src/__support/OSUtil/linux/CMakeLists.txt @@ -70,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 index 7fb996f..894868a 100644 --- a/libc/src/__support/OSUtil/linux/auxv.h +++ b/libc/src/__support/OSUtil/linux/auxv.h @@ -81,7 +81,12 @@ LIBC_INLINE void Vector::initialize_unsafe(const Entry *auxv) { [[gnu::cold]] LIBC_INLINE void Vector::fallback_initialize_unsync() { constexpr size_t AUXV_MMAP_SIZE = FALLBACK_AUXV_ENTRIES * sizeof(Entry); - long mmap_ret = syscall_impl<long>(SYS_mmap, nullptr, AUXV_MMAP_SIZE, +#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. 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/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/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/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 |