[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