[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