[llvm] [llubi] Implement vector reduction/manipulation intrinsics (PR #194345)
Zhige Chen via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 27 05:50:53 PDT 2026
https://github.com/nofe1248 updated https://github.com/llvm/llvm-project/pull/194345
>From 38b47d0c95326e4c7ba769e21344ce03ce53213a Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Mon, 27 Apr 2026 19:15:53 +0800
Subject: [PATCH 1/2] [llubi] Implement vector reduction/manipulation
intrinsics
---
.../tools/llubi/intr_vector_extract_poison.ll | 11 +
.../tools/llubi/intr_vector_insert_poison.ll | 11 +
.../tools/llubi/intr_vector_interleave.ll | 61 +++++
llvm/test/tools/llubi/intr_vector_manip.ll | 47 ++++
llvm/test/tools/llubi/intr_vector_reduce.ll | 55 +++++
.../llubi/intr_vector_splice_left_poison.ll | 11 +
.../llubi/intr_vector_splice_right_poison.ll | 11 +
.../tools/llubi/intr_vector_stepvector.ll | 17 ++
llvm/tools/llubi/lib/Interpreter.cpp | 227 ++++++++++++++++++
9 files changed, 451 insertions(+)
create mode 100644 llvm/test/tools/llubi/intr_vector_extract_poison.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_insert_poison.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_interleave.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_manip.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_reduce.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_splice_left_poison.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_splice_right_poison.ll
create mode 100644 llvm/test/tools/llubi/intr_vector_stepvector.ll
diff --git a/llvm/test/tools/llubi/intr_vector_extract_poison.ll b/llvm/test/tools/llubi/intr_vector_extract_poison.ll
new file mode 100644
index 0000000000000..a1031036ee020
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_extract_poison.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() {
+ %res = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison)
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: vector_extract with poison index
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_insert_poison.ll b/llvm/test/tools/llubi/intr_vector_insert_poison.ll
new file mode 100644
index 0000000000000..9e1f0856b866c
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_insert_poison.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() {
+ %res = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison)
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: vector_insert with poison index
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_interleave.ll b/llvm/test/tools/llubi/intr_vector_interleave.ll
new file mode 100644
index 0000000000000..d6eea81c330e4
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_interleave.ll
@@ -0,0 +1,61 @@
+; 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() {
+ %i2 = call <4 x i32> @llvm.vector.interleave2.v4i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>)
+ %i3 = call <6 x i32> @llvm.vector.interleave3.v6i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>)
+ %i4 = call <8 x i32> @llvm.vector.interleave4.v8i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>)
+ %i5 = call <10 x i32> @llvm.vector.interleave5.v10i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>)
+ %i6 = call <12 x i32> @llvm.vector.interleave6.v12i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>)
+ %i7 = call <14 x i32> @llvm.vector.interleave7.v14i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>, <2 x i32> <i32 60, i32 61>)
+ %i8 = call <16 x i32> @llvm.vector.interleave8.v16i32.v2i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>, <2 x i32> <i32 60, i32 61>, <2 x i32> <i32 70, i32 71>)
+
+ %d2 = call { <2 x i32>, <2 x i32> } @llvm.vector.deinterleave2.v4i32(<4 x i32> <i32 0, i32 10, i32 1, i32 11>)
+ %d3 = call { <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave3.v6i32(<6 x i32> <i32 0, i32 10, i32 20, i32 1, i32 11, i32 21>)
+ %d4 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave4.v8i32(<8 x i32> <i32 0, i32 10, i32 20, i32 30, i32 1, i32 11, i32 21, i32 31>)
+ %d5 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave5.v10i32(<10 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 1, i32 11, i32 21, i32 31, i32 41>)
+ %d6 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave6.v12i32(<12 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51>)
+ %d7 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave7.v14i32(<14 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61>)
+ %d8 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave8.v16i32(<16 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 70, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61, i32 71>)
+
+ %base = call <vscale x 1 x i32> @llvm.stepvector.nxv1i32()
+ %insert10 = insertelement <vscale x 1 x i32> poison, i32 10, i32 0
+ %splat10 = shufflevector <vscale x 1 x i32> %insert10, <vscale x 1 x i32> poison, <vscale x 1 x i32> zeroinitializer
+ %insert20 = insertelement <vscale x 1 x i32> poison, i32 20, i32 0
+ %splat20 = shufflevector <vscale x 1 x i32> %insert20, <vscale x 1 x i32> poison, <vscale x 1 x i32> zeroinitializer
+ %sv1 = add <vscale x 1 x i32> %base, %splat10
+ %sv2 = add <vscale x 1 x i32> %base, %splat20
+
+ %s3 = call <vscale x 3 x i32> @llvm.vector.interleave3.nxv3i32.nxv1i32(<vscale x 1 x i32> %base, <vscale x 1 x i32> %sv1, <vscale x 1 x i32> %sv2)
+
+ %sd3 = call { <vscale x 1 x i32>, <vscale x 1 x i32>, <vscale x 1 x i32> } @llvm.vector.deinterleave3.nxv3i32(<vscale x 3 x i32> %s3)
+
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: %i2 = call <4 x i32> @llvm.vector.interleave2.v4i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>) => { i32 0, i32 10, i32 1, i32 11 }
+; CHECK-NEXT: %i3 = call <6 x i32> @llvm.vector.interleave3.v6i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>) => { i32 0, i32 10, i32 20, i32 1, i32 11, i32 21 }
+; CHECK-NEXT: %i4 = call <8 x i32> @llvm.vector.interleave4.v8i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>) => { i32 0, i32 10, i32 20, i32 30, i32 1, i32 11, i32 21, i32 31 }
+; CHECK-NEXT: %i5 = call <10 x i32> @llvm.vector.interleave5.v10i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>) => { i32 0, i32 10, i32 20, i32 30, i32 40, i32 1, i32 11, i32 21, i32 31, i32 41 }
+; CHECK-NEXT: %i6 = call <12 x i32> @llvm.vector.interleave6.v12i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>) => { i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51 }
+; CHECK-NEXT: %i7 = call <14 x i32> @llvm.vector.interleave7.v14i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>, <2 x i32> <i32 60, i32 61>) => { i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61 }
+; CHECK-NEXT: %i8 = call <16 x i32> @llvm.vector.interleave8.v16i32(<2 x i32> <i32 0, i32 1>, <2 x i32> <i32 10, i32 11>, <2 x i32> <i32 20, i32 21>, <2 x i32> <i32 30, i32 31>, <2 x i32> <i32 40, i32 41>, <2 x i32> <i32 50, i32 51>, <2 x i32> <i32 60, i32 61>, <2 x i32> <i32 70, i32 71>) => { i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 70, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61, i32 71 }
+; CHECK-NEXT: %d2 = call { <2 x i32>, <2 x i32> } @llvm.vector.deinterleave2.v4i32(<4 x i32> <i32 0, i32 10, i32 1, i32 11>) => { { i32 0, i32 1 }, { i32 10, i32 11 } }
+; CHECK-NEXT: %d3 = call { <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave3.v6i32(<6 x i32> <i32 0, i32 10, i32 20, i32 1, i32 11, i32 21>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 } }
+; CHECK-NEXT: %d4 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave4.v8i32(<8 x i32> <i32 0, i32 10, i32 20, i32 30, i32 1, i32 11, i32 21, i32 31>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 }, { i32 30, i32 31 } }
+; CHECK-NEXT: %d5 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave5.v10i32(<10 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 1, i32 11, i32 21, i32 31, i32 41>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 }, { i32 30, i32 31 }, { i32 40, i32 41 } }
+; CHECK-NEXT: %d6 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave6.v12i32(<12 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 }, { i32 30, i32 31 }, { i32 40, i32 41 }, { i32 50, i32 51 } }
+; CHECK-NEXT: %d7 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave7.v14i32(<14 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 }, { i32 30, i32 31 }, { i32 40, i32 41 }, { i32 50, i32 51 }, { i32 60, i32 61 } }
+; CHECK-NEXT: %d8 = call { <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32>, <2 x i32> } @llvm.vector.deinterleave8.v16i32(<16 x i32> <i32 0, i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 70, i32 1, i32 11, i32 21, i32 31, i32 41, i32 51, i32 61, i32 71>) => { { i32 0, i32 1 }, { i32 10, i32 11 }, { i32 20, i32 21 }, { i32 30, i32 31 }, { i32 40, i32 41 }, { i32 50, i32 51 }, { i32 60, i32 61 }, { i32 70, i32 71 } }
+; CHECK-NEXT: %base = call <vscale x 1 x i32> @llvm.stepvector.nxv1i32() => { i32 0, i32 1, i32 2, i32 3 }
+; CHECK-NEXT: %insert10 = insertelement <vscale x 1 x i32> poison, i32 10, i32 0 => { i32 10, poison, poison, poison }
+; CHECK-NEXT: %splat10 = shufflevector <vscale x 1 x i32> %insert10, <vscale x 1 x i32> poison, <vscale x 1 x i32> zeroinitializer => { i32 10, i32 10, i32 10, i32 10 }
+; CHECK-NEXT: %insert20 = insertelement <vscale x 1 x i32> poison, i32 20, i32 0 => { i32 20, poison, poison, poison }
+; CHECK-NEXT: %splat20 = shufflevector <vscale x 1 x i32> %insert20, <vscale x 1 x i32> poison, <vscale x 1 x i32> zeroinitializer => { i32 20, i32 20, i32 20, i32 20 }
+; CHECK-NEXT: %sv1 = add <vscale x 1 x i32> %base, %splat10 => { i32 10, i32 11, i32 12, i32 13 }
+; CHECK-NEXT: %sv2 = add <vscale x 1 x i32> %base, %splat20 => { i32 20, i32 21, i32 22, i32 23 }
+; CHECK-NEXT: %s3 = call <vscale x 3 x i32> @llvm.vector.interleave3.nxv3i32(<vscale x 1 x i32> %base, <vscale x 1 x i32> %sv1, <vscale x 1 x i32> %sv2) => { i32 0, i32 10, i32 20, i32 1, i32 11, i32 21, i32 2, i32 12, i32 22, i32 3, i32 13, i32 23 }
+; CHECK-NEXT: %sd3 = call { <vscale x 1 x i32>, <vscale x 1 x i32>, <vscale x 1 x i32> } @llvm.vector.deinterleave3.nxv3i32(<vscale x 3 x i32> %s3) => { { i32 0, i32 1, i32 2, i32 3 }, { i32 10, i32 11, i32 12, i32 13 }, { i32 20, i32 21, i32 22, i32 23 } }
+; 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
new file mode 100644
index 0000000000000..e7949e063aef1
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_manip.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+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)
+
+ %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)
+
+ %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>)
+
+ %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)
+ %splice_left_poison_lane = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1)
+ %splice_left_poison = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 4)
+
+ %splice_right = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1)
+ %splice_right_full_rhs = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 0)
+ %splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5)
+
+ ret void
+}
+
+; CHECK: Entering function: 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: %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: %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 }
+; CHECK-NEXT: %splice_left_poison_lane = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1) => { poison, i32 2, i32 3, i32 10 }
+; CHECK-NEXT: %splice_left_poison = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 4) => poison
+; CHECK-NEXT: %splice_right = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1) => { i32 3, i32 10, i32 11, i32 12 }
+; CHECK-NEXT: %splice_right_full_rhs = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 0) => { i32 10, i32 11, i32 12, i32 13 }
+; CHECK-NEXT: %splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => poison
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vector_reduce.ll b/llvm/test/tools/llubi/intr_vector_reduce.ll
new file mode 100644
index 0000000000000..9508419a96829
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_reduce.ll
@@ -0,0 +1,55 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %reduce_add = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>)
+ %reduce_add_poison = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 poison, i32 3, i32 4>)
+
+ %reduce_mul = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 2, i32 3, i32 4, i32 5>)
+ %reduce_mul_poison = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 2, i32 poison, i32 4, i32 5>)
+
+ %reduce_and = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> <i32 15, i32 14, i32 13, i32 12>)
+ %reduce_and_poison = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> <i32 15, i32 poison, i32 13, i32 12>)
+
+ %reduce_or = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> <i32 1, i32 2, i32 4, i32 8>)
+ %reduce_or_poison = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> <i32 1, i32 poison, i32 4, i32 8>)
+
+ %reduce_xor = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> <i32 1, i32 3, i32 7, i32 15>)
+ %reduce_xor_poison = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> <i32 1, i32 poison, i32 7, i32 15>)
+
+ %reduce_smax = call i32 @llvm.vector.reduce.smax.v4i32(<4 x i32> <i32 -4, i32 -2, i32 7, i32 3>)
+ %reduce_smax_poison = call i32 @llvm.vector.reduce.smax.v4i32(<4 x i32> <i32 -4, i32 poison, i32 7, i32 3>)
+
+ %reduce_smin = call i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> <i32 -4, i32 -2, i32 7, i32 3>)
+ %reduce_smin_poison = call i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> <i32 -4, i32 poison, i32 7, i32 3>)
+
+ %reduce_umax = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> <i32 -1, i32 5, i32 9, i32 2>)
+ %reduce_umax_poison = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> <i32 -1, i32 poison, i32 9, i32 2>)
+
+ %reduce_umin = call i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> <i32 -1, i32 5, i32 9, i32 2>)
+ %reduce_umin_poison = call i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> <i32 -1, i32 poison, i32 9, i32 2>)
+
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: %reduce_add = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>) => i32 10
+; CHECK-NEXT: %reduce_add_poison = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 poison, i32 3, i32 4>) => poison
+; CHECK-NEXT: %reduce_mul = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 2, i32 3, i32 4, i32 5>) => i32 120
+; CHECK-NEXT: %reduce_mul_poison = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 2, i32 poison, i32 4, i32 5>) => poison
+; CHECK-NEXT: %reduce_and = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> <i32 15, i32 14, i32 13, i32 12>) => i32 12
+; CHECK-NEXT: %reduce_and_poison = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> <i32 15, i32 poison, i32 13, i32 12>) => poison
+; CHECK-NEXT: %reduce_or = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> <i32 1, i32 2, i32 4, i32 8>) => i32 15
+; CHECK-NEXT: %reduce_or_poison = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> <i32 1, i32 poison, i32 4, i32 8>) => poison
+; CHECK-NEXT: %reduce_xor = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> <i32 1, i32 3, i32 7, i32 15>) => i32 10
+; CHECK-NEXT: %reduce_xor_poison = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> <i32 1, i32 poison, i32 7, i32 15>) => poison
+; CHECK-NEXT: %reduce_smax = call i32 @llvm.vector.reduce.smax.v4i32(<4 x i32> <i32 -4, i32 -2, i32 7, i32 3>) => i32 7
+; CHECK-NEXT: %reduce_smax_poison = call i32 @llvm.vector.reduce.smax.v4i32(<4 x i32> <i32 -4, i32 poison, i32 7, i32 3>) => poison
+; CHECK-NEXT: %reduce_smin = call i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> <i32 -4, i32 -2, i32 7, i32 3>) => i32 -4
+; CHECK-NEXT: %reduce_smin_poison = call i32 @llvm.vector.reduce.smin.v4i32(<4 x i32> <i32 -4, i32 poison, i32 7, i32 3>) => poison
+; CHECK-NEXT: %reduce_umax = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> <i32 -1, i32 5, i32 9, i32 2>) => i32 -1
+; CHECK-NEXT: %reduce_umax_poison = call i32 @llvm.vector.reduce.umax.v4i32(<4 x i32> <i32 -1, i32 poison, i32 9, i32 2>) => poison
+; CHECK-NEXT: %reduce_umin = call i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> <i32 -1, i32 5, i32 9, i32 2>) => i32 2
+; CHECK-NEXT: %reduce_umin_poison = call i32 @llvm.vector.reduce.umin.v4i32(<4 x i32> <i32 -1, i32 poison, i32 9, i32 2>) => poison
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vector_splice_left_poison.ll b/llvm/test/tools/llubi/intr_vector_splice_left_poison.ll
new file mode 100644
index 0000000000000..c4be47ccbd422
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_splice_left_poison.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() {
+ %res = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: vector_splice_left with poison offset
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_splice_right_poison.ll b/llvm/test/tools/llubi/intr_vector_splice_right_poison.ll
new file mode 100644
index 0000000000000..bd48a813898aa
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_splice_right_poison.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() {
+ %res = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
+ ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Immediate UB detected: vector_splice_right with poison offset
+; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_stepvector.ll b/llvm/test/tools/llubi/intr_vector_stepvector.ll
new file mode 100644
index 0000000000000..3846ef897b8aa
--- /dev/null
+++ b/llvm/test/tools/llubi/intr_vector_stepvector.ll
@@ -0,0 +1,17 @@
+; 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() {
+ %1 = call <vscale x 4 x i32> @llvm.stepvector.nxv4i32()
+ %2 = call <vscale x 8 x i16> @llvm.stepvector.nxv8i16()
+ %3 = call <4 x i32> @llvm.stepvector.v4i32()
+ %4 = call <8 x i16> @llvm.stepvector.v8i16()
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %1 = call <vscale x 4 x i32> @llvm.stepvector.nxv4i32() => { i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15 }
+; CHECK-NEXT: %2 = call <vscale x 8 x i16> @llvm.stepvector.nxv8i16() => { i16 0, i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7, i16 8, i16 9, i16 10, i16 11, i16 12, i16 13, i16 14, i16 15, i16 16, i16 17, i16 18, i16 19, i16 20, i16 21, i16 22, i16 23, i16 24, i16 25, i16 26, i16 27, i16 28, i16 29, i16 30, i16 31 }
+; CHECK-NEXT: %3 = call <4 x i32> @llvm.stepvector.v4i32() => { i32 0, i32 1, i32 2, i32 3 }
+; CHECK-NEXT: %4 = call <8 x i16> @llvm.stepvector.v8i16() => { i16 0, i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7 }
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 704416ce6d737..6f108a8971026 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -379,6 +379,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Boolean == BooleanKind::True;
}
+ // Helper function to get the length of a (possible scalable) vector.
+ uint32_t getVectorLength(VectorType *Ty) {
+ const auto ElemCount = Ty->getElementCount();
+ if (ElemCount.isFixed())
+ return ElemCount.getFixedValue();
+ return ElemCount.getKnownMinValue() * Ctx.getVScale();
+ }
+
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -683,6 +691,225 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
});
}
+ case Intrinsic::vector_reduce_add:
+ case Intrinsic::vector_reduce_mul:
+ case Intrinsic::vector_reduce_and:
+ case Intrinsic::vector_reduce_or:
+ case Intrinsic::vector_reduce_xor:
+ case Intrinsic::vector_reduce_smax:
+ case Intrinsic::vector_reduce_smin:
+ case Intrinsic::vector_reduce_umax:
+ case Intrinsic::vector_reduce_umin: {
+ std::optional<APInt> Res;
+ for (const auto &V : Args[0].asAggregate()) {
+ if (V.isPoison()) {
+ Res.reset();
+ break;
+ }
+ const auto &IntV = V.asInteger();
+ if (!Res) {
+ Res = IntV;
+ continue;
+ }
+ switch (IID) {
+ case Intrinsic::vector_reduce_add:
+ *Res += IntV;
+ break;
+ case Intrinsic::vector_reduce_mul:
+ *Res *= IntV;
+ break;
+ case Intrinsic::vector_reduce_and:
+ *Res &= IntV;
+ break;
+ case Intrinsic::vector_reduce_or:
+ *Res |= IntV;
+ break;
+ case Intrinsic::vector_reduce_xor:
+ *Res ^= IntV;
+ break;
+ case Intrinsic::vector_reduce_smax:
+ *Res = APIntOps::smax(*Res, IntV);
+ break;
+ case Intrinsic::vector_reduce_smin:
+ *Res = APIntOps::smin(*Res, IntV);
+ break;
+ case Intrinsic::vector_reduce_umax:
+ *Res = APIntOps::umax(*Res, IntV);
+ break;
+ case Intrinsic::vector_reduce_umin:
+ *Res = APIntOps::umin(*Res, IntV);
+ break;
+ default:
+ llvm_unreachable("Unexpected intrinsic ID");
+ }
+ }
+ return Res ? *Res : AnyValue::poison();
+ }
+ case Intrinsic::vector_insert: {
+ if (Args[2].isPoison()) {
+ reportImmediateUB("vector_insert with poison index");
+ return AnyValue::poison();
+ }
+ 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())
+ return AnyValue::poison();
+ std::vector<AnyValue> Res;
+ Res.reserve(Vec.size());
+ for (size_t I = 0; I != Vec.size(); ++I) {
+ if (I >= Offset && I < Offset + SubVec.size())
+ Res.push_back(SubVec[I - Offset]);
+ else
+ Res.push_back(Vec[I]);
+ }
+ return std::move(Res);
+ }
+ case Intrinsic::vector_extract: {
+ if (Args[1].isPoison()) {
+ reportImmediateUB("vector_extract with poison index");
+ 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 = cast<FixedVectorType>(RetTy)->getNumElements();
+ if (Offset + DstSize > Vec.size())
+ return AnyValue::poison();
+ return std::vector(Vec.begin() + Offset, Vec.begin() + Offset + DstSize);
+ }
+ case Intrinsic::vector_reverse: {
+ auto Vec = Args[0].asAggregate();
+ std::reverse(Vec.begin(), Vec.end());
+ return std::move(Vec);
+ }
+ case Intrinsic::vector_deinterleave2:
+ case Intrinsic::vector_deinterleave3:
+ case Intrinsic::vector_deinterleave4:
+ case Intrinsic::vector_deinterleave5:
+ case Intrinsic::vector_deinterleave6:
+ case Intrinsic::vector_deinterleave7:
+ case Intrinsic::vector_deinterleave8: {
+ const unsigned Factor = [IID]() -> unsigned {
+ switch (IID) {
+ case Intrinsic::vector_deinterleave2:
+ return 2;
+ case Intrinsic::vector_deinterleave3:
+ return 3;
+ case Intrinsic::vector_deinterleave4:
+ return 4;
+ case Intrinsic::vector_deinterleave5:
+ return 5;
+ case Intrinsic::vector_deinterleave6:
+ return 6;
+ case Intrinsic::vector_deinterleave7:
+ return 7;
+ case Intrinsic::vector_deinterleave8:
+ return 8;
+ default:
+ llvm_unreachable("Unexpected intrinsic ID");
+ }
+ }();
+ const auto &Vec = Args[0].asAggregate();
+ std::vector<std::vector<AnyValue>> Res(Factor);
+ for (auto &SubVec : Res)
+ SubVec.reserve(Vec.size() / Factor);
+ for (const auto &[I, V] : enumerate(Vec))
+ Res[I % Factor].push_back(V);
+
+ std::vector<AnyValue> AggRes;
+ AggRes.reserve(Factor);
+ for (auto &SubVec : Res)
+ AggRes.emplace_back(std::move(SubVec));
+ return AnyValue(std::move(AggRes));
+ }
+ case Intrinsic::vector_interleave2:
+ case Intrinsic::vector_interleave3:
+ case Intrinsic::vector_interleave4:
+ case Intrinsic::vector_interleave5:
+ case Intrinsic::vector_interleave6:
+ case Intrinsic::vector_interleave7:
+ case Intrinsic::vector_interleave8: {
+ const unsigned Factor = [IID]() -> unsigned {
+ switch (IID) {
+ case Intrinsic::vector_interleave2:
+ return 2;
+ case Intrinsic::vector_interleave3:
+ return 3;
+ case Intrinsic::vector_interleave4:
+ return 4;
+ case Intrinsic::vector_interleave5:
+ return 5;
+ case Intrinsic::vector_interleave6:
+ return 6;
+ case Intrinsic::vector_interleave7:
+ return 7;
+ case Intrinsic::vector_interleave8:
+ return 8;
+ default:
+ llvm_unreachable("Unexpected intrinsic ID");
+ }
+ }();
+ const auto &Vec = Args[0].asAggregate();
+ std::vector<AnyValue> Res;
+ Res.reserve(Vec.size() * Factor);
+ for (size_t I = 0, E = Vec.size(); I != E; ++I) {
+ for (unsigned J = 0; J != Factor; ++J)
+ Res.push_back(Args[J].asAggregate()[I]);
+ }
+ return std::move(Res);
+ }
+ case Intrinsic::vector_splice_left: {
+ if (Args[2].isPoison()) {
+ reportImmediateUB("vector_splice_left with poison offset");
+ return AnyValue::poison();
+ }
+ const auto &LHS = Args[0].asAggregate();
+ const auto &RHS = Args[1].asAggregate();
+ const auto &Off = Args[2].asInteger();
+ const size_t Len = LHS.size();
+ if (Off.uge(Len))
+ return AnyValue::poison();
+ uint64_t Offset = Off.getZExtValue();
+ std::vector<AnyValue> Res;
+ Res.reserve(Len);
+ for (size_t I = 0; I != Len; ++I) {
+ size_t Pos = I + Offset;
+ Res.push_back(Pos < Len ? LHS[Pos] : RHS[Pos - Len]);
+ }
+ return std::move(Res);
+ }
+ case Intrinsic::vector_splice_right: {
+ if (Args[2].isPoison()) {
+ reportImmediateUB("vector_splice_right with poison offset");
+ return AnyValue::poison();
+ }
+ const auto &LHS = Args[0].asAggregate();
+ const auto &RHS = Args[1].asAggregate();
+ const auto &Off = Args[2].asInteger();
+ const size_t Len = LHS.size();
+ if (Off.ugt(Len))
+ return AnyValue::poison();
+ uint64_t Offset = Len - Off.getZExtValue();
+ std::vector<AnyValue> Res;
+ Res.reserve(Len);
+ for (size_t I = 0; I != Len; ++I) {
+ size_t Pos = I + Offset;
+ Res.push_back(Pos < Len ? LHS[Pos] : RHS[Pos - Len]);
+ }
+ return std::move(Res);
+ }
+ case Intrinsic::stepvector: {
+ std::vector<AnyValue> Res;
+ const uint32_t Len = getVectorLength(cast<VectorType>(RetTy));
+ const unsigned BitWidth = RetTy->getScalarSizeInBits();
+ Res.reserve(Len);
+ for (uint64_t I = 0; I != Len; ++I) {
+ Res.push_back(APInt(BitWidth, I, false, true));
+ }
+ return std::move(Res);
+ }
default:
Handler.onUnrecognizedInstruction(CB);
setFailed();
>From 2c9f4a742e8ea48cb491fc1c1e029aed36680f14 Mon Sep 17 00:00:00 2001
From: Zhige Chen <zhige_chen at outlook.com>
Date: Mon, 27 Apr 2026 20:50:39 +0800
Subject: [PATCH 2/2] [llubi] Return poison instead of report UB when poison
index/offset is provided in vector intrinsic calls
---
.../tools/llubi/intr_vector_extract_poison.ll | 11 --
.../tools/llubi/intr_vector_insert_poison.ll | 11 --
llvm/test/tools/llubi/intr_vector_manip.ll | 9 ++
.../llubi/intr_vector_splice_left_poison.ll | 11 --
.../llubi/intr_vector_splice_right_poison.ll | 11 --
llvm/tools/llubi/lib/Interpreter.cpp | 107 +++++++++---------
6 files changed, 61 insertions(+), 99 deletions(-)
delete mode 100644 llvm/test/tools/llubi/intr_vector_extract_poison.ll
delete mode 100644 llvm/test/tools/llubi/intr_vector_insert_poison.ll
delete mode 100644 llvm/test/tools/llubi/intr_vector_splice_left_poison.ll
delete mode 100644 llvm/test/tools/llubi/intr_vector_splice_right_poison.ll
diff --git a/llvm/test/tools/llubi/intr_vector_extract_poison.ll b/llvm/test/tools/llubi/intr_vector_extract_poison.ll
deleted file mode 100644
index a1031036ee020..0000000000000
--- a/llvm/test/tools/llubi/intr_vector_extract_poison.ll
+++ /dev/null
@@ -1,11 +0,0 @@
-; 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() {
- %res = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison)
- ret void
-}
-
-; CHECK: Entering function: main
-; CHECK-NEXT: Immediate UB detected: vector_extract with poison index
-; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_insert_poison.ll b/llvm/test/tools/llubi/intr_vector_insert_poison.ll
deleted file mode 100644
index 9e1f0856b866c..0000000000000
--- a/llvm/test/tools/llubi/intr_vector_insert_poison.ll
+++ /dev/null
@@ -1,11 +0,0 @@
-; 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() {
- %res = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison)
- ret void
-}
-
-; CHECK: Entering function: main
-; CHECK-NEXT: Immediate UB detected: vector_insert with poison index
-; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_manip.ll b/llvm/test/tools/llubi/intr_vector_manip.ll
index e7949e063aef1..8795fff7c80d3 100644
--- a/llvm/test/tools/llubi/intr_vector_manip.ll
+++ b/llvm/test/tools/llubi/intr_vector_manip.ll
@@ -23,6 +23,11 @@ define void @main() {
%splice_right_full_rhs = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 0)
%splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5)
+ %extract_poison_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison)
+ %insert_poison_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison)
+ %splice_left_poison_idx = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
+ %splice_right_poison_idx = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
+
ret void
}
@@ -43,5 +48,9 @@ define void @main() {
; CHECK-NEXT: %splice_right = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1) => { i32 3, i32 10, i32 11, i32 12 }
; CHECK-NEXT: %splice_right_full_rhs = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 0) => { i32 10, i32 11, i32 12, i32 13 }
; CHECK-NEXT: %splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => poison
+; CHECK-NEXT: %extract_poison_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison) => poison
+; CHECK-NEXT: %insert_poison_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison) => poison
+; CHECK-NEXT: %splice_left_poison_idx = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => poison
+; CHECK-NEXT: %splice_right_poison_idx = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => poison
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/intr_vector_splice_left_poison.ll b/llvm/test/tools/llubi/intr_vector_splice_left_poison.ll
deleted file mode 100644
index c4be47ccbd422..0000000000000
--- a/llvm/test/tools/llubi/intr_vector_splice_left_poison.ll
+++ /dev/null
@@ -1,11 +0,0 @@
-; 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() {
- %res = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
- ret void
-}
-
-; CHECK: Entering function: main
-; CHECK-NEXT: Immediate UB detected: vector_splice_left with poison offset
-; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/intr_vector_splice_right_poison.ll b/llvm/test/tools/llubi/intr_vector_splice_right_poison.ll
deleted file mode 100644
index bd48a813898aa..0000000000000
--- a/llvm/test/tools/llubi/intr_vector_splice_right_poison.ll
+++ /dev/null
@@ -1,11 +0,0 @@
-; 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() {
- %res = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison)
- ret void
-}
-
-; CHECK: Entering function: main
-; CHECK-NEXT: Immediate UB detected: vector_splice_right with poison offset
-; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 6f108a8971026..6a280728f0033 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -380,13 +380,55 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
// Helper function to get the length of a (possible scalable) vector.
- uint32_t getVectorLength(VectorType *Ty) {
+ uint32_t getVectorNumElements(VectorType *Ty) {
const auto ElemCount = Ty->getElementCount();
if (ElemCount.isFixed())
return ElemCount.getFixedValue();
return ElemCount.getKnownMinValue() * Ctx.getVScale();
}
+ unsigned getDeinterleaveIntrinsicFactor(Intrinsic::ID IID) {
+ switch (IID) {
+ case Intrinsic::vector_deinterleave2:
+ return 2;
+ case Intrinsic::vector_deinterleave3:
+ return 3;
+ case Intrinsic::vector_deinterleave4:
+ return 4;
+ case Intrinsic::vector_deinterleave5:
+ return 5;
+ case Intrinsic::vector_deinterleave6:
+ return 6;
+ case Intrinsic::vector_deinterleave7:
+ return 7;
+ case Intrinsic::vector_deinterleave8:
+ return 8;
+ default:
+ llvm_unreachable("Unexpected intrinsic ID");
+ }
+ }
+
+ unsigned getInterleaveIntrinsicFactor(Intrinsic::ID IID) {
+ switch (IID) {
+ case Intrinsic::vector_interleave2:
+ return 2;
+ case Intrinsic::vector_interleave3:
+ return 3;
+ case Intrinsic::vector_interleave4:
+ return 4;
+ case Intrinsic::vector_interleave5:
+ return 5;
+ case Intrinsic::vector_interleave6:
+ return 6;
+ case Intrinsic::vector_interleave7:
+ return 7;
+ case Intrinsic::vector_interleave8:
+ return 8;
+ default:
+ llvm_unreachable("Unexpected intrinsic ID");
+ }
+ }
+
public:
InstExecutor(Context &C, EventHandler &H, Function &F,
ArrayRef<AnyValue> Args, AnyValue &RetVal)
@@ -746,10 +788,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return Res ? *Res : AnyValue::poison();
}
case Intrinsic::vector_insert: {
- if (Args[2].isPoison()) {
- reportImmediateUB("vector_insert with poison index");
+ if (Args[2].isPoison())
return AnyValue::poison();
- }
const auto &Vec = Args[0].asAggregate();
const auto &SubVec = Args[1].asAggregate();
const auto &Idx = Args[2].asInteger();
@@ -767,10 +807,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return std::move(Res);
}
case Intrinsic::vector_extract: {
- if (Args[1].isPoison()) {
- reportImmediateUB("vector_extract with poison index");
+ if (Args[1].isPoison())
return AnyValue::poison();
- }
const auto &Vec = Args[0].asAggregate();
const auto &Idx = Args[1].asInteger();
const uint64_t Offset = Idx.getZExtValue();
@@ -791,26 +829,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
case Intrinsic::vector_deinterleave6:
case Intrinsic::vector_deinterleave7:
case Intrinsic::vector_deinterleave8: {
- const unsigned Factor = [IID]() -> unsigned {
- switch (IID) {
- case Intrinsic::vector_deinterleave2:
- return 2;
- case Intrinsic::vector_deinterleave3:
- return 3;
- case Intrinsic::vector_deinterleave4:
- return 4;
- case Intrinsic::vector_deinterleave5:
- return 5;
- case Intrinsic::vector_deinterleave6:
- return 6;
- case Intrinsic::vector_deinterleave7:
- return 7;
- case Intrinsic::vector_deinterleave8:
- return 8;
- default:
- llvm_unreachable("Unexpected intrinsic ID");
- }
- }();
+ const unsigned Factor = getDeinterleaveIntrinsicFactor(IID);
const auto &Vec = Args[0].asAggregate();
std::vector<std::vector<AnyValue>> Res(Factor);
for (auto &SubVec : Res)
@@ -831,26 +850,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
case Intrinsic::vector_interleave6:
case Intrinsic::vector_interleave7:
case Intrinsic::vector_interleave8: {
- const unsigned Factor = [IID]() -> unsigned {
- switch (IID) {
- case Intrinsic::vector_interleave2:
- return 2;
- case Intrinsic::vector_interleave3:
- return 3;
- case Intrinsic::vector_interleave4:
- return 4;
- case Intrinsic::vector_interleave5:
- return 5;
- case Intrinsic::vector_interleave6:
- return 6;
- case Intrinsic::vector_interleave7:
- return 7;
- case Intrinsic::vector_interleave8:
- return 8;
- default:
- llvm_unreachable("Unexpected intrinsic ID");
- }
- }();
+ const unsigned Factor = getInterleaveIntrinsicFactor(IID);
const auto &Vec = Args[0].asAggregate();
std::vector<AnyValue> Res;
Res.reserve(Vec.size() * Factor);
@@ -861,10 +861,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return std::move(Res);
}
case Intrinsic::vector_splice_left: {
- if (Args[2].isPoison()) {
- reportImmediateUB("vector_splice_left with poison offset");
+ if (Args[2].isPoison())
return AnyValue::poison();
- }
const auto &LHS = Args[0].asAggregate();
const auto &RHS = Args[1].asAggregate();
const auto &Off = Args[2].asInteger();
@@ -881,10 +879,8 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return std::move(Res);
}
case Intrinsic::vector_splice_right: {
- if (Args[2].isPoison()) {
- reportImmediateUB("vector_splice_right with poison offset");
+ if (Args[2].isPoison())
return AnyValue::poison();
- }
const auto &LHS = Args[0].asAggregate();
const auto &RHS = Args[1].asAggregate();
const auto &Off = Args[2].asInteger();
@@ -902,11 +898,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
case Intrinsic::stepvector: {
std::vector<AnyValue> Res;
- const uint32_t Len = getVectorLength(cast<VectorType>(RetTy));
+ const uint32_t Len = getVectorNumElements(cast<VectorType>(RetTy));
const unsigned BitWidth = RetTy->getScalarSizeInBits();
Res.reserve(Len);
for (uint64_t I = 0; I != Len; ++I) {
- Res.push_back(APInt(BitWidth, I, false, true));
+ Res.push_back(
+ APInt(BitWidth, I, /*IsSigned=*/false, /*ImplicitTrunc*/ true));
}
return std::move(Res);
}
More information about the llvm-commits
mailing list