[llvm-branch-commits] [llvm] [SCEV] Derive x >=s 0 from x <u y and y <=u 2^(n-1) (PR #231631)

Dmitry Makogon via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 22 15:01:33 PDT 2026


https://github.com/d-makogon updated https://github.com/llvm/llvm-project/pull/231631

>From a7da425a6670071d0209e19ec6955355268a9b93 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 12:37:47 +0000
Subject: [PATCH 1/3] [SCEV][IndVars][NFC] Add tests for x >=s 0 from x <u y
 and y <=u 2^(n-1)

Let n be the bit width of x and y. If x <u y and y <=u 2^(n-1), then
x >=s 0. SCEV can't prove this yet.

Add tests for it.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 .../implied-nonneg-from-unsigned-bound.ll     | 287 ++++++++++++++++++
 1 file changed, 287 insertions(+)
 create mode 100644 llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll

diff --git a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
new file mode 100644
index 0000000000000..4e982e4ab0603
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
@@ -0,0 +1,287 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=indvars -S < %s | FileCheck %s
+
+; If x <u y and y <=u INT_MIN, then x >=s 0.
+; If x <=u y and y <=u INT_MAX, then x >=s 0.
+; In these tests the guard compares y with x, and the exit compares
+; smax(x, w) with a constant.
+;
+; FIXME: SCEV cannot prove this yet, fold the exits to constants where possible.
+
+target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
+
+; If y >u x and y <=u INT_MIN, then x >=s 0, which implies smax(x, w) >s -1.
+define void @ugt_smin(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @ugt_smin(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !2
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp slt i32 %m, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >=u x and y <=u INT_MAX, then x >=s 0, which implies smax(x, w) >s -1.
+define void @uge(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @uge(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !1
+  %c = icmp uge i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp slt i32 %m, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <u INT_MAX, then x >=s 0, which implies smax(x, w) >s -1.
+define void @ugt_sle(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @ugt_sle(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[M]], -1
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp sle i32 %m, -1
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <u INT_MAX, then x >=s 0, which implies smax(x, w) >=s 0.
+define void @ugt_sge(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @ugt_sge(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[M]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp sge i32 %m, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <u INT_MAX, then x >=s 0, which implies smax(x, w) >=s -5.
+define void @ugt_weaker(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @ugt_weaker(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], -5
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp slt i32 %m, -5
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <=u INT_MIN + 1, x may be INT_MIN. So smax(x, w) <s 0 cannot
+; be folded.
+define void @neg_ugt_smin_plus_1(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @neg_ugt_smin_plus_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG3:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !3
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %w)
+  %q = icmp slt i32 %m, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+declare void @side()
+
+; [0, INT_MAX)
+!0 = !{i32 0, i32 2147483647}
+; [0, INT_MAX]
+!1 = !{i32 0, i32 -2147483648}
+; [0, INT_MIN] as unsigned
+!2 = !{i32 0, i32 -2147483647}
+; [0, INT_MIN + 1] as unsigned
+!3 = !{i32 0, i32 -2147483646}
+;.
+; CHECK: [[RNG0]] = !{i32 0, i32 -2147483647}
+; CHECK: [[RNG1]] = !{i32 0, i32 -2147483648}
+; CHECK: [[RNG2]] = !{i32 0, i32 2147483647}
+; CHECK: [[RNG3]] = !{i32 0, i32 -2147483646}
+;.

>From 90e919d8a2afa33f6997683e8e6dc29b7cebe9f0 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 12:38:10 +0000
Subject: [PATCH 2/3] [SCEV][IndVars][NFC] Add tests for x >=s 0 from x <u y
 with swapped operands

Let n be the bit width of x and y. If x <u y and y <=u 2^(n-1), then
x >=s 0. SCEV can't prove this yet when the operands of the compare to
prove are in the opposite order to the guard, e.g. 0 <=s x with
x <u y known.

Add tests for it.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 ...lied-nonneg-from-unsigned-bound-swapped.ll | 1196 +++++++++++++++++
 1 file changed, 1196 insertions(+)
 create mode 100644 llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll

diff --git a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll
new file mode 100644
index 0000000000000..e380f40b15113
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll
@@ -0,0 +1,1196 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=indvars -S < %s | FileCheck %s
+
+; If x <u y and y <=u INT_MIN, then x >=s 0.
+; If x <=u y and y <=u INT_MAX, then x >=s 0.
+; The guards and the exit conditions are written with different predicates.
+; In most tests the operands of the exit condition are in the opposite order
+; to the guard.
+;
+; FIXME: SCEV cannot prove this yet, fold the exits to constants where possible.
+
+target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
+
+; If x <u y and y <u INT_MAX, then x >=s 0.
+define void @ult(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <=u INT_MIN, then x >=s 0.
+define void @ult_smin(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_smin(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !2
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <=u y and y <=u INT_MAX, then x >=s 0.
+define void @ule(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ule(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !1
+  %c = icmp ule i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <u INT_MAX, then x >=s 0.
+define void @ugt(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ugt(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <=u INT_MIN, then x >=s 0.
+define void @ugt_smin(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ugt_smin(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !2
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >=u x and y <=u INT_MAX, then x >=s 0.
+define void @uge(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @uge(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !1
+  %c = icmp uge i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >s -1.
+define void @ult_query_sle(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_sle(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[X]], -1
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sle i32 %x, -1
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies 0 <=s x.
+define void @ult_query_sgt_swapped(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_sgt_swapped(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 0, [[X]]
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sgt i32 0, %x
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0.
+define void @ult_query_sge(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_sge(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sge i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >s -1.
+define void @ult_query_sgt(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_sgt(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sgt i32 %x, -1
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies -1 <s x.
+define void @ult_query_slt_swapped(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_slt_swapped(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 -1, [[X]]
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 -1, %x
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies 0 <=s x.
+define void @ult_query_sle_swapped(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_sle_swapped(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 0, [[X]]
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sle i32 0, %x
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <u INT_MAX, then x >=s 0.
+define void @ugt_query_sge(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ugt_query_sge(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sge i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >=u x and y <=u INT_MAX, then x >=s 0, which implies x >s -1.
+define void @uge_query_sgt(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @uge_query_sgt(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !1
+  %c = icmp uge i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sgt i32 %x, -1
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >=s z
+; for any z <=s 0.
+define void @ult_query_nonpos(i32 %x, ptr %p, ptr %pz) {
+; CHECK-LABEL: define void @ult_query_nonpos(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]], ptr [[PZ:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[Z:%.*]] = load i32, ptr [[PZ]], align 4, !range [[RNG3:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], [[Z]]
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %z = load i32, ptr %pz, !range !4
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, %z
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >=s -5.
+define void @ult_query_const(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_const(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], -5
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, -5
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u 1000, then x >=s 0. Here y <u 1000 is known only from
+; a dominating guard.
+define void @ult_y_guarded(i32 %x, i32 %y) {
+; CHECK-LABEL: define void @ult_y_guarded(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y_OK:%.*]] = icmp ult i32 [[Y]], 1000
+; CHECK-NEXT:    br i1 [[Y_OK]], label %[[GUARD:.*]], label %[[EXIT:.*]]
+; CHECK:       [[GUARD]]:
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y.ok = icmp ult i32 %y, 1000
+  br i1 %y.ok, label %guard, label %exit
+guard:
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %guard ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, which implies x + 1 >=s 1.
+define void @ult_query_offset(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @ult_query_offset(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[X_1:%.*]] = add nsw i32 [[X]], 1
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X_1]], 1
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %x.1 = add nsw i32 %x, 1
+  %q = icmp slt i32 %x.1, 1
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y, x may be negative, e.g. x = -2 and y = -1. So x <s 0 cannot be
+; folded.
+define void @neg_ult_unknown_y(i32 %x, i32 %y) {
+; CHECK-LABEL: define void @neg_ult_unknown_y(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <=u INT_MIN + 1, x may be INT_MIN. So x <s 0 cannot be folded.
+define void @neg_ult_smin_plus_1(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_ult_smin_plus_1(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG4:![0-9]+]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !3
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >u x and y <=u INT_MIN + 1, x may be INT_MIN. So x <s 0 cannot be folded.
+define void @neg_ugt_smin_plus_1(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_ugt_smin_plus_1(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG4]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !3
+  %c = icmp ugt i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <=u y and y <=u INT_MIN, x may be INT_MIN. So x <s 0 cannot be folded.
+define void @neg_ule_smin(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_ule_smin(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !2
+  %c = icmp ule i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y >=u x and y <=u INT_MIN, x may be INT_MIN. So x <s 0 cannot be folded.
+define void @neg_uge_smin(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_uge_smin(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !2
+  %c = icmp uge i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <s y and y >=s 0, x may be negative, e.g. x = -1 and y = 0. So x <s 0
+; cannot be folded.
+define void @neg_slt(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_slt(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp slt i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If w <u y and y <u INT_MAX, then w >=s 0, but x may be negative. So x <s 0
+; cannot be folded.
+define void @neg_ult_other(i32 %x, i32 %w, ptr %p) {
+; CHECK-LABEL: define void @neg_ult_other(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[W:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[W]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %w, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If y <u x and y <u INT_MAX, x may be negative, e.g. x = -1 and y = 0. So
+; x <s 0 cannot be folded.
+define void @neg_ult_reversed(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_ult_reversed(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[Y]], [[X]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %y, %x
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp slt i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, but x may be 0. So x <=s 0 cannot
+; be folded.
+define void @neg_query_sgt_zero(i32 %x, ptr %p) {
+; CHECK-LABEL: define void @neg_query_sgt_zero(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sle i32 %x, 0
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+; If x <u y and y <u INT_MAX, then x >=s 0, but x and z may both be 0. So
+; x <=s z cannot be folded.
+define void @neg_query_nonpos(i32 %x, ptr %p, ptr %pz) {
+; CHECK-LABEL: define void @neg_query_nonpos(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]], ptr [[PZ:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[Y:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]]
+; CHECK-NEXT:    [[Z:%.*]] = load i32, ptr [[PZ]], align 4, !range [[RNG3]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[X]], [[Z]]
+; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK:       [[LATCH]]:
+; CHECK-NEXT:    call void @side()
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[LOOP]], label %[[EXIT_LOOPEXIT]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %y = load i32, ptr %p, !range !0
+  %z = load i32, ptr %pz, !range !4
+  %c = icmp ult i32 %x, %y
+  br i1 %c, label %loop, label %exit
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
+  %q = icmp sle i32 %x, %z
+  br i1 %q, label %exit, label %latch
+latch:
+  call void @side()
+  %i.next = add nuw nsw i32 %i, 1
+  %ec = icmp ult i32 %i.next, 100
+  br i1 %ec, label %loop, label %exit
+exit:
+  ret void
+}
+
+declare void @side()
+
+; [0, INT_MAX)
+!0 = !{i32 0, i32 2147483647}
+; [0, INT_MAX]
+!1 = !{i32 0, i32 -2147483648}
+; [0, INT_MIN] as unsigned
+!2 = !{i32 0, i32 -2147483647}
+; [0, INT_MIN + 1] as unsigned
+!3 = !{i32 0, i32 -2147483646}
+; [INT_MIN, 0]
+!4 = !{i32 -2147483648, i32 1}
+;.
+; CHECK: [[RNG0]] = !{i32 0, i32 2147483647}
+; CHECK: [[RNG1]] = !{i32 0, i32 -2147483647}
+; CHECK: [[RNG2]] = !{i32 0, i32 -2147483648}
+; CHECK: [[RNG3]] = !{i32 -2147483648, i32 1}
+; CHECK: [[RNG4]] = !{i32 0, i32 -2147483646}
+;.

>From f34bc42622e4c690cade8e8405946c6e9544acd0 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 12:39:21 +0000
Subject: [PATCH 3/3] [SCEV] Derive x >=s 0 from x <u y and y <=u 2^(n-1)

Let n be the bit width of x and y. If x <u y and y <=u 2^(n-1), then
x <u 2^(n-1), so x >=s 0. Here y <=u 2^(n-1) means y >=s 0 or
y == INT_MIN. In the same way, if x <=u y and y <u 2^(n-1), then
x >=s 0.

This lets a guard like x <u len prove x >=s 0 when len is non-negative.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 21 ++++++++
 ...lied-nonneg-from-unsigned-bound-swapped.ll | 53 ++++++++-----------
 .../implied-nonneg-from-unsigned-bound.ll     | 22 ++------
 3 files changed, 47 insertions(+), 49 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 77018958e3679..749ee43357ea1 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12384,6 +12384,27 @@ bool ScalarEvolution::isImpliedCondViaSignFlippedPred(
   assert((ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred)) && "Must be!");
   assert((ICmpInst::isLT(FoundPred) || ICmpInst::isLE(FoundPred)) &&
          "Must be!");
+  if (ICmpInst::isSigned(Pred)) {
+    // Use implication:
+    // x <u y && y <=u 2^(n-1) --> x >=s 0,
+    // x <=u y && y <u 2^(n-1) --> x >=s 0,
+    // where n is the bit width of x and y.
+    // In both cases x <u 2^(n-1), so x >=s 0.
+    // y <=u 2^(n-1) means y >=s 0 or y == INT_MIN.
+    // If the implication's left part holds, try proving the query from x >=s
+    // 0.
+    Type *Ty = FoundLHS->getType();
+    unsigned BitWidth = getTypeSizeInBits(Ty);
+    const SCEV *SignedMin = getConstant(APInt::getSignedMinValue(BitWidth));
+    CmpPredicate BoundPred =
+        ICmpInst::isLT(FoundPred) ? ICmpInst::ICMP_ULE : ICmpInst::ICMP_ULT;
+    if (isKnownViaNonRecursiveReasoning(BoundPred, FoundRHS, SignedMin)) {
+      // x >=s 0, written as 0 <=s x, or as -1 <s x to match a strict query.
+      const SCEV *Bound = ICmpInst::isLE(Pred) ? getZero(Ty) : getMinusOne(Ty);
+      if (isImpliedCondOperands(Pred, LHS, RHS, Bound, FoundLHS, CtxI))
+        return true;
+    }
+  }
   if (ICmpInst::isSigned(Pred) && isKnownNonNegative(RHS)) {
     // Use implication:
     // x <u y && y >=s 0 --> x <s y.
diff --git a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll
index e380f40b15113..ea85e24a56fe3 100644
--- a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll
+++ b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound-swapped.ll
@@ -6,8 +6,6 @@
 ; The guards and the exit conditions are written with different predicates.
 ; In most tests the operands of the exit condition are in the opposite order
 ; to the guard.
-;
-; FIXME: SCEV cannot prove this yet, fold the exits to constants where possible.
 
 target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
 
@@ -23,8 +21,7 @@ define void @ult(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -64,8 +61,7 @@ define void @ult_smin(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -105,8 +101,7 @@ define void @ule(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -146,8 +141,7 @@ define void @ugt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -187,8 +181,7 @@ define void @ugt_smin(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -228,8 +221,7 @@ define void @uge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -269,8 +261,7 @@ define void @ult_query_sle(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -310,8 +301,7 @@ define void @ult_query_sgt_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 0, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -351,8 +341,7 @@ define void @ult_query_sge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -392,8 +381,7 @@ define void @ult_query_sgt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -433,8 +421,7 @@ define void @ult_query_slt_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 -1, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -474,8 +461,7 @@ define void @ult_query_sle_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 0, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -515,8 +501,7 @@ define void @ugt_query_sge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -556,8 +541,7 @@ define void @uge_query_sgt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -587,6 +571,8 @@ exit:
 
 ; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >=s z
 ; for any z <=s 0.
+; TODO: fold the exit. This needs x >=s z to be proven from x >=s 0 and
+; z <=s 0, where z may be INT_MIN.
 define void @ult_query_nonpos(i32 %x, ptr %p, ptr %pz) {
 ; CHECK-LABEL: define void @ult_query_nonpos(
 ; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]], ptr [[PZ:%.*]]) {
@@ -641,8 +627,7 @@ define void @ult_query_const(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], -5
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -672,6 +657,8 @@ exit:
 
 ; If x <u y and y <u 1000, then x >=s 0. Here y <u 1000 is known only from
 ; a dominating guard.
+; TODO: fold the exit. This needs the bound on y to be proven from the guard,
+; not only from the range of y.
 define void @ult_y_guarded(i32 %x, i32 %y) {
 ; CHECK-LABEL: define void @ult_y_guarded(
 ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
@@ -717,6 +704,8 @@ exit:
 }
 
 ; If x <u y and y <u INT_MAX, then x >=s 0, which implies x + 1 >=s 1.
+; TODO: fold the exit. This needs x + 1 >=s 1 to be proven from x >=s 0 for
+; the nsw add.
 define void @ult_query_offset(i32 %x, ptr %p) {
 ; CHECK-LABEL: define void @ult_query_offset(
 ; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
diff --git a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
index 4e982e4ab0603..bff7996785c27 100644
--- a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
+++ b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
@@ -5,8 +5,6 @@
 ; If x <=u y and y <=u INT_MAX, then x >=s 0.
 ; In these tests the guard compares y with x, and the exit compares
 ; smax(x, w) with a constant.
-;
-; FIXME: SCEV cannot prove this yet, fold the exits to constants where possible.
 
 target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
 
@@ -22,9 +20,7 @@ define void @ugt_smin(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -65,9 +61,7 @@ define void @uge(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -108,9 +102,7 @@ define void @ugt_sle(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[M]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -151,9 +143,7 @@ define void @ugt_sge(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[M]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -194,9 +184,7 @@ define void @ugt_weaker(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], -5
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1



More information about the llvm-branch-commits mailing list