Make the SCoP generation resistent wrt. error blocks

When error blocks are not terminated by an unreachable they have successors
  that might only be reachable via error blocks. Additionally, branches in
  error blocks are not checked during SCoP detection, thus we might not be able
  to handle them. With this patch we do not try to model error block exit
  conditions. Anything that is only reachable via error blocks is ignored too,
  as it will not be executed in the optimized version of the SCoP anyway.

llvm-svn: 249099
This commit is contained in:
Johannes Doerfert 2015-10-01 23:48:18 +00:00
parent f80f3b0449
commit f56738041e
3 changed files with 215 additions and 39 deletions

View File

@ -1192,15 +1192,15 @@ private:
void buildDomains(Region *R, LoopInfo &LI, ScopDetection &SD,
DominatorTree &DT);
/// @brief Check if a basic block is trivial.
/// @brief Check if a region part should be represented in the SCoP or not.
///
/// A trivial basic block does not contain any useful calculation. Therefore,
/// it does not need to be represented as a polyhedral statement.
/// If @p RN does not contain any useful calculation or is only reachable
/// via error blocks we do not model it in the polyhedral representation.
///
/// @param BB The basic block to check
/// @param RN The region part to check.
///
/// @return True if the basic block is trivial, otherwise false.
bool isTrivialBB(BasicBlock *BB);
/// @return True if the part should be ignored, otherwise false.
bool isIgnored(RegionNode *RN);
/// @brief Add parameter constraints to @p C that imply a non-empty domain.
__isl_give isl_set *addNonEmptyDomainConstraints(__isl_take isl_set *C) const;

View File

@ -1693,6 +1693,15 @@ static inline unsigned getNumBlocksInRegionNode(RegionNode *RN) {
return NumBlocks;
}
static bool containsErrorBlock(RegionNode *RN) {
if (!RN->isSubRegion())
return isErrorBlock(*RN->getNodeAs<BasicBlock>());
for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
if (isErrorBlock(*BB))
return true;
return false;
}
///}
static inline __isl_give isl_set *addDomainDimId(__isl_take isl_set *Domain,
@ -1765,22 +1774,29 @@ void Scop::buildDomainsWithBranchConstraints(Region *R, LoopInfo &LI,
}
}
// Error blocks are assumed not to be executed. Therefor they are not
// checked properly in the ScopDetection. Any attempt to generate control
// conditions from them might result in a crash. However, this is only true
// for the first step of the domain generation (this function) where we
// push the control conditions of a block to the successors. In the second
// step (propagateDomainConstraints) we only receive domain constraints from
// 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))
continue;
BasicBlock *BB = getRegionNodeBasicBlock(RN);
TerminatorInst *TI = BB->getTerminator();
// Unreachable instructions do not have successors so we can skip them.
if (isa<UnreachableInst>(TI)) {
// Assume unreachables only in error blocks.
assert(isErrorBlock(*BB));
isl_set *Domain = DomainMap.lookup(BB);
if (!Domain) {
DEBUG(dbgs() << "\tSkip: " << BB->getName()
<< ", it is only reachable from error blocks.\n");
continue;
}
isl_set *Domain = DomainMap[BB];
DEBUG(dbgs() << "\tVisit: " << BB->getName() << " : " << Domain << "\n");
assert(Domain && "Due to reverse post order traversal of the region all "
"predecessor of the current region node should have been "
"visited and a domain for this region node should have "
"been set.");
Loop *BBLoop = getRegionNodeLoop(RN, LI);
int BBLoopDepth = getRelativeLoopDepth(BBLoop);
@ -1873,15 +1889,6 @@ getDomainForBlock(BasicBlock *BB, DenseMap<BasicBlock *, isl_set *> &DomainMap,
return getDomainForBlock(R->getEntry(), DomainMap, RI);
}
static bool containsErrorBlock(RegionNode *RN) {
if (!RN->isSubRegion())
return isErrorBlock(*RN->getNodeAs<BasicBlock>());
for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
if (isErrorBlock(*BB))
return true;
return false;
}
void Scop::propagateDomainConstraints(Region *R, LoopInfo &LI,
ScopDetection &SD, DominatorTree &DT) {
// Iterate over the region R and propagate the domain constrains from the
@ -1909,16 +1916,26 @@ void Scop::propagateDomainConstraints(Region *R, LoopInfo &LI,
}
}
// Get the domain for the current block and check if it was initialized or
// not. The only way it was not is if this block is only reachable via error
// blocks, thus will not be executed under the assumptions we make. Such
// blocks have to be skipped as their predecessors might not have domains
// either. It would not benefit us to compute the domain anyway, only the
// domains of the error blocks that are reachable from non-error blocks
// are needed to generate assumptions.
BasicBlock *BB = getRegionNodeBasicBlock(RN);
isl_set *&Domain = DomainMap[BB];
if (!Domain) {
DEBUG(dbgs() << "\tSkip: " << BB->getName()
<< ", it is only reachable from error blocks.\n");
DomainMap.erase(BB);
continue;
}
DEBUG(dbgs() << "\tVisit: " << BB->getName() << " : " << Domain << "\n");
Loop *BBLoop = getRegionNodeLoop(RN, LI);
int BBLoopDepth = getRelativeLoopDepth(BBLoop);
isl_set *&Domain = DomainMap[BB];
assert(Domain && "Due to reverse post order traversal of the region all "
"predecessor of the current region node should have been "
"visited and a domain for this region node should have "
"been set.");
isl_set *PredDom = isl_set_empty(isl_set_get_space(Domain));
for (auto *PredBB : predecessors(BB)) {
@ -2019,8 +2036,12 @@ void Scop::addLoopBoundsToHeaderDomain(Loop *L, LoopInfo &LI) {
L->getLoopLatches(LatchBlocks);
for (BasicBlock *LatchBB : LatchBlocks) {
assert(DomainMap.count(LatchBB));
isl_set *LatchBBDom = DomainMap[LatchBB];
// If the latch is only reachable via error statements we skip it.
isl_set *LatchBBDom = DomainMap.lookup(LatchBB);
if (!LatchBBDom)
continue;
isl_set *BackedgeCondition = nullptr;
TerminatorInst *TI = LatchBB->getTerminator();
@ -2752,11 +2773,28 @@ bool Scop::restrictDomains(__isl_take isl_union_set *Domain) {
ScalarEvolution *Scop::getSE() const { return SE; }
bool Scop::isTrivialBB(BasicBlock *BB) {
if (getAccessFunctions(BB) && !isErrorBlock(*BB))
return false;
bool Scop::isIgnored(RegionNode *RN) {
BasicBlock *BB = getRegionNodeBasicBlock(RN);
return true;
// Check if there are accesses contained.
bool ContainsAccesses = false;
if (!RN->isSubRegion())
ContainsAccesses = getAccessFunctions(BB);
else
for (BasicBlock *RBB : RN->getNodeAs<Region>()->blocks())
ContainsAccesses |= (getAccessFunctions(RBB) != nullptr);
if (!ContainsAccesses)
return true;
// Check for reachability via non-error blocks.
if (!DomainMap.count(BB))
return true;
// Check if error blocks are contained.
if (containsErrorBlock(RN))
return true;
return false;
}
struct MapToDimensionDataTy {
@ -2863,14 +2901,13 @@ void Scop::buildSchedule(
auto &LSchedulePair = LoopSchedules[L];
LSchedulePair.second += getNumBlocksInRegionNode(RN);
BasicBlock *BB = getRegionNodeBasicBlock(RN);
if (RN->isSubRegion() || !isTrivialBB(BB)) {
if (!isIgnored(RN)) {
ScopStmt *Stmt;
if (RN->isSubRegion())
Stmt = addScopStmt(nullptr, RN->getNodeAs<Region>());
else
Stmt = addScopStmt(BB, nullptr);
Stmt = addScopStmt(RN->getNodeAs<BasicBlock>(), nullptr);
auto *UDomain = isl_union_set_from_set(Stmt->getDomain());
auto *StmtSchedule = isl_schedule_from_domain(UDomain);

View File

@ -0,0 +1,139 @@
; RUN: opt %loadPolly -polly-scops -polly-detect-unprofitable -polly-error-functions=timer_start,timer_stop -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
; handle. Additionally, some blocks might be only reachable via error blocks.
; Such blocks should not be considered to be valid and therefor ignored.
;
; void timer_start(void);
; void timer_stop(void);
; void kernel(int *A, int *B, int timeit, int N) {
;
; if (timeit) {
; timer_start();
; // split BB
; A[0] = 0; // Do not create a statement for this block
; }
;
; for (int i = 0; i < N; i++)
; A[i] += B[i];
;
; if (timeit) {
; timer_stop();
; if (invalid float branch) // Do not crash on the float branch
; timer_start();
; }
;
; for (int i = 0; i < N; i++)
; A[i] += B[i];
;
; if (timeit)
; timer_stop();
; }
;
; The assumed context is empty because all statements are executed only
; if timeit != 0. This is due to the fact that they are not "reached"
; by the error blocks that are executed for timeit == 0.
;
; CHECK: Region: %entry.split---%if.end.20
; CHECK: Assumed Context:
; CHECK-NEXT: [timeit, N] -> { : }
; CHECK: Statements {
; CHECK-NOT: Stmt_if_then_split
; CHECK: Stmt_for_body
; CHECK-NOT: Stmt_if_then_split
; CHECK: Stmt_for_body_9
; CHECK-NOT: Stmt_if_then_split
; CHECK: }
;
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @kernel(i32* %A, i32* %B, i32 %timeit, i32 %N) {
entry:
br label %entry.split
entry.split:
%tobool = icmp eq i32 %timeit, 0
br i1 %tobool, label %for.cond.pre, label %if.then
if.then: ; preds = %entry
call void @timer_start()
br label %if.then.split
; Dead block if we assume if.then not to be executed because of the call
if.then.split: ; preds = %if.then
%A0 = getelementptr inbounds i32, i32* %A, i64 0
store i32 0, i32* %A0, align 4
br label %for.cond.pre
for.cond.pre:
%tmp = sext i32 %N to i64
br label %for.cond
for.cond: ; preds = %for.inc, %if.end
%indvars.iv1 = phi i64 [ %indvars.iv.next2, %for.inc ], [ 0, %for.cond.pre ]
%cmp = icmp slt i64 %indvars.iv1, %tmp
br i1 %cmp, label %for.body, label %for.end
for.body: ; preds = %for.cond
%arrayidx = getelementptr inbounds i32, i32* %B, i64 %indvars.iv1
%tmp3 = load i32, i32* %arrayidx, align 4
%arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv1
%tmp4 = load i32, i32* %arrayidx2, align 4
%add = add nsw i32 %tmp4, %tmp3
store i32 %add, i32* %arrayidx2, align 4
br label %for.inc
for.inc: ; preds = %for.body
%indvars.iv.next2 = add nuw nsw i64 %indvars.iv1, 1
br label %for.cond
for.end: ; preds = %for.cond
%tobool3 = icmp eq i32 %timeit, 0
br i1 %tobool3, label %if.end.5, label %if.then.4
if.then.4: ; preds = %for.end
call void @timer_stop()
%na = fcmp one float 4.0, 5.0
br i1 %na, label %if.end.5, label %if.then.4.rem
if.then.4.rem: ; preds = %for.end
call void @timer_start()
br label %if.end.5
if.end.5: ; preds = %for.end, %if.then.4
%tmp5 = sext i32 %N to i64
br label %for.cond.7
for.cond.7: ; preds = %for.inc.15, %if.end.5
%indvars.iv = phi i64 [ %indvars.iv.next, %for.inc.15 ], [ 0, %if.end.5 ]
%cmp8 = icmp slt i64 %indvars.iv, %tmp5
br i1 %cmp8, label %for.body.9, label %for.end.17
for.body.9: ; preds = %for.cond.7
%arrayidx11 = getelementptr inbounds i32, i32* %B, i64 %indvars.iv
%tmp6 = load i32, i32* %arrayidx11, align 4
%arrayidx13 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv
%tmp7 = load i32, i32* %arrayidx13, align 4
%add14 = add nsw i32 %tmp7, %tmp6
store i32 %add14, i32* %arrayidx13, align 4
br label %for.inc.15
for.inc.15: ; preds = %for.body.9
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
br label %for.cond.7
for.end.17: ; preds = %for.cond.7
%tobool18 = icmp eq i32 %timeit, 0
br i1 %tobool18, label %if.end.20, label %if.then.19
if.then.19: ; preds = %for.end.17
call void @timer_stop()
br label %if.end.20
if.end.20: ; preds = %for.end.17, %if.then.19
ret void
}
declare void @timer_start()
declare void @timer_stop()