mirror of
https://github.com/RPCSX/llvm.git
synced 2025-02-03 02:53:06 +00:00
[SCEV] Use guards to prove predicates
We can use calls to @llvm.experimental.guard to prove predicates, relying on the fact that in all locations domianted by a call to @llvm.experimental.guard the predicate it is guarding is known to be true. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268997 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e598f97f6c
commit
cbda428c36
@ -464,6 +464,12 @@ namespace llvm {
|
||||
///
|
||||
Function &F;
|
||||
|
||||
/// Does the module have any calls to the llvm.experimental.guard intrinsic
|
||||
/// at all? If this is false, we avoid doing work that will only help if
|
||||
/// thare are guards present in the IR.
|
||||
///
|
||||
bool HasGuards;
|
||||
|
||||
/// The target library information for the target we are targeting.
|
||||
///
|
||||
TargetLibraryInfo &TLI;
|
||||
@ -1007,6 +1013,11 @@ namespace llvm {
|
||||
const SCEV *FoundLHS,
|
||||
const SCEV *FoundRHS);
|
||||
|
||||
/// Return true if the condition denoted by \p LHS \p Pred \p RHS is implied
|
||||
/// by a call to \c @llvm.experimental.guard in \p BB.
|
||||
bool isImpliedViaGuard(BasicBlock *BB, ICmpInst::Predicate Pred,
|
||||
const SCEV *LHS, const SCEV *RHS);
|
||||
|
||||
/// Test whether the condition described by Pred, LHS, and RHS is true
|
||||
/// whenever the condition described by Pred, FoundLHS, and FoundRHS is
|
||||
/// true.
|
||||
|
@ -7805,6 +7805,23 @@ bool ScalarEvolution::isKnownPredicateViaSplitting(ICmpInst::Predicate Pred,
|
||||
isKnownPredicate(CmpInst::ICMP_SLT, LHS, RHS);
|
||||
}
|
||||
|
||||
bool ScalarEvolution::isImpliedViaGuard(BasicBlock *BB,
|
||||
ICmpInst::Predicate Pred,
|
||||
const SCEV *LHS, const SCEV *RHS) {
|
||||
// No need to even try if we know the module has no guards.
|
||||
if (!HasGuards)
|
||||
return false;
|
||||
|
||||
return any_of(*BB, [&](Instruction &I) {
|
||||
using namespace llvm::PatternMatch;
|
||||
|
||||
Value *Condition;
|
||||
return match(&I, m_Intrinsic<Intrinsic::experimental_guard>(
|
||||
m_Value(Condition))) &&
|
||||
isImpliedCond(Pred, LHS, RHS, Condition, false);
|
||||
});
|
||||
}
|
||||
|
||||
/// isLoopBackedgeGuardedByCond - Test whether the backedge of the loop is
|
||||
/// protected by a conditional between LHS and RHS. This is used to
|
||||
/// to eliminate casts.
|
||||
@ -7872,12 +7889,18 @@ ScalarEvolution::isLoopBackedgeGuardedByCond(const Loop *L,
|
||||
if (!DT.isReachableFromEntry(L->getHeader()))
|
||||
return false;
|
||||
|
||||
if (isImpliedViaGuard(Latch, Pred, LHS, RHS))
|
||||
return true;
|
||||
|
||||
for (DomTreeNode *DTN = DT[Latch], *HeaderDTN = DT[L->getHeader()];
|
||||
DTN != HeaderDTN; DTN = DTN->getIDom()) {
|
||||
|
||||
assert(DTN && "should reach the loop header before reaching the root!");
|
||||
|
||||
BasicBlock *BB = DTN->getBlock();
|
||||
if (isImpliedViaGuard(BB, Pred, LHS, RHS))
|
||||
return true;
|
||||
|
||||
BasicBlock *PBB = BB->getSinglePredecessor();
|
||||
if (!PBB)
|
||||
continue;
|
||||
@ -7930,6 +7953,9 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L,
|
||||
Pair.first;
|
||||
Pair = getPredecessorWithUniqueSuccessorForBB(Pair.first)) {
|
||||
|
||||
if (isImpliedViaGuard(Pair.first, Pred, LHS, RHS))
|
||||
return true;
|
||||
|
||||
BranchInst *LoopEntryPredicate =
|
||||
dyn_cast<BranchInst>(Pair.first->getTerminator());
|
||||
if (!LoopEntryPredicate ||
|
||||
@ -9462,11 +9488,26 @@ ScalarEvolution::ScalarEvolution(Function &F, TargetLibraryInfo &TLI,
|
||||
CouldNotCompute(new SCEVCouldNotCompute()),
|
||||
WalkingBEDominatingConds(false), ProvingSplitPredicate(false),
|
||||
ValuesAtScopes(64), LoopDispositions(64), BlockDispositions(64),
|
||||
FirstUnknown(nullptr) {}
|
||||
FirstUnknown(nullptr) {
|
||||
|
||||
// To use guards for proving predicates, we need to scan every instruction in
|
||||
// relevant basic blocks, and not just terminators. Doing this is a waste of
|
||||
// time if the IR does not actually contain any calls to
|
||||
// @llvm.experimental.guard, so do a quick check and remember this beforehand.
|
||||
//
|
||||
// This pessimizes the case where a pass that preserves ScalarEvolution wants
|
||||
// to _add_ guards to the module when there weren't any before, and wants
|
||||
// ScalarEvolution to optimize based on those guards. For now we prefer to be
|
||||
// efficient in lieu of being smart in that rather obscure case.
|
||||
|
||||
auto *GuardDecl = F.getParent()->getFunction(
|
||||
Intrinsic::getName(Intrinsic::experimental_guard));
|
||||
HasGuards = GuardDecl && !GuardDecl->use_empty();
|
||||
}
|
||||
|
||||
ScalarEvolution::ScalarEvolution(ScalarEvolution &&Arg)
|
||||
: F(Arg.F), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT), LI(Arg.LI),
|
||||
CouldNotCompute(std::move(Arg.CouldNotCompute)),
|
||||
: F(Arg.F), HasGuards(Arg.HasGuards), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT),
|
||||
LI(Arg.LI), CouldNotCompute(std::move(Arg.CouldNotCompute)),
|
||||
ValueExprMap(std::move(Arg.ValueExprMap)),
|
||||
WalkingBEDominatingConds(false), ProvingSplitPredicate(false),
|
||||
BackedgeTakenCounts(std::move(Arg.BackedgeTakenCounts)),
|
||||
|
141
test/Analysis/ScalarEvolution/guards.ll
Normal file
141
test/Analysis/ScalarEvolution/guards.ll
Normal file
@ -0,0 +1,141 @@
|
||||
; RUN: opt -S -indvars < %s | FileCheck %s
|
||||
|
||||
; Check that SCEV is able to recognize and use guards to prove
|
||||
; conditions gaurding loop entries and backedges. This isn't intended
|
||||
; to be a comprehensive test of SCEV's simplification capabilities,
|
||||
; tests directly testing e.g. if SCEV can elide a sext should go
|
||||
; elsewhere.
|
||||
|
||||
target datalayout = "n8:16:32:64"
|
||||
|
||||
declare void @llvm.experimental.guard(i1, ...)
|
||||
|
||||
define void @test_1(i1* %cond_buf, i32* %len_buf) {
|
||||
; CHECK-LABEL: @test_1(
|
||||
entry:
|
||||
%len = load i32, i32* %len_buf, !range !{i32 1, i32 2147483648}
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
; CHECK: loop:
|
||||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
|
||||
; CHECK: %iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
; CHECK: leave:
|
||||
|
||||
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
|
||||
%iv.inc = add i32 %iv, 1
|
||||
|
||||
%iv.cmp = icmp slt i32 %iv, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
|
||||
|
||||
%iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
|
||||
%becond = load volatile i1, i1* %cond_buf
|
||||
br i1 %becond, label %loop, label %leave
|
||||
|
||||
leave:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_2(i32 %n, i32* %len_buf) {
|
||||
; CHECK-LABEL: @test_2(
|
||||
; CHECK: [[LEN_SEXT:%[^ ]+]] = sext i32 %len to i64
|
||||
; CHECK: br label %loop
|
||||
|
||||
entry:
|
||||
%len = load i32, i32* %len_buf, !range !{i32 0, i32 2147483648}
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
; CHECK: loop:
|
||||
; CHECK: %indvars.iv = phi i64 [ %indvars.iv.next, %loop ], [ 0, %entry ]
|
||||
; CHECK: %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
|
||||
; CHECK: %iv.inc.cmp = icmp slt i64 %indvars.iv.next, [[LEN_SEXT]]
|
||||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
; CHECK: leave:
|
||||
|
||||
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
|
||||
%iv.inc = add i32 %iv, 1
|
||||
|
||||
%iv.sext = sext i32 %iv to i64
|
||||
|
||||
%iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
|
||||
%becond = icmp ne i32 %iv, %n
|
||||
br i1 %becond, label %loop, label %leave
|
||||
|
||||
leave:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_3(i1* %cond_buf, i32* %len_buf) {
|
||||
; CHECK-LABEL: @test_3(
|
||||
|
||||
entry:
|
||||
%len = load i32, i32* %len_buf
|
||||
%entry.cond = icmp sgt i32 %len, 0
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %entry.cond) [ "deopt"() ]
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
; CHECK: loop:
|
||||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
|
||||
; CHECK: %iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
; CHECK: leave:
|
||||
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
|
||||
%iv.inc = add i32 %iv, 1
|
||||
|
||||
%iv.cmp = icmp slt i32 %iv, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
|
||||
|
||||
%iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
|
||||
%becond = load volatile i1, i1* %cond_buf
|
||||
br i1 %becond, label %loop, label %leave
|
||||
|
||||
leave:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_4(i1* %cond_buf, i32* %len_buf) {
|
||||
; CHECK-LABEL: @test_4(
|
||||
|
||||
entry:
|
||||
%len = load i32, i32* %len_buf
|
||||
%entry.cond = icmp sgt i32 %len, 0
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %entry.cond) [ "deopt"() ]
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %be ]
|
||||
%iv.inc = add i32 %iv, 1
|
||||
|
||||
%cond = load volatile i1, i1* %cond_buf
|
||||
br i1 %cond, label %left, label %be
|
||||
|
||||
left:
|
||||
; Does not dominate the backedge, so cannot be used in the inductive proof
|
||||
%iv.inc.cmp = icmp slt i32 %iv.inc, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
|
||||
br label %be
|
||||
|
||||
be:
|
||||
; CHECK: be:
|
||||
; CHECK-NEXT: %iv.cmp = icmp slt i32 %iv, %len
|
||||
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
|
||||
; CHECK: leave:
|
||||
|
||||
%iv.cmp = icmp slt i32 %iv, %len
|
||||
call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
|
||||
|
||||
%becond = load volatile i1, i1* %cond_buf
|
||||
br i1 %becond, label %loop, label %leave
|
||||
|
||||
leave:
|
||||
ret void
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user