[llvm] [ADT] Add `llvm::conditionally_reverse()` iterator (PR #171040)

Benjamin Maxwell via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 7 07:27:11 PST 2025


https://github.com/MacDue updated https://github.com/llvm/llvm-project/pull/171040

>From cf49ccb1c48dd234c82d89e501006eb0c60f3f43 Mon Sep 17 00:00:00 2001
From: MacDue <macdue at dueutil.tech>
Date: Sun, 7 Dec 2025 14:05:30 +0000
Subject: [PATCH 1/2] [ADT] Add `llvm::conditionally_reverse()` iterator

This patch adds a simple iterator range that allows conditionally
iterating a collection in reverse. It works with any collection
supported by `llvm::reverse(Collection)`.

```
void foo(bool Reverse, std::vector<int>& C) {
  for (auto I : conditionally_reverse(C, Reverse)) {
    // ...
  }
}
```

This patch updates a couple of cases in LLVM to show how this can be
used.

Clang also does a decent job optimizing this: https://godbolt.org/z/sdrv1KzG6
---
 llvm/include/llvm/ADT/STLExtras.h         | 44 +++++++++++++++++++++++
 llvm/lib/CodeGen/PrologEpilogInserter.cpp | 31 ++++++----------
 llvm/lib/CodeGen/RegisterClassInfo.cpp    |  7 +---
 llvm/unittests/ADT/STLExtrasTest.cpp      | 26 ++++++++++++++
 4 files changed, 81 insertions(+), 27 deletions(-)

diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index af0e4a36be1b1..b8bc757f31ba8 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -40,6 +40,7 @@
 #include <tuple>
 #include <type_traits>
 #include <utility>
+#include <variant>
 
 #ifdef EXPENSIVE_CHECKS
 #include <random> // for std::mt19937
@@ -1415,6 +1416,49 @@ template <typename ContainerTy> auto make_second_range(ContainerTy &&c) {
       });
 }
 
+namespace detail {
+/// A barebones wrapper around a variant of one or more iterators on the same
+/// collection. All iterators must return the same value/reference types.
+template <typename Iterator, typename... Iterators>
+struct variant_iterator_impl {
+  template <typename AnyIterator>
+  variant_iterator_impl(AnyIterator &&I) : I(std::move(I)){};
+  variant_iterator_impl &operator++() {
+    std::visit([](auto &I) { ++I; }, I);
+    return *this;
+  }
+  typename std::iterator_traits<Iterator>::reference operator*() const {
+    return std::visit([](auto &&I) -> decltype(*I) { return *I; }, I);
+  }
+  inline friend bool operator==(const variant_iterator_impl &LHS,
+                                const variant_iterator_impl &RHS) {
+    return LHS.I == RHS.I;
+  }
+  friend bool operator!=(const variant_iterator_impl &LHS,
+                         const variant_iterator_impl &RHS) {
+    return !(LHS == RHS);
+  }
+  std::variant<Iterator, Iterators...> I;
+};
+} // namespace detail
+
+/// Return a range that conditionally reverses \p C. The collection is iterated
+/// in reverse if \p Reverse is true (otherwise, it is iterated forwards).
+template <typename ContainerTy>
+[[nodiscard]] auto conditionally_reverse(ContainerTy &&C, bool Reverse) {
+  using ForwardIteratorT = decltype(C.begin());
+  using ReverseIteratorT = decltype(reverse(C).begin());
+  using ConditionallyReverseIteratorT =
+      detail::variant_iterator_impl<ForwardIteratorT, ReverseIteratorT>;
+  if (Reverse) {
+    auto ReverseRange = reverse(C);
+    return make_range(ConditionallyReverseIteratorT(ReverseRange.begin()),
+                      ConditionallyReverseIteratorT(ReverseRange.end()));
+  }
+  return make_range(ConditionallyReverseIteratorT(C.begin()),
+                    ConditionallyReverseIteratorT(C.end()));
+}
+
 //===----------------------------------------------------------------------===//
 //     Extra additions to <utility>
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index 510faa3f56f3e..b8830f99e5ca8 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -911,29 +911,18 @@ void PEIImpl::calculateFrameObjectOffsets(MachineFunction &MF) {
   Align MaxAlign = MFI.getMaxAlign();
   // First assign frame offsets to stack objects that are used to spill
   // callee saved registers.
-  if (StackGrowsDown) {
-    for (int FI = MFI.getObjectIndexBegin(); FI < MFI.getObjectIndexEnd();
-         FI++) {
-      // Only allocate objects on the default stack.
-      if (!MFI.isCalleeSavedObjectIndex(FI) ||
-          MFI.getStackID(FI) != TargetStackID::Default)
-        continue;
-      // TODO: should we be using MFI.isDeadObjectIndex(FI) here?
-      AdjustStackOffset(MFI, FI, StackGrowsDown, Offset, MaxAlign);
-    }
-  } else {
-    for (int FI = MFI.getObjectIndexEnd() - 1; FI >= MFI.getObjectIndexBegin();
-         FI--) {
-      // Only allocate objects on the default stack.
-      if (!MFI.isCalleeSavedObjectIndex(FI) ||
-          MFI.getStackID(FI) != TargetStackID::Default)
-        continue;
+  auto AllFIs = seq(MFI.getObjectIndexBegin(), MFI.getObjectIndexEnd());
+  for (int FI : conditionally_reverse(AllFIs, /*Reverse=*/!StackGrowsDown)) {
+    // Only allocate objects on the default stack.
+    if (!MFI.isCalleeSavedObjectIndex(FI) ||
+        MFI.getStackID(FI) != TargetStackID::Default)
+      continue;
 
-      if (MFI.isDeadObjectIndex(FI))
-        continue;
+    // TODO: should this just be if (MFI.isDeadObjectIndex(FI))
+    if (!StackGrowsDown && MFI.isDeadObjectIndex(FI))
+      continue;
 
-      AdjustStackOffset(MFI, FI, StackGrowsDown, Offset, MaxAlign);
-    }
+    AdjustStackOffset(MFI, FI, StackGrowsDown, Offset, MaxAlign);
   }
 
   assert(MaxAlign == MFI.getMaxAlign() &&
diff --git a/llvm/lib/CodeGen/RegisterClassInfo.cpp b/llvm/lib/CodeGen/RegisterClassInfo.cpp
index bbeb7adae825c..9690402db54cb 100644
--- a/llvm/lib/CodeGen/RegisterClassInfo.cpp
+++ b/llvm/lib/CodeGen/RegisterClassInfo.cpp
@@ -145,12 +145,7 @@ void RegisterClassInfo::compute(const TargetRegisterClass *RC) const {
   // FIXME: Once targets reserve registers instead of removing them from the
   // allocation order, we can simply use begin/end here.
   ArrayRef<MCPhysReg> RawOrder = RC->getRawAllocationOrder(*MF, Reverse);
-  std::vector<MCPhysReg> ReverseOrder;
-  if (Reverse) {
-    llvm::append_range(ReverseOrder, reverse(RawOrder));
-    RawOrder = ArrayRef<MCPhysReg>(ReverseOrder);
-  }
-  for (unsigned PhysReg : RawOrder) {
+  for (unsigned PhysReg : conditionally_reverse(RawOrder, Reverse)) {
     // Remove reserved registers from the allocation order.
     if (Reserved.test(PhysReg))
       continue;
diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 85567775e4ebd..50c7f898de8a3 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -1693,6 +1693,32 @@ TEST(STLExtrasTest, ProductOf) {
   EXPECT_EQ(product_of(V3), 2.0f);
 }
 
+TEST(STLExtrasTest, ConditionallyReverse) {
+  // Test that you can modify the underlying entries of an lvalue range through
+  // the enumeration iterator.
+  std::vector<char> foo = {'a', 'b', 'c'};
+  std::vector<char> ReverseResults;
+
+  // Test backwards.
+  for (char Value : llvm::conditionally_reverse(foo, /*Reverse=*/true)) {
+    ReverseResults.emplace_back(Value);
+  }
+  EXPECT_THAT(ReverseResults, ElementsAre('c', 'b', 'a'));
+
+  // Test forwards.
+  std::vector<char> ForwardResults;
+  for (char Value : llvm::conditionally_reverse(foo, /*Reverse=*/false)) {
+    ForwardResults.emplace_back(Value);
+  }
+  EXPECT_THAT(ForwardResults, ElementsAre('a', 'b', 'c'));
+
+  // Test modifying collection.
+  for (char &Value : llvm::conditionally_reverse(foo, /*Reverse=*/true)) {
+    ++Value;
+  }
+  EXPECT_THAT(foo, ElementsAre('b', 'c', 'd'));
+}
+
 struct Foo;
 struct Bar {};
 

>From 315648aac3afdd11651589d3d74368adb802a5f3 Mon Sep 17 00:00:00 2001
From: Benjamin Maxwell <macdue at dueutil.tech>
Date: Sun, 7 Dec 2025 15:27:02 +0000
Subject: [PATCH 2/2] Remove incorrect comment

---
 llvm/unittests/ADT/STLExtrasTest.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 50c7f898de8a3..fd5bd7d3fec57 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -1694,8 +1694,6 @@ TEST(STLExtrasTest, ProductOf) {
 }
 
 TEST(STLExtrasTest, ConditionallyReverse) {
-  // Test that you can modify the underlying entries of an lvalue range through
-  // the enumeration iterator.
   std::vector<char> foo = {'a', 'b', 'c'};
   std::vector<char> ReverseResults;
 



More information about the llvm-commits mailing list