[libc-commits] [libc] [libc] Implement vector 'split' and 'concat' routines (PR #157537)
Joseph Huber via libc-commits
libc-commits at lists.llvm.org
Tue Sep 9 12:20:18 PDT 2025
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/157537
>From 842115d3bc511b283398035ae71e857f99b516ad Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 8 Sep 2025 15:10:30 -0500
Subject: [PATCH 1/3] [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.
---
libc/src/__support/CPP/CMakeLists.txt | 11 ++
libc/src/__support/CPP/simd.h | 38 +++++++
libc/src/__support/CPP/tuple.h | 106 ++++++++++++++++++
.../__support/CPP/utility/integer_sequence.h | 10 ++
4 files changed, 165 insertions(+)
create mode 100644 libc/src/__support/CPP/tuple.h
diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index a389a6d1702fe..53b0b17774dac 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -211,8 +211,19 @@ add_object_library(
libc.src.__support.macros.properties.os
)
+add_header_library(
+ tuple
+ HDRS
+ tuple.h
+ DEPENDS
+ utility
+)
+
add_header_library(
simd
HDRS
simd.h
+ DEPENDS
+ utility
+ tuple
)
diff --git a/libc/src/__support/CPP/simd.h b/libc/src/__support/CPP/simd.h
index 449455c5c0390..bdafdec705d35 100644
--- a/libc/src/__support/CPP/simd.h
+++ b/libc/src/__support/CPP/simd.h
@@ -16,7 +16,9 @@
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/algorithm.h"
#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/tuple.h"
#include "src/__support/CPP/type_traits.h"
+#include "src/__support/CPP/utility/integer_sequence.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
@@ -51,6 +53,7 @@ template <typename T> inline constexpr size_t native_vector_size = 1;
template <typename T> LIBC_INLINE constexpr T poison() {
return __builtin_nondeterministic_value(T());
}
+
} // namespace internal
// Type aliases.
@@ -267,6 +270,41 @@ LIBC_INLINE constexpr simd<T, N> select(simd<bool, N> m, simd<T, N> x,
return m ? x : y;
}
+namespace internal {
+template <typename T, size_t N, size_t M, size_t... I>
+LIBC_INLINE constexpr static cpp::simd<T, N + M>
+concat(cpp::simd<T, N> x, cpp::simd<T, M> y, index_sequence<I...>) {
+ return __builtin_shufflevector(x, y, I...);
+}
+
+template <typename T, size_t N, size_t Count, size_t Offset, size_t... I>
+LIBC_INLINE constexpr static cpp::simd<T, Count>
+slice(cpp::simd<T, N> x, cpp::index_sequence<I...>) {
+ return __builtin_shufflevector(x, x, (Offset + I)...);
+}
+template <typename T, size_t N, size_t Offset, size_t Head, size_t... Tail>
+LIBC_INLINE constexpr static auto split(cpp::simd<T, N> x) {
+ auto first = cpp::make_tuple(
+ slice<T, N, Head, Offset>(x, cpp::make_index_sequence<Head>{}));
+ if constexpr (sizeof...(Tail) > 0)
+ return cpp::tuple_cat(first, split<T, N, Offset + Head, Tail...>(x));
+ else
+ return first;
+}
+
+} // namespace internal
+
+// Shuffling helpers.
+template <typename T, size_t N>
+LIBC_INLINE constexpr static cpp::simd<T, N + N> concat(cpp::simd<T, N> x,
+ cpp::simd<T, N> y) {
+ return internal::concat(x, y, make_index_sequence<N + N>{});
+}
+template <size_t... Sizes, typename T, size_t N> auto split(cpp::simd<T, N> x) {
+ static_assert((... + Sizes) == N, "split sizes must sum to vector size");
+ return internal::split<T, N, 0, Sizes...>(x);
+}
+
// TODO: where expressions, scalar overloads, ABI types.
} // namespace cpp
diff --git a/libc/src/__support/CPP/tuple.h b/libc/src/__support/CPP/tuple.h
new file mode 100644
index 0000000000000..01d374a0841aa
--- /dev/null
+++ b/libc/src/__support/CPP/tuple.h
@@ -0,0 +1,106 @@
+//===-- tuple utility -------------------------------------------*- C++ -*-===//
+//
+// 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_CPP_UTILITY_TUPLE_H
+#define LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
+
+#include "src/__support/CPP/type_traits/decay.h"
+#include "src/__support/CPP/utility/integer_sequence.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace cpp {
+
+template <typename... Ts> struct tuple;
+template <> struct tuple<> {};
+
+template <typename Head, typename... Tail>
+struct tuple<Head, Tail...> : tuple<Tail...> {
+ Head head;
+
+ LIBC_INLINE constexpr tuple() = default;
+
+ LIBC_INLINE constexpr tuple(const Head &h, const Tail &...t)
+ : tuple<Tail...>(t...), head(h) {}
+
+ LIBC_INLINE constexpr Head &get_head() { return head; }
+ LIBC_INLINE constexpr const Head &get_head() const { return head; }
+
+ LIBC_INLINE constexpr tuple<Tail...> &get_tail() { return *this; }
+ LIBC_INLINE constexpr const tuple<Tail...> &get_tail() const { return *this; }
+};
+
+template <typename... Ts> LIBC_INLINE constexpr auto make_tuple(Ts &&...args) {
+ return tuple<cpp::decay_t<Ts>...>(static_cast<Ts &&>(args)...);
+}
+template <typename... Ts> LIBC_INLINE constexpr auto tie(Ts &...args) {
+ return tuple<Ts &...>(args...);
+}
+
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr auto &get(tuple<Head, Tail...> &t) {
+ if constexpr (I == 0)
+ return t.get_head();
+ else
+ return get<I - 1>(t.get_tail());
+}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr const auto &get(const tuple<Head, Tail...> &t) {
+ if constexpr (I == 0)
+ return t.get_head();
+ else
+ return get<I - 1>(t.get_tail());
+}
+
+template <typename T> struct tuple_size;
+template <typename... Ts> struct tuple_size<tuple<Ts...>> {
+ static constexpr size_t value = sizeof...(Ts);
+};
+
+template <size_t I, typename T> struct tuple_element;
+template <size_t I, typename Head, typename... Tail>
+struct tuple_element<I, tuple<Head, Tail...>>
+ : tuple_element<I - 1, tuple<Tail...>> {};
+template <typename Head, typename... Tail>
+struct tuple_element<0, tuple<Head, Tail...>> {
+ using type = Head;
+};
+
+namespace internal {
+template <typename... As, typename... Bs, size_t... I, size_t... J>
+LIBC_INLINE constexpr auto
+tuple_cat(const tuple<As...> &a, const tuple<Bs...> &b,
+ cpp::index_sequence<I...>, cpp::index_sequence<J...>) {
+ return tuple<As..., Bs...>(get<I>(a)..., get<J>(b)...);
+}
+
+template <typename First, typename Second, typename... Rest>
+LIBC_INLINE constexpr auto tuple_cat(const First &f, const Second &s,
+ const Rest &...rest) {
+ auto concat =
+ tuple_cat(f, s, cpp::make_index_sequence<tuple_size<First>::value>{},
+ cpp::make_index_sequence<tuple_size<Second>::value>{});
+ if constexpr (sizeof...(Rest))
+ return tuple_cat(concat, rest...);
+ else
+ return concat;
+}
+} // namespace internal
+
+template <typename... Tuples>
+LIBC_INLINE constexpr auto tuple_cat(const Tuples &...tuples) {
+ static_assert(sizeof...(Tuples) > 0, "need at least one element");
+ if constexpr (sizeof...(Tuples) == 1)
+ return (tuples, ...);
+ else
+ return internal::tuple_cat(tuples...);
+}
+
+} // namespace cpp
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
diff --git a/libc/src/__support/CPP/utility/integer_sequence.h b/libc/src/__support/CPP/utility/integer_sequence.h
index 06643d505aca0..37faa4f1d5aac 100644
--- a/libc/src/__support/CPP/utility/integer_sequence.h
+++ b/libc/src/__support/CPP/utility/integer_sequence.h
@@ -5,12 +5,15 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_INTEGER_SEQUENCE_H
#include "src/__support/CPP/type_traits/is_integral.h"
#include "src/__support/macros/config.h"
+#include <stddef.h>
+
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
@@ -30,10 +33,17 @@ template <typename T> struct make_integer_sequence<T, -1> {
};
} // namespace detail
+// index sequence
template <typename T, int N>
using make_integer_sequence =
typename detail::make_integer_sequence<T, N - 1>::type;
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+template <int N>
+using make_index_sequence =
+ typename detail::make_integer_sequence<size_t, N - 1>::type;
+
} // namespace cpp
} // namespace LIBC_NAMESPACE_DECL
>From 92c05a6a997304b29040e81030126fb301978bde Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 9 Sep 2025 11:31:25 -0500
Subject: [PATCH 2/3] Support structured bindings
---
libc/src/__support/CPP/tuple.h | 40 +++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/CPP/tuple.h b/libc/src/__support/CPP/tuple.h
index 01d374a0841aa..5c928cdcd693c 100644
--- a/libc/src/__support/CPP/tuple.h
+++ b/libc/src/__support/CPP/tuple.h
@@ -24,6 +24,13 @@ struct tuple<Head, Tail...> : tuple<Tail...> {
LIBC_INLINE constexpr tuple() = default;
+ template <typename OHead, typename... OTail>
+ LIBC_INLINE constexpr tuple &operator=(const tuple<OHead, OTail...> &other) {
+ head = other.get_head();
+ this->get_tail() = other.get_tail();
+ return *this;
+ }
+
LIBC_INLINE constexpr tuple(const Head &h, const Tail &...t)
: tuple<Tail...>(t...), head(h) {}
@@ -55,6 +62,20 @@ LIBC_INLINE constexpr const auto &get(const tuple<Head, Tail...> &t) {
else
return get<I - 1>(t.get_tail());
}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr auto &&get(tuple<Head, Tail...> &&t) {
+ if constexpr (I == 0)
+ return static_cast<Head &&>(t.get_head());
+ else
+ return get<I - 1>(static_cast<tuple<Tail...> &&>(t.get_tail()));
+}
+template <size_t I, typename Head, typename... Tail>
+LIBC_INLINE constexpr const auto &&get(const tuple<Head, Tail...> &&t) {
+ if constexpr (I == 0)
+ return static_cast<const Head &&>(t.get_head());
+ else
+ return get<I - 1>(static_cast<const tuple<Tail...> &&>(t.get_tail()));
+}
template <typename T> struct tuple_size;
template <typename... Ts> struct tuple_size<tuple<Ts...>> {
@@ -67,7 +88,7 @@ struct tuple_element<I, tuple<Head, Tail...>>
: tuple_element<I - 1, tuple<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_element<0, tuple<Head, Tail...>> {
- using type = Head;
+ using type = cpp::remove_cv_t<cpp::remove_reference_t<Head>>;
};
namespace internal {
@@ -103,4 +124,21 @@ LIBC_INLINE constexpr auto tuple_cat(const Tuples &...tuples) {
} // namespace cpp
} // namespace LIBC_NAMESPACE_DECL
+// For structured binding support.
+namespace std {
+
+template <class T> struct tuple_size;
+template <size_t I, class T> struct tuple_element;
+
+template <typename... Ts>
+struct tuple_size<LIBC_NAMESPACE::cpp::tuple<Ts...>>
+ : LIBC_NAMESPACE::cpp::tuple_size<LIBC_NAMESPACE::cpp::tuple<Ts...>> {};
+
+template <size_t I, typename... Ts>
+struct tuple_element<I, LIBC_NAMESPACE::cpp::tuple<Ts...>>
+ : LIBC_NAMESPACE::cpp::tuple_element<I, LIBC_NAMESPACE::cpp::tuple<Ts...>> {
+};
+
+} // namespace std
+
#endif // LLVM_LIBC_SRC___SUPPORT_CPP_UTILITY_TUPLE_H
>From d9cce7c1267d38d4f817de18d73c8e687f6fd62a Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 9 Sep 2025 14:19:53 -0500
Subject: [PATCH 3/3] Allow arbitrary concat
---
libc/src/__support/CPP/simd.h | 48 ++++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 6 deletions(-)
diff --git a/libc/src/__support/CPP/simd.h b/libc/src/__support/CPP/simd.h
index bdafdec705d35..d73c030a6fdb0 100644
--- a/libc/src/__support/CPP/simd.h
+++ b/libc/src/__support/CPP/simd.h
@@ -271,10 +271,38 @@ LIBC_INLINE constexpr simd<T, N> select(simd<bool, N> m, simd<T, N> x,
}
namespace internal {
+template <typename T, size_t N, size_t O, size_t... I>
+LIBC_INLINE constexpr static cpp::simd<T, sizeof...(I)>
+extend(cpp::simd<T, N> x, cpp::index_sequence<I...>) {
+ return __builtin_shufflevector(x, x, (I < O ? static_cast<int>(I) : -1)...);
+}
+template <typename T, size_t N, size_t M, size_t O>
+LIBC_INLINE constexpr static auto extend(cpp::simd<T, N> x) {
+ if constexpr (N == M)
+ return x;
+ else if constexpr (M <= 2 * N)
+ return extend<T, N, M>(x, cpp::make_index_sequence<M>{});
+ else
+ return extend<T, 2 * N, M, O>(
+ extend<T, N, 2 * N>(x, cpp::make_index_sequence<2 * N>{}));
+}
template <typename T, size_t N, size_t M, size_t... I>
LIBC_INLINE constexpr static cpp::simd<T, N + M>
-concat(cpp::simd<T, N> x, cpp::simd<T, M> y, index_sequence<I...>) {
- return __builtin_shufflevector(x, y, I...);
+concat(cpp::simd<T, N> x, cpp::simd<T, M> y, cpp::index_sequence<I...>) {
+ constexpr size_t L = (N > M ? N : M);
+
+ auto x_ext = extend<T, N, L, N>(x);
+ auto y_ext = extend<T, M, L, M>(y);
+
+ auto remap = [](size_t idx) -> int {
+ if (idx < N)
+ return static_cast<int>(idx);
+ if (idx < N + M)
+ return static_cast<int>((idx - N) + L);
+ return -1;
+ };
+
+ return __builtin_shufflevector(x_ext, y_ext, remap(I)...);
}
template <typename T, size_t N, size_t Count, size_t Offset, size_t... I>
@@ -295,10 +323,18 @@ LIBC_INLINE constexpr static auto split(cpp::simd<T, N> x) {
} // namespace internal
// Shuffling helpers.
-template <typename T, size_t N>
-LIBC_INLINE constexpr static cpp::simd<T, N + N> concat(cpp::simd<T, N> x,
- cpp::simd<T, N> y) {
- return internal::concat(x, y, make_index_sequence<N + N>{});
+template <typename T, size_t N, size_t M>
+LIBC_INLINE constexpr static auto concat(cpp::simd<T, N> x, cpp::simd<T, M> y) {
+ return internal::concat(x, y, make_index_sequence<N + M>{});
+}
+template <typename T, size_t N, size_t M, typename... Rest>
+LIBC_INLINE constexpr static auto concat(cpp::simd<T, N> x, cpp::simd<T, M> y,
+ Rest... rest) {
+ auto xy = concat(x, y);
+ if constexpr (sizeof...(Rest))
+ return concat(xy, rest...);
+ else
+ return xy;
}
template <size_t... Sizes, typename T, size_t N> auto split(cpp::simd<T, N> x) {
static_assert((... + Sizes) == N, "split sizes must sum to vector size");
More information about the libc-commits
mailing list