[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