diff --git a/libc/src/__support/builtin_wrappers.h b/libc/src/__support/builtin_wrappers.h --- a/libc/src/__support/builtin_wrappers.h +++ b/libc/src/__support/builtin_wrappers.h @@ -14,6 +14,18 @@ #include "src/__support/CPP/type_traits.h" #include "src/__support/compiler_features.h" +#if defined __has_builtin +#if __has_builtin(__builtin_clzs) +#define LLVM_LIBC_HAS_BUILTIN_CLZS +#endif +#endif + +#if defined __has_builtin +#if __has_builtin(__builtin_ctzs) +#define LLVM_LIBC_HAS_BUILTIN_CTZS +#endif +#endif + namespace __llvm_libc { // The following overloads are matched based on what is accepted by @@ -30,6 +42,14 @@ } template static inline int clz(T val); +template <> inline int clz(unsigned short val) { +#if defined LLVM_LIBC_HAS_BUILTIN_CLZS + return __builtin_clzs(val); +#else + return __builtin_clz(static_cast(val)) - + 8 * (sizeof(unsigned int) - sizeof(unsigned short)); +#endif +} template <> inline int clz(unsigned int val) { return __builtin_clz(val); } @@ -41,6 +61,13 @@ } template static inline int ctz(T val); +template <> inline int ctz(unsigned short val) { +#if defined LLVM_LIBC_HAS_BUILTIN_CTZS + return __builtin_ctzs(val); +#else + return __builtin_ctz(static_cast(val)); +#endif +} template <> inline int ctz(unsigned int val) { return __builtin_ctz(val); } diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt --- a/libc/test/src/__support/CMakeLists.txt +++ b/libc/test/src/__support/CMakeLists.txt @@ -97,6 +97,17 @@ libc.src.__support.fixedvector ) +add_libc_unittest( + builtin_wrappers_test + SUITE + libc_support_unittests + SRCS + builtin_wrappers_test.cpp + DEPENDS + libc.src.__support.builtin_wrappers + libc.src.__support.CPP.limits +) + add_executable( libc_str_to_float_comparison_test str_to_float_comparison_test.cpp diff --git a/libc/test/src/__support/builtin_wrappers_test.cpp b/libc/test/src/__support/builtin_wrappers_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/builtin_wrappers_test.cpp @@ -0,0 +1,116 @@ +//===-- Unittests for Builtin Wrappers ------------------------------------===// +// +// 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/limits.h" +#include "src/__support/builtin_wrappers.h" +#include "utils/UnitTest/LibcTest.h" + +namespace __llvm_libc { + +template static int trivial_ctz(T x) { + size_t i = 0; + while (i < 8 * sizeof(T)) { + if (x & 1) + break; + i++; + x >>= 1; + } + return static_cast(i); +} + +template static int trivial_clz(T x) { + size_t i = 0; + while (x != 0) { + i++; + x >>= 1; + } + return 8 * sizeof(T) - static_cast(i); +} + +template +struct LlvmLibcBuiltinWrapperZeroCount : testing::Test { + using Type = T; + void check_all(const T min, const T max) { + + for (T i = min; i <= max; i++) { + if (i != 0) { + ASSERT_EQ(Trivial(i), Unsafe(i)); + } + ASSERT_EQ(Trivial(i), Safe(i)); + if (i == max) + break; + } + } + void per_byte() { + for (size_t i = 0; i < sizeof(T); ++i) { + for (T j = 0; j <= 0xFF; ++j) { + T target = j << (8 * i); + if (target != 0) { + ASSERT_EQ(Trivial(target), Unsafe(target)); + } + ASSERT_EQ(Trivial(target), Safe(target)); + } + } + } +}; + +template +using LlvmLibcBuiltinWrapperClz = + LlvmLibcBuiltinWrapperZeroCount, safe_clz, + unsafe_clz>; + +template +using LlvmLibcBuiltinWrapperCtz = + LlvmLibcBuiltinWrapperZeroCount, safe_ctz, + unsafe_ctz>; + +using LlvmLibcBuiltinWrapperUnsignedShortClz = + LlvmLibcBuiltinWrapperClz; + +using LlvmLibcBuiltinWrapperUnsignedShortCtz = + LlvmLibcBuiltinWrapperCtz; + +using LlvmLibcBuiltinWrapperUnsignedIntClz = + LlvmLibcBuiltinWrapperClz; + +using LlvmLibcBuiltinWrapperUnsignedIntCtz = + LlvmLibcBuiltinWrapperCtz; + +using LlvmLibcBuiltinWrapperUnsignedLongClz = + LlvmLibcBuiltinWrapperClz; + +using LlvmLibcBuiltinWrapperUnsignedLongCtz = + LlvmLibcBuiltinWrapperCtz; + +using LlvmLibcBuiltinWrapperUnsignedLongLongClz = + LlvmLibcBuiltinWrapperClz; + +using LlvmLibcBuiltinWrapperUnsignedLongLongCtz = + LlvmLibcBuiltinWrapperCtz; + +TEST_F(LlvmLibcBuiltinWrapperUnsignedShortClz, CheckAll) { + check_all(cpp::numeric_limits::min(), cpp::numeric_limits::max()); +} + +TEST_F(LlvmLibcBuiltinWrapperUnsignedShortCtz, CheckAll) { + check_all(cpp::numeric_limits::min(), cpp::numeric_limits::max()); +} + +TEST_F(LlvmLibcBuiltinWrapperUnsignedIntClz, CheckPerByte) { per_byte(); } + +TEST_F(LlvmLibcBuiltinWrapperUnsignedIntCtz, CheckPerByte) { per_byte(); } + +TEST_F(LlvmLibcBuiltinWrapperUnsignedLongClz, CheckPerByte) { per_byte(); } + +TEST_F(LlvmLibcBuiltinWrapperUnsignedLongCtz, CheckPerByte) { per_byte(); } + +TEST_F(LlvmLibcBuiltinWrapperUnsignedLongLongClz, CheckPerByte) { per_byte(); } + +TEST_F(LlvmLibcBuiltinWrapperUnsignedLongLongCtz, CheckPerByte) { per_byte(); } + +} // namespace __llvm_libc