llvm/llvm/test/Transforms/LICM/min_max.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S %s -passes='loop-mssa(licm)'  -verify-memoryssa  | FileCheck %s

; turn to %iv <u umin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ult(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Do not optimize: pointer types.
define ptr @test_ult_ptr_neg(ptr %start, ptr %inv_1, ptr %inv_2) {
; CHECK-LABEL: @test_ult_ptr_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi ptr [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult ptr [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ult ptr [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = getelementptr i32, ptr [[IV]], i64 4
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi ptr [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret ptr [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi ptr [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult ptr %iv, %inv_1
  %cmp_2 = icmp ult ptr %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = getelementptr i32, ptr %iv, i64 4
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret ptr %iv
}

; turn to %iv <=u umin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ule(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ule(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ule i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ule i32 %iv, %inv_1
  %cmp_2 = icmp ule i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <s smin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_slt(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_slt(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp slt i32 %iv, %inv_1
  %cmp_2 = icmp slt i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <=s smin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sle(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sle(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sle i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sle i32 %iv, %inv_1
  %cmp_2 = icmp sle i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >u umax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ugt(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ugt(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ugt i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ugt i32 %iv, %inv_1
  %cmp_2 = icmp ugt i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >=u umax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_uge(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_uge(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp uge i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp uge i32 %iv, %inv_1
  %cmp_2 = icmp uge i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >s smax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sgt(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sgt(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sgt i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sgt i32 %iv, %inv_1
  %cmp_2 = icmp sgt i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >=s smax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sge(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sge(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sge i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sge i32 %iv, %inv_1
  %cmp_2 = icmp sge i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_ult_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_ule_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ule_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ule i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ule i32 %iv, %inv_1
  %cmp_2 = icmp ule i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_slt_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_slt_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp slt i32 %iv, %inv_1
  %cmp_2 = icmp slt i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_sle_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sle_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sle i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sle i32 %iv, %inv_1
  %cmp_2 = icmp sle i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_ugt_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ugt_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ugt i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ugt i32 %iv, %inv_1
  %cmp_2 = icmp ugt i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_uge_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_uge_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp uge i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp uge i32 %iv, %inv_1
  %cmp_2 = icmp uge i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_sgt_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sgt_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sgt i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sgt i32 %iv, %inv_1
  %cmp_2 = icmp sgt i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Turn OR to AND and handle accordingly.
define i32 @test_sge_inv(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sge_inv(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sge i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sge i32 %iv, %inv_1
  %cmp_2 = icmp sge i32 %iv, %inv_2
  %loop_cond = or i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <u umin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ult_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ugt i32 %inv_1, %iv
  %cmp_2 = icmp ugt i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <=u umin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ule_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ule_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ule i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp uge i32 %inv_1, %iv
  %cmp_2 = icmp uge i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <s smin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_slt_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_slt_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp slt i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sgt i32 %inv_1, %iv
  %cmp_2 = icmp sgt i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <=s smin(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sle_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sle_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sle i32 [[IV]], [[INVARIANT_SMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sge i32 %inv_1, %iv
  %cmp_2 = icmp sge i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >u umax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_ugt_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ugt_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ugt i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %inv_1, %iv
  %cmp_2 = icmp ult i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >=u umax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_uge_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_uge_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp uge i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ule i32 %inv_1, %iv
  %cmp_2 = icmp ule i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >s smax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sgt_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sgt_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sgt i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp slt i32 %inv_1, %iv
  %cmp_2 = icmp slt i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv >=s smax(inv_1, inv_2) and hoist it out of loop.
define i32 @test_sge_swapped(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_sge_swapped(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp sge i32 [[IV]], [[INVARIANT_SMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp sle i32 %inv_1, %iv
  %cmp_2 = icmp sle i32 %inv_2, %iv
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; inv_2 needs freeze because hoisted umax would create a new use that didn't exist before.
; Counter-example: %cmp_1 = false, %inv_2 = poison.
define i32 @test_logical_and_ult_needs_freeze(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_logical_and_ult_needs_freeze(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INV_2_FR:%.*]] = freeze i32 [[INV_2:%.*]]
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2_FR]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = select i1 %cmp_1, i1 %cmp_2, i1 false
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <u umin(inv_1, inv_2) and hoist it out of loop.
; https://alive2.llvm.org/ce/z/mhKGtT
define i32 @test_logical_and_ult_pos(i32 %start, i32 %inv_1, i32 noundef %inv_2) {
; CHECK-LABEL: @test_logical_and_ult_pos(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INV_2_FR:%.*]] = freeze i32 [[INV_2:%.*]]
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2_FR]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = select i1 %cmp_1, i1 %cmp_2, i1 false
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; inv_2 needs freeze because hoisted umax would create a new use that didn't exist before.
define i32 @test_logical_or_ult_inv_needs_freeze(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_logical_or_ult_inv_needs_freeze(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INV_2_FR:%.*]] = freeze i32 [[INV_2:%.*]]
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2_FR]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = select i1 %cmp_1, i1 true, i1 %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; turn to %iv <u umax(inv_1, inv_2) and hoist it out of loop.
; https://alive2.llvm.org/ce/z/zgyG5N
define i32 @test_logical_or_ult_inv_pos(i32 %start, i32 %inv_1, i32 noundef %inv_2) {
; CHECK-LABEL: @test_logical_or_ult_inv_pos(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INV_2_FR:%.*]] = freeze i32 [[INV_2:%.*]]
; CHECK-NEXT:    [[INVARIANT_UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2_FR]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMAX]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = select i1 %cmp_1, i1 true, i1 %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Mismatched predicates, do not optimize.
define i32 @test_mismatched_predicates_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_mismatched_predicates_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ule i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ule i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; TODO: Turn ule against constant into ult against constant, then do the same as in test_ult.
; https://alive2.llvm.org/ce/z/rucunQ
define i32 @test_mismatched_predicates_constant_pos_1(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_mismatched_predicates_constant_pos_1(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ule i32 [[IV]], 100
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ule i32 %iv, 100
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; TODO: Turn ult against a constant into ule, then do the same as in test_ule.
; https://alive2.llvm.org/ce/z/-ht-pU
define i32 @test_mismatched_predicates_constant_pos_2(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_mismatched_predicates_constant_pos_2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], 100
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ule i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, 100
  %cmp_2 = icmp ule i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Do not optimize: `ule -1` cannot be turned into `ult 0` because of overflow.
; However, this condition is trivially true anyways, so do not bother about it.
define i32 @test_mismatched_predicates_constant_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_mismatched_predicates_constant_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ule i32 [[IV]], -1
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ule i32 %iv, -1
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Do not optimize: predicate eq.
define i32 @test_eq_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_eq_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp eq i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp eq i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp eq i32 %iv, %inv_1
  %cmp_2 = icmp eq i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Do not optimize: predicate ne.
define i32 @test_ne_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ne_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ne i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ne i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ne i32 %iv, %inv_1
  %cmp_2 = icmp ne i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv
}

; Do not optimize: extra use.
define i32 @test_ult_extra_use_1_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_extra_use_1_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ult i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1_LCSSA:%.*]] = phi i1 [ [[CMP_1]], [[LOOP]] ]
; CHECK-NEXT:    call void @use(i1 [[CMP_1_LCSSA]])
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  call void @use(i1 %cmp_1)
  ret i32 %iv
}

define i32 @test_ult_extra_use_2_neg(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_extra_use_2_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ult i32 [[IV]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_2_LCSSA:%.*]] = phi i1 [ [[CMP_2]], [[LOOP]] ]
; CHECK-NEXT:    call void @use(i1 [[CMP_2_LCSSA]])
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  call void @use(i1 %cmp_2)
  ret i32 %iv
}

; This can be optimized, despite the fact that loop_cond has other uses.
define i32 @test_ult_extra_use_result_pos(i32 %start, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_extra_use_result_pos(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INVARIANT_UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]])
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_UMIN]]
; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ]
; CHECK-NEXT:    [[LOOP_COND_LCSSA:%.*]] = phi i1 [ [[LOOP_COND]], [[LOOP]] ]
; CHECK-NEXT:    call void @use(i1 [[LOOP_COND_LCSSA]])
; CHECK-NEXT:    ret i32 [[IV_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv = phi i32 [%start, %entry], [%iv.next, %loop]
  %cmp_1 = icmp ult i32 %iv, %inv_1
  %cmp_2 = icmp ult i32 %iv, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv.next = add i32 %iv, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  call void @use(i1 %loop_cond)
  ret i32 %iv
}

; Do not optimize: comparison against different variant values.
define i32 @test_ult_different_variants_neg(i32 %start_1, i32 %start_2, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_different_variants_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV_1:%.*]] = phi i32 [ [[START_1:%.*]], [[ENTRY:%.*]] ], [ [[IV_1_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[IV_2:%.*]] = phi i32 [ [[START_2:%.*]], [[ENTRY]] ], [ [[IV_2_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV_1]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ult i32 [[IV_2]], [[INV_2:%.*]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_1_NEXT]] = add i32 [[IV_1]], 1
; CHECK-NEXT:    [[IV_2_NEXT]] = add i32 [[IV_2]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_1_LCSSA:%.*]] = phi i32 [ [[IV_1]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_1_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv_1 = phi i32 [%start_1, %entry], [%iv_1.next, %loop]
  %iv_2 = phi i32 [%start_2, %entry], [%iv_2.next, %loop]
  %cmp_1 = icmp ult i32 %iv_1, %inv_1
  %cmp_2 = icmp ult i32 %iv_2, %inv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv_1.next = add i32 %iv_1, 1
  %iv_2.next = add i32 %iv_2, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv_1
}

; Do not optimize: one of comparisons is against loop-variant.
define i32 @test_ult_compare_against_variant_neg(i32 %start_1, i32 %start_2, i32 %inv_1, i32 %inv_2) {
; CHECK-LABEL: @test_ult_compare_against_variant_neg(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV_1:%.*]] = phi i32 [ [[START_1:%.*]], [[ENTRY:%.*]] ], [ [[IV_1_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[IV_2:%.*]] = phi i32 [ [[START_2:%.*]], [[ENTRY]] ], [ [[IV_2_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[CMP_1:%.*]] = icmp ult i32 [[IV_1]], [[INV_1:%.*]]
; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ult i32 [[IV_1]], [[IV_2]]
; CHECK-NEXT:    [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]]
; CHECK-NEXT:    [[IV_1_NEXT]] = add i32 [[IV_1]], 1
; CHECK-NEXT:    [[IV_2_NEXT]] = add i32 [[IV_2]], 1
; CHECK-NEXT:    br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_1_LCSSA:%.*]] = phi i32 [ [[IV_1]], [[LOOP]] ]
; CHECK-NEXT:    ret i32 [[IV_1_LCSSA]]
;
entry:
  br label %loop

loop:
  %iv_1 = phi i32 [%start_1, %entry], [%iv_1.next, %loop]
  %iv_2 = phi i32 [%start_2, %entry], [%iv_2.next, %loop]
  %cmp_1 = icmp ult i32 %iv_1, %inv_1
  %cmp_2 = icmp ult i32 %iv_1, %iv_2
  %loop_cond = and i1 %cmp_1, %cmp_2
  %iv_1.next = add i32 %iv_1, 1
  %iv_2.next = add i32 %iv_2, 1
  br i1 %loop_cond, label %loop, label %exit

exit:
  ret i32 %iv_1
}

declare void @use(i1)