[libc-commits] [libc] [libc] Clean up mask helpers after allowing implicit conversions (PR #158681)
Joseph Huber via libc-commits
libc-commits at lists.llvm.org
Mon Sep 15 19:19:10 PDT 2025
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/158681
>From 0e4285852fe66d46e1ba8e7b41b6ea3d4f2ed797 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 15 Sep 2025 11:18:16 -0500
Subject: [PATCH] [libc] Clean up mask helpers after allowing implicit
conversions
Summary:
I landed a change in clang that allows integral vectors to implicitly
convert to boolean ones. This means I can simplify the interface and
remove the need to cast to bool on every use. Also do some other
cleanups of the traits.
---
libc/src/__support/CPP/simd.h | 115 ++++++++++--------
.../__support/CPP/type_traits/is_unsigned.h | 6 +
.../memory_utils/generic/inline_strlen.h | 4 +-
libc/test/src/__support/CPP/simd_test.cpp | 14 ++-
4 files changed, 77 insertions(+), 62 deletions(-)
diff --git a/libc/src/__support/CPP/simd.h b/libc/src/__support/CPP/simd.h
index 3c7e65acc3c0a..d2a5b17fa4b9f 100644
--- a/libc/src/__support/CPP/simd.h
+++ b/libc/src/__support/CPP/simd.h
@@ -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>
@@ -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>
@@ -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.
@@ -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))));
}
diff --git a/libc/src/__support/CPP/type_traits/is_unsigned.h b/libc/src/__support/CPP/type_traits/is_unsigned.h
index 3ae6337ceb50a..b4267eedd19fc 100644
--- a/libc/src/__support/CPP/type_traits/is_unsigned.h
+++ b/libc/src/__support/CPP/type_traits/is_unsigned.h
@@ -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 {
@@ -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;
diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h
index 68fba2afb3a5c..5e553e301d4da 100644
--- a/libc/src/string/memory_utils/generic/inline_strlen.h
+++ b/libc/src/string/memory_utils/generic/inline_strlen.h
@@ -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);
diff --git a/libc/test/src/__support/CPP/simd_test.cpp b/libc/test/src/__support/CPP/simd_test.cpp
index b4f5685e3b1d1..c8f34df8ab028 100644
--- a/libc/test/src/__support/CPP/simd_test.cpp
+++ b/libc/test/src/__support/CPP/simd_test.cpp
@@ -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));
}
More information about the libc-commits
mailing list