Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 61 additions & 54 deletions libc/src/__support/CPP/simd.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ using simd = T [[clang::ext_vector_type(N)]];
template <typename T>
using simd_mask = simd<bool, internal::native_vector_size<T>>;

// Type trait helpers.
template <typename T>
struct simd_size : cpp::integral_constant<size_t, __builtin_vectorelements(T)> {
};
template <class T> constexpr size_t simd_size_v = simd_size<T>::value;

template <typename T> struct is_simd : cpp::integral_constant<bool, false> {};
template <typename T, unsigned N>
struct is_simd<simd<T, N>> : cpp::integral_constant<bool, true> {};
template <class T> constexpr bool is_simd_v = is_simd<T>::value;

template <typename T>
struct is_simd_mask : cpp::integral_constant<bool, false> {};
template <unsigned N>
struct is_simd_mask<simd<bool, N>> : cpp::integral_constant<bool, true> {};
template <class T> constexpr bool is_simd_mask_v = is_simd_mask<T>::value;

template <typename T> struct simd_element_type;
template <typename T, size_t N> struct simd_element_type<simd<T, N>> {
using type = T;
};
template <typename T>
using simd_element_type_t = typename simd_element_type<T>::type;
namespace internal {

template <typename T>
Expand Down Expand Up @@ -123,34 +146,14 @@ LIBC_INLINE constexpr static auto split(cpp::simd<T, N> x) {
return result;
}

} // namespace internal

// Type trait helpers.
// Helper trait
template <typename T>
struct simd_size : cpp::integral_constant<size_t, __builtin_vectorelements(T)> {
};
template <class T> constexpr size_t simd_size_v = simd_size<T>::value;

template <typename T> struct is_simd : cpp::integral_constant<bool, false> {};
template <typename T, unsigned N>
struct is_simd<simd<T, N>> : cpp::integral_constant<bool, true> {};
template <class T> constexpr bool is_simd_v = is_simd<T>::value;

template <typename T>
struct is_simd_mask : cpp::integral_constant<bool, false> {};
template <unsigned N>
struct is_simd_mask<simd<bool, N>> : cpp::integral_constant<bool, true> {};
template <class T> constexpr bool is_simd_mask_v = is_simd_mask<T>::value;
using enable_if_integral_t = cpp::enable_if_t<cpp::is_integral_v<T>, T>;

template <typename T> struct simd_element_type;
template <typename T, size_t N> struct simd_element_type<simd<T, N>> {
using type = T;
};
template <typename T>
using simd_element_type_t = typename simd_element_type<T>::type;
using enable_if_simd_t = cpp::enable_if_t<is_simd_v<T>, bool>;

template <typename T>
using enable_if_simd_t = cpp::enable_if_t<is_simd_v<T>, T>;
} // namespace internal

// Casting.
template <typename To, typename From, size_t N>
Expand All @@ -159,29 +162,34 @@ LIBC_INLINE constexpr static simd<To, N> simd_cast(simd<From, N> v) {
}

// SIMD mask operations.
template <size_t N> LIBC_INLINE constexpr static bool all_of(simd<bool, N> m) {
return __builtin_reduce_and(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static bool all_of(simd<T, N> v) {
return __builtin_reduce_and(simd_cast<bool>(v));
}
template <size_t N> LIBC_INLINE constexpr static bool any_of(simd<bool, N> m) {
return __builtin_reduce_or(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static bool any_of(simd<T, N> v) {
return __builtin_reduce_or(simd_cast<bool>(v));
}
template <size_t N> LIBC_INLINE constexpr static bool none_of(simd<bool, N> m) {
return !any_of(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static bool none_of(simd<T, N> v) {
return !any_of(v);
}
template <size_t N> LIBC_INLINE constexpr static bool some_of(simd<bool, N> m) {
return any_of(m) && !all_of(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static bool some_of(simd<T, N> v) {
return any_of(v) && !all_of(v);
}
template <size_t N> LIBC_INLINE constexpr static int popcount(simd<bool, N> m) {
return __builtin_popcountg(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static int popcount(simd<T, N> v) {
return __builtin_popcountg(v);
}
template <size_t N>
LIBC_INLINE constexpr static int find_first_set(simd<bool, N> m) {
return __builtin_ctzg(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static int find_first_set(simd<T, N> v) {
return __builtin_ctzg(simd_cast<bool>(v));
}
template <size_t N>
LIBC_INLINE constexpr static int find_last_set(simd<bool, N> m) {
constexpr size_t size = simd_size_v<simd<bool, N>>;
return size - 1 - __builtin_clzg(m);
template <typename T, size_t N, internal::enable_if_integral_t<T> = 0>
LIBC_INLINE constexpr static int find_last_set(simd<T, N> v) {
constexpr size_t size = simd_size_v<simd<T, N>>;
return size - 1 - __builtin_clzg(simd_cast<bool>(v));
}

// Elementwise operations.
Expand Down Expand Up @@ -279,33 +287,32 @@ LIBC_INLINE constexpr static T hmax(simd<T, N> v) {
}

// Accessor helpers.
template <typename T>
LIBC_INLINE enable_if_simd_t<T> load_unaligned(const void *ptr) {
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T load_unaligned(const void *ptr) {
T tmp;
__builtin_memcpy(&tmp, ptr, sizeof(T));
return tmp;
}
template <typename T>
LIBC_INLINE enable_if_simd_t<T> load_aligned(const void *ptr) {
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T load_aligned(const void *ptr) {
return load_unaligned<T>(__builtin_assume_aligned(ptr, alignof(T)));
}
template <typename T>
LIBC_INLINE enable_if_simd_t<T> store_unaligned(T v, void *ptr) {
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T store_unaligned(T v, void *ptr) {
__builtin_memcpy(ptr, &v, sizeof(T));
}
template <typename T>
LIBC_INLINE enable_if_simd_t<T> store_aligned(T v, void *ptr) {
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T store_aligned(T v, void *ptr) {
store_unaligned<T>(v, __builtin_assume_aligned(ptr, alignof(T)));
}
template <typename T>
LIBC_INLINE enable_if_simd_t<T>
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T
masked_load(simd<bool, simd_size_v<T>> m, void *ptr,
T passthru = internal::poison<simd_element_type<T>>()) {
return __builtin_masked_load(m, ptr, passthru);
}
template <typename T>
LIBC_INLINE enable_if_simd_t<T> masked_store(simd<bool, simd_size_v<T>> m, T v,
void *ptr) {
template <typename T, internal::enable_if_simd_t<T> = 0>
LIBC_INLINE T masked_store(simd<bool, simd_size_v<T>> m, T v, void *ptr) {
__builtin_masked_store(
m, v, static_cast<T *>(__builtin_assume_aligned(ptr, alignof(T))));
}
Expand Down
6 changes: 6 additions & 0 deletions libc/src/__support/CPP/type_traits/is_unsigned.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"

#include <stddef.h>

namespace LIBC_NAMESPACE_DECL {
namespace cpp {

Expand Down Expand Up @@ -46,6 +48,10 @@ template <typename T> struct is_unsigned {
LIBC_INLINE constexpr bool operator()() const { return is_unsigned::value; }
};
#endif // LIBC_COMPILER_HAS_FIXED_POINT
#if LIBC_HAS_VECTOR_TYPE
template <typename T, size_t N>
struct is_unsigned<T [[clang::ext_vector_type(N)]]> : bool_constant<false> {};
#endif

template <typename T>
LIBC_INLINE_VAR constexpr bool is_unsigned_v = is_unsigned<T>::value;
Expand Down
4 changes: 2 additions & 2 deletions libc/src/string/memory_utils/generic/inline_strlen.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ string_length(const char *src) {
__builtin_align_down(src, alignment));

cpp::simd<char> chars = cpp::load_aligned<cpp::simd<char>>(aligned);
cpp::simd_mask<char> mask = cpp::simd_cast<bool>(chars == null_byte);
cpp::simd_mask<char> mask = chars == null_byte;
size_t offset = src - reinterpret_cast<const char *>(aligned);
if (cpp::any_of(shift_mask(mask, offset)))
return cpp::find_first_set(shift_mask(mask, offset));

for (;;) {
cpp::simd<char> chars = cpp::load_aligned<cpp::simd<char>>(++aligned);
cpp::simd_mask<char> mask = cpp::simd_cast<bool>(chars == null_byte);
cpp::simd_mask<char> mask = chars == null_byte;
if (cpp::any_of(mask))
return (reinterpret_cast<const char *>(aligned) - src) +
cpp::find_first_set(mask);
Expand Down
14 changes: 8 additions & 6 deletions libc/test/src/__support/CPP/simd_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,25 @@ TEST(LlvmLibcSIMDTest, MaskOperations) {

EXPECT_TRUE(cpp::any_of(mask));
EXPECT_FALSE(cpp::all_of(mask));
EXPECT_FALSE(cpp::none_of(mask));
EXPECT_TRUE(cpp::some_of(mask));
EXPECT_EQ(cpp::find_first_set(mask), 0);
EXPECT_EQ(cpp::find_last_set(mask), 2);
EXPECT_EQ(cpp::popcount(mask), 2);
}

TEST(LlvmLibcSIMDTest, SplitConcat) {
cpp::simd<char, 8> v{1, 1, 2, 2, 3, 3, 4, 4};
auto [v1, v2, v3, v4] = cpp::split<2, 2, 2, 2>(v);
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(v1 == 1)));
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(v2 == 2)));
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(v3 == 3)));
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(v4 == 4)));
EXPECT_TRUE(cpp::all_of(v1 == 1));
EXPECT_TRUE(cpp::all_of(v2 == 2));
EXPECT_TRUE(cpp::all_of(v3 == 3));
EXPECT_TRUE(cpp::all_of(v4 == 4));

cpp::simd<char, 8> m = cpp::concat(v1, v2, v3, v4);
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(m == v)));
EXPECT_TRUE(cpp::all_of(m == v));

cpp::simd<char, 1> c(~0);
cpp::simd<char, 8> n = cpp::concat(c, c, c, c, c, c, c, c);
EXPECT_TRUE(cpp::all_of(cpp::simd_cast<bool>(n == ~0)));
EXPECT_TRUE(cpp::all_of(n == ~0));
}
Loading