[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