[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