[llvm] [IR][InstSimplify] Clarify poison elements in cttz.elts results in poison (PR #223935)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 16 00:50:43 PDT 2026
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/223935
>From 2b7c382b338bf1be2e5731d8aa14060d5803ca72 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 16 Sep 2026 15:25:12 +0800
Subject: [PATCH 1/3] [IR][InstSimplify] Clarify poison elements in cttz.elts
results in poison
The generic expansion for cttz.elts in LegalizeVectorOps is
(reduce.umax (select input, step_vector, 0))
Because reductions produce poison if any element is poison, this will also produce poison.
ConstantFolding currently has short circuiting behaviour, e.g. `cttz.elts <0, 1, poison, poison> -> 1`, but we should probably make this consistent with reductions and just return poison. This updates LangRef and ConstantFolding to be more explicit about this.
---
llvm/docs/LangRef.md | 1 +
llvm/lib/Analysis/ConstantFolding.cpp | 3 ++-
llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll | 4 ++--
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index a29e8f7508a7f..c59132e9b1da5 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -21170,6 +21170,7 @@ The '`llvm.experimental.cttz.elts`' intrinsic counts the trailing (least
significant) zero elements in a vector. If `src == 0` the result is the
number of elements in the input vector.
+If any element in the input vector is poison, the result is poison.
#### '`llvm.experimental.get.vector.length`' Intrinsic
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index aca2556cd48eb..05b7c0c52a5d1 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -3969,7 +3969,8 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
if (!FVTy)
return nullptr;
unsigned Width = Ty->getIntegerBitWidth();
- if (APInt::getMaxValue(Width).ult(FVTy->getNumElements()))
+ if (APInt::getMaxValue(Width).ult(FVTy->getNumElements()) ||
+ Operands[0]->containsPoisonElement())
return PoisonValue::get(Ty);
for (unsigned I = 0; I < FVTy->getNumElements(); ++I) {
Constant *Elt = Operands[0]->getAggregateElement(I);
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll b/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
index db91fd68fcbe6..928cb3ddde2a9 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
@@ -43,7 +43,7 @@ define i32 @cttz_elts_v4i32_used_lane_undef() {
define i32 @cttz_elts_v4i32_unused_lane_poison() {
; CHECK-LABEL: @cttz_elts_v4i32_unused_lane_poison(
-; CHECK-NEXT: ret i32 1
+; CHECK-NEXT: ret i32 poison
;
%res = call i32 @llvm.experimental.cttz.elts.i32.v4i32(<4 x i32> <i32 0, i32 1, i32 poison, i32 3>, i1 false)
ret i32 %res
@@ -51,7 +51,7 @@ define i32 @cttz_elts_v4i32_unused_lane_poison() {
define i32 @cttz_elts_v4i32_used_lane_poison() {
; CHECK-LABEL: @cttz_elts_v4i32_used_lane_poison(
-; CHECK-NEXT: ret i32 3
+; CHECK-NEXT: ret i32 poison
;
%res = call i32 @llvm.experimental.cttz.elts.i32.v4i32(<4 x i32> <i32 0, i32 0, i32 poison, i32 3>, i1 false)
ret i32 %res
>From d03ed6d904e12a61508f861c2581526657fbd343 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 16 Sep 2026 15:47:53 +0800
Subject: [PATCH 2/3] Precommit llubi test
---
llvm/test/tools/llubi/intr_experimental_vector.ll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/test/tools/llubi/intr_experimental_vector.ll b/llvm/test/tools/llubi/intr_experimental_vector.ll
index 100e77c189c60..59cb5534e5116 100644
--- a/llvm/test/tools/llubi/intr_experimental_vector.ll
+++ b/llvm/test/tools/llubi/intr_experimental_vector.ll
@@ -5,6 +5,7 @@ define void @main() {
%cttz_some = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 false, i1 true, i1 false>, i1 true)
%cttz_all = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 false)
%cttz_poison = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 true)
+ %cttz_poison_elt = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 true, i1 poison, i1 false>, i1 false)
%evl_zero = call i32 @llvm.experimental.get.vector.length.i32(i32 0, i32 4, i1 false)
%evl_short = call i32 @llvm.experimental.get.vector.length.i32(i32 3, i32 4, i1 false)
@@ -79,6 +80,7 @@ define void @main() {
; CHECK-NEXT: %cttz_some = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 false, i1 true, i1 false>, i1 true) => i8 2
; CHECK-NEXT: %cttz_all = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 false) => i8 4
; CHECK-NEXT: %cttz_poison = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 true) => poison
+; CHECK-NEXT: %cttz_poison_elt = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 true, i1 poison, i1 false>, i1 false) => i8 1
; CHECK-NEXT: %evl_zero = call i32 @llvm.experimental.get.vector.length.i32(i32 0, i32 4, i1 false) => i32 0
; CHECK-NEXT: %evl_short = call i32 @llvm.experimental.get.vector.length.i32(i32 3, i32 4, i1 false) => i32 3
; CHECK-NEXT: %evl_full = call i32 @llvm.experimental.get.vector.length.i32(i32 9, i32 4, i1 false) => i32 3
>From 2ab328806ea3ac2430ae6fa7d5b9d2613209c4b5 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 16 Sep 2026 15:49:44 +0800
Subject: [PATCH 3/3] Update llubi
---
llvm/test/tools/llubi/intr_experimental_vector.ll | 2 +-
llvm/tools/llubi/lib/Interpreter.cpp | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/llvm/test/tools/llubi/intr_experimental_vector.ll b/llvm/test/tools/llubi/intr_experimental_vector.ll
index 59cb5534e5116..931d51abbc70f 100644
--- a/llvm/test/tools/llubi/intr_experimental_vector.ll
+++ b/llvm/test/tools/llubi/intr_experimental_vector.ll
@@ -80,7 +80,7 @@ define void @main() {
; CHECK-NEXT: %cttz_some = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 false, i1 true, i1 false>, i1 true) => i8 2
; CHECK-NEXT: %cttz_all = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 false) => i8 4
; CHECK-NEXT: %cttz_poison = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> zeroinitializer, i1 true) => poison
-; CHECK-NEXT: %cttz_poison_elt = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 true, i1 poison, i1 false>, i1 false) => i8 1
+; CHECK-NEXT: %cttz_poison_elt = call i8 @llvm.experimental.cttz.elts.i8.v4i1(<4 x i1> <i1 false, i1 true, i1 poison, i1 false>, i1 false) => poison
; CHECK-NEXT: %evl_zero = call i32 @llvm.experimental.get.vector.length.i32(i32 0, i32 4, i1 false) => i32 0
; CHECK-NEXT: %evl_short = call i32 @llvm.experimental.get.vector.length.i32(i32 3, i32 4, i1 false) => i32 3
; CHECK-NEXT: %evl_full = call i32 @llvm.experimental.get.vector.length.i32(i32 9, i32 4, i1 false) => i32 3
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index d840685e90cee..0c7e74f52f689 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -1756,10 +1756,12 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
if (!isUIntN(RetBW, Vec.size()))
return AnyValue::poison();
- uint64_t Count = 0;
- for (const AnyValue &V : Vec) {
+ for (const AnyValue &V : Vec)
if (V.isPoison())
return AnyValue::poison();
+
+ uint64_t Count = 0;
+ for (const AnyValue &V : Vec) {
if (!V.asInteger().isZero())
break;
++Count;
More information about the llvm-commits
mailing list