forked from OSchip/llvm-project
Compute safety information in a much finer granularity.
Summary: Instead of keeping a variable indicating whether there are early exits in the loop. We keep all the early exits. This improves LICM's ability to move instructions out of the loop based on is-guaranteed-to-execute. I am going to update compilation time as well soon. Reviewers: hfinkel, sanjoy, efriedma, mkuper Reviewed By: hfinkel Subscribers: llvm-commits, mzolotukhin Differential Revision: https://reviews.llvm.org/D32433 llvm-svn: 301196
This commit is contained in:
parent
9c66185315
commit
a266923d57
|
@ -46,9 +46,9 @@ class TargetLibraryInfo;
|
||||||
/// \brief Captures loop safety information.
|
/// \brief Captures loop safety information.
|
||||||
/// It keep information for loop & its header may throw exception.
|
/// It keep information for loop & its header may throw exception.
|
||||||
struct LoopSafetyInfo {
|
struct LoopSafetyInfo {
|
||||||
bool MayThrow = false; // The current loop contains an instruction which
|
// The early exits in the loop, excluding loop exits.
|
||||||
// may throw.
|
// These are calls that might throw, infinite loop, etc.
|
||||||
bool HeaderMayThrow = false; // Same as previous, but specific to loop header
|
SmallVector<Instruction *, 4> EarlyExits;
|
||||||
// Used to update funclet bundle operands.
|
// Used to update funclet bundle operands.
|
||||||
DenseMap<BasicBlock *, ColorVector> BlockColors;
|
DenseMap<BasicBlock *, ColorVector> BlockColors;
|
||||||
|
|
||||||
|
|
|
@ -478,27 +478,17 @@ bool llvm::hoistRegion(DomTreeNode *N, AliasAnalysis *AA, LoopInfo *LI,
|
||||||
///
|
///
|
||||||
void llvm::computeLoopSafetyInfo(LoopSafetyInfo *SafetyInfo, Loop *CurLoop) {
|
void llvm::computeLoopSafetyInfo(LoopSafetyInfo *SafetyInfo, Loop *CurLoop) {
|
||||||
assert(CurLoop != nullptr && "CurLoop cant be null");
|
assert(CurLoop != nullptr && "CurLoop cant be null");
|
||||||
BasicBlock *Header = CurLoop->getHeader();
|
// Iterate over loop instructions and compute early exit points.
|
||||||
// Setting default safety values.
|
for (Loop::block_iterator BB = CurLoop->block_begin(),
|
||||||
SafetyInfo->MayThrow = false;
|
|
||||||
SafetyInfo->HeaderMayThrow = false;
|
|
||||||
// Iterate over header and compute safety info.
|
|
||||||
for (BasicBlock::iterator I = Header->begin(), E = Header->end();
|
|
||||||
(I != E) && !SafetyInfo->HeaderMayThrow; ++I)
|
|
||||||
SafetyInfo->HeaderMayThrow |=
|
|
||||||
!isGuaranteedToTransferExecutionToSuccessor(&*I);
|
|
||||||
|
|
||||||
SafetyInfo->MayThrow = SafetyInfo->HeaderMayThrow;
|
|
||||||
// Iterate over loop instructions and compute safety info.
|
|
||||||
// Skip header as it has been computed and stored in HeaderMayThrow.
|
|
||||||
// The first block in loopinfo.Blocks is guaranteed to be the header.
|
|
||||||
assert(Header == *CurLoop->getBlocks().begin() && "First block must be header");
|
|
||||||
for (Loop::block_iterator BB = std::next(CurLoop->block_begin()),
|
|
||||||
BBE = CurLoop->block_end();
|
BBE = CurLoop->block_end();
|
||||||
(BB != BBE) && !SafetyInfo->MayThrow; ++BB)
|
BB != BBE; ++BB) {
|
||||||
for (BasicBlock::iterator I = (*BB)->begin(), E = (*BB)->end();
|
for (BasicBlock::iterator I = (*BB)->begin(), E = (*BB)->end(); I != E;
|
||||||
(I != E) && !SafetyInfo->MayThrow; ++I)
|
++I) {
|
||||||
SafetyInfo->MayThrow |= !isGuaranteedToTransferExecutionToSuccessor(&*I);
|
if (isGuaranteedToTransferExecutionToSuccessor(&*I))
|
||||||
|
continue;
|
||||||
|
SafetyInfo->EarlyExits.push_back(&*I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compute funclet colors if we might sink/hoist in a function with a funclet
|
// Compute funclet colors if we might sink/hoist in a function with a funclet
|
||||||
// personality routine.
|
// personality routine.
|
||||||
|
@ -1094,7 +1084,7 @@ bool llvm::promoteLoopAccessesToScalars(
|
||||||
|
|
||||||
// Do we know this object does not escape ?
|
// Do we know this object does not escape ?
|
||||||
bool IsKnownNonEscapingObject = false;
|
bool IsKnownNonEscapingObject = false;
|
||||||
if (SafetyInfo->MayThrow) {
|
if (!SafetyInfo->EarlyExits.empty()) {
|
||||||
// If a loop can throw, we have to insert a store along each unwind edge.
|
// If a loop can throw, we have to insert a store along each unwind edge.
|
||||||
// That said, we can't actually make the unwind edge explicit. Therefore,
|
// That said, we can't actually make the unwind edge explicit. Therefore,
|
||||||
// we have to prove that the store is dead along the unwind edge.
|
// we have to prove that the store is dead along the unwind edge.
|
||||||
|
|
|
@ -272,7 +272,7 @@ bool LoopIdiomRecognize::runOnCountableLoop() {
|
||||||
// Give up if the loop has instructions may throw.
|
// Give up if the loop has instructions may throw.
|
||||||
LoopSafetyInfo SafetyInfo;
|
LoopSafetyInfo SafetyInfo;
|
||||||
computeLoopSafetyInfo(&SafetyInfo, CurLoop);
|
computeLoopSafetyInfo(&SafetyInfo, CurLoop);
|
||||||
if (SafetyInfo.MayThrow)
|
if (!SafetyInfo.EarlyExits.empty())
|
||||||
return MadeChange;
|
return MadeChange;
|
||||||
|
|
||||||
// Scan all the blocks in the loop that are not in subloops.
|
// Scan all the blocks in the loop that are not in subloops.
|
||||||
|
|
|
@ -1044,21 +1044,11 @@ bool llvm::isGuaranteedToExecute(const Instruction &Inst,
|
||||||
const DominatorTree *DT, const Loop *CurLoop,
|
const DominatorTree *DT, const Loop *CurLoop,
|
||||||
const LoopSafetyInfo *SafetyInfo) {
|
const LoopSafetyInfo *SafetyInfo) {
|
||||||
// We have to check to make sure that the instruction dominates all
|
// We have to check to make sure that the instruction dominates all
|
||||||
// of the exit blocks. If it doesn't, then there is a path out of the loop
|
// of the exit points. If it doesn't, then there is a path out of the loop
|
||||||
// which does not execute this instruction, so we can't hoist it.
|
// which does not execute this instruction and its not guaranteed to execute.
|
||||||
|
for (Instruction *ExitInst : SafetyInfo->EarlyExits)
|
||||||
// If the instruction is in the header block for the loop (which is very
|
if (!DT->dominates(&Inst, ExitInst))
|
||||||
// common), it is always guaranteed to dominate the exit blocks. Since this
|
return false;
|
||||||
// is a common case, and can save some work, check it now.
|
|
||||||
if (Inst.getParent() == CurLoop->getHeader())
|
|
||||||
// If there's a throw in the header block, we can't guarantee we'll reach
|
|
||||||
// Inst.
|
|
||||||
return !SafetyInfo->HeaderMayThrow;
|
|
||||||
|
|
||||||
// Somewhere in this loop there is an instruction which may throw and make us
|
|
||||||
// exit the loop.
|
|
||||||
if (SafetyInfo->MayThrow)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Get the exit blocks for the current loop.
|
// Get the exit blocks for the current loop.
|
||||||
SmallVector<BasicBlock *, 8> ExitBlocks;
|
SmallVector<BasicBlock *, 8> ExitBlocks;
|
||||||
|
@ -1071,7 +1061,9 @@ bool llvm::isGuaranteedToExecute(const Instruction &Inst,
|
||||||
|
|
||||||
// As a degenerate case, if the loop is statically infinite then we haven't
|
// As a degenerate case, if the loop is statically infinite then we haven't
|
||||||
// proven anything since there are no exit blocks.
|
// proven anything since there are no exit blocks.
|
||||||
if (ExitBlocks.empty())
|
// However, we also special case instruction from the header as the header
|
||||||
|
// is always guaranteed to execute.
|
||||||
|
if (ExitBlocks.empty() && Inst.getParent() != CurLoop->getHeader())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// FIXME: In general, we have to prove that the loop isn't an infinite loop.
|
// FIXME: In general, we have to prove that the loop isn't an infinite loop.
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
; RUN: opt -S -licm < %s | FileCheck %s
|
||||||
|
|
||||||
|
declare void @use(i64 %a)
|
||||||
|
declare void @use_nothrow(i64 %a) nounwind
|
||||||
|
declare void @use_nothing()
|
||||||
|
|
||||||
|
; We can move this udiv out of the loop as it comes before
|
||||||
|
; the call instruction that may throw.
|
||||||
|
define void @throw_header1(i64 %x, i64 %y, i1* %cond) {
|
||||||
|
; CHECK-LABEL: throw_header1
|
||||||
|
; CHECK: %div = udiv i64 %x, %y
|
||||||
|
; CHECK-LABEL: loop
|
||||||
|
; CHECK: call void @use(i64 %div)
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop: ; preds = %entry, %for.inc
|
||||||
|
%div = udiv i64 %x, %y
|
||||||
|
call void @use(i64 %div)
|
||||||
|
br label %loop
|
||||||
|
}
|
||||||
|
|
||||||
|
; We can not move this udiv out of the loop as it comes after
|
||||||
|
; the call instruction that may throw.
|
||||||
|
define void @throw_header2(i64 %x, i64 %y, i1* %cond) {
|
||||||
|
; CHECK-LABEL: throw_header2
|
||||||
|
; CHECK-LABEL: loop
|
||||||
|
; CHECK: call void @use_nothing()
|
||||||
|
; CHECK: %div = udiv i64 %x, %y
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop: ; preds = %entry, %for.inc
|
||||||
|
call void @use_nothing()
|
||||||
|
%div = udiv i64 %x, %y
|
||||||
|
call void @use_nothrow(i64 %div)
|
||||||
|
br label %loop
|
||||||
|
}
|
||||||
|
|
||||||
|
; We can move this udiv out of the loop as it comes before
|
||||||
|
; the call instruction that may throw.
|
||||||
|
define void @throw_body1(i64 %x, i64 %y, i1* %cond) {
|
||||||
|
; CHECK-LABEL: throw_body1
|
||||||
|
; CHECK: %div = udiv i64 %x, %y
|
||||||
|
; CHECK-LABEL: loop
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop: ; preds = %entry, %for.inc
|
||||||
|
br label %body
|
||||||
|
|
||||||
|
body:
|
||||||
|
%div = udiv i64 %x, %y
|
||||||
|
call void @use(i64 %div)
|
||||||
|
br i1 undef, label %loop, label %exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; We can not move this udiv out of the loop as it comes after
|
||||||
|
; the call instruction that may throw.
|
||||||
|
define void @throw_body2(i64 %x, i64 %y, i1* %cond) {
|
||||||
|
; CHECK-LABEL: throw_body2
|
||||||
|
; CHECK-LABEL: loop
|
||||||
|
; CHECK: call void @use_nothing()
|
||||||
|
; CHECK: %div = udiv i64 %x, %y
|
||||||
|
entry:
|
||||||
|
br label %loop
|
||||||
|
|
||||||
|
loop: ; preds = %entry, %for.inc
|
||||||
|
br label %body
|
||||||
|
|
||||||
|
body:
|
||||||
|
call void @use_nothing()
|
||||||
|
%div = udiv i64 %x, %y
|
||||||
|
call void @use_nothrow(i64 %div)
|
||||||
|
br i1 undef, label %loop, label %exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ret void
|
||||||
|
}
|
|
@ -21,20 +21,6 @@ loop2:
|
||||||
call void @use_nothrow(i64 %div)
|
call void @use_nothrow(i64 %div)
|
||||||
br label %loop
|
br label %loop
|
||||||
}
|
}
|
||||||
; Negative test
|
|
||||||
define void @throw_header(i64 %x, i64 %y, i1* %cond) {
|
|
||||||
; CHECK-LABEL: throw_header
|
|
||||||
; CHECK-LABEL: loop
|
|
||||||
; CHECK: %div = udiv i64 %x, %y
|
|
||||||
; CHECK: call void @use(i64 %div)
|
|
||||||
entry:
|
|
||||||
br label %loop
|
|
||||||
|
|
||||||
loop: ; preds = %entry, %for.inc
|
|
||||||
%div = udiv i64 %x, %y
|
|
||||||
call void @use(i64 %div)
|
|
||||||
br label %loop
|
|
||||||
}
|
|
||||||
|
|
||||||
; The header is known no throw, but the loop is not. We can
|
; The header is known no throw, but the loop is not. We can
|
||||||
; still lift out of the header.
|
; still lift out of the header.
|
||||||
|
|
Loading…
Reference in New Issue