[llvm] [InstSimplify] fold exact non zero right shifts on undefs to poison (PR #212943)

Karol Zwolak via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 30 00:31:30 PDT 2026


https://github.com/karolzwolak created https://github.com/llvm/llvm-project/pull/212943

None

>From 0328507f18dddf5a08aed60c8012f7348954749c Mon Sep 17 00:00:00 2001
From: Karol Zwolak <karolzwolak7 at gmail.com>
Date: Wed, 29 Jul 2026 10:55:17 +0200
Subject: [PATCH 1/3] [InstSimplify] fold exact non zero right shifts on undefs
 to poison


>From 2966bea2108432df6c9d1ded9dbe6ae8a5f987aa Mon Sep 17 00:00:00 2001
From: Karol Zwolak <karolzwolak7 at gmail.com>
Date: Wed, 29 Jul 2026 12:31:15 +0200
Subject: [PATCH 2/3] pre-commit tests

---
 llvm/test/Transforms/InstSimplify/shift.ll | 85 ++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/shift.ll b/llvm/test/Transforms/InstSimplify/shift.ll
index a6de24e24f63c..59adc9143cbde 100644
--- a/llvm/test/Transforms/InstSimplify/shift.ll
+++ b/llvm/test/Transforms/InstSimplify/shift.ll
@@ -409,3 +409,88 @@ define i8 @shl_nsw_nuw_6_do_nothing(i8 %x) {
   %y = shl nsw nuw i8 %x, 6
   ret i8 %y
 }
+
+; shift amount is a nonzero constant -> poison
+define i32 @lshr_exact_undef_by_const_nonzero() {
+; CHECK-LABEL: @lshr_exact_undef_by_const_nonzero(
+; CHECK-NEXT:    ret i32 0
+;
+  %r = lshr exact i32 undef, 1
+  ret i32 %r
+}
+
+define i32 @ashr_exact_undef_by_const_nonzero() {
+; CHECK-LABEL: @ashr_exact_undef_by_const_nonzero(
+; CHECK-NEXT:    ret i32 0
+;
+  %r = ashr exact i32 undef, 1
+  ret i32 %r
+}
+
+; shift amount is a literal 0 -> undef (no bit is shifted out)
+define i32 @lshr_exact_undef_by_const_zero() {
+; CHECK-LABEL: @lshr_exact_undef_by_const_zero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = lshr exact i32 undef, 0
+  ret i32 %r
+}
+
+define i32 @ashr_exact_undef_by_const_zero() {
+; CHECK-LABEL: @ashr_exact_undef_by_const_zero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = ashr exact i32 undef, 0
+  ret i32 %r
+}
+
+; shift amount is known to be 0 (but not a literal) -> undef
+define i32 @lshr_exact_undef_by_known_zero(i32 range(i32 0, 1) %a) {
+; CHECK-LABEL: @lshr_exact_undef_by_known_zero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = lshr exact i32 undef, %a
+  ret i32 %r
+}
+
+define i32 @ashr_exact_undef_by_known_zero(i32 range(i32 0, 1) %a) {
+; CHECK-LABEL: @ashr_exact_undef_by_known_zero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = ashr exact i32 undef, %a
+  ret i32 %r
+}
+
+; shift amount is known to be nonzero -> poison
+define i32 @lshr_exact_undef_by_known_nonzero(i32 range(i32 1, 10) %a) {
+; CHECK-LABEL: @lshr_exact_undef_by_known_nonzero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = lshr exact i32 undef, %a
+  ret i32 %r
+}
+
+define i32 @ashr_exact_undef_by_known_nonzero(i32 range(i32 1, 10) %a) {
+; CHECK-LABEL: @ashr_exact_undef_by_known_nonzero(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = ashr exact i32 undef, %a
+  ret i32 %r
+}
+
+; shift amount is unknown (may be 0) -> undef, not poison
+define i32 @lshr_exact_undef_by_unknown(i32 %a) {
+; CHECK-LABEL: @lshr_exact_undef_by_unknown(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = lshr exact i32 undef, %a
+  ret i32 %r
+}
+
+define i32 @ashr_exact_undef_by_unknown(i32 %a) {
+; CHECK-LABEL: @ashr_exact_undef_by_unknown(
+; CHECK-NEXT:    ret i32 undef
+;
+  %r = ashr exact i32 undef, %a
+  ret i32 %r
+}

>From 1dd8377a6d9c37dd4024466b7fcbc50a9dc0fd86 Mon Sep 17 00:00:00 2001
From: Karol Zwolak <karolzwolak7 at gmail.com>
Date: Wed, 29 Jul 2026 12:33:50 +0200
Subject: [PATCH 3/3] actual implementation

---
 llvm/lib/Analysis/InstructionSimplify.cpp        | 12 +++++++++---
 llvm/lib/IR/ConstantFold.cpp                     |  1 -
 llvm/test/Transforms/InstSimplify/shift.ll       |  8 ++++----
 llvm/test/Transforms/LoopInstSimplify/pr55072.ll |  2 +-
 4 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ddaa44f43e631..95144e60912f8 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -1372,6 +1372,13 @@ static Value *simplifyShift(Instruction::BinaryOps Opcode, Value *Op0,
 static Value *simplifyRightShift(Instruction::BinaryOps Opcode, Value *Op0,
                                  Value *Op1, bool IsExact,
                                  const SimplifyQuery &Q, unsigned MaxRecurse) {
+  // Handle exact undef before `simplifyShift()` as it doesn't know about
+  // `IsExact` and might fold it to something else.
+  // undef exact>> X -> poison iff X is known nonzero
+  // undef exact>> X -> undef with unknown X
+  if (Q.isUndefValue(Op0) && IsExact)
+    return isKnownNonZero(Op1, Q) ? PoisonValue::get(Op0->getType()) : Op0;
+
   if (Value *V =
           simplifyShift(Opcode, Op0, Op1, /*IsNSW*/ false, Q, MaxRecurse))
     return V;
@@ -1380,10 +1387,9 @@ static Value *simplifyRightShift(Instruction::BinaryOps Opcode, Value *Op0,
   if (Op0 == Op1)
     return Constant::getNullValue(Op0->getType());
 
-  // undef >> X -> 0
-  // undef >> X -> undef (if it's exact)
+  // undef non-exact>> X -> 0
   if (Q.isUndefValue(Op0))
-    return IsExact ? Op0 : Constant::getNullValue(Op0->getType());
+    return Constant::getNullValue(Op0->getType());
 
   // The low bit cannot be shifted out of an exact shift if it is set.
   // TODO: Generalize by counting trailing zeros (see fold for exact division).
diff --git a/llvm/lib/IR/ConstantFold.cpp b/llvm/lib/IR/ConstantFold.cpp
index f8abd576f93c4..14f171ce10f6a 100644
--- a/llvm/lib/IR/ConstantFold.cpp
+++ b/llvm/lib/IR/ConstantFold.cpp
@@ -720,7 +720,6 @@ Constant *llvm::ConstantFoldBinaryInstruction(unsigned Opcode, Constant *C1,
       // X >>a undef -> poison
       if (isa<UndefValue>(C2))
         return PoisonValue::get(C2->getType());
-      // TODO: undef >>a X -> poison if the shift is exact
       // undef >>a X -> 0
       return Constant::getNullValue(C1->getType());
     case Instruction::Shl:
diff --git a/llvm/test/Transforms/InstSimplify/shift.ll b/llvm/test/Transforms/InstSimplify/shift.ll
index 59adc9143cbde..ef096b07ec289 100644
--- a/llvm/test/Transforms/InstSimplify/shift.ll
+++ b/llvm/test/Transforms/InstSimplify/shift.ll
@@ -413,7 +413,7 @@ define i8 @shl_nsw_nuw_6_do_nothing(i8 %x) {
 ; shift amount is a nonzero constant -> poison
 define i32 @lshr_exact_undef_by_const_nonzero() {
 ; CHECK-LABEL: @lshr_exact_undef_by_const_nonzero(
-; CHECK-NEXT:    ret i32 0
+; CHECK-NEXT:    ret i32 poison
 ;
   %r = lshr exact i32 undef, 1
   ret i32 %r
@@ -421,7 +421,7 @@ define i32 @lshr_exact_undef_by_const_nonzero() {
 
 define i32 @ashr_exact_undef_by_const_nonzero() {
 ; CHECK-LABEL: @ashr_exact_undef_by_const_nonzero(
-; CHECK-NEXT:    ret i32 0
+; CHECK-NEXT:    ret i32 poison
 ;
   %r = ashr exact i32 undef, 1
   ret i32 %r
@@ -464,7 +464,7 @@ define i32 @ashr_exact_undef_by_known_zero(i32 range(i32 0, 1) %a) {
 ; shift amount is known to be nonzero -> poison
 define i32 @lshr_exact_undef_by_known_nonzero(i32 range(i32 1, 10) %a) {
 ; CHECK-LABEL: @lshr_exact_undef_by_known_nonzero(
-; CHECK-NEXT:    ret i32 undef
+; CHECK-NEXT:    ret i32 poison
 ;
   %r = lshr exact i32 undef, %a
   ret i32 %r
@@ -472,7 +472,7 @@ define i32 @lshr_exact_undef_by_known_nonzero(i32 range(i32 1, 10) %a) {
 
 define i32 @ashr_exact_undef_by_known_nonzero(i32 range(i32 1, 10) %a) {
 ; CHECK-LABEL: @ashr_exact_undef_by_known_nonzero(
-; CHECK-NEXT:    ret i32 undef
+; CHECK-NEXT:    ret i32 poison
 ;
   %r = ashr exact i32 undef, %a
   ret i32 %r
diff --git a/llvm/test/Transforms/LoopInstSimplify/pr55072.ll b/llvm/test/Transforms/LoopInstSimplify/pr55072.ll
index e7de21a01aa4c..bd038567982c8 100644
--- a/llvm/test/Transforms/LoopInstSimplify/pr55072.ll
+++ b/llvm/test/Transforms/LoopInstSimplify/pr55072.ll
@@ -9,7 +9,7 @@ define i32 @test_01() {
 ; CHECK:       loop:
 ; CHECK-NEXT:    br label [[LOOP]]
 ; CHECK:       unreached:
-; CHECK-NEXT:    ret i32 0
+; CHECK-NEXT:    ret i32 poison
 ;
 bb:
   br label %loop



More information about the llvm-commits mailing list