mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-15 20:51:35 +00:00
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:
parent
284093033f
commit
08d90a3cee
@ -131,6 +131,7 @@ private:
|
||||
|
||||
/// @brief Analysis passes used.
|
||||
//@{
|
||||
const DominatorTree *DT;
|
||||
ScalarEvolution *SE;
|
||||
LoopInfo *LI;
|
||||
RegionInfo *RI;
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
50
polly/test/ScopInfo/non-pure-function-call.ll
Normal file
50
polly/test/ScopInfo/non-pure-function-call.ll
Normal 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()
|
@ -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
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user