[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