[llvm] [SandboxVec][VecUtils] Lane Enumerator (PR #188355)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 10:53:03 PDT 2026
https://github.com/vporpo updated https://github.com/llvm/llvm-project/pull/188355
>From 8c65f2aadc93c437ff04203448a0a05b0d003f74 Mon Sep 17 00:00:00 2001
From: Vasileios Porpodas <vasileios.porpodas at amd.com>
Date: Tue, 24 Mar 2026 00:33:23 +0000
Subject: [PATCH 1/2] [SandboxVec][VecUtils] Lane Enumerator
This patch introduces an iterator that helps us iterate over lane-value pairs in a range.
For example, given a container `(i32 %v0, <2 x i32> %v1, i32 %v2)` we get:
Lane Value
0 %v0
1 %v1
3 %v2
We use this iterator to replace the lane counting logic in BottomUpVec.cpp.
---
.../Vectorize/SandboxVectorizer/VecUtils.h | 64 +++++++++++++++++++
.../SandboxVectorizer/Passes/BottomUpVec.cpp | 13 +---
.../SandboxVectorizer/VecUtilsTest.cpp | 31 +++++++++
3 files changed, 97 insertions(+), 11 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
index 6d5ddbf5f1259..102950e99291c 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
@@ -17,6 +17,7 @@
#include "llvm/SandboxIR/Type.h"
#include "llvm/SandboxIR/Utils.h"
#include "llvm/Support/Compiler.h"
+#include <iterator>
namespace llvm {
/// Traits for DenseMap.
@@ -304,6 +305,69 @@ class VecUtils {
WhereIt, Ctx, "Unpack");
}
+ /// Iterate over all lanes and Value pairs commulatively.
+ // For example, given a range: {i32 %v0, <2 x i32> %v1, i32 %v2} we get:
+ // Lane Elm
+ // 0 %v0
+ // 1 %v1
+ // 3 %v2
+ template <typename RangeT, typename RangeIteratorT>
+ class LaneValueEnumerator {
+ const RangeT &Range;
+ /// Cummulative Lane count.
+ unsigned Lane;
+ /// Points to current element.
+ RangeIteratorT It;
+ RangeIteratorT ItE;
+
+ public:
+ LaneValueEnumerator(const RangeT &Range, unsigned Lane, RangeIteratorT It,
+ RangeIteratorT ItE)
+ : Range(Range), Lane(Lane), It(It), ItE(ItE) {}
+ using iterator_catecotry = std::input_iterator_tag;
+ // NOTE: dereference returns by value instead of by reference.
+ using value_type = std::pair<unsigned, Value *>;
+ using difference_type = std::ptrdiff_t;
+ using pointer = std::pair<unsigned, Value *> *;
+ using reference = std::pair<unsigned, Value *> &;
+ LaneValueEnumerator operator++() {
+ assert(It != ItE && "Already at end!");
+ auto *Ty = Utils::getExpectedType(*It);
+ if (auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
+ Lane += VecTy->getNumElements();
+ } else {
+ assert(!isa<VectorType>(Ty) && "Expected scalar type!");
+ Lane += 1;
+ }
+ ++It;
+ return *this;
+ }
+ value_type operator*() const { return {Lane, *It}; }
+ bool operator==(const LaneValueEnumerator &Other) const {
+ assert(&Range == &Other.Range && "Different ranges!");
+ // If at end, don't check Lane.
+ if (It == ItE)
+ return It == Other.It;
+ return It == Other.It && Lane == Other.Lane;
+ }
+ bool operator!=(const LaneValueEnumerator &Other) const {
+ return !(*this == Other);
+ }
+ };
+ // Deduction guide.
+ template <typename RangeT, typename RangeIteratorT>
+ LaneValueEnumerator(RangeT, unsigned, RangeIteratorT, RangeIteratorT)
+ -> LaneValueEnumerator<RangeT, RangeIteratorT>;
+
+ /// Helper for creating LaneValueEnumerator ranges. Can be used in for loops
+ /// like: `for (auto [Lane, V] : enumerateLanes(Range))`
+ template <typename ValueContainerT>
+ static auto enumerateLanes(const ValueContainerT &Range) {
+ auto Begin = LaneValueEnumerator(Range, 0, Range.begin(), Range.end());
+ auto End = LaneValueEnumerator(Range, 0, Range.end(), Range.end());
+ return make_range(Begin, End);
+ }
+
#ifndef NDEBUG
/// Helper dump function for debugging.
LLVM_DUMP_METHOD static void dump(ArrayRef<Value *> Bndl);
diff --git a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.cpp b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.cpp
index 2c573951fb8c5..6bf257fcf8b1d 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.cpp
@@ -360,8 +360,8 @@ void BottomUpVec::emitUnpacksForExternalUses(const ArrayRef<Value *> Bndl,
: std::next(
VecUtils::getLastPHIOrSelf(&*BB->begin())->getIterator());
}
- unsigned Lane = 0;
- for (Value *Elm : Bndl) {
+
+ for (auto [Lane, Elm] : VecUtils::enumerateLanes(Bndl)) {
for (User *U : Elm->users()) {
// Skip users that we just vectorized.
if (IMaps->isVectorized(U))
@@ -369,15 +369,6 @@ void BottomUpVec::emitUnpacksForExternalUses(const ArrayRef<Value *> Bndl,
auto *LastUnpackV = VecUtils::unpack(Vec, Elm->getType(), Lane, WhereIt);
Elm->replaceAllUsesWith(LastUnpackV);
}
-
- // Increment Lane.
- auto *ElmTy = Utils::getExpectedType(Elm);
- if (auto *VecTy = dyn_cast<FixedVectorType>(ElmTy)) {
- Lane += VecTy->getNumElements();
- } else {
- assert(!isa<VectorType>(ElmTy) && "Expected scalar type!");
- Lane += 1;
- }
}
}
diff --git a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/VecUtilsTest.cpp b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/VecUtilsTest.cpp
index fe694393e71d5..110c560098894 100644
--- a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/VecUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/VecUtilsTest.cpp
@@ -679,3 +679,34 @@ define void @foo(<4 x i32> %vec, i32 %scalar) {
EXPECT_DEBUG_DEATH(sandboxir::VecUtils::unpack(Vec, Ty2xi8, 4, WhereIt),
".*type.*");
}
+
+TEST_F(VecUtilsTest, LaneValueEnumerator) {
+ parseIR(R"IR(
+define void @foo(i32 %s0, <4 x i32> %v0, i32 %s1, <2 x i32> %v1, <3 x i32> %v2, i32 %s2) {
+bb0:
+ ret void
+}
+)IR");
+ Function &LLVMF = *M->getFunction("foo");
+
+ sandboxir::Context Ctx(C);
+ auto &F = *Ctx.createFunction(&LLVMF);
+ unsigned Idx = 0;
+ sandboxir::Value *S0 = F.getArg(Idx++);
+ sandboxir::Value *V0 = F.getArg(Idx++);
+ sandboxir::Value *S1 = F.getArg(Idx++);
+ sandboxir::Value *V1 = F.getArg(Idx++);
+ sandboxir::Value *V2 = F.getArg(Idx++);
+ sandboxir::Value *S2 = F.getArg(Idx++);
+
+ SmallVector<sandboxir::Value *, 6> Bndl({S0, V0, S1, V1, V2, S2});
+ SmallVector<unsigned, 6> Lanes;
+ SmallVector<sandboxir::Value *, 6> Elms;
+ for (auto [Lane, Elm] : sandboxir::VecUtils::enumerateLanes(Bndl)) {
+ Lanes.push_back(Lane);
+ Elms.push_back(Elm);
+ }
+
+ EXPECT_EQ(Elms, Bndl);
+ EXPECT_THAT(Lanes, testing::ElementsAre(0, 1, 5, 6, 8, 11));
+}
>From cda308b442f3617c941932d6d7f05a39a305465e Mon Sep 17 00:00:00 2001
From: Vasileios Porpodas <vasileios.porpodas at amd.com>
Date: Mon, 30 Mar 2026 17:47:00 +0000
Subject: [PATCH 2/2] fixup! [SandboxVec][VecUtils] Lane Enumerator
---
.../Vectorize/SandboxVectorizer/VecUtils.h | 34 ++++++++-----------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
index 102950e99291c..f6f25a7ef48ee 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h
@@ -305,25 +305,25 @@ class VecUtils {
WhereIt, Ctx, "Unpack");
}
- /// Iterate over all lanes and Value pairs commulatively.
+ /// Iterate over all lanes and Value pairs.
// For example, given a range: {i32 %v0, <2 x i32> %v1, i32 %v2} we get:
// Lane Elm
// 0 %v0
// 1 %v1
// 3 %v2
- template <typename RangeT, typename RangeIteratorT>
- class LaneValueEnumerator {
- const RangeT &Range;
- /// Cummulative Lane count.
- unsigned Lane;
+ template <typename RangeIteratorT> class LaneValueEnumerator {
/// Points to current element.
RangeIteratorT It;
RangeIteratorT ItE;
+ /// Accumulator of lanes.
+ unsigned Lane;
public:
- LaneValueEnumerator(const RangeT &Range, unsigned Lane, RangeIteratorT It,
- RangeIteratorT ItE)
- : Range(Range), Lane(Lane), It(It), ItE(ItE) {}
+ // Note that We can start counting from a non-zero BeginLane, though the
+ // user must make sure it corresponds to the correct lane matching Begin.
+ LaneValueEnumerator(RangeIteratorT Begin, RangeIteratorT End,
+ unsigned BeginLane)
+ : It(Begin), ItE(End), Lane(BeginLane) {}
using iterator_catecotry = std::input_iterator_tag;
// NOTE: dereference returns by value instead of by reference.
using value_type = std::pair<unsigned, Value *>;
@@ -344,27 +344,23 @@ class VecUtils {
}
value_type operator*() const { return {Lane, *It}; }
bool operator==(const LaneValueEnumerator &Other) const {
- assert(&Range == &Other.Range && "Different ranges!");
- // If at end, don't check Lane.
- if (It == ItE)
- return It == Other.It;
- return It == Other.It && Lane == Other.Lane;
+ return It == Other.It;
}
bool operator!=(const LaneValueEnumerator &Other) const {
return !(*this == Other);
}
};
// Deduction guide.
- template <typename RangeT, typename RangeIteratorT>
- LaneValueEnumerator(RangeT, unsigned, RangeIteratorT, RangeIteratorT)
- -> LaneValueEnumerator<RangeT, RangeIteratorT>;
+ template <typename RangeIteratorT>
+ LaneValueEnumerator(RangeIteratorT, RangeIteratorT,
+ unsigned) -> LaneValueEnumerator<RangeIteratorT>;
/// Helper for creating LaneValueEnumerator ranges. Can be used in for loops
/// like: `for (auto [Lane, V] : enumerateLanes(Range))`
template <typename ValueContainerT>
static auto enumerateLanes(const ValueContainerT &Range) {
- auto Begin = LaneValueEnumerator(Range, 0, Range.begin(), Range.end());
- auto End = LaneValueEnumerator(Range, 0, Range.end(), Range.end());
+ auto Begin = LaneValueEnumerator(Range.begin(), Range.end(), 0);
+ auto End = LaneValueEnumerator(Range.end(), Range.end(), 0);
return make_range(Begin, End);
}
More information about the llvm-commits
mailing list