Treat conditionally executed non-pure calls as errors

This replaces the support for user defined error functions by a
  heuristic that tries to determine if a call to a non-pure function
  should be considered "an error". If so the block is assumed not to be
  executed at runtime. While treating all non-pure function calls as
  errors will allow a lot more regions to be analyzed, it will also
  cause us to dismiss a lot again due to an infeasible runtime context.
  This patch tries to limit that effect. A non-pure function call is
  considered an error if it is executed only in conditionally with
  regards to a cheap but simple heuristic.

llvm-svn: 249611
This commit is contained in:
Johannes Doerfert 2015-10-07 20:32:43 +00:00
parent 284093033f
commit 08d90a3cee
8 changed files with 97 additions and 26 deletions

View File

@ -131,6 +131,7 @@ private:
/// @brief Analysis passes used.
//@{
const DominatorTree *DT;
ScalarEvolution *SE;
LoopInfo *LI;
RegionInfo *RI;

View File

@ -121,13 +121,20 @@ llvm::Value *expandCodeFor(Scop &S, llvm::ScalarEvolution &SE,
/// the following conditions:
///
/// - It is terminated by an unreachable instruction
/// - It contains a call to a function listed in the command line argument
/// --polly-error-functions=name1,name2,name3
/// - It contains a call to a non-pure function that is not immediately
/// dominated by a loop header and that does not dominate the region exit.
/// This is a heuristic to pick only error blocks that are conditionally
/// executed and can be assumed to be not executed at all without the domains
/// beeing available.
///
/// @param BB The block to check.
/// @param R The analyzed region.
/// @param LI The loop info analysis.
/// @param DT The dominator tree of the function.
///
/// @return True if the block is a error block, false otherwise.
bool isErrorBlock(llvm::BasicBlock &BB);
bool isErrorBlock(llvm::BasicBlock &BB, const llvm::Region &R,
llvm::LoopInfo &LI, const llvm::DominatorTree &DT);
/// @brief Return the condition for the terminator @p TI.
///

View File

@ -973,7 +973,7 @@ bool ScopDetection::allBlocksValid(DetectionContext &Context) const {
for (BasicBlock *BB : CurRegion.blocks()) {
// Do not check exception blocks as we will never include them in the SCoP.
if (isErrorBlock(*BB))
if (isErrorBlock(*BB, CurRegion, *LI, *DT))
continue;
if (!isValidCFG(*BB, false, Context) && !KeepGoing)
@ -1096,6 +1096,7 @@ bool ScopDetection::runOnFunction(llvm::Function &F) {
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
Region *TopRegion = RI->getTopLevelRegion();
releaseMemory();
@ -1171,6 +1172,7 @@ void polly::ScopDetection::verifyAnalysis() const {
void ScopDetection::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<LoopInfoWrapperPass>();
AU.addRequired<ScalarEvolutionWrapperPass>();
AU.addRequired<DominatorTreeWrapperPass>();
// We also need AA and RegionInfo when we are verifying analysis.
AU.addRequiredTransitive<AAResultsWrapperPass>();
AU.addRequiredTransitive<RegionInfoPass>();
@ -1205,6 +1207,7 @@ INITIALIZE_PASS_BEGIN(ScopDetection, "polly-detect",
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
INITIALIZE_PASS_END(ScopDetection, "polly-detect",
"Polly - Detect static control parts (SCoPs)", false, false)

View File

@ -1698,11 +1698,12 @@ static inline unsigned getNumBlocksInRegionNode(RegionNode *RN) {
return NumBlocks;
}
static bool containsErrorBlock(RegionNode *RN) {
static bool containsErrorBlock(RegionNode *RN, const Region &R, LoopInfo &LI,
const DominatorTree &DT) {
if (!RN->isSubRegion())
return isErrorBlock(*RN->getNodeAs<BasicBlock>());
return isErrorBlock(*RN->getNodeAs<BasicBlock>(), R, LI, DT);
for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
if (isErrorBlock(*BB))
if (isErrorBlock(*BB, R, LI, DT))
return true;
return false;
}
@ -1785,7 +1786,7 @@ void Scop::buildDomainsWithBranchConstraints(Region *R) {
// the predecessors and can therefor look at the domain of a error block.
// That allows us to generate the assumptions needed for them not to be
// executed at runtime.
if (containsErrorBlock(RN))
if (containsErrorBlock(RN, getRegion(), LI, DT))
continue;
BasicBlock *BB = getRegionNodeBasicBlock(RN);
@ -1993,7 +1994,7 @@ void Scop::propagateDomainConstraints(Region *R) {
addLoopBoundsToHeaderDomain(BBLoop);
// Add assumptions for error blocks.
if (containsErrorBlock(RN)) {
if (containsErrorBlock(RN, getRegion(), LI, DT)) {
IsOptimized = true;
isl_set *DomPar = isl_set_params(isl_set_copy(Domain));
addAssumption(isl_set_complement(DomPar));
@ -2888,7 +2889,7 @@ bool Scop::isIgnored(RegionNode *RN) {
return true;
// Check if error blocks are contained.
if (containsErrorBlock(RN))
if (containsErrorBlock(RN, getRegion(), LI, DT))
return true;
return false;

View File

@ -28,11 +28,6 @@ using namespace polly;
#define DEBUG_TYPE "polly-scop-helper"
static cl::list<std::string>
ErrorFunctions("polly-error-functions",
cl::desc("A list of error functions"), cl::Hidden,
cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory));
Value *polly::getPointerOperand(Instruction &Inst) {
if (LoadInst *load = dyn_cast<LoadInst>(&Inst))
return load->getPointerOperand();
@ -346,22 +341,36 @@ Value *polly::expandCodeFor(Scop &S, ScalarEvolution &SE, const DataLayout &DL,
return Expander.expandCodeFor(E, Ty, IP);
}
bool polly::isErrorBlock(BasicBlock &BB) {
bool polly::isErrorBlock(BasicBlock &BB, const Region &R, LoopInfo &LI,
const DominatorTree &DT) {
if (isa<UnreachableInst>(BB.getTerminator()))
return true;
if (ErrorFunctions.empty())
if (LI.isLoopHeader(&BB))
return false;
if (DT.dominates(&BB, R.getExit()))
return false;
// FIXME: This is a simple heuristic to determine if the load is executed
// in a conditional. However, we actually would need the control
// condition, i.e., the post dominance frontier. Alternatively we
// could walk up the dominance tree until we find a block that is
// not post dominated by the load and check if it is a conditional
// or a loop header.
auto *DTNode = DT.getNode(&BB);
auto *IDomBB = DTNode->getIDom()->getBlock();
if (LI.isLoopHeader(IDomBB))
return false;
for (Instruction &Inst : BB)
if (CallInst *CI = dyn_cast<CallInst>(&Inst))
if (Function *F = CI->getCalledFunction()) {
const auto &FnName = F->getName();
for (const auto &ErrorFn : ErrorFunctions)
if (FnName.equals(ErrorFn))
return true;
}
if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
if (!CI->doesNotAccessMemory())
return true;
if (CI->doesNotReturn())
return true;
}
return false;
}

View File

@ -0,0 +1,50 @@
; RUN: opt %loadPolly -polly-scops -analyze < %s | FileCheck %s
;
; CHECK: Assumed Context:
; CHECK-NEXT: [N] -> { : N <= 101 }
;
; void g(void);
; void f(int *A, int N) {
; for (int i = 0; i < N; i++) {
; if (i > 100)
; g();
; A[i]++;
; }
; }
;
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @f(i32* %A, i32 %N) {
entry:
%tmp = sext i32 %N to i64
br label %for.cond
for.cond: ; preds = %for.inc, %entry
%indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ]
%cmp = icmp slt i64 %indvars.iv, %tmp
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%cmp1 = icmp sgt i64 %indvars.iv, 100
br i1 %cmp1, label %if.then, label %if.end
if.then: ; preds = %for.body
call void @g() #2
br label %if.end
if.end: ; preds = %if.then, %for.body
%arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv
%tmp1 = load i32, i32* %arrayidx, align 4
%inc = add nsw i32 %tmp1, 1
store i32 %inc, i32* %arrayidx, align 4
br label %for.inc
for.inc: ; preds = %if.end
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
br label %for.cond
for.end: ; preds = %for.cond
ret void
}
declare void @g()

View File

@ -1,4 +1,4 @@
; RUN: opt %loadPolly -polly-scops -polly-error-functions=timer_start,timer_stop -analyze < %s | FileCheck %s
; RUN: opt %loadPolly -polly-scops -analyze < %s | FileCheck %s
;
; Error blocks are skipped during SCoP detection. Hence, we have to skip
; them during SCoP too as they might contain accesses or branches we cannot

View File

@ -1,4 +1,4 @@
; RUN: opt %loadPolly -polly-scops -polly-error-functions=timer_start,timer_stop -analyze < %s | FileCheck %s
; RUN: opt %loadPolly -polly-scops -analyze < %s | FileCheck %s
;
; Allow the user to define function names that are treated as
; error functions and assumed not to be executed.