llvm/llvm/test/Transforms/GuardWidening/basic.ll

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

declare void @llvm.experimental.guard(i1,...)

; Basic test case: we wide the first check to check both the
; conditions.
define void @f_0(i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_0(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
;
entry:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void
}

; Same as @f_0, but with using a more general notion of postdominance.
define void @f_1(i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_1(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       right:
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       merge:
; CHECK-NEXT:    ret void
;
entry:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:
  br label %merge

right:
  br label %merge

merge:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void
}

; Like @f_1, but we have some code we need to hoist before we can
; widen a dominanting check.
define void @f_2(i32 %a, i32 %b) {
; CHECK-LABEL: @f_2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]]
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10
; CHECK-NEXT:    [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       right:
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       merge:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ult i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:
  br label %merge

right:
  br label %merge

merge:
  %cond_1 = icmp ult i32 %b, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void
}

; Negative test: don't hoist stuff out of control flow
; indiscriminately, since that can make us do more work than needed.
define void @f_3(i32 %a, i32 %b) {
; CHECK-LABEL: @f_3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
; CHECK:       right:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ult i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:

  %cond_1 = icmp ult i32 %b, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void

right:
  ret void
}

; But hoisting out of control flow is fine if it makes a loop computed
; condition loop invariant.  This behavior may require some tuning in
; the future.
define void @f_4(i32 %a, i32 %b) {
; CHECK-LABEL: @f_4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]]
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10
; CHECK-NEXT:    [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LOOP:%.*]], label [[LEAVE:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    br i1 undef, label [[LOOP]], label [[LEAVE]]
; CHECK:       leave:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ult i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %loop, label %leave

loop:
  %cond_1 = icmp ult i32 %b, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  br i1 undef, label %loop, label %leave

leave:
  ret void
}

; Hoisting out of control flow is also fine if we can widen the
; dominating check without doing any extra work.
define void @f_5(i32 %a) {
; CHECK-LABEL: @f_5(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ugt i32 [[A:%.*]], 7
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = icmp uge i32 [[A]], 11
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    [[COND_1:%.*]] = icmp ugt i32 [[A]], 10
; CHECK-NEXT:    ret void
; CHECK:       right:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ugt i32 %a, 7
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:
  %cond_1 = icmp ugt i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void

right:
  ret void
}

; Negative test: the load from %a can be safely speculated to before
; the first guard, but there is no guarantee that it will produce the
; same value.
define void @f_6(ptr dereferenceable(32) %a, ptr %b, i1 %unknown) {
; CHECK-LABEL: @f_6(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_0:%.*]] = load i1, ptr [[A:%.*]], align 1
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ]
; CHECK-NEXT:    store i1 [[UNKNOWN:%.*]], ptr [[B:%.*]], align 1
; CHECK-NEXT:    [[COND_1:%.*]] = load i1, ptr [[A]], align 1
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
;
entry:
  %cond_0 = load i1, ptr %a
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  store i1 %unknown, ptr %b
  %cond_1 = load i1, ptr %a
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void
}

; All else equal, we try to widen the earliest guard we can.  This
; heuristic can use some tuning.
define void @f_7(i32 %a, ptr %cond_buf) {
; CHECK-LABEL: @f_7(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_GW_FR:%.*]] = freeze i32 [[A:%.*]]
; CHECK-NEXT:    [[COND_1:%.*]] = load volatile i1, ptr [[COND_BUF:%.*]], align 1
; CHECK-NEXT:    [[COND_3:%.*]] = icmp ult i32 [[A_GW_FR]], 7
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_3]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    [[COND_2:%.*]] = load volatile i1, ptr [[COND_BUF]], align 1
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_2]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    br label [[LEFT]]
; CHECK:       right:
; CHECK-NEXT:    ret void
;
entry:

  %cond_1 = load volatile i1, ptr %cond_buf
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  %cond_2 = load volatile i1, ptr %cond_buf
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:
  %cond_3 = icmp ult i32 %a, 7
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ]
  br label %left

right:
  ret void
}

; In this case the earliest dominating guard is in a loop, and we
; don't want to put extra work in there.  This heuristic can use some
; tuning.
define void @f_8(i32 %a, i1 %cond_1, i1 %cond_2) {
; CHECK-LABEL: @f_8(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_GW_FR:%.*]] = freeze i32 [[A:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LOOP]], label [[LEAVE:%.*]]
; CHECK:       leave:
; CHECK-NEXT:    [[COND_3:%.*]] = icmp ult i32 [[A_GW_FR]], 7
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_2:%.*]], [[COND_3]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LOOP2:%.*]], label [[LEAVE2:%.*]]
; CHECK:       loop2:
; CHECK-NEXT:    br label [[LOOP2]]
; CHECK:       leave2:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  br i1 undef, label %loop, label %leave

leave:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
  br i1 undef, label %loop2, label %leave2

loop2:
  %cond_3 = icmp ult i32 %a, 7
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ]
  br label %loop2

leave2:
  ret void
}

; In cases like these where there isn't any "obviously profitable"
; widening sites, we refuse to do anything.
define void @f_9(i32 %a, i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_9(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[FIRST_LOOP:%.*]]
; CHECK:       first_loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[FIRST_LOOP]], label [[SECOND_LOOP:%.*]]
; CHECK:       second_loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    br label [[SECOND_LOOP]]
;
entry:
  br label %first_loop

first_loop:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %first_loop, label %second_loop

second_loop:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  br label %second_loop
}

; Same situation as in @f_9: no "obviously profitable" widening sites,
; so we refuse to do anything.
define void @f_10(i32 %a, i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_10(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LOOP]], label [[NO_LOOP:%.*]]
; CHECK:       no_loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %loop, label %no_loop

no_loop:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void
}

; With guards in loops, we're okay hoisting out the guard into the
; containing loop.
define void @f_11(i32 %a, i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_11(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT:    br label [[INNER:%.*]]
; CHECK:       inner:
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[INNER]], label [[OUTER:%.*]]
; CHECK:       outer:
; CHECK-NEXT:    br label [[INNER]]
;
entry:
  br label %inner

inner:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %inner, label %outer

outer:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  br label %inner
}

; Checks that we are adequately guarded against exponential-time
; behavior when hoisting code.
define void @f_12(i32 %a0) {
; CHECK-LABEL: @f_12(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A0_GW_FR:%.*]] = freeze i32 [[A0:%.*]]
; CHECK-NEXT:    [[A1:%.*]] = mul i32 [[A0_GW_FR]], [[A0_GW_FR]]
; CHECK-NEXT:    [[A2:%.*]] = mul i32 [[A1]], [[A1]]
; CHECK-NEXT:    [[A3:%.*]] = mul i32 [[A2]], [[A2]]
; CHECK-NEXT:    [[A4:%.*]] = mul i32 [[A3]], [[A3]]
; CHECK-NEXT:    [[A5:%.*]] = mul i32 [[A4]], [[A4]]
; CHECK-NEXT:    [[A6:%.*]] = mul i32 [[A5]], [[A5]]
; CHECK-NEXT:    [[A7:%.*]] = mul i32 [[A6]], [[A6]]
; CHECK-NEXT:    [[A8:%.*]] = mul i32 [[A7]], [[A7]]
; CHECK-NEXT:    [[A9:%.*]] = mul i32 [[A8]], [[A8]]
; CHECK-NEXT:    [[A10:%.*]] = mul i32 [[A9]], [[A9]]
; CHECK-NEXT:    [[A11:%.*]] = mul i32 [[A10]], [[A10]]
; CHECK-NEXT:    [[A12:%.*]] = mul i32 [[A11]], [[A11]]
; CHECK-NEXT:    [[A13:%.*]] = mul i32 [[A12]], [[A12]]
; CHECK-NEXT:    [[A14:%.*]] = mul i32 [[A13]], [[A13]]
; CHECK-NEXT:    [[A15:%.*]] = mul i32 [[A14]], [[A14]]
; CHECK-NEXT:    [[A16:%.*]] = mul i32 [[A15]], [[A15]]
; CHECK-NEXT:    [[A17:%.*]] = mul i32 [[A16]], [[A16]]
; CHECK-NEXT:    [[A18:%.*]] = mul i32 [[A17]], [[A17]]
; CHECK-NEXT:    [[A19:%.*]] = mul i32 [[A18]], [[A18]]
; CHECK-NEXT:    [[A20:%.*]] = mul i32 [[A19]], [[A19]]
; CHECK-NEXT:    [[A21:%.*]] = mul i32 [[A20]], [[A20]]
; CHECK-NEXT:    [[A22:%.*]] = mul i32 [[A21]], [[A21]]
; CHECK-NEXT:    [[A23:%.*]] = mul i32 [[A22]], [[A22]]
; CHECK-NEXT:    [[A24:%.*]] = mul i32 [[A23]], [[A23]]
; CHECK-NEXT:    [[A25:%.*]] = mul i32 [[A24]], [[A24]]
; CHECK-NEXT:    [[A26:%.*]] = mul i32 [[A25]], [[A25]]
; CHECK-NEXT:    [[A27:%.*]] = mul i32 [[A26]], [[A26]]
; CHECK-NEXT:    [[A28:%.*]] = mul i32 [[A27]], [[A27]]
; CHECK-NEXT:    [[A29:%.*]] = mul i32 [[A28]], [[A28]]
; CHECK-NEXT:    [[A30:%.*]] = mul i32 [[A29]], [[A29]]
; CHECK-NEXT:    [[COND:%.*]] = trunc i32 [[A30]] to i1
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 true, [[COND]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
;

; Eliding the earlier 29 multiplications for brevity

entry:
  call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
  %a1 = mul i32 %a0, %a0
  %a2 = mul i32 %a1, %a1
  %a3 = mul i32 %a2, %a2
  %a4 = mul i32 %a3, %a3
  %a5 = mul i32 %a4, %a4
  %a6 = mul i32 %a5, %a5
  %a7 = mul i32 %a6, %a6
  %a8 = mul i32 %a7, %a7
  %a9 = mul i32 %a8, %a8
  %a10 = mul i32 %a9, %a9
  %a11 = mul i32 %a10, %a10
  %a12 = mul i32 %a11, %a11
  %a13 = mul i32 %a12, %a12
  %a14 = mul i32 %a13, %a13
  %a15 = mul i32 %a14, %a14
  %a16 = mul i32 %a15, %a15
  %a17 = mul i32 %a16, %a16
  %a18 = mul i32 %a17, %a17
  %a19 = mul i32 %a18, %a18
  %a20 = mul i32 %a19, %a19
  %a21 = mul i32 %a20, %a20
  %a22 = mul i32 %a21, %a21
  %a23 = mul i32 %a22, %a22
  %a24 = mul i32 %a23, %a23
  %a25 = mul i32 %a24, %a24
  %a26 = mul i32 %a25, %a25
  %a27 = mul i32 %a26, %a26
  %a28 = mul i32 %a27, %a27
  %a29 = mul i32 %a28, %a28
  %a30 = mul i32 %a29, %a29
  %cond = trunc i32 %a30 to i1
  call void(i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
  ret void
}

define void @f_13(i32 %a) {
; CHECK-LABEL: @f_13(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = icmp ult i32 [[A]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    [[COND_1:%.*]] = icmp slt i32 [[A]], 10
; CHECK-NEXT:    ret void
; CHECK:       right:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ult i32 %a, 14
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:
  %cond_1 = icmp slt i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void

right:
  ret void
}

define void @f_14(i32 %a) {
; CHECK-LABEL: @f_14(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]]
; CHECK:       left:
; CHECK-NEXT:    [[COND_1:%.*]] = icmp sgt i32 [[A]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
; CHECK:       right:
; CHECK-NEXT:    ret void
;
entry:

  %cond_0 = icmp ult i32 %a, 14
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  br i1 undef, label %left, label %right

left:

  %cond_1 = icmp sgt i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
  ret void

right:
  ret void
}

; Make sure we do not widen guard by trivial true conditions into something.
define void @f_15(i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_15(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
; CHECK-NEXT:    ret void
;
entry:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
  ret void
}

; Make sure we do not widen guard by trivial false conditions into something.
define void @f_16(i1 %cond_0, i1 %cond_1) {
; CHECK-LABEL: @f_16(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ]
; CHECK-NEXT:    ret void
;
entry:

  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
  call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ]
  ret void
}