; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s

; If nothing is known we can't change anything
define i8 @ucmp_0(i32 %x, i32 %y) {
; CHECK-LABEL: @ucmp_0(
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_0(i32 %x, i32 %y) {
; CHECK-LABEL: @scmp_0(
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; If we know that range of LHS < range of RHS then return -1
define i8 @ucmp_1(i32 %x, i32 %y) {
  ; X is within [4, 8)
; CHECK-LABEL: @ucmp_1(
; CHECK-NEXT:    [[COND1:%.*]] = icmp samesign uge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i32 [[X]], 8
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[COND3:%.*]] = icmp uge i32 [[Y:%.*]], 8
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND3]])
; CHECK-NEXT:    ret i8 -1
;
  %cond1 = icmp uge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  %cond2 = icmp ult i32 %x, 8
  call void @llvm.assume(i1 %cond2)
  ; Y is within [8, UNSIGNED_MAX)
  %cond3 = icmp uge i32 %y, 8
  call void @llvm.assume(i1 %cond3)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_1(i32 %x, i32 %y) {
  ; X is within [-5, 3)
; CHECK-LABEL: @scmp_1(
; CHECK-NEXT:    [[COND1:%.*]] = icmp sge i32 [[X:%.*]], -5
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp slt i32 [[X]], 3
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[COND3:%.*]] = icmp sge i32 [[Y:%.*]], 3
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND3]])
; CHECK-NEXT:    ret i8 -1
;
  %cond1 = icmp sge i32 %x, -5
  call void @llvm.assume(i1 %cond1)
  %cond2 = icmp slt i32 %x, 3
  call void @llvm.assume(i1 %cond2)
  ; Y is within [3, SIGNED_MAX)
  %cond3 = icmp sge i32 %y, 3
  call void @llvm.assume(i1 %cond3)

  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; If we know that range of LHS > range of RHS then return 1
define i8 @ucmp_2(i32 %x, i32 %y) {
  ; X is within [4, UNSIGNED_MAX)
; CHECK-LABEL: @ucmp_2(
; CHECK-NEXT:    [[COND1:%.*]] = icmp uge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    ret i8 1
;
  %cond1 = icmp uge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  ; Y is within [0, 4)
  %cond2 = icmp ult i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_2(i32 %x, i32 %y) {
  ; X is within [4, SIGNED_MAX)
; CHECK-LABEL: @scmp_2(
; CHECK-NEXT:    [[COND1:%.*]] = icmp sge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp slt i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    ret i8 1
;
  %cond1 = icmp sge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  ; Y is within [SIGNED_MIN, 4)
  %cond2 = icmp slt i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; If we know that LHS and RHS are both constants then return 0
define i8 @ucmp_5(i32 %x, i32 %y) {
; CHECK-LABEL: @ucmp_5(
; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    ret i8 0
;
  %cond1 = icmp eq i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  %cond2 = icmp eq i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_5(i32 %x, i32 %y) {
; CHECK-LABEL: @scmp_5(
; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i32 [[X:%.*]], -5
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i32 [[Y:%.*]], -5
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    ret i8 0
;
  %cond1 = icmp eq i32 %x, -5
  call void @llvm.assume(i1 %cond1)
  %cond2 = icmp eq i32 %y, -5
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; We can infer ranges based on the location where a UCMP/SCMP result is used
define i8 @scmp_6(i32 noundef %x) {
; CHECK-LABEL: @scmp_6(
; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[X:%.*]], 10
; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i8 -1, i8 5
; CHECK-NEXT:    ret i8 [[TMP2]]
;
  %1 = icmp slt i32 %x, 10
  %2 = call i8 @llvm.scmp(i32 %x, i32 10)
  %3 = select i1 %1, i8 %2, i8 5
  ret i8 %3
}

; Negative test: ranges overlap
define i8 @ucmp_3(i32 %x, i32 %y) {
  ; X is within [4, UNSIGNED_MAX)
; CHECK-LABEL: @ucmp_3(
; CHECK-NEXT:    [[COND1:%.*]] = icmp uge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i32 [[Y:%.*]], 6
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %cond1 = icmp uge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  ; Y is within [0, 6)
  %cond2 = icmp ult i32 %y, 6
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_3(i32 %x, i32 %y) {
  ; X is within [2, SIGNED_MAX)
; CHECK-LABEL: @scmp_3(
; CHECK-NEXT:    [[COND1:%.*]] = icmp sge i32 [[X:%.*]], 2
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp slt i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 [[Y]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %cond1 = icmp sge i32 %x, 2
  call void @llvm.assume(i1 %cond1)
  ; Y is within [SIGNED_MIN, 4)
  %cond2 = icmp slt i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; Negative test: mismatched signedness of range-establishing comparisons and
;                of the intrinsic
define i8 @ucmp_4(i32 %x, i32 %y) {
  ; X is within [4, SIGNED_MAX)
; CHECK-LABEL: @ucmp_4(
; CHECK-NEXT:    [[COND1:%.*]] = icmp sge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp slt i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %cond1 = icmp sge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  ; Y is within [0, 4)
  %cond2 = icmp slt i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @scmp_4(i32 %x, i32 %y) {
  ; X is within [4, UNSIGNED_MAX)
; CHECK-LABEL: @scmp_4(
; CHECK-NEXT:    [[COND1:%.*]] = icmp uge i32 [[X:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i32 [[Y:%.*]], 4
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 [[Y]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %cond1 = icmp uge i32 %x, 4
  call void @llvm.assume(i1 %cond1)
  ; Y is within [0, 4)
  %cond2 = icmp ult i32 %y, 4
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
  ret i8 %1
}

; Negative test: ranges are the same, but we can't be sure the values are equal
define i8 @ucmp_6(i32 %x, i32 %y) {
  ; Both X and Y are within [0, 10]
; CHECK-LABEL: @ucmp_6(
; CHECK-NEXT:    [[COND1:%.*]] = icmp ule i32 [[X:%.*]], 10
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
; CHECK-NEXT:    [[COND2:%.*]] = icmp ule i32 [[Y:%.*]], 10
; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
; CHECK-NEXT:    ret i8 [[TMP1]]
;
  %cond1 = icmp ule i32 %x, 10
  call void @llvm.assume(i1 %cond1)
  %cond2 = icmp ule i32 %y, 10
  call void @llvm.assume(i1 %cond2)

  %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
  ret i8 %1
}

define i8 @ucmp_switch(i32 %x, i32 %y) {
; CHECK-LABEL: @ucmp_switch(
; CHECK-NEXT:    [[CMP:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT:    switch i8 [[CMP]], label [[DEFAULT_UNREACHABLE:%.*]] [
; CHECK-NEXT:      i8 1, label [[BB_1:%.*]]
; CHECK-NEXT:      i8 -1, label [[BB_NEG1:%.*]]
; CHECK-NEXT:      i8 0, label [[BB_0:%.*]]
; CHECK-NEXT:    ]
; CHECK:       bb.neg2:
; CHECK-NEXT:    ret i8 -2
; CHECK:       bb.neg1:
; CHECK-NEXT:    ret i8 -1
; CHECK:       bb.0:
; CHECK-NEXT:    ret i8 0
; CHECK:       bb.1:
; CHECK-NEXT:    ret i8 1
; CHECK:       bb.2:
; CHECK-NEXT:    ret i8 2
; CHECK:       default.unreachable:
; CHECK-NEXT:    unreachable
; CHECK:       default:
; CHECK-NEXT:    ret i8 123
;
  %cmp = call i8 @llvm.ucmp(i32 %x, i32 %y)
  switch i8 %cmp, label %default [
  i8 -2, label %bb.neg2
  i8 -1, label %bb.neg1
  i8 0, label %bb.0
  i8 1, label %bb.1
  i8 2, label %bb.2
  ]

bb.neg2:
  ret i8 -2

bb.neg1:
  ret i8 -1

bb.0:
  ret i8 0

bb.1:
  ret i8 1

bb.2:
  ret i8 2

default:
  ret i8 123
}

define i8 @ucmp_switch_callsite_range(i32 %x, i32 %y) {
; CHECK-LABEL: @ucmp_switch_callsite_range(
; CHECK-NEXT:    [[CMP:%.*]] = call range(i8 -2, 3) i8 @llvm.ucmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT:    switch i8 [[CMP]], label [[DEFAULT_UNREACHABLE:%.*]] [
; CHECK-NEXT:      i8 1, label [[BB_1:%.*]]
; CHECK-NEXT:      i8 -1, label [[BB_NEG1:%.*]]
; CHECK-NEXT:      i8 0, label [[BB_0:%.*]]
; CHECK-NEXT:    ]
; CHECK:       bb.neg2:
; CHECK-NEXT:    ret i8 -2
; CHECK:       bb.neg1:
; CHECK-NEXT:    ret i8 -1
; CHECK:       bb.0:
; CHECK-NEXT:    ret i8 0
; CHECK:       bb.1:
; CHECK-NEXT:    ret i8 1
; CHECK:       bb.2:
; CHECK-NEXT:    ret i8 2
; CHECK:       default.unreachable:
; CHECK-NEXT:    unreachable
; CHECK:       default:
; CHECK-NEXT:    ret i8 123
;
  %cmp = call range(i8 -2, 3) i8 @llvm.ucmp(i32 %x, i32 %y)
  switch i8 %cmp, label %default [
  i8 -2, label %bb.neg2
  i8 -1, label %bb.neg1
  i8 0, label %bb.0
  i8 1, label %bb.1
  i8 2, label %bb.2
  ]

bb.neg2:
  ret i8 -2

bb.neg1:
  ret i8 -1

bb.0:
  ret i8 0

bb.1:
  ret i8 1

bb.2:
  ret i8 2

default:
  ret i8 123
}

define i8 @scmp_switch(i32 %x, i32 %y) {
; CHECK-LABEL: @scmp_switch(
; CHECK-NEXT:    [[CMP:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT:    switch i8 [[CMP]], label [[DEFAULT_UNREACHABLE:%.*]] [
; CHECK-NEXT:      i8 1, label [[BB_1:%.*]]
; CHECK-NEXT:      i8 -1, label [[BB_NEG1:%.*]]
; CHECK-NEXT:      i8 0, label [[BB_0:%.*]]
; CHECK-NEXT:    ]
; CHECK:       bb.neg2:
; CHECK-NEXT:    ret i8 -2
; CHECK:       bb.neg1:
; CHECK-NEXT:    ret i8 -1
; CHECK:       bb.0:
; CHECK-NEXT:    ret i8 0
; CHECK:       bb.1:
; CHECK-NEXT:    ret i8 1
; CHECK:       bb.2:
; CHECK-NEXT:    ret i8 2
; CHECK:       default.unreachable:
; CHECK-NEXT:    unreachable
; CHECK:       default:
; CHECK-NEXT:    ret i8 123
;
  %cmp = call i8 @llvm.scmp(i32 %x, i32 %y)
  switch i8 %cmp, label %default [
  i8 -2, label %bb.neg2
  i8 -1, label %bb.neg1
  i8 0, label %bb.0
  i8 1, label %bb.1
  i8 2, label %bb.2
  ]

bb.neg2:
  ret i8 -2

bb.neg1:
  ret i8 -1

bb.0:
  ret i8 0

bb.1:
  ret i8 1

bb.2:
  ret i8 2

default:
  ret i8 123
}
