; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5

; RUN: opt -S -passes=instcombine < %s | FileCheck %s

; When C0 is neither 0 nor 1:
;   umax(nuw_mul(x, C0), x + 1) is optimized to:
;   x == 0 ? 1 : nuw_mul(x, C0)
; When C0 is not 0:
;   umax(nuw_shl(x, C0), x + 1) is optimized to:
;   x == 0 ? 1 : nuw_shl(x, C0)

; Positive Test Cases for `shl`

define i64 @test_shl_by_2(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_by_2(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[TMP2:%.*]] = shl nuw i64 [[X]], 2
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[TMP2]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 2
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_by_5(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_by_5(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[TMP2:%.*]] = shl nuw i64 [[X]], 5
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[TMP2]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 5
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_with_nsw(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_with_nsw(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw i64 [[X]], 2
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw nsw i64 %x, 2
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define <2 x i64> @test_shl_vector_by_2(<2 x i64> %x) {
; CHECK-LABEL: define <2 x i64> @test_shl_vector_by_2(
; CHECK-SAME: <2 x i64> [[X:%.*]]) {
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw <2 x i64> [[X]], splat (i64 2)
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq <2 x i64> [[X]], zeroinitializer
; CHECK-NEXT:    [[MAX:%.*]] = select <2 x i1> [[TMP1]], <2 x i64> splat (i64 1), <2 x i64> [[SHL]]
; CHECK-NEXT:    ret <2 x i64> [[MAX]]
;
  %x1 = add <2 x i64> %x, <i64 1, i64 1>
  %shl = shl nuw <2 x i64> %x, <i64 2, i64 2>
  %max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %shl, <2 x i64> %x1)
  ret <2 x i64> %max
}

; Commuted Test Cases for `shl`

define i64 @test_shl_umax_commuted(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_umax_commuted(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 [[X]], 2
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 2
  %max = call i64 @llvm.umax.i64(i64 %x1, i64 %shl)
  ret i64 %max
}

; Negative Test Cases for `shl`

define i64 @test_shl_by_zero(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_by_zero(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 0
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_add_by_2(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_add_by_2(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 2
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 [[X]], 2
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 2
  %shl = shl nuw i64 %x, 2
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_without_nuw(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_without_nuw(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[X]], 2
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[SHL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl i64 %x, 2
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_umin(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_umin(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 [[X]], 2
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umin.i64(i64 [[SHL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 2
  %max = call i64 @llvm.umin.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

; Multi-use Test Cases for `shl`
declare void @use(i64)

define i64 @test_shl_multi_use_add(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_multi_use_add(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    call void @use(i64 [[X1]])
; CHECK-NEXT:    [[TMP2:%.*]] = shl nuw i64 [[X]], 3
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  call void @use(i64 %x1)
  %shl = shl nuw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

define i64 @test_shl_multi_use_shl(i64 %x) {
; CHECK-LABEL: define i64 @test_shl_multi_use_shl(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 [[X]], 2
; CHECK-NEXT:    call void @use(i64 [[SHL]])
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[SHL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %shl = shl nuw i64 %x, 2
  call void @use(i64 %shl)
  %max = call i64 @llvm.umax.i64(i64 %shl, i64 %x1)
  ret i64 %max
}

; Positive Test Cases for `mul`

define i64 @test_mul_by_3(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_by_3(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_by_5(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_by_5(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 5
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 5
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_with_nsw(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_with_nsw(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[X]], 3
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw nsw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define <2 x i64> @test_mul_vector_by_3(<2 x i64> %x) {
; CHECK-LABEL: define <2 x i64> @test_mul_vector_by_3(
; CHECK-SAME: <2 x i64> [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw <2 x i64> [[X]], splat (i64 3)
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq <2 x i64> [[X]], zeroinitializer
; CHECK-NEXT:    [[MAX:%.*]] = select <2 x i1> [[TMP1]], <2 x i64> splat (i64 1), <2 x i64> [[MUL]]
; CHECK-NEXT:    ret <2 x i64> [[MAX]]
;
  %x1 = add <2 x i64> %x, <i64 1, i64 1>
  %mul = mul nuw <2 x i64> %x, <i64 3, i64 3>
  %max = call <2 x i64> @llvm.umax.v2i64(<2 x i64> %mul, <2 x i64> %x1)
  ret <2 x i64> %max
}

; Commuted Test Cases for `mul`

define i64 @test_mul_max_commuted(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_max_commuted(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %x1, i64 %mul)
  ret i64 %max
}

; Negative Test Cases for `mul`

define i64 @test_mul_by_zero(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_by_zero(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    ret i64 [[X1]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 0
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_by_1(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_by_1(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[X]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 1
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_add_by_2(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_add_by_2(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 2
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 2
  %mul = mul nuw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_without_nuw(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_without_nuw(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[X]], 3
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[MUL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_umin(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_umin(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 3
  %max = call i64 @llvm.umin.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

; Multi-use Test Cases for `mul`

define i64 @test_mul_multi_use_add(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_multi_use_add(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[X1:%.*]] = add i64 [[X]], 1
; CHECK-NEXT:    call void @use(i64 [[X1]])
; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    [[MAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP2]], i64 [[X1]])
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  call void @use(i64 %x1)
  %mul = mul nuw i64 %x, 3
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}

define i64 @test_mul_multi_use_mul(i64 %x) {
; CHECK-LABEL: define i64 @test_mul_multi_use_mul(
; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i64 [[X]], 3
; CHECK-NEXT:    call void @use(i64 [[MUL]])
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[X]], 0
; CHECK-NEXT:    [[MAX:%.*]] = select i1 [[TMP1]], i64 1, i64 [[MUL]]
; CHECK-NEXT:    ret i64 [[MAX]]
;
  %x1 = add i64 %x, 1
  %mul = mul nuw i64 %x, 3
  call void @use(i64 %mul)
  %max = call i64 @llvm.umax.i64(i64 %mul, i64 %x1)
  ret i64 %max
}
