[llvm] [llubi] Vector manipulation intrinsics cleanup (PR #195004)

Zhige Chen via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 22:23:31 PDT 2026


https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/195004

>From 55154647e66668e5a9b4ce6a0217bfa14c787459 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Thu, 30 Apr 2026 13:18:50 +0800
Subject: [PATCH 1/2] [llubi] Vector manipulation intrinsics cleanup

---
 .../llubi/intr_vector_extract_bad_idx.ll      | 11 ++++++
 ...tr_vector_extract_scalable_idx_overflow.ll | 12 ++++++
 .../tools/llubi/intr_vector_insert_bad_idx.ll | 11 ++++++
 ...ntr_vector_insert_scalable_idx_overflow.ll | 12 ++++++
 llvm/test/tools/llubi/intr_vector_manip.ll    |  8 ++--
 llvm/tools/llubi/lib/Interpreter.cpp          | 38 ++++++++++++++++---
 6 files changed, 82 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/tools/llubi/intr_vector_extract_bad_idx.ll
 create mode 100644 llvm/test/tools/llubi/intr_vector_extract_scalable_idx_overflow.ll
 create mode 100644 llvm/test/tools/llubi/intr_vector_insert_bad_idx.ll
 create mode 100644 llvm/test/tools/llubi/intr_vector_insert_scalable_idx_overflow.ll

diff --git a/llvm/test/tools/llubi/intr_vector_extract_bad_idx.ll b/llvm/test/tools/llubi/intr_vector_extract_bad_idx.ll
new file mode 100644
index 0000000000000..e4c8462e87ff7
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_extract_bad_idx.ll
@@ -0,0 +1,11 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %extract_bad_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 1)
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: llvm.vector.extract index is not a multiple of the result's known minimum vector length.
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_extract_scalable_idx_overflow.ll b/llvm/test/tools/llubi/intr_vector_extract_scalable_idx_overflow.ll
new file mode 100644
index 0000000000000..a1a40eb1574e5
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_extract_scalable_idx_overflow.ll
@@ -0,0 +1,12 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose --vscale=4 < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %extract_idx_overflow = call <vscale x 2 x i32> @llvm.vector.extract.nxv2i32.nxv4i32(<vscale x 4 x i32> zeroinitializer, i64 9223372036854775808)
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT:   %extract_idx_overflow = call <vscale x 2 x i32> @llvm.vector.extract.nxv2i32.nxv4i32(<vscale x 4 x i32> zeroinitializer, i64 -9223372036854775808) => poison
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vector_insert_bad_idx.ll b/llvm/test/tools/llubi/intr_vector_insert_bad_idx.ll
new file mode 100644
index 0000000000000..0158e3e1642e1
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_insert_bad_idx.ll
@@ -0,0 +1,11 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %insert_bad_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> zeroinitializer, i64 1)
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: llvm.vector.insert index is not a multiple of the subvector's known minimum vector length.
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_insert_scalable_idx_overflow.ll b/llvm/test/tools/llubi/intr_vector_insert_scalable_idx_overflow.ll
new file mode 100644
index 0000000000000..0b087d998b95c
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_insert_scalable_idx_overflow.ll
@@ -0,0 +1,12 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose --vscale=4 < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %insert_idx_overflow = call <vscale x 4 x i32> @llvm.vector.insert.nxv4i32.nxv2i32(<vscale x 4 x i32> zeroinitializer, <vscale x 2 x i32> zeroinitializer, i64 9223372036854775808)
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT:   %insert_idx_overflow = call <vscale x 4 x i32> @llvm.vector.insert.nxv4i32.nxv2i32(<vscale x 4 x i32> zeroinitializer, <vscale x 2 x i32> zeroinitializer, i64 -9223372036854775808) => poison
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vector_manip.ll b/llvm/test/tools/llubi/intr_vector_manip.ll
index bcccb9de93d8a..3dcbbd226c4ad 100644
--- a/llvm/test/tools/llubi/intr_vector_manip.ll
+++ b/llvm/test/tools/llubi/intr_vector_manip.ll
@@ -5,12 +5,12 @@ define void @main() {
   %insert_mid = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 10, i32 11>, i64 2)
   %insert_poison_lane = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 poison, i32 11>, i64 2)
   %insert_tail = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 4)
-  %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 5)
+  %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 6)
 
   %extract_mid = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 2)
   %extract_poison_lane = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 poison, i32 2, i32 3, i32 4, i32 5>, i64 0)
   %extract_tail = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 4)
-  %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 5)
+  %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 6)
 
   %reverse = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>)
   %reverse_poison = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>)
@@ -35,11 +35,11 @@ define void @main() {
 ; CHECK-NEXT:   %insert_mid = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 10, i32 11>, i64 2) => { i32 0, i32 1, i32 10, i32 11, i32 4, i32 5 }
 ; CHECK-NEXT:   %insert_poison_lane = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 poison, i32 11>, i64 2) => { i32 0, i32 1, poison, i32 11, i32 4, i32 5 }
 ; CHECK-NEXT:   %insert_tail = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 4) => { i32 0, i32 0, i32 0, i32 0, i32 9, i32 10 }
-; CHECK-NEXT:   %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 5) => poison
+; CHECK-NEXT:   %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 6) => poison
 ; CHECK-NEXT:   %extract_mid = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 2) => { i32 2, i32 3 }
 ; CHECK-NEXT:   %extract_poison_lane = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 poison, i32 2, i32 3, i32 4, i32 5>, i64 0) => { i32 0, poison }
 ; CHECK-NEXT:   %extract_tail = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 4) => { i32 4, i32 5 }
-; CHECK-NEXT:   %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 5) => poison
+; CHECK-NEXT:   %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 6) => poison
 ; CHECK-NEXT:   %reverse = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>) => { i32 3, i32 2, i32 1, i32 0 }
 ; CHECK-NEXT:   %reverse_poison = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>) => { i32 3, i32 2, poison, i32 0 }
 ; CHECK-NEXT:   %splice_left = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 2) => { i32 2, i32 3, i32 10, i32 11 }
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 26e01c0e4bd70..e87ebfabc5db9 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -22,6 +22,8 @@
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/Support/Allocator.h"
 
+#include <limits>
+
 namespace llvm::ubi {
 
 using namespace PatternMatch;
@@ -744,8 +746,21 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       const auto &Vec = Args[0].asAggregate();
       const auto &SubVec = Args[1].asAggregate();
       const auto &Idx = Args[2].asInteger();
-      const uint64_t Offset = Idx.getZExtValue();
-      if (Offset + SubVec.size() > Vec.size())
+      auto EC = cast<VectorType>(CB.getArgOperand(1)->getType())
+                    ->getElementCount();
+      const uint64_t RawOffset = Idx.getZExtValue();
+      if (RawOffset % EC.getKnownMinValue() != 0) {
+        reportImmediateUB("llvm.vector.insert index is not a multiple of the "
+                          "subvector's known minimum vector length.");
+        return AnyValue::poison();
+      }
+      const uint32_t VScale = Ctx.getVScale();
+      if (EC.isScalable() && VScale != 0 &&
+          RawOffset > std::numeric_limits<uint64_t>::max() / VScale)
+        return AnyValue::poison();
+      const uint64_t Offset =
+          EC.isScalable() ? RawOffset * VScale : RawOffset;
+      if (Offset > Vec.size() || SubVec.size() > Vec.size() - Offset)
         return AnyValue::poison();
       std::vector<AnyValue> Res;
       Res.reserve(Vec.size());
@@ -762,10 +777,21 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
         return AnyValue::poison();
       const auto &Vec = Args[0].asAggregate();
       const auto &Idx = Args[1].asInteger();
-      const uint64_t Offset = Idx.getZExtValue();
-      const uint64_t DstSize =
-          Ctx.getEVL(cast<VectorType>(RetTy)->getElementCount());
-      if (Offset + DstSize > Vec.size())
+      auto EC = cast<VectorType>(RetTy)->getElementCount();
+      const uint64_t RawOffset = Idx.getZExtValue();
+      if (RawOffset % EC.getKnownMinValue() != 0) {
+        reportImmediateUB("llvm.vector.extract index is not a multiple of the "
+                          "result's known minimum vector length.");
+        return AnyValue::poison();
+      }
+      const uint32_t VScale = Ctx.getVScale();
+      if (EC.isScalable() && VScale != 0 &&
+          RawOffset > std::numeric_limits<uint64_t>::max() / VScale)
+        return AnyValue::poison();
+      const uint64_t Offset =
+          EC.isScalable() ? RawOffset * VScale : RawOffset;
+      const uint64_t DstSize = Ctx.getEVL(EC);
+      if (Offset > Vec.size() || DstSize > Vec.size() - Offset)
         return AnyValue::poison();
       return std::vector(Vec.begin() + Offset, Vec.begin() + Offset + DstSize);
     }

>From 515628b9f51c0bad208f19e6f95b5572c2328a84 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhigec_cpp at outlook.com>
Date: Thu, 30 Apr 2026 13:23:15 +0800
Subject: [PATCH 2/2] [llubi] Format code

---
 llvm/tools/llubi/lib/Interpreter.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index e87ebfabc5db9..453fc3a3dc3bd 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -746,8 +746,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       const auto &Vec = Args[0].asAggregate();
       const auto &SubVec = Args[1].asAggregate();
       const auto &Idx = Args[2].asInteger();
-      auto EC = cast<VectorType>(CB.getArgOperand(1)->getType())
-                    ->getElementCount();
+      auto EC =
+          cast<VectorType>(CB.getArgOperand(1)->getType())->getElementCount();
       const uint64_t RawOffset = Idx.getZExtValue();
       if (RawOffset % EC.getKnownMinValue() != 0) {
         reportImmediateUB("llvm.vector.insert index is not a multiple of the "
@@ -758,8 +758,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       if (EC.isScalable() && VScale != 0 &&
           RawOffset > std::numeric_limits<uint64_t>::max() / VScale)
         return AnyValue::poison();
-      const uint64_t Offset =
-          EC.isScalable() ? RawOffset * VScale : RawOffset;
+      const uint64_t Offset = EC.isScalable() ? RawOffset * VScale : RawOffset;
       if (Offset > Vec.size() || SubVec.size() > Vec.size() - Offset)
         return AnyValue::poison();
       std::vector<AnyValue> Res;
@@ -788,8 +787,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       if (EC.isScalable() && VScale != 0 &&
           RawOffset > std::numeric_limits<uint64_t>::max() / VScale)
         return AnyValue::poison();
-      const uint64_t Offset =
-          EC.isScalable() ? RawOffset * VScale : RawOffset;
+      const uint64_t Offset = EC.isScalable() ? RawOffset * VScale : RawOffset;
       const uint64_t DstSize = Ctx.getEVL(EC);
       if (Offset > Vec.size() || DstSize > Vec.size() - Offset)
         return AnyValue::poison();



More information about the llvm-commits mailing list