[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