[llvm] [InstCombine] Fold `ceil(X / (2 ^ C)) == 0` -> `X == 0` (PR #143683)
Iris Shi via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 22 02:36:01 PDT 2025
https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/143683
>From 8e9969dd645f452e915c33b96af2e0e30547a452 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Wed, 11 Jun 2025 19:00:27 +0800
Subject: [PATCH 1/6] pre-commit test
[InstCombine] Fold `ceil(X >> C) == 0 -> X == 0`
update test
1
address review comments
rm header
1
1
---
.../InstCombine/InstCombineCompares.cpp | 1 +
.../test/Transforms/InstCombine/ceil-shift.ll | 285 ++++++++++++++++++
2 files changed, 286 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/ceil-shift.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 084e7fbaa268a..07a21f99f1f3b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1298,6 +1298,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// eq/ne (mul X, Y)) with (icmp eq/ne X/Y) and if X/Y is known non-zero that
// will fold to a constant elsewhere.
}
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/ceil-shift.ll b/llvm/test/Transforms/InstCombine/ceil-shift.ll
new file mode 100644
index 0000000000000..7d460d5f399f5
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/ceil-shift.ll
@@ -0,0 +1,285 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ceil_shift4(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %is_zero = icmp eq i32 %quot_or_rem, 0
+ ret i1 %is_zero
+}
+
+define i1 @ceil_shift4_add(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4_add(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %ceil = add i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %ceil, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift6(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift6(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %quot = lshr i32 %arg0, 6
+ %rem = and i32 %arg0, 63
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift6_ne(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift6_ne(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %quot = lshr i32 %arg0, 6
+ %rem = and i32 %arg0, 63
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp ne i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift11(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift11(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 11
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 2047
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %quot = lshr i32 %arg0, 11
+ %rem = and i32 %arg0, 2047
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift11_ne(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift11_ne(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %quot = lshr i32 %arg0, 6
+ %rem = and i32 %arg0, 63
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp ne i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift0(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift0(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: ret i1 [[TMP1]]
+;
+ %quot = lshr i32 %arg0, 0
+ %rem = and i32 %arg0, 0
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift4_comm(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4_comm(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %zext_has_rem, %quot
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+declare void @use(i32)
+
+define i1 @ceil_shift4_used_1(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4_used_1(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: call void @use(i32 [[TMP1]])
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[TMP1]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 4
+ call void @use(i32 %quot)
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift4_used_5(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4_used_5(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP5:%.*]] = or i32 [[TMP1]], [[TMP4]]
+; CHECK-NEXT: call void @use(i32 [[TMP5]])
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ call void @use(i32 %quot_or_rem)
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
+; CHECK-LABEL: define <4 x i1> @ceil_shift4_v4i32(
+; CHECK-SAME: <4 x i32> [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr <4 x i32> [[ARG0]], splat (i32 16)
+; CHECK-NEXT: [[REM:%.*]] = and <4 x i32> [[ARG0]], splat (i32 65535)
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <4 x i32> [[REM]], zeroinitializer
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <4 x i1> [[HAS_REM]] to <4 x i32>
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <4 x i32> [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: ret <4 x i1> [[TMP1]]
+;
+ %quot = lshr <4 x i32> %arg0, splat (i32 16)
+ %rem = and <4 x i32> %arg0, splat (i32 65535)
+ %has_rem = icmp ne <4 x i32> %rem, zeroinitializer
+ %zext_has_rem = zext <4 x i1> %has_rem to <4 x i32>
+ %quot_or_rem = or <4 x i32> %quot, %zext_has_rem
+ %res = icmp eq <4 x i32> %quot_or_rem, zeroinitializer
+ ret <4 x i1> %res
+}
+
+define <8 x i1> @ceil_shift4_v8i16(<8 x i16> %arg0) {
+; CHECK-LABEL: define <8 x i1> @ceil_shift4_v8i16(
+; CHECK-SAME: <8 x i16> [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr <8 x i16> [[ARG0]], splat (i16 4)
+; CHECK-NEXT: [[REM:%.*]] = and <8 x i16> [[ARG0]], splat (i16 15)
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <8 x i16> [[REM]], zeroinitializer
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <8 x i1> [[HAS_REM]] to <8 x i16>
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <8 x i16> [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: ret <8 x i1> [[TMP1]]
+;
+ %quot = lshr <8 x i16> %arg0, splat (i16 4)
+ %rem = and <8 x i16> %arg0, splat (i16 15)
+ %has_rem = icmp ne <8 x i16> %rem, zeroinitializer
+ %zext_has_rem = zext <8 x i1> %has_rem to <8 x i16>
+ %quot_or_rem = or <8 x i16> %quot, %zext_has_rem
+ %res = icmp eq <8 x i16> %quot_or_rem, zeroinitializer
+ ret <8 x i1> %res
+}
+
+; negative tests
+
+define i1 @ceil_shift_not_mask_1(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift_not_mask_1(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[ARG0]], 31
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP5:%.*]] = or i32 [[TMP1]], [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 31
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
+
+define i1 @ceil_shift_not_mask_2(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift_not_mask_2(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 5
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP5:%.*]] = or i32 [[TMP1]], [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 0
+; CHECK-NEXT: ret i1 [[TMP6]]
+;
+ %quot = lshr i32 %arg0, 5
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_or_rem = or i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_or_rem, 0
+ ret i1 %res
+}
>From 1a91c572f00c6edb6762de366cc9578ad2785042 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sat, 14 Jun 2025 10:09:44 +0800
Subject: [PATCH 2/6] [InstCombine] Fold `ceil(X >> C) == 0 -> X == 0`
---
.../InstCombine/InstCombineCompares.cpp | 14 ++++
.../test/Transforms/InstCombine/ceil-shift.ll | 69 +++----------------
2 files changed, 24 insertions(+), 59 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 07a21f99f1f3b..7508a4f8e73d3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1299,6 +1299,20 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// will fold to a constant elsewhere.
}
+ // icmp eq/ne ((X >> C) | (X & mask(C) != 0)), 0 -> icmp eq/ne X, 0
+ if (ICmpInst::isEquality(Pred)) {
+ Value *X;
+ const APInt *C1, *C2;
+ if (match(Cmp.getOperand(0),
+ m_OneUse(m_c_Or(
+ m_LShr(m_Value(X), m_APInt(C1)),
+ m_ZExt(m_SpecificICmp(ICmpInst::ICMP_NE,
+ m_And(m_Deferred(X), m_LowBitMask(C2)),
+ m_Zero()))))) &&
+ C2->popcount() == C1->getZExtValue())
+ return new ICmpInst(Pred, X, ConstantInt::getNullValue(X->getType()));
+ }
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/ceil-shift.ll b/llvm/test/Transforms/InstCombine/ceil-shift.ll
index 7d460d5f399f5..f3552aee9cb66 100644
--- a/llvm/test/Transforms/InstCombine/ceil-shift.ll
+++ b/llvm/test/Transforms/InstCombine/ceil-shift.ll
@@ -4,12 +4,7 @@
define i1 @ceil_shift4(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 4
@@ -24,12 +19,7 @@ define i1 @ceil_shift4(i32 %arg0) {
define i1 @ceil_shift4_add(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_add(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -44,12 +34,7 @@ define i1 @ceil_shift4_add(i32 %arg0) {
define i1 @ceil_shift6(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 6
@@ -64,12 +49,7 @@ define i1 @ceil_shift6(i32 %arg0) {
define i1 @ceil_shift6_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -84,12 +64,7 @@ define i1 @ceil_shift6_ne(i32 %arg0) {
define i1 @ceil_shift11(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 11
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 2047
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 11
@@ -104,12 +79,7 @@ define i1 @ceil_shift11(i32 %arg0) {
define i1 @ceil_shift11_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -139,12 +109,7 @@ define i1 @ceil_shift0(i32 %arg0) {
define i1 @ceil_shift4_comm(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_comm(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -163,11 +128,7 @@ define i1 @ceil_shift4_used_1(i32 %arg0) {
; CHECK-SAME: i32 [[ARG0:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[TMP1]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -205,12 +166,7 @@ define i1 @ceil_shift4_used_5(i32 %arg0) {
define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
; CHECK-LABEL: define <4 x i1> @ceil_shift4_v4i32(
; CHECK-SAME: <4 x i32> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr <4 x i32> [[ARG0]], splat (i32 16)
-; CHECK-NEXT: [[REM:%.*]] = and <4 x i32> [[ARG0]], splat (i32 65535)
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <4 x i32> [[REM]], zeroinitializer
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <4 x i1> [[HAS_REM]] to <4 x i32>
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <4 x i32> [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ARG0]], zeroinitializer
; CHECK-NEXT: ret <4 x i1> [[TMP1]]
;
%quot = lshr <4 x i32> %arg0, splat (i32 16)
@@ -225,12 +181,7 @@ define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
define <8 x i1> @ceil_shift4_v8i16(<8 x i16> %arg0) {
; CHECK-LABEL: define <8 x i1> @ceil_shift4_v8i16(
; CHECK-SAME: <8 x i16> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr <8 x i16> [[ARG0]], splat (i16 4)
-; CHECK-NEXT: [[REM:%.*]] = and <8 x i16> [[ARG0]], splat (i16 15)
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <8 x i16> [[REM]], zeroinitializer
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <8 x i1> [[HAS_REM]] to <8 x i16>
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <8 x i16> [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[ARG0]], zeroinitializer
; CHECK-NEXT: ret <8 x i1> [[TMP1]]
;
%quot = lshr <8 x i16> %arg0, splat (i16 4)
>From 88301673e99451a4efa3ec9aeda4650f468ee6ec Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sat, 14 Jun 2025 21:02:56 +0800
Subject: [PATCH 3/6] add test
---
.../test/Transforms/InstCombine/ceil-shift.ll | 111 ++++++++++++++++--
1 file changed, 101 insertions(+), 10 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/ceil-shift.ll b/llvm/test/Transforms/InstCombine/ceil-shift.ll
index f3552aee9cb66..7d47ebb166529 100644
--- a/llvm/test/Transforms/InstCombine/ceil-shift.ll
+++ b/llvm/test/Transforms/InstCombine/ceil-shift.ll
@@ -4,7 +4,12 @@
define i1 @ceil_shift4(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 4
@@ -19,7 +24,12 @@ define i1 @ceil_shift4(i32 %arg0) {
define i1 @ceil_shift4_add(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_add(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -34,7 +44,12 @@ define i1 @ceil_shift4_add(i32 %arg0) {
define i1 @ceil_shift6(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 6
@@ -49,7 +64,12 @@ define i1 @ceil_shift6(i32 %arg0) {
define i1 @ceil_shift6_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -64,7 +84,12 @@ define i1 @ceil_shift6_ne(i32 %arg0) {
define i1 @ceil_shift11(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 11
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 2047
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 11
@@ -79,7 +104,12 @@ define i1 @ceil_shift11(i32 %arg0) {
define i1 @ceil_shift11_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -109,7 +139,12 @@ define i1 @ceil_shift0(i32 %arg0) {
define i1 @ceil_shift4_comm(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_comm(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -128,7 +163,11 @@ define i1 @ceil_shift4_used_1(i32 %arg0) {
; CHECK-SAME: i32 [[ARG0:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[TMP1]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -163,10 +202,37 @@ define i1 @ceil_shift4_used_5(i32 %arg0) {
ret i1 %res
}
+define i1 @ceil_shift4_used_add_nuw_nsw(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift4_used_add_nuw_nsw(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
+; CHECK-NEXT: [[CEIL:%.*]] = add nuw nsw i32 [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: call void @use(i32 [[CEIL]])
+; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CEIL]], 0
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %quot = lshr i32 %arg0, 4
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %ceil = add nuw nsw i32 %quot, %zext_has_rem
+ call void @use(i32 %ceil)
+ %res = icmp eq i32 %ceil, 0
+ ret i1 %res
+}
+
define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
; CHECK-LABEL: define <4 x i1> @ceil_shift4_v4i32(
; CHECK-SAME: <4 x i32> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ARG0]], zeroinitializer
+; CHECK-NEXT: [[QUOT:%.*]] = lshr <4 x i32> [[ARG0]], splat (i32 16)
+; CHECK-NEXT: [[REM:%.*]] = and <4 x i32> [[ARG0]], splat (i32 65535)
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <4 x i32> [[REM]], zeroinitializer
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <4 x i1> [[HAS_REM]] to <4 x i32>
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <4 x i32> [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[QUOT_OR_REM]], zeroinitializer
; CHECK-NEXT: ret <4 x i1> [[TMP1]]
;
%quot = lshr <4 x i32> %arg0, splat (i32 16)
@@ -181,7 +247,12 @@ define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
define <8 x i1> @ceil_shift4_v8i16(<8 x i16> %arg0) {
; CHECK-LABEL: define <8 x i1> @ceil_shift4_v8i16(
; CHECK-SAME: <8 x i16> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[ARG0]], zeroinitializer
+; CHECK-NEXT: [[QUOT:%.*]] = lshr <8 x i16> [[ARG0]], splat (i16 4)
+; CHECK-NEXT: [[REM:%.*]] = and <8 x i16> [[ARG0]], splat (i16 15)
+; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <8 x i16> [[REM]], zeroinitializer
+; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <8 x i1> [[HAS_REM]] to <8 x i16>
+; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <8 x i16> [[QUOT]], [[ZEXT_HAS_REM]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[QUOT_OR_REM]], zeroinitializer
; CHECK-NEXT: ret <8 x i1> [[TMP1]]
;
%quot = lshr <8 x i16> %arg0, splat (i16 4)
@@ -234,3 +305,23 @@ define i1 @ceil_shift_not_mask_2(i32 %arg0) {
%res = icmp eq i32 %quot_or_rem, 0
ret i1 %res
}
+
+define i1 @ceil_shift_not_add_or(i32 %arg0) {
+; CHECK-LABEL: define i1 @ceil_shift_not_add_or(
+; CHECK-SAME: i32 [[ARG0:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
+; CHECK-NEXT: [[HAS_REM_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[ARG0]], 32
+; CHECK-NEXT: [[RES1:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: [[RES:%.*]] = or i1 [[HAS_REM_NOT]], [[RES1]]
+; CHECK-NEXT: ret i1 [[RES]]
+;
+ %quot = lshr i32 %arg0, 5
+ %rem = and i32 %arg0, 15
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %quot_and_rem = and i32 %quot, %zext_has_rem
+ %res = icmp eq i32 %quot_and_rem, 0
+ ret i1 %res
+}
+
>From 32cf84843ebdba923c050daad495a72b3cbb2948 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sat, 14 Jun 2025 21:09:26 +0800
Subject: [PATCH 4/6] match both or and add
---
.../InstCombine/InstCombineCompares.cpp | 17 +++--
.../test/Transforms/InstCombine/ceil-shift.ll | 74 +++----------------
2 files changed, 22 insertions(+), 69 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7508a4f8e73d3..04d821ba6fa91 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1299,16 +1299,19 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// will fold to a constant elsewhere.
}
- // icmp eq/ne ((X >> C) | (X & mask(C) != 0)), 0 -> icmp eq/ne X, 0
+ // icmp eq/ne ((X >> C) or/add (X & mask(C) != 0)), 0 -> icmp eq/ne X, 0
if (ICmpInst::isEquality(Pred)) {
+ auto *BO = dyn_cast<BinaryOperator>(Cmp.getOperand(0));
Value *X;
const APInt *C1, *C2;
- if (match(Cmp.getOperand(0),
- m_OneUse(m_c_Or(
- m_LShr(m_Value(X), m_APInt(C1)),
- m_ZExt(m_SpecificICmp(ICmpInst::ICMP_NE,
- m_And(m_Deferred(X), m_LowBitMask(C2)),
- m_Zero()))))) &&
+ if (BO &&
+ (BO->getOpcode() == Instruction::Add ||
+ BO->getOpcode() == Instruction::Or) &&
+ match(BO, m_c_BinOp(m_LShr(m_Value(X), m_APInt(C1)),
+ m_ZExt(m_SpecificICmp(
+ ICmpInst::ICMP_NE,
+ m_And(m_Deferred(X), m_LowBitMask(C2)),
+ m_Zero())))) &&
C2->popcount() == C1->getZExtValue())
return new ICmpInst(Pred, X, ConstantInt::getNullValue(X->getType()));
}
diff --git a/llvm/test/Transforms/InstCombine/ceil-shift.ll b/llvm/test/Transforms/InstCombine/ceil-shift.ll
index 7d47ebb166529..8c24d7474ab94 100644
--- a/llvm/test/Transforms/InstCombine/ceil-shift.ll
+++ b/llvm/test/Transforms/InstCombine/ceil-shift.ll
@@ -4,12 +4,7 @@
define i1 @ceil_shift4(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 4
@@ -24,12 +19,7 @@ define i1 @ceil_shift4(i32 %arg0) {
define i1 @ceil_shift4_add(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_add(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -44,12 +34,7 @@ define i1 @ceil_shift4_add(i32 %arg0) {
define i1 @ceil_shift6(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 6
@@ -64,12 +49,7 @@ define i1 @ceil_shift6(i32 %arg0) {
define i1 @ceil_shift6_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift6_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -84,12 +64,7 @@ define i1 @ceil_shift6_ne(i32 %arg0) {
define i1 @ceil_shift11(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 11
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 2047
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP1]]
;
%quot = lshr i32 %arg0, 11
@@ -104,12 +79,7 @@ define i1 @ceil_shift11(i32 %arg0) {
define i1 @ceil_shift11_ne(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift11_ne(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 6
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 63
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 6
@@ -139,12 +109,7 @@ define i1 @ceil_shift0(i32 %arg0) {
define i1 @ceil_shift4_comm(i32 %arg0) {
; CHECK-LABEL: define i1 @ceil_shift4_comm(
; CHECK-SAME: i32 [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr i32 [[ARG0]], 4
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -163,11 +128,7 @@ define i1 @ceil_shift4_used_1(i32 %arg0) {
; CHECK-SAME: i32 [[ARG0:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[ARG0]], 4
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[REM:%.*]] = and i32 [[ARG0]], 15
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne i32 [[REM]], 0
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or i32 [[TMP1]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[QUOT_OR_REM]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -189,7 +150,7 @@ define i1 @ceil_shift4_used_5(i32 %arg0) {
; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
; CHECK-NEXT: [[TMP5:%.*]] = or i32 [[TMP1]], [[TMP4]]
; CHECK-NEXT: call void @use(i32 [[TMP5]])
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[TMP6]]
;
%quot = lshr i32 %arg0, 4
@@ -211,7 +172,7 @@ define i1 @ceil_shift4_used_add_nuw_nsw(i32 %arg0) {
; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext i1 [[HAS_REM]] to i32
; CHECK-NEXT: [[CEIL:%.*]] = add nuw nsw i32 [[QUOT]], [[ZEXT_HAS_REM]]
; CHECK-NEXT: call void @use(i32 [[CEIL]])
-; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CEIL]], 0
+; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[ARG0]], 0
; CHECK-NEXT: ret i1 [[RES]]
;
%quot = lshr i32 %arg0, 4
@@ -227,12 +188,7 @@ define i1 @ceil_shift4_used_add_nuw_nsw(i32 %arg0) {
define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
; CHECK-LABEL: define <4 x i1> @ceil_shift4_v4i32(
; CHECK-SAME: <4 x i32> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr <4 x i32> [[ARG0]], splat (i32 16)
-; CHECK-NEXT: [[REM:%.*]] = and <4 x i32> [[ARG0]], splat (i32 65535)
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <4 x i32> [[REM]], zeroinitializer
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <4 x i1> [[HAS_REM]] to <4 x i32>
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <4 x i32> [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <4 x i32> [[ARG0]], zeroinitializer
; CHECK-NEXT: ret <4 x i1> [[TMP1]]
;
%quot = lshr <4 x i32> %arg0, splat (i32 16)
@@ -247,12 +203,7 @@ define <4 x i1> @ceil_shift4_v4i32(<4 x i32> %arg0) {
define <8 x i1> @ceil_shift4_v8i16(<8 x i16> %arg0) {
; CHECK-LABEL: define <8 x i1> @ceil_shift4_v8i16(
; CHECK-SAME: <8 x i16> [[ARG0:%.*]]) {
-; CHECK-NEXT: [[QUOT:%.*]] = lshr <8 x i16> [[ARG0]], splat (i16 4)
-; CHECK-NEXT: [[REM:%.*]] = and <8 x i16> [[ARG0]], splat (i16 15)
-; CHECK-NEXT: [[HAS_REM:%.*]] = icmp ne <8 x i16> [[REM]], zeroinitializer
-; CHECK-NEXT: [[ZEXT_HAS_REM:%.*]] = zext <8 x i1> [[HAS_REM]] to <8 x i16>
-; CHECK-NEXT: [[QUOT_OR_REM:%.*]] = or <8 x i16> [[QUOT]], [[ZEXT_HAS_REM]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[QUOT_OR_REM]], zeroinitializer
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq <8 x i16> [[ARG0]], zeroinitializer
; CHECK-NEXT: ret <8 x i1> [[TMP1]]
;
%quot = lshr <8 x i16> %arg0, splat (i16 4)
@@ -324,4 +275,3 @@ define i1 @ceil_shift_not_add_or(i32 %arg0) {
%res = icmp eq i32 %quot_and_rem, 0
ret i1 %res
}
-
>From e854101347cc23125e3cb5783e6ceba97bc59f16 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sat, 21 Jun 2025 14:24:38 +0800
Subject: [PATCH 5/6] add `stripNullTest` and update `isKnownNonZero`
---
llvm/include/llvm/Analysis/ValueTracking.h | 5 +++
llvm/lib/Analysis/ValueTracking.cpp | 26 ++++++++++++++++
.../InstCombine/InstCombineCompares.cpp | 21 +++----------
.../test/Transforms/InstCombine/ceil-shift.ll | 31 +++++++++++++++++++
4 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index e215c90b5a72a..4596b2563c1d8 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -999,6 +999,11 @@ LLVM_ABI void
findValuesAffectedByCondition(Value *Cond, bool IsAssume,
function_ref<void(Value *)> InsertAffected);
+/// Returns the inner value X if the expression has the form f(X)
+/// where f(X) == 0 if and only if X == 0, otherwise returns nullptr.
+LLVM_ABI Value *stripNullTest(Value *V);
+LLVM_ABI const Value *stripNullTest(const Value *V);
+
} // end namespace llvm
#endif // LLVM_ANALYSIS_VALUETRACKING_H
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index a17417cb5189c..3df9af4bc95fe 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3521,6 +3521,9 @@ bool isKnownNonZero(const Value *V, const APInt &DemandedElts,
isKnownNonNullFromDominatingCondition(V, Q.CxtI, Q.DT))
return true;
+ if (const Value *Stripped = stripNullTest(V))
+ return isKnownNonZero(Stripped, DemandedElts, Q, Depth);
+
return false;
}
@@ -10170,3 +10173,26 @@ void llvm::findValuesAffectedByCondition(
}
}
}
+
+const Value *llvm::stripNullTest(const Value *V) {
+ // (X >> C) or/add (X & mask(C) != 0)
+ if (const auto *BO = dyn_cast<BinaryOperator>(V)) {
+ if (BO->getOpcode() == Instruction::Add ||
+ BO->getOpcode() == Instruction::Or) {
+ const Value *X;
+ const APInt *C1, *C2;
+ if (match(BO, m_c_BinOp(m_LShr(m_Value(X), m_APInt(C1)),
+ m_ZExt(m_SpecificICmp(
+ ICmpInst::ICMP_NE,
+ m_And(m_Deferred(X), m_LowBitMask(C2)),
+ m_Zero())))) &&
+ C2->popcount() == C1->getZExtValue())
+ return X;
+ }
+ }
+ return nullptr;
+}
+
+Value *llvm::stripNullTest(Value *V) {
+ return const_cast<Value *>(stripNullTest(const_cast<const Value *>(V)));
+}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 04d821ba6fa91..84de0dbe4b197 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1299,22 +1299,11 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// will fold to a constant elsewhere.
}
- // icmp eq/ne ((X >> C) or/add (X & mask(C) != 0)), 0 -> icmp eq/ne X, 0
- if (ICmpInst::isEquality(Pred)) {
- auto *BO = dyn_cast<BinaryOperator>(Cmp.getOperand(0));
- Value *X;
- const APInt *C1, *C2;
- if (BO &&
- (BO->getOpcode() == Instruction::Add ||
- BO->getOpcode() == Instruction::Or) &&
- match(BO, m_c_BinOp(m_LShr(m_Value(X), m_APInt(C1)),
- m_ZExt(m_SpecificICmp(
- ICmpInst::ICMP_NE,
- m_And(m_Deferred(X), m_LowBitMask(C2)),
- m_Zero())))) &&
- C2->popcount() == C1->getZExtValue())
- return new ICmpInst(Pred, X, ConstantInt::getNullValue(X->getType()));
- }
+ // (icmp eq/ne f(X), 0) -> (icmp eq/ne X, 0)
+ // where f(X) == 0 if and only if X == 0
+ if (ICmpInst::isEquality(Pred))
+ if (Value *Stripped = stripNullTest(Cmp.getOperand(0)))
+ return new ICmpInst(Pred, Stripped, Cmp.getOperand(1));
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/ceil-shift.ll b/llvm/test/Transforms/InstCombine/ceil-shift.ll
index 8c24d7474ab94..d4b37786bb26f 100644
--- a/llvm/test/Transforms/InstCombine/ceil-shift.ll
+++ b/llvm/test/Transforms/InstCombine/ceil-shift.ll
@@ -275,3 +275,34 @@ define i1 @ceil_shift_not_add_or(i32 %arg0) {
%res = icmp eq i32 %quot_and_rem, 0
ret i1 %res
}
+
+define i32 @ceil_shift_should_infer_ge_zero(i32 %x) {
+; CHECK-LABEL: define i32 @ceil_shift_should_infer_ge_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[COND_NOT:%.*]] = icmp eq i32 [[X]], 0
+; CHECK-NEXT: br i1 [[COND_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[X]], 20
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[X]], 1048575
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP5:%.*]] = add nuw nsw i32 [[TMP1]], [[TMP4]]
+; CHECK-NEXT: ret i32 [[TMP5]]
+; CHECK: [[IF_ELSE]]:
+; CHECK-NEXT: ret i32 0
+;
+ %cond = icmp ne i32 %x, 0
+ br i1 %cond, label %if.then, label %if.else
+
+if.then:
+ %quot = lshr i32 %x, 20
+ %rem = and i32 %x, 1048575
+ %has_rem = icmp ne i32 %rem, 0
+ %zext_has_rem = zext i1 %has_rem to i32
+ %ceil = add nuw nsw i32 %quot, %zext_has_rem
+ %max = call i32 @llvm.umax.i32(i32 %ceil, i32 1)
+ ret i32 %max
+
+if.else:
+ ret i32 0
+}
>From 689446ae5498b0762bff10fbf37a28b0fa1c1352 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sun, 22 Jun 2025 17:35:50 +0800
Subject: [PATCH 6/6] Update
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Co-authored-by: Yingwei Zheng <dtcxzyw2333 at gmail.com>
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 84de0dbe4b197..32c7ede47e893 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1303,7 +1303,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// where f(X) == 0 if and only if X == 0
if (ICmpInst::isEquality(Pred))
if (Value *Stripped = stripNullTest(Cmp.getOperand(0)))
- return new ICmpInst(Pred, Stripped, Cmp.getOperand(1));
+ return new ICmpInst(Pred, Stripped, Constant::getNullValue(Stripped->getType()));
return nullptr;
}
More information about the llvm-commits
mailing list