[llvm] [llvm] Add `Repeated<T>` for memory-efficient repeated-value ranges (PR #186721)

via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 15 17:46:59 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-adt

Author: Jakub Kuderski (kuhar)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/186721.diff


3 Files Affected:

- (added) llvm/include/llvm/ADT/Repeated.h (+122) 
- (modified) llvm/unittests/ADT/CMakeLists.txt (+1) 
- (added) llvm/unittests/ADT/RepeatedTest.cpp (+106) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/186721


More information about the llvm-commits mailing list