Files
clang-p2996/llvm/test/Transforms/JumpThreading/guards.ll
Nikita Popov 5d12b976b0 [ValueTracking] Don't assume readonly function will return
This is similar to D94106, but for the
isGuaranteedToTransferExecutionToSuccessor() helper. We should not
assume that readonly functions will return, as this is only true for
mustprogress functions (in which case we already infer willreturn).
As with the DCE change, for now continue assuming that readonly
intrinsics will return, as not all target intrinsics have been
annotated yet.

Differential Revision: https://reviews.llvm.org/D95288
2021-01-24 10:40:21 +01:00

384 lines
10 KiB
LLVM

; RUN: opt < %s -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(
%cond = icmp slt i32 %a, 10
br i1 %cond, label %T1, label %F1
T1:
; CHECK: T1.split
; CHECK: %v1 = call i32 @f1()
; CHECK-NEXT: %retVal
; CHECK-NEXT: br label %Merge
%v1 = call i32 @f1()
br label %Merge
F1:
; CHECK: F1.split
; CHECK: %v2 = call i32 @f2()
; CHECK-NEXT: %retVal
; CHECK-NEXT: %condGuard
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %condGuard
; CHECK-NEXT: br label %Merge
%v2 = call i32 @f2()
br label %Merge
Merge:
; CHECK: Merge
; CHECK-NOT: call void(i1, ...) @llvm.experimental.guard(
%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(
%cond = icmp slt i32 %a, 20
br i1 %cond, label %T1, label %F1
T1:
; CHECK: T1.split:
; CHECK-NEXT: %v1 = call i32 @f1()
; CHECK-NEXT: %retVal
; CHECK-NEXT: %condGuard
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %condGuard
; CHECK-NEXT: br label %Merge
%v1 = call i32 @f1()
br label %Merge
F1:
; CHECK: F1.split:
; CHECK-NEXT: %v2 = call i32 @f2()
; CHECK-NEXT: %retVal
; CHECK-NEXT: br label %Merge
%v2 = call i32 @f2()
br label %Merge
Merge:
; CHECK: Merge
; CHECK-NOT: call void(i1, ...) @llvm.experimental.guard(
%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(
%cond = icmp slt i32 %a, 20
br i1 %cond, label %T1, label %F1
T1:
; CHECK: T1:
; CHECK-NEXT: %v1 = call i32 @f1()
; CHECK-NEXT: br label %Merge
%v1 = call i32 @f1()
br label %Merge
F1:
; CHECK: F1:
; CHECK-NEXT: %v2 = call i32 @f2()
; CHECK-NEXT: br label %Merge
%v2 = call i32 @f2()
br label %Merge
Merge:
; CHECK: Merge
; CHECK: %condGuard = icmp slt i32 %a, 10
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
%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(
%cond = icmp slt i32 %a, 10
br i1 %cond, label %T1, label %F1
T1:
; CHECK: T1:
; CHECK-NEXT: %v1 = call i32 @f1()
; CHECK-NEXT: br label %Merge
%v1 = call i32 @f1()
br label %Merge
F1:
; CHECK: F1:
; CHECK-NEXT: %v2 = call i32 @f2()
; CHECK-NEXT: br label %Merge
%v2 = call i32 @f2()
br label %Merge
Merge:
; CHECK: Merge
; CHECK: %condGuard = icmp sgt i32 %a, 20
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
%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(
br i1 %cond1, label %Pred, label %Exit
Pred:
; CHECK: Pred:
; CHECK-NEXT: switch i32 %a, label %Exit
switch i32 %a, label %Exit [
i32 10, label %Merge
i32 20, label %Merge
]
Merge:
; CHECK: Merge:
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
; CHECK-NEXT: br label %Exit
call void(i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
br label %Exit
Exit:
; CHECK: Exit:
; CHECK-NEXT: ret i32 %a
ret i32 %a
}
define void @not_a_diamond2(i32 %a, i1 %cond1) {
; CHECK-LABEL: @not_a_diamond2(
br label %Parent
Merge:
call void(i1, ...) @llvm.experimental.guard(i1 %cond1)[ "deopt"() ]
ret void
Pred:
; CHECK-NEXT: Pred:
; CHECK-NEXT: switch i32 %a, label %Exit
switch i32 %a, label %Exit [
i32 10, label %Merge
i32 20, label %Merge
]
Parent:
br label %Pred
Exit:
; CHECK: Merge:
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
; CHECK-NEXT: ret void
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(i8* %addr, i32 %i, i32 %length) {
; CHECK-LABEL: dont_fold_guard
; CHECK: %wide.chk = and i1 %c1, %c2
; CHECK-NEXT: experimental.guard(i1 %wide.chk)
; 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(i8* %addr, i32 %i, i32 %length) {
; CHECK-LABEL: dont_fold_guard2
; CHECK: %wide.chk = and i1 %c1, %c2
; CHECK-NEXT: experimental.guard(i1 %wide.chk)
; CHECK-NEXT: 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(i8* %addr, i1 %cmp, i32 %i, i32 %length) {
; CHECK-LABEL: dont_fold_guard3
; CHECK: guard(i1 %cmp)
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-LABEL: L2:
; CHECK-NEXT: %cmp = icmp eq i32 %i, 0
; CHECK-NEXT: guard(i1 %cmp)
; CHECK-NEXT: dummy(i1 true)
; CHECK-NEXT: @f(i1 true)
; 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(i8* %p, i1 %load.is.valid) {
; CHECK-LABEL: @unsafe_pre_across_guard(
; CHECK-NOT: loaded.pr
; CHECK: 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, i8* %p
; CHECK-NEXT: %continue = icmp eq i8 %loaded, 0
; CHECK-NEXT: br i1 %continue, label %exit, label %loop
entry:
br label %loop
loop: ; preds = %loop, %entry
call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
%loaded = load i8, i8* %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(i8* noalias nocapture readonly dereferenceable(8) %p, i1 %load.is.valid) {
; CHECK-LABEL: @safe_pre_across_guard(
; CHECK: entry:
; CHECK-NEXT: %loaded.pr = load i8, i8* %p
; 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
entry:
br label %loop
loop: ; preds = %loop, %entry
call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
%loaded = load i8, i8* %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(i8* %p) {
; CHECK-LABEL: @unsafe_pre_across_call(
; CHECK-NOT: loaded.pr
; CHECK: entry:
; CHECK-NEXT: br label %loop
; CHECK: loop:
; CHECK-NEXT: call i32 @f1()
; CHECK-NEXT: %loaded = load i8, i8* %p
; CHECK-NEXT: %continue = icmp eq i8 %loaded, 0
; CHECK-NEXT: br i1 %continue, label %exit, label %loop
entry:
br label %loop
loop: ; preds = %loop, %entry
call i32 @f1()
%loaded = load i8, i8* %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(i8* noalias nocapture readonly dereferenceable(8) %p) {
; CHECK-LABEL: @safe_pre_across_call(
; CHECK: entry:
; CHECK-NEXT: %loaded.pr = load i8, i8* %p
; CHECK-NEXT: br label %loop
; CHECK: loop:
; CHECK-NEXT: %loaded = phi i8 [ %loaded, %loop ], [ %loaded.pr, %entry ]
; CHECK-NEXT: call i32 @f1()
; CHECK-NEXT: %continue = icmp eq i8 %loaded, 0
; CHECK-NEXT: br i1 %continue, label %exit, label %loop
entry:
br label %loop
loop: ; preds = %loop, %entry
call i32 @f1()
%loaded = load i8, i8* %p
%continue = icmp eq i8 %loaded, 0
br i1 %continue, label %exit, label %loop
exit: ; preds = %loop
ret void
}