[llvm] [InstCombine] Fold zext(icmp ne X, C) to X - C when X is in (C, C+1) (PR #208914)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 11 06:35:56 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: muhammadshayanasim1-web
<details>
<summary>Changes</summary>
Addresses #<!-- -->207218
When the value of `X` is in `(CST, CST + 1)`, `zext(X != CST)`
is equivalent to `X - CST`. This fold removes the icmp/zext pair,
and replaces it with a single sub.
This PR only implments the first case from the issue (`src_zext_ne_lower_cst`), while
the remaining three are left as follow-up.
Added test coverage for scalar/vector cases, widening zext, multi-use
icmp, wrapped ranges, and negative cases.
---
Full diff: https://github.com/llvm/llvm-project/pull/208914.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp (+22)
- (modified) llvm/test/Transforms/InstCombine/zext.ll (+93)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index f30a7b5e891ab..bf7fda20f012c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1407,6 +1407,28 @@ Instruction *InstCombinerImpl::transformZExtICmp(ICmpInst *Cmp,
return replaceInstUsesWith(Zext, IntCast);
}
}
+
+ ConstantRange CR = computeConstantRange(Cmp->getOperand(0), false, SQ);
+
+ if (Cmp->isEquality() && !CR.isSizeLargerThan(2) && CR.contains(*Op1CV)) {
+
+ Value *In = Cmp->getOperand(0);
+ bool SkipTransform = In->getType() != Zext.getType() && !Cmp->hasOneUse();
+
+ if (CR.getLower().eq(*Op1CV) &&
+ Cmp->getPredicate() == ICmpInst::ICMP_NE && !SkipTransform) {
+
+ In = Builder.CreateSub(In, ConstantInt::get(In->getType(), *Op1CV));
+
+ if (In->getType() == Zext.getType())
+ return replaceInstUsesWith(Zext, In);
+
+ if (Cmp->hasOneUse()) {
+ Value *IntCast = Builder.CreateIntCast(In, Zext.getType(), false);
+ return replaceInstUsesWith(Zext, IntCast);
+ }
+ }
+ }
}
if (Cmp->isEquality()) {
diff --git a/llvm/test/Transforms/InstCombine/zext.ll b/llvm/test/Transforms/InstCombine/zext.ll
index 3fb1e77ae2335..7faab734c6623 100644
--- a/llvm/test/Transforms/InstCombine/zext.ll
+++ b/llvm/test/Transforms/InstCombine/zext.ll
@@ -1080,3 +1080,96 @@ define <2 x i8> @zext_or_trunc_nuw_vec(<2 x i8> %x, <2 x i4> %y) {
%zext = zext <2 x i4> %or to <2 x i8>
ret <2 x i8> %zext
}
+
+; zext (X != CST) --> X - CST if X in {CST, CST + 1}
+
+define i32 @zext_ne_lower_cst_sub(i32 range(i32 7, 9) %x) {
+; CHECK-LABEL: @zext_ne_lower_cst_sub(
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[X:%.*]], -7
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define <4 x i32> @zext_ne_lower_cst_sub_vec(<4 x i32> range(i32 7, 9) %x) {
+; CHECK-LABEL: @zext_ne_lower_cst_sub_vec(
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw <4 x i32> [[X:%.*]], splat (i32 -7)
+; CHECK-NEXT: ret <4 x i32> [[TMP2]]
+;
+ %1 = icmp ne <4 x i32> %x, splat(i32 7)
+ %2 = zext <4 x i1> %1 to <4 x i32>
+ ret <4 x i32> %2
+}
+
+define i64 @zext_ne_lower_cst_sub_widen(i32 range(i32 7, 9) %x) {
+; CHECK-LABEL: @zext_ne_lower_cst_sub_widen(
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[X:%.*]], -7
+; CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64
+; CHECK-NEXT: ret i64 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ %2 = zext i1 %1 to i64
+ ret i64 %2
+}
+
+define i32 @zext_ne_multiuse_icmp(i32 range(i32 7, 9) %x) {
+; CHECK-LABEL: @zext_ne_multiuse_icmp(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], 7
+; CHECK-NEXT: call void @use1(i1 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[X]], -7
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ call void @use1(i1 %1)
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @zext_ne_lower_wrapped(i32 range(i32 -1, 1) %x) {
+; CHECK-LABEL: @zext_ne_lower_wrapped(
+; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[X:%.*]], 1
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, -1
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+; Negative cases
+
+define i32 @neg_zext_ne_range_too_wide(i32 range(i32 7, 10) %x) {
+; CHECK-LABEL: @neg_zext_ne_range_too_wide(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], 7
+; CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i32 @neg_zext_ne_no_range(i32 %x) {
+; CHECK-LABEL: @neg_zext_ne_no_range(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], 7
+; CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ %2 = zext i1 %1 to i32
+ ret i32 %2
+}
+
+define i64 @neg_zext_ne_widen_multiuse_icmp(i32 range(i32 7, 9) %x) {
+; CHECK-LABEL: @neg_zext_ne_widen_multiuse_icmp(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], 7
+; CHECK-NEXT: call void @use1(i1 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = zext i1 [[TMP1]] to i64
+; CHECK-NEXT: ret i64 [[TMP2]]
+;
+ %1 = icmp ne i32 %x, 7
+ call void @use1(i1 %1)
+ %2 = zext i1 %1 to i64
+ ret i64 %2
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/208914
More information about the llvm-commits
mailing list