llvm/llvm/test/Transforms/JumpThreading/guards.ll

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

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

declare i32 @f1()
declare i32 @f2()

define i32 @branch_implies_guard(i32 %a) {
; CHECK-LABEL: @branch_implies_guard(
; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10
; CHECK-NEXT:    br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]
; CHECK:       T1.split:
; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
; CHECK-NEXT:    [[RETVAL3:%.*]] = add i32 [[V1]], 10
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       F1.split:
; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
; CHECK-NEXT:    [[RETVAL1:%.*]] = add i32 [[V2]], 10
; CHECK-NEXT:    [[CONDGUARD2:%.*]] = icmp slt i32 [[A]], 20
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       Merge:
; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[T1_SPLIT]] ], [ [[RETVAL1]], [[F1_SPLIT]] ]
; CHECK-NEXT:    ret i32 [[TMP1]]
;
  %cond = icmp slt i32 %a, 10
  br i1 %cond, label %T1, label %F1

T1:
  %v1 = call i32 @f1()
  br label %Merge

F1:
  %v2 = call i32 @f2()
  br label %Merge

Merge:
  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
  %retVal = add i32 %retPhi, 10
  %condGuard = icmp slt i32 %a, 20
  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
  ret i32 %retVal
}

define i32 @not_branch_implies_guard(i32 %a) {
; CHECK-LABEL: @not_branch_implies_guard(
; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 20
; CHECK-NEXT:    br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]
; CHECK:       T1.split:
; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
; CHECK-NEXT:    [[RETVAL1:%.*]] = add i32 [[V1]], 10
; CHECK-NEXT:    [[CONDGUARD2:%.*]] = icmp sgt i32 [[A]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       F1.split:
; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
; CHECK-NEXT:    [[RETVAL3:%.*]] = add i32 [[V2]], 10
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       Merge:
; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[F1_SPLIT]] ], [ [[RETVAL1]], [[T1_SPLIT]] ]
; CHECK-NEXT:    ret i32 [[TMP1]]
;
  %cond = icmp slt i32 %a, 20
  br i1 %cond, label %T1, label %F1

T1:
  %v1 = call i32 @f1()
  br label %Merge

F1:
  %v2 = call i32 @f2()
  br label %Merge

Merge:
  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
  %retVal = add i32 %retPhi, 10
  %condGuard = icmp sgt i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
  ret i32 %retVal
}

define i32 @branch_overlaps_guard(i32 %a) {
; CHECK-LABEL: @branch_overlaps_guard(
; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 20
; CHECK-NEXT:    br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]
; CHECK:       T1:
; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       F1:
; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       Merge:
; CHECK-NEXT:    [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]
; CHECK-NEXT:    [[RETVAL:%.*]] = add i32 [[RETPHI]], 10
; CHECK-NEXT:    [[CONDGUARD:%.*]] = icmp slt i32 [[A]], 10
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]
; CHECK-NEXT:    ret i32 [[RETVAL]]
;
  %cond = icmp slt i32 %a, 20
  br i1 %cond, label %T1, label %F1

T1:
  %v1 = call i32 @f1()
  br label %Merge

F1:
  %v2 = call i32 @f2()
  br label %Merge

Merge:
  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
  %retVal = add i32 %retPhi, 10
  %condGuard = icmp slt i32 %a, 10
  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
  ret i32 %retVal
}

define i32 @branch_doesnt_overlap_guard(i32 %a) {
; CHECK-LABEL: @branch_doesnt_overlap_guard(
; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10
; CHECK-NEXT:    br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]
; CHECK:       T1:
; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
; CHECK-NEXT:    br label [[MERGE:%.*]]
; CHECK:       F1:
; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
; CHECK-NEXT:    br label [[MERGE]]
; CHECK:       Merge:
; CHECK-NEXT:    [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]
; CHECK-NEXT:    [[RETVAL:%.*]] = add i32 [[RETPHI]], 10
; CHECK-NEXT:    [[CONDGUARD:%.*]] = icmp sgt i32 [[A]], 20
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]
; CHECK-NEXT:    ret i32 [[RETVAL]]
;
  %cond = icmp slt i32 %a, 10
  br i1 %cond, label %T1, label %F1

T1:
  %v1 = call i32 @f1()
  br label %Merge

F1:
  %v2 = call i32 @f2()
  br label %Merge

Merge:
  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
  %retVal = add i32 %retPhi, 10
  %condGuard = icmp sgt i32 %a, 20
  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
  ret i32 %retVal
}

define i32 @not_a_diamond1(i32 %a, i1 %cond1) {
; CHECK-LABEL: @not_a_diamond1(
; CHECK-NEXT:    br i1 [[COND1:%.*]], label [[PRED:%.*]], label [[EXIT:%.*]]
; CHECK:       Pred:
; CHECK-NEXT:    switch i32 [[A:%.*]], label [[EXIT]] [
; CHECK-NEXT:    i32 10, label [[MERGE:%.*]]
; CHECK-NEXT:    i32 20, label [[MERGE]]
; CHECK-NEXT:    ]
; CHECK:       Merge:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND1]]) [ "deopt"() ]
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       Exit:
; CHECK-NEXT:    ret i32 [[A]]
;
  br i1 %cond1, label %Pred, label %Exit

Pred:
  switch i32 %a, label %Exit [
  i32 10, label %Merge
  i32 20, label %Merge
  ]

Merge:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
  br label %Exit

Exit:
  ret i32 %a
}

define void @not_a_diamond2(i32 %a, i1 %cond1) {
; CHECK-LABEL: @not_a_diamond2(
; CHECK-NEXT:  Pred:
; CHECK-NEXT:    switch i32 [[A:%.*]], label [[EXIT:%.*]] [
; CHECK-NEXT:    i32 10, label [[MERGE:%.*]]
; CHECK-NEXT:    i32 20, label [[MERGE]]
; CHECK-NEXT:    ]
; CHECK:       Merge:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND1:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    ret void
; CHECK:       Exit:
; CHECK-NEXT:    ret void
;
  br label %Parent

Merge:
  call void(i1, ...) @llvm.experimental.guard(i1 %cond1)[ "deopt"() ]
  ret void

Pred:
  switch i32 %a, label %Exit [
  i32 10, label %Merge
  i32 20, label %Merge
  ]

Parent:
  br label %Pred

Exit:
  ret void
}

declare void @never_called(i1)

; LVI uses guard to identify value of %c2 in branch as true, we cannot replace that
; guard with guard(true & c1).
define void @dont_fold_guard(ptr %addr, i32 %i, i32 %length) {
; CHECK-LABEL: @dont_fold_guard(
; CHECK-NEXT:  BB1:
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]
; CHECK-NEXT:    [[C2:%.*]] = icmp eq i32 [[I]], 0
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    call void @never_called(i1 true)
; CHECK-NEXT:    ret void
;
  %c1 = icmp ult i32 %i, %length
  %c2 = icmp eq i32 %i, 0
  %wide.chk = and i1 %c1, %c2
  call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
  br i1 %c2, label %BB1, label %BB2

BB1:
  call void @never_called(i1 %c2)
  ret void

BB2:
  ret void
}

declare void @dummy(i1) nounwind willreturn
; same as dont_fold_guard1 but there's a use immediately after guard and before
; branch. We can fold that use.
define void @dont_fold_guard2(ptr %addr, i32 %i, i32 %length) {
; CHECK-LABEL: @dont_fold_guard2(
; CHECK-NEXT:  BB1:
; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]
; CHECK-NEXT:    [[C2:%.*]] = icmp eq i32 [[I]], 0
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
; CHECK-NEXT:    call void @dummy(i1 true)
; CHECK-NEXT:    call void @never_called(i1 true)
; CHECK-NEXT:    ret void
;
  %c1 = icmp ult i32 %i, %length
  %c2 = icmp eq i32 %i, 0
  %wide.chk = and i1 %c1, %c2
  call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
  call void @dummy(i1 %c2)
  br i1 %c2, label %BB1, label %BB2

BB1:
  call void @never_called(i1 %c2)
  ret void

BB2:
  ret void
}

; same as dont_fold_guard1 but condition %cmp is not an instruction.
; We cannot fold the guard under any circumstance.
; FIXME: We can merge unreachableBB2 into not_zero.
define void @dont_fold_guard3(ptr %addr, i1 %cmp, i32 %i, i32 %length) {
; CHECK-LABEL: @dont_fold_guard3(
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CMP:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    br i1 [[CMP]], label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK:       BB1:
; CHECK-NEXT:    call void @never_called(i1 [[CMP]])
; CHECK-NEXT:    ret void
; CHECK:       BB2:
; CHECK-NEXT:    ret void
;
  call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]
  br i1 %cmp, label %BB1, label %BB2

BB1:
  call void @never_called(i1 %cmp)
  ret void

BB2:
  ret void
}

declare void @f(i1)
; Same as dont_fold_guard1 but use switch instead of branch.
; triggers source code `ProcessThreadableEdges`.
define void @dont_fold_guard4(i1 %cmp1, i32 %i) nounwind {
; CHECK-LABEL: @dont_fold_guard4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[CMP1:%.*]], label [[L2:%.*]], label [[L3:%.*]]
; CHECK:       L2:
; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[I:%.*]], 0
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ]
; CHECK-NEXT:    call void @dummy(i1 true)
; CHECK-NEXT:    call void @f(i1 true)
; CHECK-NEXT:    ret void
; CHECK:       L3:
; CHECK-NEXT:    ret void
;
entry:
  br i1 %cmp1, label %L0, label %L3
L0:
  %cmp = icmp eq i32 %i, 0
  call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]
  call void @dummy(i1 %cmp)
  switch i1 %cmp, label %L3 [
  i1 false, label %L1
  i1 true, label %L2
  ]

L1:
  ret void
L2:
  call void @f(i1 %cmp)
  ret void
L3:
  ret void
}

; Make sure that we don't PRE a non-speculable load across a guard.
define void @unsafe_pre_across_guard(ptr %p, i1 %load.is.valid) {
; CHECK-LABEL: @unsafe_pre_across_guard(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1
; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
  %loaded = load i8, ptr %p
  %continue = icmp eq i8 %loaded, 0
  br i1 %continue, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

; Make sure that we can safely PRE a speculable load across a guard.
define void @safe_pre_across_guard(ptr noalias nocapture readonly dereferenceable(8) %p, i1 %load.is.valid) nofree nosync {
; CHECK-LABEL: @safe_pre_across_guard(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]
; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;

entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
  %loaded = load i8, ptr %p
  %continue = icmp eq i8 %loaded, 0
  br i1 %continue, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

; Make sure that we don't PRE a non-speculable load across a call which may
; alias with the load.
define void @unsafe_pre_across_call(ptr %p) {
; CHECK-LABEL: @unsafe_pre_across_call(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @f1()
; CHECK-NEXT:    [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1
; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  call i32 @f1()
  %loaded = load i8, ptr %p
  %continue = icmp eq i8 %loaded, 0
  br i1 %continue, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

; Make sure that we can safely PRE a speculable load across a call.
define void @safe_pre_across_call(ptr noalias nocapture readonly dereferenceable(8) %p) nofree nosync {
; CHECK-LABEL: @safe_pre_across_call(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @f1()
; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;

entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  call i32 @f1()
  %loaded = load i8, ptr %p
  %continue = icmp eq i8 %loaded, 0
  br i1 %continue, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}