117 changes: 117 additions & 0 deletions libc/src/__support/bit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//===-- Mimics llvm/ADT/Bit.h -----------------------------------*- C++ -*-===//
// Provides useful bit functions.
//
// 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_BIT_H
#define LLVM_LIBC_SRC___SUPPORT_BIT_H

#include "src/__support/CPP/type_traits.h" // make_unsigned
#include "src/__support/macros/attributes.h" // LIBC_INLINE

namespace LIBC_NAMESPACE {

// The following overloads are matched based on what is accepted by
// __builtin_clz/ctz* rather than using the exactly-sized aliases from stdint.h.
// This way, we can avoid making any assumptions about integer sizes and let the
// compiler match for us.
namespace __internal {

template <typename T> LIBC_INLINE int constexpr correct_zero(T val, int bits) {
if (val == T(0))
return sizeof(T(0)) * 8;
else
return bits;
}

template <typename T> LIBC_INLINE constexpr int clz(T val);
template <> LIBC_INLINE int clz<unsigned char>(unsigned char val) {
return __builtin_clz(static_cast<unsigned int>(val)) -
8 * static_cast<int>(sizeof(unsigned int) - sizeof(unsigned char));
}
template <> LIBC_INLINE int clz<unsigned short>(unsigned short val) {
return __builtin_clz(static_cast<unsigned int>(val)) -
8 * static_cast<int>(sizeof(unsigned int) - sizeof(unsigned short));
}
template <> LIBC_INLINE int clz<unsigned int>(unsigned int val) {
return __builtin_clz(val);
}
template <>
LIBC_INLINE constexpr int clz<unsigned long int>(unsigned long int val) {
return __builtin_clzl(val);
}
template <>
LIBC_INLINE constexpr int
clz<unsigned long long int>(unsigned long long int val) {
return __builtin_clzll(val);
}

template <typename T> LIBC_INLINE constexpr int ctz(T val);
template <> LIBC_INLINE int ctz<unsigned char>(unsigned char val) {
return __builtin_ctz(static_cast<unsigned int>(val));
}
template <> LIBC_INLINE int ctz<unsigned short>(unsigned short val) {
return __builtin_ctz(static_cast<unsigned int>(val));
}
template <> LIBC_INLINE int ctz<unsigned int>(unsigned int val) {
return __builtin_ctz(val);
}
template <>
LIBC_INLINE constexpr int ctz<unsigned long int>(unsigned long int val) {
return __builtin_ctzl(val);
}
template <>
LIBC_INLINE constexpr int
ctz<unsigned long long int>(unsigned long long int val) {
return __builtin_ctzll(val);
}
} // namespace __internal

template <typename T> LIBC_INLINE constexpr int safe_ctz(T val) {
return __internal::correct_zero(val, __internal::ctz(val));
}

template <typename T> LIBC_INLINE constexpr int unsafe_ctz(T val) {
return __internal::ctz(val);
}

template <typename T> LIBC_INLINE constexpr int safe_clz(T val) {
return __internal::correct_zero(val, __internal::clz(val));
}

template <typename T> LIBC_INLINE constexpr int unsafe_clz(T val) {
return __internal::clz(val);
}

template <typename T> LIBC_INLINE constexpr T next_power_of_two(T val) {
if (val == 0)
return 1;
T idx = safe_clz(val - 1);
return static_cast<T>(1) << ((8ull * sizeof(T)) - idx);
}

template <typename T> LIBC_INLINE constexpr bool is_power_of_two(T val) {
return val != 0 && (val & (val - 1)) == 0;
}

template <typename T> LIBC_INLINE constexpr T offset_to(T val, T align) {
return (-val) & (align - 1);
}

template <typename T> LIBC_INLINE constexpr T rotate_left(T val, T amount) {
// Implementation taken from "Safe, Efficient, and Portable Rotate in C/C++"
// https://blog.regehr.org/archives/1063
// Using the safe version as the rotation pattern is now recognized by both
// GCC and Clang.
using U = cpp::make_unsigned_t<T>;
U v = static_cast<U>(val);
U a = static_cast<U>(amount);
return (v << a) | (v >> ((-a) & (sizeof(U) * 8 - 1)));
}
} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC___SUPPORT_BIT_H
9 changes: 4 additions & 5 deletions libc/src/__support/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_HASH_H
#define LLVM_LIBC_SRC___SUPPORT_HASH_H

#include "src/__support/CPP/bit.h" // rotl
#include "src/__support/CPP/limits.h" // numeric_limits
#include "src/__support/UInt128.h" // UInt128
#include "src/__support/bit.h" // rotate_left
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include <stdint.h> // For uint64_t

Expand Down Expand Up @@ -104,7 +103,7 @@ class HashState {
uint64_t combined =
folded_multiply(low ^ extra_keys[0], high ^ extra_keys[1]);
buffer = (buffer + pad) ^ combined;
buffer = cpp::rotl(buffer, ROTATE);
buffer = rotate_left(buffer, ROTATE);
}
LIBC_INLINE static uint64_t mix(uint64_t seed) {
HashState mixer{RANDOMNESS[0][0], RANDOMNESS[0][1], RANDOMNESS[0][2],
Expand Down Expand Up @@ -153,9 +152,9 @@ class HashState {
}
}
LIBC_INLINE uint64_t finish() {
int rot = buffer & 63;
uint64_t rot = buffer & 63;
uint64_t folded = folded_multiply(buffer, pad);
return cpp::rotl(folded, rot);
return rotate_left(folded, rot);
}
};

Expand Down
1 change: 1 addition & 0 deletions libc/src/__support/integer_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "src/__support/CPP/type_traits.h"
#include "src/__support/common.h"

#include "bit.h"
#include "math_extras.h"
#include "number_pair.h"

Expand Down
17 changes: 3 additions & 14 deletions libc/src/__support/memory_size.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/bit.h" // has_single_bit
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/bit.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/optimization.h"
#include "src/string/memory_utils/utils.h"

namespace LIBC_NAMESPACE {
namespace internal {
Expand All @@ -38,26 +37,17 @@ class SafeMemSize {
public:
LIBC_INLINE_VAR static constexpr size_t MAX_MEM_SIZE =
static_cast<size_t>(cpp::numeric_limits<type>::max());

LIBC_INLINE explicit SafeMemSize(size_t value)
: value(value <= MAX_MEM_SIZE ? static_cast<type>(value) : -1) {}

LIBC_INLINE static constexpr size_t offset_to(size_t val, size_t align) {
return (-val) & (align - 1);
}

LIBC_INLINE operator size_t() { return static_cast<size_t>(value); }

LIBC_INLINE bool valid() { return value >= 0; }

LIBC_INLINE SafeMemSize operator+(const SafeMemSize &other) {
type result;
if (LIBC_UNLIKELY((value | other.value) < 0))
result = -1;
result = value + other.value;
return SafeMemSize{result};
}

LIBC_INLINE SafeMemSize operator*(const SafeMemSize &other) {
type result;
if (LIBC_UNLIKELY((value | other.value) < 0))
Expand All @@ -66,12 +56,11 @@ class SafeMemSize {
result = -1;
return SafeMemSize{result};
}

LIBC_INLINE SafeMemSize align_up(size_t alignment) {
if (!cpp::has_single_bit(alignment) || alignment > MAX_MEM_SIZE || !valid())
if (!is_power_of_two(alignment) || alignment > MAX_MEM_SIZE || !valid())
return SafeMemSize{type{-1}};

type offset = offset_to(value, alignment);
type offset = LIBC_NAMESPACE::offset_to<size_t>(value, alignment);

if (LIBC_UNLIKELY(offset > static_cast<type>(MAX_MEM_SIZE) - value))
return SafeMemSize{type{-1}};
Expand Down
5 changes: 3 additions & 2 deletions libc/src/__support/str_to_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/UInt128.h"
#include "src/__support/bit.h"
#include "src/__support/common.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/detailed_powers_of_ten.h"
Expand Down Expand Up @@ -68,12 +69,12 @@ template <class T> LIBC_INLINE uint32_t leading_zeroes(T inputNumber) {

template <>
LIBC_INLINE uint32_t leading_zeroes<uint32_t>(uint32_t inputNumber) {
return cpp::countl_zero(inputNumber);
return safe_clz(inputNumber);
}

template <>
LIBC_INLINE uint32_t leading_zeroes<uint64_t>(uint64_t inputNumber) {
return cpp::countl_zero(inputNumber);
return safe_clz(inputNumber);
}

LIBC_INLINE uint64_t low64(const UInt128 &num) {
Expand Down
1 change: 1 addition & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,7 @@ add_entrypoint_object(
.explogxf
libc.include.errno
libc.include.math
libc.src.__support.bit
libc.src.__support.CPP.bit
libc.src.__support.CPP.optional
libc.src.__support.FPUtil.fenv_impl
Expand Down
5 changes: 3 additions & 2 deletions libc/src/math/generic/powf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/FPUtil/sqrt.h" // Speedup for powf(x, 1/2) = sqrtf(x)
#include "src/__support/bit.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY

Expand Down Expand Up @@ -391,7 +392,7 @@ LIBC_INLINE bool is_odd_integer(float x) {
uint32_t x_u = cpp::bit_cast<uint32_t>(x);
int x_e = static_cast<int>((x_u & FloatProp::EXPONENT_MASK) >>
FloatProp::MANTISSA_WIDTH);
int lsb = cpp::countr_zero(x_u | FloatProp::EXPONENT_MASK);
int lsb = unsafe_ctz(x_u | FloatProp::EXPONENT_MASK);
constexpr int UNIT_EXPONENT =
static_cast<int>(FloatProp::EXPONENT_BIAS + FloatProp::MANTISSA_WIDTH);
return (x_e + lsb == UNIT_EXPONENT);
Expand All @@ -402,7 +403,7 @@ LIBC_INLINE bool is_integer(float x) {
uint32_t x_u = cpp::bit_cast<uint32_t>(x);
int x_e = static_cast<int>((x_u & FloatProp::EXPONENT_MASK) >>
FloatProp::MANTISSA_WIDTH);
int lsb = cpp::countr_zero(x_u | FloatProp::EXPONENT_MASK);
int lsb = unsafe_ctz(x_u | FloatProp::EXPONENT_MASK);
constexpr int UNIT_EXPONENT =
static_cast<int>(FloatProp::EXPONENT_BIAS + FloatProp::MANTISSA_WIDTH);
return (x_e + lsb >= UNIT_EXPONENT);
Expand Down
22 changes: 9 additions & 13 deletions libc/src/string/memory_utils/op_builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_OP_BUILTIN_H
#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_OP_BUILTIN_H

#include "src/__support/CPP/type_traits.h"
#include "src/string/memory_utils/utils.h"

namespace LIBC_NAMESPACE::builtin {
Expand Down Expand Up @@ -76,8 +75,7 @@ template <size_t Size> struct Memset {
#ifdef LLVM_LIBC_HAS_BUILTIN_MEMSET_INLINE
__builtin_memset_inline(dst, value, Size);
#else
static_assert(cpp::always_false<decltype(Size)>,
"Missing __builtin_memset_inline");
deferred_static_assert("Missing __builtin_memset_inline");
(void)dst;
(void)value;
#endif
Expand Down Expand Up @@ -109,23 +107,22 @@ template <size_t Size> struct Bcmp {
using ME = Bcmp;
static constexpr size_t SIZE = Size;
LIBC_INLINE static BcmpReturnType block(CPtr, CPtr) {
static_assert(cpp::always_false<decltype(Size)>,
"Missing __builtin_memcmp_inline");
deferred_static_assert("Missing __builtin_memcmp_inline");
return BcmpReturnType::ZERO();
}

LIBC_INLINE static BcmpReturnType tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return BcmpReturnType::ZERO();
}

LIBC_INLINE static BcmpReturnType head_tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return BcmpReturnType::ZERO();
}

LIBC_INLINE static BcmpReturnType loop_and_tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return BcmpReturnType::ZERO();
}
};
Expand All @@ -136,23 +133,22 @@ template <size_t Size> struct Memcmp {
using ME = Memcmp;
static constexpr size_t SIZE = Size;
LIBC_INLINE static MemcmpReturnType block(CPtr, CPtr) {
static_assert(cpp::always_false<decltype(Size)>,
"Missing __builtin_memcmp_inline");
deferred_static_assert("Missing __builtin_memcmp_inline");
return MemcmpReturnType::ZERO();
}

LIBC_INLINE static MemcmpReturnType tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return MemcmpReturnType::ZERO();
}

LIBC_INLINE static MemcmpReturnType head_tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return MemcmpReturnType::ZERO();
}

LIBC_INLINE static MemcmpReturnType loop_and_tail(CPtr, CPtr, size_t) {
static_assert(cpp::always_false<decltype(Size)>, "Not implemented");
deferred_static_assert("Not implemented");
return MemcmpReturnType::ZERO();
}
};
Expand Down
47 changes: 39 additions & 8 deletions libc/src/string/memory_utils/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,53 @@

namespace LIBC_NAMESPACE {

// Allows compile time error reporting in `if constexpr` branches.
template <bool flag = false>
LIBC_INLINE void deferred_static_assert(const char *msg) {
static_assert(flag, "compilation error");
(void)msg;
}

// Return whether `value` is zero or a power of two.
LIBC_INLINE constexpr bool is_power2_or_zero(size_t value) {
return (value & (value - 1U)) == 0;
}

// Return whether `value` is a power of two.
LIBC_INLINE constexpr bool is_power2(size_t value) {
return value && is_power2_or_zero(value);
}

// Compile time version of log2 that handles 0.
LIBC_INLINE constexpr size_t log2s(size_t value) {
return (value == 0 || value == 1) ? 0 : 1 + log2s(value / 2);
}

// Returns the first power of two preceding value or value if it is already a
// power of two (or 0 when value is 0).
LIBC_INLINE constexpr size_t le_power2(size_t value) {
return value == 0 ? value : 1ULL << log2s(value);
}

// Returns the first power of two following value or value if it is already a
// power of two (or 0 when value is 0).
LIBC_INLINE constexpr size_t ge_power2(size_t value) {
return is_power2_or_zero(value) ? value : 1ULL << (log2s(value) + 1);
}

// Returns the number of bytes to substract from ptr to get to the previous
// multiple of alignment. If ptr is already aligned returns 0.
template <size_t alignment>
LIBC_INLINE uintptr_t distance_to_align_down(const void *ptr) {
static_assert(cpp::has_single_bit(alignment),
"alignment must be a power of 2");
static_assert(is_power2(alignment), "alignment must be a power of 2");
return reinterpret_cast<uintptr_t>(ptr) & (alignment - 1U);
}

// Returns the number of bytes to add to ptr to get to the next multiple of
// alignment. If ptr is already aligned returns 0.
template <size_t alignment>
LIBC_INLINE uintptr_t distance_to_align_up(const void *ptr) {
static_assert(cpp::has_single_bit(alignment),
"alignment must be a power of 2");
static_assert(is_power2(alignment), "alignment must be a power of 2");
// The logic is not straightforward and involves unsigned modulo arithmetic
// but the generated code is as fast as it can be.
return -reinterpret_cast<uintptr_t>(ptr) & (alignment - 1U);
Expand Down Expand Up @@ -233,7 +265,7 @@ LIBC_INLINE ValueType load_aligned(CPtr src) {
else if constexpr (Endian::IS_BIG)
return (value << shift) | next;
else
static_assert(cpp::always_false<T>, "Invalid endianness");
deferred_static_assert("Invalid endianness");
} else {
return value;
}
Expand Down Expand Up @@ -270,7 +302,7 @@ LIBC_INLINE void store_aligned(ValueType value, Ptr dst) {
if constexpr (sizeof...(TS) > 0)
store_aligned<ValueType, TS...>(value >> shift, dst);
} else {
static_assert(cpp::always_false<T>, "Invalid endianness");
deferred_static_assert("Invalid endianness");
}
}

Expand Down Expand Up @@ -328,8 +360,7 @@ LIBC_INLINE void align_to_next_boundary(T1 *__restrict &p1, T2 *__restrict &p2,
else if constexpr (AlignOn == Arg::P2)
align_p1_to_next_boundary<SIZE>(p2, p1, count); // swapping p1 and p2.
else
static_assert(cpp::always_false<T1>,
"AlignOn must be either Arg::P1 or Arg::P2");
deferred_static_assert("AlignOn must be either Arg::P1 or Arg::P2");
}

template <size_t SIZE> struct AlignHelper {
Expand Down
13 changes: 13 additions & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ add_libc_test(
libc.src.__support.common
)


add_libc_test(
bit_test
SUITE
libc-support-tests
SRCS
bit_test.cpp
DEPENDS
libc.src.__support.bit
)


add_libc_test(
math_extras_test
SUITE
Expand All @@ -33,6 +45,7 @@ add_libc_test(
libc.src.__support.math_extras
)


add_libc_test(
high_precision_decimal_test
SUITE
Expand Down
3 changes: 1 addition & 2 deletions libc/test/src/__support/HashTable/table_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/bit.h" // bit_ceil
#include "src/__support/HashTable/randomness.h"
#include "src/__support/HashTable/table.h"
#include "test/UnitTest/Test.h"
Expand Down Expand Up @@ -38,7 +37,7 @@ TEST(LlvmLibcTableTest, Insertion) {
for (size_t k = 0; k < 256; ++k) {
keys[k].value = LIBC_NAMESPACE::Endian::to_little_endian(k);
}
constexpr size_t CAP = cpp::bit_ceil((sizeof(Group) + 1) * 8 / 7) / 8 * 7;
constexpr size_t CAP = next_power_of_two((sizeof(Group) + 1) * 8 / 7) / 8 * 7;
static_assert(CAP + 1 < 256, "CAP is too large for this test.");
HashTable *table =
HashTable::allocate(sizeof(Group) + 1, randomness::next_random_seed());
Expand Down
67 changes: 67 additions & 0 deletions libc/test/src/__support/bit_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===-- Unittests for BlockStore ------------------------------------------===//
//
// 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/bit.h"
#include "test/UnitTest/Test.h"

namespace LIBC_NAMESPACE {

TEST(LlvmLibcBlockBitTest, TODO) {
// TODO Implement me.
}

TEST(LlvmLibcBlockBitTest, OffsetTo) {
ASSERT_EQ(offset_to(0, 512), 0);
ASSERT_EQ(offset_to(1, 512), 511);
ASSERT_EQ(offset_to(2, 512), 510);
ASSERT_EQ(offset_to(13, 1), 0);
ASSERT_EQ(offset_to(13, 4), 3);
for (unsigned int i = 0; i < 31; ++i) {
ASSERT_EQ((offset_to(i, 1u << i) + i) % (1u << i), 0u);
}
}

TEST(LlvmLibcBlockBitTest, RotateLeft) {
{
unsigned current = 1;
for (unsigned i = 0; i < 8 * sizeof(unsigned); ++i) {
ASSERT_EQ(1u << i, current);
ASSERT_EQ(current, rotate_left(1u, i));
current = rotate_left(current, 1u);
}
ASSERT_EQ(current, 1u);
}
{
int current = 1;
for (int i = 0; i < 8 * static_cast<int>(sizeof(int)); ++i) {
ASSERT_EQ(1 << i, current);
ASSERT_EQ(current, rotate_left(1, i));
current = rotate_left(current, 1);
}
ASSERT_EQ(current, 1);
}
}

TEST(LlvmLibcBlockBitTest, NextPowerOfTwo) {
ASSERT_EQ(1u, next_power_of_two(0u));
for (unsigned int i = 0; i < 31; ++i) {
ASSERT_EQ(1u << (i + 1), next_power_of_two((1u << i) + 1));
ASSERT_EQ(1u << i, next_power_of_two(1u << i));
}
}

TEST(LlvmLibcBlockBitTest, IsPowerOfTwo) {
ASSERT_FALSE(is_power_of_two(0u));
ASSERT_TRUE(is_power_of_two(1u));
for (unsigned int i = 1; i < 31; ++i) {
ASSERT_TRUE(is_power_of_two(1u << i));
ASSERT_FALSE(is_power_of_two((1u << i) + 1));
}
}

} // namespace LIBC_NAMESPACE
11 changes: 0 additions & 11 deletions libc/test/src/__support/memory_size_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,5 @@ TEST(LlvmLibcMemSizeTest, AlignUp) {
auto max = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT};
ASSERT_FALSE(max.align_up(8).valid());
}

TEST(LlvmLibcBlockBitTest, OffsetTo) {
ASSERT_EQ(SafeMemSize::offset_to(0, 512), 0UL);
ASSERT_EQ(SafeMemSize::offset_to(1, 512), 511UL);
ASSERT_EQ(SafeMemSize::offset_to(2, 512), 510UL);
ASSERT_EQ(SafeMemSize::offset_to(13, 1), 0UL);
ASSERT_EQ(SafeMemSize::offset_to(13, 4), 3UL);
for (unsigned int i = 0; i < 31; ++i) {
ASSERT_EQ((SafeMemSize::offset_to(i, 1u << i) + i) % (1u << i), 0UL);
}
}
} // namespace internal
} // namespace LIBC_NAMESPACE
4 changes: 2 additions & 2 deletions libc/test/src/search/hsearch_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/bit.h" // bit_ceil
#include "src/__support/HashTable/table.h"
#include "src/__support/bit.h"
#include "src/search/hcreate.h"
#include "src/search/hcreate_r.h"
#include "src/search/hdestroy.h"
Expand Down Expand Up @@ -48,7 +48,7 @@ char search_data2[] =

constexpr size_t GROUP_SIZE = sizeof(LIBC_NAMESPACE::internal::Group);
constexpr size_t CAP =
LIBC_NAMESPACE::cpp::bit_ceil((GROUP_SIZE + 1) * 8 / 7) / 8 * 7;
LIBC_NAMESPACE::next_power_of_two((GROUP_SIZE + 1) * 8 / 7) / 8 * 7;
static_assert(CAP < sizeof(search_data), "CAP too large");

TEST(LlvmLibcHSearchTest, InsertTooMany) {
Expand Down
60 changes: 60 additions & 0 deletions libc/test/src/string/memory_utils/utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,66 @@

namespace LIBC_NAMESPACE {

TEST(LlvmLibcUtilsTest, IsPowerOfTwoOrZero) {
static const cpp::array<bool, 65> kExpectedValues{
1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 0-15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63
1 // 64
};
for (size_t i = 0; i < kExpectedValues.size(); ++i)
EXPECT_EQ(is_power2_or_zero(i), kExpectedValues[i]);
}

TEST(LlvmLibcUtilsTest, IsPowerOfTwo) {
static const cpp::array<bool, 65> kExpectedValues{
0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 0-15
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48-63
1 // 64
};
for (size_t i = 0; i < kExpectedValues.size(); ++i)
EXPECT_EQ(is_power2(i), kExpectedValues[i]);
}

TEST(LlvmLibcUtilsTest, Log2) {
static const cpp::array<size_t, 65> kExpectedValues{
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 0-15
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 16-31
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 32-47
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 48-63
6 // 64
};
for (size_t i = 0; i < kExpectedValues.size(); ++i)
EXPECT_EQ(log2s(i), kExpectedValues[i]);
}

TEST(LlvmLibcUtilsTest, LEPowerOf2) {
static const cpp::array<size_t, 65> kExpectedValues{
0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, // 0-15
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, // 16-31
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 32-47
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 48-63
64 // 64
};
for (size_t i = 0; i < kExpectedValues.size(); ++i)
EXPECT_EQ(le_power2(i), kExpectedValues[i]);
}

TEST(LlvmLibcUtilsTest, GEPowerOf2) {
static const cpp::array<size_t, 66> kExpectedValues{
0, 1, 2, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, // 0-15
16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 16-31
32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 32-47
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 48-63
64, 128 // 64-65
};
for (size_t i = 0; i < kExpectedValues.size(); ++i)
EXPECT_EQ(ge_power2(i), kExpectedValues[i]);
}

using UINT = uintptr_t;

// Converts an offset into a pointer.
Expand Down
19 changes: 17 additions & 2 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ libc_support_library(
name = "__support_integer_utils",
hdrs = ["src/__support/integer_utils.h"],
deps = [
":__support_bit",
":__support_common",
":__support_cpp_type_traits",
":__support_math_extras",
Expand All @@ -450,6 +451,7 @@ libc_support_library(
name = "__support_uint",
hdrs = ["src/__support/UInt.h"],
deps = [
":__support_bit",
":__support_cpp_array",
":__support_cpp_limits",
":__support_cpp_optional",
Expand Down Expand Up @@ -535,8 +537,8 @@ libc_support_library(
"src/__support/str_to_float.h",
],
deps = [
":__support_bit",
":__support_common",
":__support_cpp_bit",
":__support_cpp_limits",
":__support_cpp_optional",
":__support_ctype_utils",
Expand Down Expand Up @@ -585,6 +587,15 @@ libc_support_library(
],
)

libc_support_library(
name = "__support_bit",
hdrs = ["src/__support/bit.h"],
deps = [
":__support_cpp_type_traits",
":__support_macros_attributes",
],
)

libc_support_library(
name = "__support_math_extras",
hdrs = ["src/__support/math_extras.h"],
Expand All @@ -599,8 +610,8 @@ libc_support_library(
name = "__support_fputil_generic_fmod",
hdrs = ["src/__support/FPUtil/generic/FMod.h"],
deps = [
":__support_bit",
":__support_common",
":__support_cpp_bit",
":__support_cpp_limits",
":__support_cpp_type_traits",
":__support_fputil_fenv_impl",
Expand Down Expand Up @@ -672,6 +683,7 @@ libc_support_library(
hdrs = ["src/__support/FPUtil/FPBits.h"],
textual_hdrs = ["src/__support/FPUtil/x86_64/LongDoubleBits.h"],
deps = [
":__support_bit",
":__support_common",
":__support_cpp_bit",
":__support_cpp_type_traits",
Expand Down Expand Up @@ -699,6 +711,7 @@ libc_support_library(
name = "__support_fputil_hypot",
hdrs = ["src/__support/FPUtil/Hypot.h"],
deps = [
":__support_bit",
":__support_common",
":__support_cpp_bit",
":__support_cpp_type_traits",
Expand Down Expand Up @@ -769,6 +782,7 @@ libc_support_library(
name = "__support_fputil_sqrt",
hdrs = sqrt_hdrs,
deps = [
":__support_bit",
":__support_common",
":__support_cpp_bit",
":__support_cpp_type_traits",
Expand Down Expand Up @@ -796,6 +810,7 @@ libc_support_library(
# doesn't support FMA, so they can't be compiled on their own.
textual_hdrs = fma_platform_hdrs,
deps = [
":__support_bit",
":__support_cpp_bit",
":__support_cpp_type_traits",
":__support_fputil_fenv_impl",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ package(default_visibility = ["//visibility:public"])

licenses(["notice"])

libc_test(
name = "bit_test",
srcs = ["bit_test.cpp"],
deps = ["//libc:__support_bit"],
)

libc_test(
name = "math_extras_test",
srcs = ["math_extras_test.cpp"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def math_test(name, hdrs = [], deps = [], **kwargs):
srcs = [test_name + ".cpp"] + hdrs,
libc_function_deps = ["//libc:func_name".replace("func_name", name)],
deps = [
"//libc:__support_bit",
"//libc:__support_fputil_basic_operations",
"//libc:__support_fputil_fenv_impl",
"//libc:__support_fputil_float_properties",
Expand Down