[llvm] [llvm] Add `Repeated<T>` for memory-efficient repeated-value ranges (PR #186721)
Jakub Kuderski via llvm-commits
llvm-commits at lists.llvm.org
Sun Mar 15 17:59:06 PDT 2026
https://github.com/kuhar updated https://github.com/llvm/llvm-project/pull/186721
>From 7a44b5095edd6006b4cc6ca498585b1cd3deb9e1 Mon Sep 17 00:00:00 2001
From: Jakub Kuderski <jakub at nod-labs.com>
Date: Thu, 12 Mar 2026 23:27:37 -0400
Subject: [PATCH 1/3] [llvm] Add Repeated<T> for memory-efficient repeated
value ranges
Introduce a lightweight range representing N copies of the same value
without materializing a dynamic array. The range owns this value.
I plan to use it with MLIR APIs that often end up requiring N copies of
the same thing. Currently, we use `SmallVector<T>(N, Val)` for these,
which is wasteful.
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
llvm/include/llvm/ADT/Repeated.h | 122 ++++++++++++++++++++++++++++
llvm/unittests/ADT/CMakeLists.txt | 1 +
llvm/unittests/ADT/RepeatedTest.cpp | 106 ++++++++++++++++++++++++
3 files changed, 229 insertions(+)
create mode 100644 llvm/include/llvm/ADT/Repeated.h
create mode 100644 llvm/unittests/ADT/RepeatedTest.cpp
diff --git a/llvm/include/llvm/ADT/Repeated.h b/llvm/include/llvm/ADT/Repeated.h
new file mode 100644
index 0000000000000..22a951201f9f0
--- /dev/null
+++ b/llvm/include/llvm/ADT/Repeated.h
@@ -0,0 +1,122 @@
+//===- llvm/ADT/Repeated.h - Repeated value range ---------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the Repeated<T> class, a memory-efficient range representing N
+// copies of the same value.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_REPEATED_H
+#define LLVM_ADT_REPEATED_H
+
+#include "llvm/ADT/iterator.h"
+
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace llvm {
+
+/// A random-access iterator that always dereferences to the same value.
+template <typename T>
+class RepeatedIterator
+ : public iterator_facade_base<RepeatedIterator<T>,
+ std::random_access_iterator_tag, T, ptrdiff_t,
+ const T *, const T &> {
+ const T *value = nullptr;
+ ptrdiff_t index = 0;
+
+public:
+ RepeatedIterator() = default;
+ RepeatedIterator(const T *value, ptrdiff_t index)
+ : value(value), index(index) {}
+
+ const T &operator*() const { return *value; }
+
+ bool operator==(const RepeatedIterator &rhs) const {
+ assert((!value || !rhs.value || value == rhs.value) &&
+ "comparing iterators from different Repeated ranges");
+ return index == rhs.index;
+ }
+
+ bool operator<(const RepeatedIterator &rhs) const {
+ assert((!value || !rhs.value || value == rhs.value) &&
+ "comparing iterators from different Repeated ranges");
+ return index < rhs.index;
+ }
+
+ ptrdiff_t operator-(const RepeatedIterator &rhs) const {
+ assert((!value || !rhs.value || value == rhs.value) &&
+ "subtracting iterators from different Repeated ranges");
+ return index - rhs.index;
+ }
+
+ RepeatedIterator &operator+=(ptrdiff_t n) {
+ index += n;
+ return *this;
+ }
+
+ RepeatedIterator &operator-=(ptrdiff_t n) {
+ index -= n;
+ return *this;
+ }
+};
+
+/// A memory-efficient immutable range with a single value repeated N times.
+/// The value is owned by the range.
+///
+/// `Repeated<T>` is also a proper random-access range: `begin()`/`end()`
+/// return iterators that always dereference to the same stored value.
+template <typename T> struct [[nodiscard]] Repeated {
+ /// Pointer-aligned wrapper for the stored value, suitable for use as a
+ /// PointerUnion target in range types (e.g. TypeRange, ValueRange).
+ struct alignas(alignof(void *)) Storage {
+ T value;
+ };
+
+ Storage storage;
+ size_t count;
+
+ /// Create a `value` repeated `count` times.
+ /// Uses the same argument order like STD container constructors.
+ template <typename U,
+ typename = std::enable_if_t<std::is_constructible_v<T, U &&>>>
+ Repeated(size_t count, U &&value)
+ : storage{std::forward<U>(value)}, count(count) {}
+
+ using iterator = RepeatedIterator<T>;
+ using const_iterator = iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = reverse_iterator;
+ using value_type = T;
+ using size_type = size_t;
+
+ iterator begin() const { return {&storage.value, 0}; }
+ iterator end() const {
+ return {&storage.value, static_cast<ptrdiff_t>(count)};
+ }
+ reverse_iterator rbegin() const { return reverse_iterator(end()); }
+ reverse_iterator rend() const { return reverse_iterator(begin()); }
+
+ size_t size() const { return count; }
+ bool empty() const { return count == 0; }
+
+ const T &value() const { return storage.value; }
+ const T &operator[](size_t idx) const {
+ assert(idx < size() && "Out of bounds");
+ (void)idx;
+ return storage.value;
+ }
+};
+
+template <typename U> Repeated(size_t, U &&) -> Repeated<std::decay_t<U>>;
+
+} // namespace llvm
+
+#endif // LLVM_ADT_REPEATED_H
diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt
index af503d9b82843..2c82dbbb46fac 100644
--- a/llvm/unittests/ADT/CMakeLists.txt
+++ b/llvm/unittests/ADT/CMakeLists.txt
@@ -65,6 +65,7 @@ add_llvm_unittest(ADTTests
PriorityWorklistTest.cpp
RadixTreeTest.cpp
RangeAdapterTest.cpp
+ RepeatedTest.cpp
RewriteBufferTest.cpp
SCCIteratorTest.cpp
STLExtrasTest.cpp
diff --git a/llvm/unittests/ADT/RepeatedTest.cpp b/llvm/unittests/ADT/RepeatedTest.cpp
new file mode 100644
index 0000000000000..9c412a75e4af1
--- /dev/null
+++ b/llvm/unittests/ADT/RepeatedTest.cpp
@@ -0,0 +1,106 @@
+//===- RepeatedTest.cpp - Repeated unit tests -----------------------------===//
+//
+// 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 "llvm/ADT/Repeated.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+using ::testing::Each;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+
+namespace llvm {
+namespace {
+
+TEST(RepeatedTest, Construction) {
+ {
+ Repeated<int> Rep(5, 42);
+ EXPECT_EQ(Rep.value(), 42);
+ EXPECT_THAT(Rep, SizeIs(5));
+ EXPECT_EQ(Rep[0], 42);
+ EXPECT_EQ(Rep[4], 42);
+ EXPECT_THAT(Rep, ElementsAre(42, 42, 42, 42, 42));
+ }
+
+ {
+ Repeated<std::string> Rep(3, "hello");
+ EXPECT_EQ(Rep.value(), "hello");
+ EXPECT_THAT(Rep, SizeIs(3));
+ }
+
+ {
+ // Move-only type.
+ Repeated<std::unique_ptr<int>> Rep(1, std::make_unique<int>(42));
+ EXPECT_EQ(*Rep.value(), 42);
+ EXPECT_THAT(Rep, SizeIs(1));
+ }
+
+ {
+ // Empty Rep.
+ Repeated<int> EmptyRep(0, 42);
+ EXPECT_THAT(EmptyRep, IsEmpty());
+ }
+}
+
+TEST(RepeatedTest, CTAD) {
+ static_assert(std::is_same_v<decltype(Repeated(3, 42)), Repeated<int>>);
+ std::string S = "world";
+ Repeated RepStr(2, S);
+ static_assert(std::is_same_v<decltype(RepStr), Repeated<std::string>>);
+ static_assert(
+ std::is_same_v<decltype(Repeated(1, "literal")), Repeated<const char *>>);
+ SUCCEED();
+}
+
+TEST(RepeatedTest, StorageAlignment) {
+ static_assert(alignof(Repeated<int>::Storage) >= alignof(void *));
+ static_assert(alignof(Repeated<std::string>::Storage) >= alignof(void *));
+ static_assert(alignof(Repeated<void *>::Storage) >= alignof(void *));
+}
+
+TEST(RepeatedTest, IteratorRandomAccess) {
+ Repeated<int> Rep(10, 7);
+ RepeatedIterator<int> It = Rep.begin();
+
+ EXPECT_EQ(*It, 7);
+ EXPECT_EQ(*(It + 5), 7);
+
+ It += 10;
+ EXPECT_EQ(It, Rep.end());
+ --It;
+ EXPECT_LT(It, Rep.end());
+ EXPECT_EQ(Rep.end() - Rep.begin(), 10);
+ ++It;
+ EXPECT_EQ(It, Rep.end());
+}
+
+TEST(RepeatedTest, ReverseIterator) {
+ Repeated<int> Rep(5, 42);
+ std::vector<int> Reversed(Rep.rbegin(), Rep.rend());
+ EXPECT_THAT(Reversed, SizeIs(5));
+ EXPECT_THAT(Reversed, Each(42));
+}
+
+TEST(RepeatedTest, IteratorTraits) {
+ using It = RepeatedIterator<int>;
+ static_assert(std::is_default_constructible_v<It>);
+ static_assert(std::is_same_v<std::iterator_traits<It>::iterator_category,
+ std::random_access_iterator_tag>);
+ static_assert(std::is_same_v<std::iterator_traits<It>::value_type, int>);
+ static_assert(
+ std::is_same_v<std::iterator_traits<It>::difference_type, ptrdiff_t>);
+ SUCCEED();
+}
+
+} // anonymous namespace
+} // namespace llvm
>From 9b0d2159e084e235cc3f3e6557fe1f3138919b16 Mon Sep 17 00:00:00 2001
From: Jakub Kuderski <jakub at nod-labs.com>
Date: Sun, 15 Mar 2026 20:52:56 -0400
Subject: [PATCH 2/3] Fix alignment for large scalars
---
llvm/include/llvm/ADT/Repeated.h | 7 ++++---
llvm/unittests/ADT/RepeatedTest.cpp | 4 +++-
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/ADT/Repeated.h b/llvm/include/llvm/ADT/Repeated.h
index 22a951201f9f0..1574fc75fa8b5 100644
--- a/llvm/include/llvm/ADT/Repeated.h
+++ b/llvm/include/llvm/ADT/Repeated.h
@@ -16,6 +16,7 @@
#include "llvm/ADT/iterator.h"
+#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
@@ -74,9 +75,9 @@ class RepeatedIterator
/// `Repeated<T>` is also a proper random-access range: `begin()`/`end()`
/// return iterators that always dereference to the same stored value.
template <typename T> struct [[nodiscard]] Repeated {
- /// Pointer-aligned wrapper for the stored value, suitable for use as a
- /// PointerUnion target in range types (e.g. TypeRange, ValueRange).
- struct alignas(alignof(void *)) Storage {
+ /// Wrapper for the stored value, meant to be suitable for use as a
+ /// PointerUnion target in range types (e.g., TypeRange, ValueRange).
+ struct alignas(std::max(alignof(void *), alignof(T))) Storage {
T value;
};
diff --git a/llvm/unittests/ADT/RepeatedTest.cpp b/llvm/unittests/ADT/RepeatedTest.cpp
index 9c412a75e4af1..dd5fbb116bf19 100644
--- a/llvm/unittests/ADT/RepeatedTest.cpp
+++ b/llvm/unittests/ADT/RepeatedTest.cpp
@@ -65,7 +65,9 @@ TEST(RepeatedTest, CTAD) {
TEST(RepeatedTest, StorageAlignment) {
static_assert(alignof(Repeated<int>::Storage) >= alignof(void *));
static_assert(alignof(Repeated<std::string>::Storage) >= alignof(void *));
- static_assert(alignof(Repeated<void *>::Storage) >= alignof(void *));
+ static_assert(alignof(Repeated<bool>::Storage) >= alignof(void *));
+ static_assert(alignof(Repeated<long double>::Storage) >=
+ alignof(long double));
}
TEST(RepeatedTest, IteratorRandomAccess) {
>From fa9f12dc0ba1dfada9830a866107e925f33f452b Mon Sep 17 00:00:00 2001
From: Jakub Kuderski <jakub at nod-labs.com>
Date: Sun, 15 Mar 2026 20:56:37 -0400
Subject: [PATCH 3/3] Simplify
---
llvm/include/llvm/ADT/Repeated.h | 11 ++++-------
llvm/unittests/ADT/RepeatedTest.cpp | 8 --------
2 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/llvm/include/llvm/ADT/Repeated.h b/llvm/include/llvm/ADT/Repeated.h
index 1574fc75fa8b5..f821f3f1f73ca 100644
--- a/llvm/include/llvm/ADT/Repeated.h
+++ b/llvm/include/llvm/ADT/Repeated.h
@@ -16,10 +16,8 @@
#include "llvm/ADT/iterator.h"
-#include <algorithm>
#include <cassert>
#include <cstddef>
-#include <type_traits>
#include <utility>
namespace llvm {
@@ -75,9 +73,9 @@ class RepeatedIterator
/// `Repeated<T>` is also a proper random-access range: `begin()`/`end()`
/// return iterators that always dereference to the same stored value.
template <typename T> struct [[nodiscard]] Repeated {
- /// Wrapper for the stored value, meant to be suitable for use as a
- /// PointerUnion target in range types (e.g., TypeRange, ValueRange).
- struct alignas(std::max(alignof(void *), alignof(T))) Storage {
+ /// Wrapper for the stored value used as a PointerUnion target in range
+ /// types (e.g., TypeRange, ValueRange).
+ struct Storage {
T value;
};
@@ -86,8 +84,7 @@ template <typename T> struct [[nodiscard]] Repeated {
/// Create a `value` repeated `count` times.
/// Uses the same argument order like STD container constructors.
- template <typename U,
- typename = std::enable_if_t<std::is_constructible_v<T, U &&>>>
+ template <typename U>
Repeated(size_t count, U &&value)
: storage{std::forward<U>(value)}, count(count) {}
diff --git a/llvm/unittests/ADT/RepeatedTest.cpp b/llvm/unittests/ADT/RepeatedTest.cpp
index dd5fbb116bf19..f55be4b22ee5f 100644
--- a/llvm/unittests/ADT/RepeatedTest.cpp
+++ b/llvm/unittests/ADT/RepeatedTest.cpp
@@ -62,14 +62,6 @@ TEST(RepeatedTest, CTAD) {
SUCCEED();
}
-TEST(RepeatedTest, StorageAlignment) {
- static_assert(alignof(Repeated<int>::Storage) >= alignof(void *));
- static_assert(alignof(Repeated<std::string>::Storage) >= alignof(void *));
- static_assert(alignof(Repeated<bool>::Storage) >= alignof(void *));
- static_assert(alignof(Repeated<long double>::Storage) >=
- alignof(long double));
-}
-
TEST(RepeatedTest, IteratorRandomAccess) {
Repeated<int> Rep(10, 7);
RepeatedIterator<int> It = Rep.begin();
More information about the llvm-commits
mailing list