[llvm] [ValueTracking] a u>= b and a = b + NonZero implies a != 0 (PR #219103)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 21:20:40 PDT 2026
https://github.com/ThePo1es created https://github.com/llvm/llvm-project/pull/219103
For an equality against zero, isImpliedCondition also looks through
umin(x, y) with y known non-zero, which is zero iff x is. Together this
folds the zero check of the libstdc++ vector growth length, which is
multi-use and so never simplified away.
Codegen for the _M_check_len + _M_allocate shape: https://godbolt.org/z/7ed3qc16z
Alive2: https://alive2.llvm.org/ce/z/4hqZxM
Fixes https://github.com/llvm/llvm-project/issues/200661.
>From a1987becb76cfecabf5f5ed4e94d6102a219c471 Mon Sep 17 00:00:00 2001
From: ThePo1es <choijinbeom9 at gmail.com>
Date: Wed, 26 Aug 2026 00:35:25 +0900
Subject: [PATCH 1/2] [InstCombine][SimplifyCFG] Add tests for #200661 (NFC)
---
.../icmp-select-implied-nonzero.ll | 273 +++++++++++
.../SimplifyCFG/implied-nonzero-check.ll | 454 ++++++++++++++++++
2 files changed, 727 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
create mode 100644 llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
diff --git a/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll b/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
new file mode 100644
index 0000000000000..03a3182c02efe
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
@@ -0,0 +1,273 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; The zero check of the libstdc++ vector growth length (issue #200661):
+; %add u>= %size with %add = %size + umax(%size, 1) means %add != 0.
+
+declare void @use(i64)
+declare void @llvm.assume(i1)
+declare void @use32(i32)
+declare void @usev(<2 x i64>)
+
+; %len is multi-use: also passed to @use (models operator new in push_back).
+define i1 @check_len_multiuse_ne(i64 %size) {
+; CHECK-LABEL: @check_len_multiuse_ne(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+define i1 @check_len_multiuse_eq(i64 %size) {
+; CHECK-LABEL: @check_len_multiuse_eq(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp eq i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp eq i64 %len, 0
+ ret i1 %r
+}
+
+; Commuted add operands.
+define i1 @check_len_multiuse_commuted(i64 %size) {
+; CHECK-LABEL: @check_len_multiuse_commuted(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[SIZE]], [[MAX]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %size, %max
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+; Different bit width and clamp constant.
+define i1 @check_len_multiuse_i32(i32 %size) {
+; CHECK-LABEL: @check_len_multiuse_i32(
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[SIZE:%.*]], i32 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i32 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[ADD]], i32 2147483647)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i32 2147483647, i32 [[MIN]]
+; CHECK-NEXT: call void @use32(i32 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i32 @llvm.umax.i32(i32 %size, i32 1)
+ %add = add i32 %max, %size
+ %ov = icmp ult i32 %add, %size
+ %min = call i32 @llvm.umin.i32(i32 %add, i32 2147483647)
+ %len = select i1 %ov, i32 2147483647, i32 %min
+ call void @use32(i32 %len)
+ %r = icmp ne i32 %len, 0
+ ret i1 %r
+}
+
+; Negative test: with a dynamic growth amount %n, %len == 0 is possible
+; (%size == 0, %n == 0), so the compare must NOT fold to a constant.
+define i1 @check_len_dynamic_n_negative(i64 %size, i64 %n) {
+; CHECK-LABEL: @check_len_dynamic_n_negative(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 [[N:%.*]])
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 %n)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+; umin(%add, 0) folds to 0 before isImpliedCondition sees it; sanity check
+; that nothing miscompiles. The C != 0 guard itself is exercised by
+; SimplifyCFG/implied-nonzero-check.ll.
+define i1 @check_len_zero_clamp_folds_early(i64 %size) {
+; CHECK-LABEL: @check_len_zero_clamp_folds_early(
+; CHECK-NEXT: [[OV:%.*]] = icmp slt i64 [[SIZE:%.*]], 0
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 0
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: ret i1 [[OV]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 0)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+; Vector splat variant.
+define <2 x i1> @check_len_multiuse_vec(<2 x i64> %size) {
+; CHECK-LABEL: @check_len_multiuse_vec(
+; CHECK-NEXT: [[MAX:%.*]] = call <2 x i64> @llvm.umax.v2i64(<2 x i64> [[SIZE:%.*]], <2 x i64> splat (i64 1))
+; CHECK-NEXT: [[ADD:%.*]] = add <2 x i64> [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult <2 x i64> [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call <2 x i64> @llvm.umin.v2i64(<2 x i64> [[ADD]], <2 x i64> splat (i64 9223372036854775807))
+; CHECK-NEXT: [[LEN:%.*]] = select <2 x i1> [[OV]], <2 x i64> splat (i64 9223372036854775807), <2 x i64> [[MIN]]
+; CHECK-NEXT: call void @usev(<2 x i64> [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i64> [[LEN]], zeroinitializer
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %size, <2 x i64> splat (i64 1))
+ %add = add <2 x i64> %max, %size
+ %ov = icmp ult <2 x i64> %add, %size
+ %min = call <2 x i64> @llvm.umin.v2i64(<2 x i64> %add, <2 x i64> splat (i64 9223372036854775807))
+ %len = select <2 x i1> %ov, <2 x i64> splat (i64 9223372036854775807), <2 x i64> %min
+ call void @usev(<2 x i64> %len)
+ %r = icmp ne <2 x i64> %len, zeroinitializer
+ ret <2 x i1> %r
+}
+
+; The premise can also come from an assume rather than a select or a branch.
+define i1 @check_len_assume(i64 %size) {
+; CHECK-LABEL: @check_len_assume(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OK:%.*]] = icmp uge i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[OK]])
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[R:%.*]] = icmp eq i64 [[ADD]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ok = icmp uge i64 %add, %size
+ call void @llvm.assume(i1 %ok)
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ call void @use(i64 %min)
+ %r = icmp eq i64 %min, 0
+ ret i1 %r
+}
+
+; Strict form of the overflow check.
+define i1 @check_len_multiuse_strict(i64 %size) {
+; CHECK-LABEL: @check_len_multiuse_strict(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV_NOT:%.*]] = icmp ugt i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV_NOT]], i64 [[MIN]], i64 9223372036854775807
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ule i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+; Negative test: a signed compare does not say anything about wrapping.
+define i1 @check_len_signed_premise_negative(i64 %size) {
+; CHECK-LABEL: @check_len_signed_premise_negative(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp slt i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp slt i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
+
+; Negative test: one clamp lane is zero, so that lane of %min can be zero.
+define <2 x i1> @check_len_vec_zero_lane_negative(<2 x i64> %size) {
+; CHECK-LABEL: @check_len_vec_zero_lane_negative(
+; CHECK-NEXT: [[MAX:%.*]] = call <2 x i64> @llvm.umax.v2i64(<2 x i64> [[SIZE:%.*]], <2 x i64> splat (i64 1))
+; CHECK-NEXT: [[ADD:%.*]] = add <2 x i64> [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult <2 x i64> [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call <2 x i64> @llvm.umin.v2i64(<2 x i64> [[ADD]], <2 x i64> <i64 9223372036854775807, i64 0>)
+; CHECK-NEXT: [[LEN:%.*]] = select <2 x i1> [[OV]], <2 x i64> splat (i64 9223372036854775807), <2 x i64> [[MIN]]
+; CHECK-NEXT: call void @usev(<2 x i64> [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i64> [[LEN]], zeroinitializer
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %size, <2 x i64> splat (i64 1))
+ %add = add <2 x i64> %max, %size
+ %ov = icmp ult <2 x i64> %add, %size
+ %min = call <2 x i64> @llvm.umin.v2i64(<2 x i64> %add, <2 x i64> <i64 9223372036854775807, i64 0>)
+ %len = select <2 x i1> %ov, <2 x i64> splat (i64 9223372036854775807), <2 x i64> %min
+ call void @usev(<2 x i64> %len)
+ %r = icmp ne <2 x i64> %len, zeroinitializer
+ ret <2 x i1> %r
+}
+
+; Real clang -O2 codegen flags this add nsw (verified against actual
+; std::vector::push_back output); the fold must not depend on that.
+define i1 @check_len_nsw_flag(i64 %size) {
+; CHECK-LABEL: @check_len_nsw_flag(
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
+; CHECK-NEXT: call void @use(i64 [[LEN]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add nsw i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ %len = select i1 %ov, i64 9223372036854775807, i64 %min
+ call void @use(i64 %len)
+ %r = icmp ne i64 %len, 0
+ ret i1 %r
+}
diff --git a/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll b/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
new file mode 100644
index 0000000000000..d526596b6c3fb
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
@@ -0,0 +1,454 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+
+; Branch form of issue #200661: on the non-overflow path, the zero check of
+; the clamped length is dominated by !(add u< size) and must fold away.
+
+declare void @use(i64)
+declare void @sink_zero()
+declare void @sink_nonzero()
+
+define void @zero_check_dominated(i64 %size) {
+; CHECK-LABEL: @zero_check_dominated(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ call void @use(i64 %min)
+ %z = icmp eq i64 %min, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+declare void @use8(i8)
+declare i8 @llvm.umin.i8(i8, i8)
+
+; The dominating condition is about the umin itself, not about its operand
+; %x. Looking through the umin gives nothing here, so the implication must
+; still be found on the original operands and %c2 must fold.
+define void @umin_premise_still_folds(i8 %x) {
+; CHECK-LABEL: @umin_premise_still_folds(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 5)
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[U]], 2
+; CHECK-NEXT: br i1 [[C1]], label [[T:%.*]], label [[COMMON_RET:%.*]]
+; CHECK: t:
+; CHECK-NEXT: call void @use8(i8 [[U]])
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+;
+entry:
+ %u = call i8 @llvm.umin.i8(i8 %x, i8 5)
+ %c1 = icmp ugt i8 %u, 2
+ br i1 %c1, label %t, label %f
+
+t:
+ call void @use8(i8 %u)
+ %c2 = icmp ne i8 %u, 0
+ br i1 %c2, label %nz, label %z
+
+nz:
+ call void @sink_nonzero()
+ ret void
+
+z:
+ call void @sink_zero()
+ ret void
+
+f:
+ ret void
+}
+
+; Negative: umin with a ZERO clamp constant. umin(%add, 0) is always 0, so
+; the zero check is always TRUE; looking through the umin and applying the
+; nonzero-add implication would derive FALSE, a miscompile. The requirement
+; must reject this, leaving the branch alone.
+define void @zero_clamp_guard(i64 %size) {
+; CHECK-LABEL: @zero_clamp_guard(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 0)
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 0)
+ call void @use(i64 %min)
+ %z = icmp eq i64 %min, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; No umin involved: %add u>= %x with %add = %x + %nz, %nz != 0 implies
+; %add != 0, so the zero check folds.
+define void @add_nonzero_only(i64 %x, i64 %y) {
+; CHECK-LABEL: @add_nonzero_only(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[NZ:%.*]] = or i64 [[Y:%.*]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X:%.*]], [[NZ]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[X]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: call void @use(i64 [[ADD]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[ADD]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %nz = or i64 %y, 1
+ %add = add i64 %x, %nz
+ %ov = icmp ult i64 %add, %x
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ call void @use(i64 %add)
+ %z = icmp eq i64 %add, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; Same as above with the premise written the other way around (%x u<= %add).
+define void @add_nonzero_swapped_premise(i64 %x, i64 %y) {
+; CHECK-LABEL: @add_nonzero_swapped_premise(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[NZ:%.*]] = or i64 [[Y:%.*]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X:%.*]], [[NZ]]
+; CHECK-NEXT: [[OK:%.*]] = icmp ule i64 [[X]], [[ADD]]
+; CHECK-NEXT: br i1 [[OK]], label [[NORMAL:%.*]], label [[COMMON_RET:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: call void @use(i64 [[ADD]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[ADD]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %nz = or i64 %y, 1
+ %add = add i64 %x, %nz
+ %ok = icmp ule i64 %x, %add
+ br i1 %ok, label %normal, label %overflow
+
+overflow:
+ ret void
+
+normal:
+ call void @use(i64 %add)
+ %z = icmp eq i64 %add, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; Negative for the nonzero-add implication: the addend is not known non-zero,
+; so %add may be 0.
+define void @add_maybe_zero_negative(i64 %x, i64 %y) {
+; CHECK-LABEL: @add_maybe_zero_negative(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[X]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: call void @use(i64 [[ADD]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[ADD]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %add = add i64 %x, %y
+ %ov = icmp ult i64 %add, %x
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ call void @use(i64 %add)
+ %z = icmp eq i64 %add, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; Premise written as %size u<= %add; the umin is looked through before the
+; operands are normalized.
+define void @umin_swapped_premise(i64 %size) {
+; CHECK-LABEL: @umin_swapped_premise(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OK:%.*]] = icmp ule i64 [[SIZE]], [[ADD]]
+; CHECK-NEXT: br i1 [[OK]], label [[NORMAL:%.*]], label [[COMMON_RET:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ok = icmp ule i64 %size, %add
+ br i1 %ok, label %normal, label %overflow
+
+overflow:
+ ret void
+
+normal:
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 9223372036854775807)
+ call void @use(i64 %min)
+ %z = icmp eq i64 %min, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; Commuted umin operands (SimplifyCFG sees non-canonical IR).
+define void @umin_commuted(i64 %size) {
+; CHECK-LABEL: @umin_commuted(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 9223372036854775807, i64 [[ADD]])
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ %min = call i64 @llvm.umin.i64(i64 9223372036854775807, i64 %add)
+ call void @use(i64 %min)
+ %z = icmp eq i64 %min, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
+
+; The premise is about the umin itself, but its operand %x also appears in
+; it. The query on %x alone (%u s< %x implies %x != 0?) cannot be decided,
+; so the implication must be retried on the original operands, where %x in
+; [-128, 0] and %u s< %x give %u != 0.
+define void @umin_signed_premise_range(i8 range(i8 -128, 1) %x) {
+; CHECK-LABEL: @umin_signed_premise_range(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[U:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 -56)
+; CHECK-NEXT: [[P:%.*]] = icmp slt i8 [[U]], [[X]]
+; CHECK-NEXT: br i1 [[P]], label [[NONZERO:%.*]], label [[EXIT:%.*]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ %u = call i8 @llvm.umin.i8(i8 %x, i8 -56)
+ %p = icmp slt i8 %u, %x
+ br i1 %p, label %check, label %exit
+
+check:
+ %iszero = icmp eq i8 %u, 0
+ br i1 %iszero, label %iszero_bb, label %nonzero
+
+iszero_bb:
+ call void @sink_zero()
+ br label %exit
+
+nonzero:
+ call void @sink_nonzero()
+ br label %exit
+
+exit:
+ ret void
+}
+
+; The clamp operand only needs to be known non-zero, not a constant.
+define void @umin_nonconst_clamp(i64 %size, i64 %y) {
+; CHECK-LABEL: @umin_nonconst_clamp(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SIZE:%.*]], i64 1)
+; CHECK-NEXT: [[ADD:%.*]] = add i64 [[MAX]], [[SIZE]]
+; CHECK-NEXT: [[OV:%.*]] = icmp ult i64 [[ADD]], [[SIZE]]
+; CHECK-NEXT: br i1 [[OV]], label [[COMMON_RET:%.*]], label [[NORMAL:%.*]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: normal:
+; CHECK-NEXT: [[CLAMP:%.*]] = or i64 [[Y:%.*]], 1
+; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 [[CLAMP]])
+; CHECK-NEXT: call void @use(i64 [[MIN]])
+; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
+; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
+; CHECK: iszero:
+; CHECK-NEXT: call void @sink_zero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: nonzero:
+; CHECK-NEXT: call void @sink_nonzero()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+entry:
+ %max = call i64 @llvm.umax.i64(i64 %size, i64 1)
+ %add = add i64 %max, %size
+ %ov = icmp ult i64 %add, %size
+ br i1 %ov, label %overflow, label %normal
+
+overflow:
+ ret void
+
+normal:
+ %clamp = or i64 %y, 1
+ %min = call i64 @llvm.umin.i64(i64 %add, i64 %clamp)
+ call void @use(i64 %min)
+ %z = icmp eq i64 %min, 0
+ br i1 %z, label %iszero, label %nonzero
+
+iszero:
+ call void @sink_zero()
+ ret void
+
+nonzero:
+ call void @sink_nonzero()
+ ret void
+}
>From 0281d4e8313abc2f4d232a1038c27583659554a4 Mon Sep 17 00:00:00 2001
From: ThePo1es <choijinbeom9 at gmail.com>
Date: Wed, 26 Aug 2026 00:35:48 +0900
Subject: [PATCH 2/2] [ValueTracking] a u>= b and a = b + NonZero implies a !=
0
For an equality against zero, isImpliedCondition also looks through
umin(x, y) with y known non-zero, which is zero iff x is. Together this
folds the zero check of the libstdc++ vector growth length, which is
multi-use and so never simplified away.
Alive2: https://alive2.llvm.org/ce/z/4hqZxM
Fixes https://github.com/llvm/llvm-project/issues/200661.
---
llvm/lib/Analysis/ValueTracking.cpp | 27 +++++++++++++-
.../icmp-select-implied-nonzero.ll | 24 +++++--------
.../SimplifyCFG/implied-nonzero-check.ll | 36 -------------------
3 files changed, 34 insertions(+), 53 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f4ed5e07038da..41477b97e9cbe 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9801,6 +9801,8 @@ isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
if (!LHSIsTrue)
LPred = ICmpInst::getInverseCmpPredicate(LPred);
+ SimplifyQuery SQ(DL);
+
// We can have non-canonical operands, so try to normalize any common operand
// to L0/R0.
if (L0 == R1) {
@@ -9829,7 +9831,6 @@ isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
// further constraint the constant ranges. At the moment this leads to
// several regressions related to not transforming `multi_use(A + C0) eq/ne
// C1` (see discussion: D58633).
- SimplifyQuery SQ(DL);
ConstantRange LCR = computeConstantRange(L1, ICmpInst::isSigned(LPred), SQ,
MaxAnalysisRecursionDepth - 1);
ConstantRange RCR = computeConstantRange(R1, ICmpInst::isSigned(RPred), SQ,
@@ -9887,6 +9888,15 @@ isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
return RPred.dropSameSign() == ICmpInst::ICMP_NE;
}
+ // L0 u>= L1 with L0 = L1 + Addend and Addend != 0 implies L0 != 0:
+ // If L0 == 0 then L1 u<= 0, i.e. L1 == 0, and then L0 == Addend != 0.
+ const Value *Addend;
+ if (LPred == ICmpInst::ICMP_UGE && L0 == R0 && ICmpInst::isEquality(RPred) &&
+ match(R1, m_Zero()) &&
+ match(L0, m_c_Add(m_Specific(L1), m_Value(Addend))) &&
+ isKnownNonZero(Addend, SQ))
+ return RPred.dropSameSign() == ICmpInst::ICMP_NE;
+
// L0 = R0 = L1 + R1, L0 >=u L1 implies R0 >=u R1, L0 <u L1 implies R0 <u R1
if (L0 == R0 &&
(LPred == ICmpInst::ICMP_ULT || LPred == ICmpInst::ICMP_UGE) &&
@@ -10017,6 +10027,21 @@ llvm::isImpliedCondition(const Value *LHS, CmpPredicate RHSPred,
if (match(LHS, m_Not(m_Value(LHS))))
LHSIsTrue = !LHSIsTrue;
+ // umin(X, Y) with Y != 0 is zero iff X is zero, so for an equality against
+ // zero it is enough to look at X. Fall through with the original operands
+ // otherwise, e.g. when LHS is about the umin itself.
+ const Value *X, *Y;
+ if (ICmpInst::isEquality(RHSPred) && match(RHSOp1, m_Zero()) &&
+ match(RHSOp0, m_UMin(m_Value(X), m_Value(Y)))) {
+ SimplifyQuery SQ(DL);
+ if (isKnownNonZero(X, SQ))
+ std::swap(X, Y);
+ if (isKnownNonZero(Y, SQ))
+ if (std::optional<bool> Res = isImpliedCondition(
+ LHS, RHSPred, X, RHSOp1, DL, LHSIsTrue, Depth + 1))
+ return Res;
+ }
+
// Both LHS and RHS are icmps.
if (RHSOp0->getType()->getScalarType()->isIntOrPtrTy()) {
CmpPredicate LHSPred;
diff --git a/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll b/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
index 03a3182c02efe..3cc7b370520ff 100644
--- a/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-select-implied-nonzero.ll
@@ -18,8 +18,7 @@ define i1 @check_len_multiuse_ne(i64 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
; CHECK-NEXT: call void @use(i64 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 true
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add i64 %max, %size
@@ -39,8 +38,7 @@ define i1 @check_len_multiuse_eq(i64 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
; CHECK-NEXT: call void @use(i64 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp eq i64 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 false
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add i64 %max, %size
@@ -61,8 +59,7 @@ define i1 @check_len_multiuse_commuted(i64 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
; CHECK-NEXT: call void @use(i64 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 true
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add i64 %size, %max
@@ -83,8 +80,7 @@ define i1 @check_len_multiuse_i32(i32 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[ADD]], i32 2147483647)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i32 2147483647, i32 [[MIN]]
; CHECK-NEXT: call void @use32(i32 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 true
;
%max = call i32 @llvm.umax.i32(i32 %size, i32 1)
%add = add i32 %max, %size
@@ -148,8 +144,7 @@ define <2 x i1> @check_len_multiuse_vec(<2 x i64> %size) {
; CHECK-NEXT: [[MIN:%.*]] = call <2 x i64> @llvm.umin.v2i64(<2 x i64> [[ADD]], <2 x i64> splat (i64 9223372036854775807))
; CHECK-NEXT: [[LEN:%.*]] = select <2 x i1> [[OV]], <2 x i64> splat (i64 9223372036854775807), <2 x i64> [[MIN]]
; CHECK-NEXT: call void @usev(<2 x i64> [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i64> [[LEN]], zeroinitializer
-; CHECK-NEXT: ret <2 x i1> [[R]]
+; CHECK-NEXT: ret <2 x i1> splat (i1 true)
;
%max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %size, <2 x i64> splat (i64 1))
%add = add <2 x i64> %max, %size
@@ -170,8 +165,7 @@ define i1 @check_len_assume(i64 %size) {
; CHECK-NEXT: call void @llvm.assume(i1 [[OK]])
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: call void @use(i64 [[MIN]])
-; CHECK-NEXT: [[R:%.*]] = icmp eq i64 [[ADD]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 false
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add i64 %max, %size
@@ -192,8 +186,7 @@ define i1 @check_len_multiuse_strict(i64 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV_NOT]], i64 [[MIN]], i64 9223372036854775807
; CHECK-NEXT: call void @use(i64 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 true
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add i64 %max, %size
@@ -259,8 +252,7 @@ define i1 @check_len_nsw_flag(i64 %size) {
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: [[LEN:%.*]] = select i1 [[OV]], i64 9223372036854775807, i64 [[MIN]]
; CHECK-NEXT: call void @use(i64 [[LEN]])
-; CHECK-NEXT: [[R:%.*]] = icmp ne i64 [[LEN]], 0
-; CHECK-NEXT: ret i1 [[R]]
+; CHECK-NEXT: ret i1 true
;
%max = call i64 @llvm.umax.i64(i64 %size, i64 1)
%add = add nsw i64 %max, %size
diff --git a/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll b/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
index d526596b6c3fb..3da16449c72bc 100644
--- a/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
+++ b/llvm/test/Transforms/SimplifyCFG/implied-nonzero-check.ll
@@ -20,12 +20,6 @@ define void @zero_check_dominated(i64 %size) {
; CHECK: normal:
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: call void @use(i64 [[MIN]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
@@ -156,12 +150,6 @@ define void @add_nonzero_only(i64 %x, i64 %y) {
; CHECK-NEXT: ret void
; CHECK: normal:
; CHECK-NEXT: call void @use(i64 [[ADD]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[ADD]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
@@ -200,12 +188,6 @@ define void @add_nonzero_swapped_premise(i64 %x, i64 %y) {
; CHECK-NEXT: ret void
; CHECK: normal:
; CHECK-NEXT: call void @use(i64 [[ADD]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[ADD]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
@@ -289,12 +271,6 @@ define void @umin_swapped_premise(i64 %size) {
; CHECK: normal:
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 9223372036854775807)
; CHECK-NEXT: call void @use(i64 [[MIN]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
@@ -335,12 +311,6 @@ define void @umin_commuted(i64 %size) {
; CHECK: normal:
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 9223372036854775807, i64 [[ADD]])
; CHECK-NEXT: call void @use(i64 [[MIN]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
@@ -419,12 +389,6 @@ define void @umin_nonconst_clamp(i64 %size, i64 %y) {
; CHECK-NEXT: [[CLAMP:%.*]] = or i64 [[Y:%.*]], 1
; CHECK-NEXT: [[MIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 [[CLAMP]])
; CHECK-NEXT: call void @use(i64 [[MIN]])
-; CHECK-NEXT: [[Z:%.*]] = icmp eq i64 [[MIN]], 0
-; CHECK-NEXT: br i1 [[Z]], label [[ISZERO:%.*]], label [[NONZERO:%.*]]
-; CHECK: iszero:
-; CHECK-NEXT: call void @sink_zero()
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: nonzero:
; CHECK-NEXT: call void @sink_nonzero()
; CHECK-NEXT: br label [[COMMON_RET]]
;
More information about the llvm-commits
mailing list