[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