[llvm] [InstCombine] Fold trunc nuw/nsw (shl exact %x, %y) to i1 -> icmp ne %x, 0 (PR #210499)
Andreas Jonson via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 18 03:25:54 PDT 2026
https://github.com/andjo403 created https://github.com/llvm/llvm-project/pull/210499
Regression found from https://github.com/llvm/llvm-project/pull/184182
Proof: https://alive2.llvm.org/ce/z/de6gru
>From 169630d38c6600db0c1b3be070d7ed943668a441 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 18 Jul 2026 09:39:40 +0200
Subject: [PATCH 1/2] [InstCombine] Precommit test (NFC)
---
llvm/test/Transforms/InstCombine/trunc.ll | 114 ++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll
index 29aff222dbd87..655db7b1b17d0 100644
--- a/llvm/test/Transforms/InstCombine/trunc.ll
+++ b/llvm/test/Transforms/InstCombine/trunc.ll
@@ -1354,3 +1354,117 @@ define i8 @neg_trunc_nuw_i8_trunc_nuw_i16(i32 %x) {
%e = trunc nuw i16 %c to i8
ret i8 %e
}
+
+define i1 @trunc_nuw_lshr_exact(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nuw_lshr_exact(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[LSHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %lshr = lshr exact i8 %x, %c
+ %ret = trunc nuw i8 %lshr to i1
+ ret i1 %ret
+}
+
+define i1 @trunc_nsw_lshr_exact(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nsw_lshr_exact(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc nsw i8 [[LSHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %lshr = lshr exact i8 %x, %c
+ %ret = trunc nsw i8 %lshr to i1
+ ret i1 %ret
+}
+
+define i1 @trunc_nuw_ashr_exact(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nuw_ashr_exact(
+; CHECK-NEXT: [[ASHR1:%.*]] = lshr i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc i8 [[ASHR1]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %ashr = ashr exact i8 %x, %c
+ %ret = trunc nuw i8 %ashr to i1
+ ret i1 %ret
+}
+
+define i1 @trunc_nsw_ashr_exact(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nsw_ashr_exact(
+; CHECK-NEXT: [[ASHR1:%.*]] = lshr i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc i8 [[ASHR1]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %ashr = ashr exact i8 %x, %c
+ %ret = trunc nsw i8 %ashr to i1
+ ret i1 %ret
+}
+
+define i1 @trunc_nuw_lshr_exact_use(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nuw_lshr_exact_use(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LSHR]])
+; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[LSHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %lshr = lshr exact i8 %x, %c
+ call void @use.i8(i8 %lshr)
+ %ret = trunc nuw i8 %lshr to i1
+ ret i1 %ret
+}
+
+define i1 @trunc_nuw_ashr_exact_use(i8 %x, i8 %c) {
+; CHECK-LABEL: @trunc_nuw_ashr_exact_use(
+; CHECK-NEXT: [[ASHR:%.*]] = ashr exact i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[ASHR]])
+; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[ASHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %ashr = ashr exact i8 %x, %c
+ call void @use.i8(i8 %ashr)
+ %ret = trunc nuw i8 %ashr to i1
+ ret i1 %ret
+}
+
+define <2 x i1> @trunc_nuw_lshr_icmp_vec(<2 x i8> %x, <2 x i8> %c) {
+; CHECK-LABEL: @trunc_nuw_lshr_icmp_vec(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr exact <2 x i8> [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc nuw <2 x i8> [[LSHR]] to <2 x i1>
+; CHECK-NEXT: ret <2 x i1> [[RET]]
+;
+ %lshr = lshr exact <2 x i8> %x, %c
+ %ret = trunc nuw <2 x i8> %lshr to <2 x i1>
+ ret <2 x i1> %ret
+}
+
+define <2 x i1> @trunc_nuw_ashr_icmp_vec(<2 x i8> %x, <2 x i8> %c) {
+; CHECK-LABEL: @trunc_nuw_ashr_icmp_vec(
+; CHECK-NEXT: [[ASHR1:%.*]] = lshr <2 x i8> [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc <2 x i8> [[ASHR1]] to <2 x i1>
+; CHECK-NEXT: ret <2 x i1> [[RET]]
+;
+ %ashr = ashr exact <2 x i8> %x, %c
+ %ret = trunc nuw <2 x i8> %ashr to <2 x i1>
+ ret <2 x i1> %ret
+}
+
+define i1 @neg_trunc_lshr_exact(i8 %x, i8 %c) {
+; CHECK-LABEL: @neg_trunc_lshr_exact(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc i8 [[LSHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %lshr = lshr exact i8 %x, %c
+ %ret = trunc i8 %lshr to i1
+ ret i1 %ret
+}
+
+define i1 @neg_trunc_nuw_lshr(i8 %x, i8 %c) {
+; CHECK-LABEL: @neg_trunc_nuw_lshr(
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i8 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[LSHR]] to i1
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %lshr = lshr i8 %x, %c
+ %ret = trunc nuw i8 %lshr to i1
+ ret i1 %ret
+}
>From d826cdd6d065e772a4f478ed878cdc9482280f68 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 18 Jul 2026 12:18:13 +0200
Subject: [PATCH 2/2] [InstCombine] Fold trunc nuw/nsw (shl exact %x, %y) to i1
-> icmp ne %x, 0
---
.../InstCombine/InstCombineCasts.cpp | 6 ++++-
llvm/test/Transforms/InstCombine/trunc.ll | 22 +++++++------------
2 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index ebfb796b070ae..2db6396b9d661 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1088,6 +1088,11 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
}
}
}
+ Value *X;
+ if (DestWidth == 1 &&
+ (Trunc.hasNoUnsignedWrap() || Trunc.hasNoSignedWrap()) &&
+ match(Src, m_Exact(m_Shr(m_Value(X), m_Value()))))
+ return new ICmpInst(ICmpInst::ICMP_NE, X, Constant::getNullValue(SrcTy));
// See if we can simplify any instructions used by the input whose sole
// purpose is to compute bits we don't care about.
@@ -1097,7 +1102,6 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
if (DestWidth == 1) {
Value *Zero = Constant::getNullValue(SrcTy);
- Value *X;
const APInt *C1;
Constant *C2;
if (match(Src, m_OneUse(m_Shr(m_Shl(m_Power2(C1), m_Value(X)),
diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll
index 655db7b1b17d0..507a4678a7a1f 100644
--- a/llvm/test/Transforms/InstCombine/trunc.ll
+++ b/llvm/test/Transforms/InstCombine/trunc.ll
@@ -1357,8 +1357,7 @@ define i8 @neg_trunc_nuw_i8_trunc_nuw_i16(i32 %x) {
define i1 @trunc_nuw_lshr_exact(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nuw_lshr_exact(
-; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[LSHR]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%lshr = lshr exact i8 %x, %c
@@ -1368,8 +1367,7 @@ define i1 @trunc_nuw_lshr_exact(i8 %x, i8 %c) {
define i1 @trunc_nsw_lshr_exact(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nsw_lshr_exact(
-; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc nsw i8 [[LSHR]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%lshr = lshr exact i8 %x, %c
@@ -1379,8 +1377,7 @@ define i1 @trunc_nsw_lshr_exact(i8 %x, i8 %c) {
define i1 @trunc_nuw_ashr_exact(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nuw_ashr_exact(
-; CHECK-NEXT: [[ASHR1:%.*]] = lshr i8 [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc i8 [[ASHR1]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%ashr = ashr exact i8 %x, %c
@@ -1390,8 +1387,7 @@ define i1 @trunc_nuw_ashr_exact(i8 %x, i8 %c) {
define i1 @trunc_nsw_ashr_exact(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nsw_ashr_exact(
-; CHECK-NEXT: [[ASHR1:%.*]] = lshr i8 [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc i8 [[ASHR1]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%ashr = ashr exact i8 %x, %c
@@ -1403,7 +1399,7 @@ define i1 @trunc_nuw_lshr_exact_use(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nuw_lshr_exact_use(
; CHECK-NEXT: [[LSHR:%.*]] = lshr exact i8 [[X:%.*]], [[C:%.*]]
; CHECK-NEXT: call void @use.i8(i8 [[LSHR]])
-; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[LSHR]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%lshr = lshr exact i8 %x, %c
@@ -1416,7 +1412,7 @@ define i1 @trunc_nuw_ashr_exact_use(i8 %x, i8 %c) {
; CHECK-LABEL: @trunc_nuw_ashr_exact_use(
; CHECK-NEXT: [[ASHR:%.*]] = ashr exact i8 [[X:%.*]], [[C:%.*]]
; CHECK-NEXT: call void @use.i8(i8 [[ASHR]])
-; CHECK-NEXT: [[RET:%.*]] = trunc nuw i8 [[ASHR]] to i1
+; CHECK-NEXT: [[RET:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[RET]]
;
%ashr = ashr exact i8 %x, %c
@@ -1427,8 +1423,7 @@ define i1 @trunc_nuw_ashr_exact_use(i8 %x, i8 %c) {
define <2 x i1> @trunc_nuw_lshr_icmp_vec(<2 x i8> %x, <2 x i8> %c) {
; CHECK-LABEL: @trunc_nuw_lshr_icmp_vec(
-; CHECK-NEXT: [[LSHR:%.*]] = lshr exact <2 x i8> [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc nuw <2 x i8> [[LSHR]] to <2 x i1>
+; CHECK-NEXT: [[RET:%.*]] = icmp ne <2 x i8> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[RET]]
;
%lshr = lshr exact <2 x i8> %x, %c
@@ -1438,8 +1433,7 @@ define <2 x i1> @trunc_nuw_lshr_icmp_vec(<2 x i8> %x, <2 x i8> %c) {
define <2 x i1> @trunc_nuw_ashr_icmp_vec(<2 x i8> %x, <2 x i8> %c) {
; CHECK-LABEL: @trunc_nuw_ashr_icmp_vec(
-; CHECK-NEXT: [[ASHR1:%.*]] = lshr <2 x i8> [[X:%.*]], [[C:%.*]]
-; CHECK-NEXT: [[RET:%.*]] = trunc <2 x i8> [[ASHR1]] to <2 x i1>
+; CHECK-NEXT: [[RET:%.*]] = icmp ne <2 x i8> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[RET]]
;
%ashr = ashr exact <2 x i8> %x, %c
More information about the llvm-commits
mailing list