[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