Skip to content

Commit f5f8920

Browse files
committed
[libc] Implement vector 'split' and 'concat' routines
Summary: This provides some helpers for the split and concatenation routines for changing the size of an existing vector. This includes a simple tuple type to do the splitting. The tuple doesn't support structured bindings yet. The concat function is more limited than what would be ideal, but the shufflevector builtin requires things of equivalent sizes and I didn't think it was worth wrangling with that just yet.
1 parent 44d1404 commit f5f8920

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

libc/src/__support/CPP/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,7 @@ add_header_library(
224224
simd
225225
HDRS
226226
simd.h
227+
DEPENDS
228+
.utility
229+
.tuple
227230
)

libc/src/__support/CPP/simd.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "hdr/stdint_proxy.h"
1717
#include "src/__support/CPP/algorithm.h"
1818
#include "src/__support/CPP/limits.h"
19+
#include "src/__support/CPP/tuple.h"
1920
#include "src/__support/CPP/type_traits.h"
21+
#include "src/__support/CPP/utility/integer_sequence.h"
2022
#include "src/__support/macros/attributes.h"
2123
#include "src/__support/macros/config.h"
2224

@@ -51,6 +53,7 @@ template <typename T> LIBC_INLINE constexpr size_t native_vector_size = 1;
5153
template <typename T> LIBC_INLINE constexpr T poison() {
5254
return __builtin_nondeterministic_value(T());
5355
}
56+
5457
} // namespace internal
5558

5659
// Type aliases.
@@ -273,6 +276,77 @@ LIBC_INLINE constexpr static simd<T, N> select(simd<bool, N> m, simd<T, N> x,
273276
return m ? x : y;
274277
}
275278

279+
namespace internal {
280+
template <typename T, size_t N, size_t O, size_t... I>
281+
LIBC_INLINE constexpr static cpp::simd<T, sizeof...(I)>
282+
extend(cpp::simd<T, N> x, cpp::index_sequence<I...>) {
283+
return __builtin_shufflevector(x, x, (I < O ? static_cast<int>(I) : -1)...);
284+
}
285+
template <typename T, size_t N, size_t M, size_t O>
286+
LIBC_INLINE constexpr static auto extend(cpp::simd<T, N> x) {
287+
if constexpr (N == M)
288+
return x;
289+
else if constexpr (M <= 2 * N)
290+
return extend<T, N, M>(x, cpp::make_index_sequence<M>{});
291+
else
292+
return extend<T, 2 * N, M, O>(
293+
extend<T, N, 2 * N>(x, cpp::make_index_sequence<2 * N>{}));
294+
}
295+
template <typename T, size_t N, size_t M, size_t... I>
296+
LIBC_INLINE constexpr static cpp::simd<T, N + M>
297+
concat(cpp::simd<T, N> x, cpp::simd<T, M> y, cpp::index_sequence<I...>) {
298+
constexpr size_t L = (N > M ? N : M);
299+
300+
auto x_ext = extend<T, N, L, N>(x);
301+
auto y_ext = extend<T, M, L, M>(y);
302+
303+
auto remap = [](size_t idx) -> int {
304+
if (idx < N)
305+
return static_cast<int>(idx);
306+
if (idx < N + M)
307+
return static_cast<int>((idx - N) + L);
308+
return -1;
309+
};
310+
311+
return __builtin_shufflevector(x_ext, y_ext, remap(I)...);
312+
}
313+
314+
template <typename T, size_t N, size_t Count, size_t Offset, size_t... I>
315+
LIBC_INLINE constexpr static cpp::simd<T, Count>
316+
slice(cpp::simd<T, N> x, cpp::index_sequence<I...>) {
317+
return __builtin_shufflevector(x, x, (Offset + I)...);
318+
}
319+
template <typename T, size_t N, size_t Offset, size_t Head, size_t... Tail>
320+
LIBC_INLINE constexpr static auto split(cpp::simd<T, N> x) {
321+
auto first = cpp::make_tuple(
322+
slice<T, N, Head, Offset>(x, cpp::make_index_sequence<Head>{}));
323+
if constexpr (sizeof...(Tail) > 0)
324+
return cpp::tuple_cat(first, split<T, N, Offset + Head, Tail...>(x));
325+
else
326+
return first;
327+
}
328+
329+
} // namespace internal
330+
331+
// Shuffling helpers.
332+
template <typename T, size_t N, size_t M>
333+
LIBC_INLINE constexpr static auto concat(cpp::simd<T, N> x, cpp::simd<T, M> y) {
334+
return internal::concat(x, y, make_index_sequence<N + M>{});
335+
}
336+
template <typename T, size_t N, size_t M, typename... Rest>
337+
LIBC_INLINE constexpr static auto concat(cpp::simd<T, N> x, cpp::simd<T, M> y,
338+
Rest... rest) {
339+
auto xy = concat(x, y);
340+
if constexpr (sizeof...(Rest))
341+
return concat(xy, rest...);
342+
else
343+
return xy;
344+
}
345+
template <size_t... Sizes, typename T, size_t N> auto split(cpp::simd<T, N> x) {
346+
static_assert((... + Sizes) == N, "split sizes must sum to vector size");
347+
return internal::split<T, N, 0, Sizes...>(x);
348+
}
349+
276350
// TODO: where expressions, scalar overloads, ABI types.
277351

278352
} // namespace cpp

libc/test/src/__support/CPP/simd_test.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,29 @@ TEST(LlvmLibcSIMDTest, MaskOperations) {
6868
EXPECT_EQ(cpp::find_first_set(mask), 0);
6969
EXPECT_EQ(cpp::find_last_set(mask), 2);
7070
}
71+
72+
TEST(LlvmLibcSIMDTest, SplitConcat) {
73+
cpp::simd<char, 8> v(1);
74+
auto [v1, v2, v3, v4] = cpp::split<2, 2, 2, 2>(v);
75+
static_assert(cpp::simd_size_v<decltype(v1)> == 2 &&
76+
cpp::simd_size_v<decltype(v2)> == 2 &&
77+
cpp::simd_size_v<decltype(v3)> == 2 &&
78+
cpp::simd_size_v<decltype(v4)> == 2,
79+
"invalid size");
80+
81+
v1 = cpp::simd<char, 2>(1);
82+
v2 = cpp::simd<char, 2>(2);
83+
v3 = cpp::simd<char, 2>(3);
84+
v4 = cpp::simd<char, 2>(4);
85+
cpp::simd<char, 8> m = cpp::concat(v1, v2, v3, v4);
86+
static_assert(cpp::simd_size_v<decltype(m)> == 8, "invalid size");
87+
88+
cpp::simd<char, 8> c = {1, 1, 2, 2, 3, 3, 4, 4};
89+
for (int i = 0; i < 8; ++i)
90+
EXPECT_EQ(c[i], m[i]);
91+
92+
cpp::simd<char, 1> c1('\0');
93+
cpp::simd<char, 8> c2('\0');
94+
cpp::simd<char, 9> c3 = cpp::concat(c1, c2);
95+
static_assert(cpp::simd_size_v<decltype(c3)> == 9, "invalid size");
96+
}

0 commit comments

Comments
 (0)