Skip to content

Commit

Permalink
[Polly] Introduce caching for the isErrorBlock function. NFC.
Browse files Browse the repository at this point in the history
Compilation of the file insn-attrtab.c of the SPEC CPU 2017 502.gcc_r
benchmark takes excessive time (> 30min) with Polly enabled. Most time
is spent in the isErrorBlock function querying the DominatorTree.
The isErrorBlock is invoked redundantly over the course of ScopDetection
and ScopBuilder. This patch introduces a caching mechanism for its
result.

Instead of a free function, isErrorBlock is moved to ScopDetection where
its cache map resides. This also means that many functions directly or
indirectly calling isErrorBlock are not "const" anymore. The
DetectionContextMap was marked as "mutable", but IMHO it never should
have been since it stores the detection result.

502.gcc_r only takes excessive time with the new pass manager. The
reason seeams to be that it invalidates the ScopDetection analysis more
often than the legacy pass manager, for unknown reasons.
  • Loading branch information
Meinersbur committed Aug 18, 2021
1 parent cc7bcef commit 58e4e71
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 134 deletions.
47 changes: 34 additions & 13 deletions polly/include/polly/ScopDetection.h
Expand Up @@ -214,7 +214,11 @@ class ScopDetection {
/// Map to remember detection contexts for all regions.
using DetectionContextMapTy =
DenseMap<BBPair, std::unique_ptr<DetectionContext>>;
mutable DetectionContextMapTy DetectionContextMap;
DetectionContextMapTy DetectionContextMap;

/// Cache for the isErrorBlock function.
DenseMap<std::tuple<const BasicBlock *, const Region *>, bool>
ErrorBlockCache;

/// Remove cached results for @p R.
void removeCachedResults(const Region &R);
Expand Down Expand Up @@ -305,7 +309,7 @@ class ScopDetection {
/// @param Context The context of scop detection.
///
/// @return True if all blocks in R are valid, false otherwise.
bool allBlocksValid(DetectionContext &Context) const;
bool allBlocksValid(DetectionContext &Context);

/// Check if a region has sufficient compute instructions.
///
Expand Down Expand Up @@ -347,7 +351,7 @@ class ScopDetection {
/// @param Context The context of scop detection.
///
/// @return True if R is a Scop, false otherwise.
bool isValidRegion(DetectionContext &Context) const;
bool isValidRegion(DetectionContext &Context);

/// Check if an intrinsic call can be part of a Scop.
///
Expand Down Expand Up @@ -420,7 +424,7 @@ class ScopDetection {
/// @param Context The context of scop detection.
///
/// @return True if the instruction is valid, false otherwise.
bool isValidInstruction(Instruction &Inst, DetectionContext &Context) const;
bool isValidInstruction(Instruction &Inst, DetectionContext &Context);

/// Check if the switch @p SI with condition @p Condition is valid.
///
Expand All @@ -444,7 +448,7 @@ class ScopDetection {
///
/// @return True if the branch @p BI is valid.
bool isValidBranch(BasicBlock &BB, BranchInst *BI, Value *Condition,
bool IsLoopBranch, DetectionContext &Context) const;
bool IsLoopBranch, DetectionContext &Context);

/// Check if the SCEV @p S is affine in the current @p Context.
///
Expand Down Expand Up @@ -472,15 +476,15 @@ class ScopDetection {
///
/// @return True if the BB contains only valid control flow.
bool isValidCFG(BasicBlock &BB, bool IsLoopBranch, bool AllowUnreachable,
DetectionContext &Context) const;
DetectionContext &Context);

/// Is a loop valid with respect to a given region.
///
/// @param L The loop to check.
/// @param Context The context of scop detection.
///
/// @return True if the loop is valid in the region.
bool isValidLoop(Loop *L, DetectionContext &Context) const;
bool isValidLoop(Loop *L, DetectionContext &Context);

/// Count the number of loops and the maximal loop depth in @p L.
///
Expand All @@ -505,7 +509,7 @@ class ScopDetection {
/// @param Context The context of scop detection.
///
/// @return True if ISL can compute the trip count of the loop.
bool canUseISLTripCount(Loop *L, DetectionContext &Context) const;
bool canUseISLTripCount(Loop *L, DetectionContext &Context);

/// Print the locations of all detected scops.
void printLocations(Function &F);
Expand Down Expand Up @@ -550,7 +554,7 @@ class ScopDetection {
/// referenced by a Scop that is still to be processed.
///
/// @return Return true if R is the maximum Region in a Scop, false otherwise.
bool isMaxRegionInScop(const Region &R, bool Verify = true) const;
bool isMaxRegionInScop(const Region &R, bool Verify = true);

/// Return the detection context for @p R, nullptr if @p R was invalid.
DetectionContext *getDetectionContext(const Region *R) const;
Expand Down Expand Up @@ -596,12 +600,12 @@ class ScopDetection {

/// Verify if all valid Regions in this Function are still valid
/// after some transformations.
void verifyAnalysis() const;
void verifyAnalysis();

/// Verify if R is still a valid part of Scop after some transformations.
///
/// @param R The Region to verify.
void verifyRegion(const Region &R) const;
void verifyRegion(const Region &R);

/// Count the number of loops and the maximal loop depth in @p R.
///
Expand All @@ -615,6 +619,24 @@ class ScopDetection {
countBeneficialLoops(Region *R, ScalarEvolution &SE, LoopInfo &LI,
unsigned MinProfitableTrips);

/// Check if the block is a error block.
///
/// A error block is currently any block that fulfills at least one of
/// the following conditions:
///
/// - It is terminated by an unreachable instruction
/// - 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 being available.
///
/// @param BB The block to check.
/// @param R The analyzed region.
///
/// @return True if the block is a error block, false otherwise.
bool isErrorBlock(llvm::BasicBlock &BB, const llvm::Region &R);

private:
/// OptimizationRemarkEmitter object used to emit diagnostic remarks
OptimizationRemarkEmitter &ORE;
Expand Down Expand Up @@ -652,8 +674,7 @@ struct ScopDetectionWrapperPass : public FunctionPass {
void print(raw_ostream &OS, const Module *) const override;
//@}

ScopDetection &getSD() { return *Result; }
const ScopDetection &getSD() const { return *Result; }
ScopDetection &getSD() const { return *Result; }
};
} // namespace polly

Expand Down
10 changes: 4 additions & 6 deletions polly/include/polly/Support/SCEVValidator.h
Expand Up @@ -18,6 +18,7 @@ class SCEVConstant;
} // namespace llvm

namespace polly {
class ScopDetection;

/// Check if a call is side-effect free and has only constant arguments.
///
Expand Down Expand Up @@ -94,17 +95,14 @@ extractConstantFactor(const llvm::SCEV *M, llvm::ScalarEvolution &SE);
/// conditions that seemed non-affine before are now in fact affine.
const llvm::SCEV *tryForwardThroughPHI(const llvm::SCEV *Expr, llvm::Region &R,
llvm::ScalarEvolution &SE,
llvm::LoopInfo &LI,
const llvm::DominatorTree &DT);
ScopDetection *SD);

/// Return a unique non-error block incoming value for @p PHI if available.
///
/// @param R The region to run our code on.
/// @param LI The loopinfo tree
/// @param DT The dominator tree
/// @param SD The ScopDetection
llvm::Value *getUniqueNonErrorValue(llvm::PHINode *PHI, llvm::Region *R,
llvm::LoopInfo &LI,
const llvm::DominatorTree &DT);
ScopDetection *SD);
} // namespace polly

#endif
21 changes: 0 additions & 21 deletions polly/include/polly/Support/ScopHelper.h
Expand Up @@ -413,27 +413,6 @@ llvm::Value *expandCodeFor(Scop &S, llvm::ScalarEvolution &SE,
llvm::Instruction *IP, ValueMapT *VMap,
llvm::BasicBlock *RTCBB);

/// Check if the block is a error block.
///
/// A error block is currently any block that fulfills at least one of
/// the following conditions:
///
/// - It is terminated by an unreachable instruction
/// - 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
/// being 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, const llvm::Region &R,
llvm::LoopInfo &LI, const llvm::DominatorTree &DT);

/// Return the condition for the terminator @p TI.
///
/// For unconditional branches the "i1 true" condition will be returned.
Expand Down
24 changes: 12 additions & 12 deletions polly/lib/Analysis/ScopBuilder.cpp
Expand Up @@ -180,12 +180,12 @@ getRegionNodeSuccessor(RegionNode *RN, Instruction *TI, unsigned idx) {
return TI->getSuccessor(idx);
}

static bool containsErrorBlock(RegionNode *RN, const Region &R, LoopInfo &LI,
const DominatorTree &DT) {
static bool containsErrorBlock(RegionNode *RN, const Region &R,
ScopDetection *SD) {
if (!RN->isSubRegion())
return isErrorBlock(*RN->getNodeAs<BasicBlock>(), R, LI, DT);
return SD->isErrorBlock(*RN->getNodeAs<BasicBlock>(), R);
for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
if (isErrorBlock(*BB, R, LI, DT))
if (SD->isErrorBlock(*BB, R))
return true;
return false;
}
Expand Down Expand Up @@ -448,7 +448,7 @@ bool ScopBuilder::buildConditionSets(
.release();
} else if (auto *PHI = dyn_cast<PHINode>(Condition)) {
auto *Unique = dyn_cast<ConstantInt>(
getUniqueNonErrorValue(PHI, &scop->getRegion(), LI, DT));
getUniqueNonErrorValue(PHI, &scop->getRegion(), &SD));

if (Unique->isZero())
ConsequenceCondSet = isl_set_empty(isl_set_get_space(Domain));
Expand Down Expand Up @@ -497,8 +497,8 @@ bool ScopBuilder::buildConditionSets(
const SCEV *LeftOperand = SE.getSCEVAtScope(ICond->getOperand(0), L),
*RightOperand = SE.getSCEVAtScope(ICond->getOperand(1), L);

LeftOperand = tryForwardThroughPHI(LeftOperand, R, SE, LI, DT);
RightOperand = tryForwardThroughPHI(RightOperand, R, SE, LI, DT);
LeftOperand = tryForwardThroughPHI(LeftOperand, R, SE, &SD);
RightOperand = tryForwardThroughPHI(RightOperand, R, SE, &SD);

switch (ICond->getPredicate()) {
case ICmpInst::ICMP_ULT:
Expand Down Expand Up @@ -844,7 +844,7 @@ bool ScopBuilder::buildDomains(
scop->setDomain(EntryBB, Domain);

if (IsOnlyNonAffineRegion)
return !containsErrorBlock(R->getNode(), *R, LI, DT);
return !containsErrorBlock(R->getNode(), *R, &SD);

if (!buildDomainsWithBranchConstraints(R, InvalidDomainMap))
return false;
Expand Down Expand Up @@ -897,7 +897,7 @@ bool ScopBuilder::buildDomainsWithBranchConstraints(
}
}

if (containsErrorBlock(RN, scop->getRegion(), LI, DT))
if (containsErrorBlock(RN, scop->getRegion(), &SD))
scop->notifyErrorBlock();
;

Expand Down Expand Up @@ -1013,7 +1013,7 @@ bool ScopBuilder::propagateInvalidStmtDomains(
}
}

bool ContainsErrorBlock = containsErrorBlock(RN, scop->getRegion(), LI, DT);
bool ContainsErrorBlock = containsErrorBlock(RN, scop->getRegion(), &SD);
BasicBlock *BB = getRegionNodeBasicBlock(RN);
isl::set &Domain = scop->getOrInitEmptyDomain(BB);
assert(!Domain.is_null() && "Cannot propagate a nullptr");
Expand Down Expand Up @@ -2257,7 +2257,7 @@ void ScopBuilder::buildAccessFunctions(ScopStmt *Stmt, BasicBlock &BB,

// We do not build access functions for error blocks, as they may contain
// instructions we can not model.
if (isErrorBlock(BB, scop->getRegion(), LI, DT))
if (SD.isErrorBlock(BB, scop->getRegion()))
return;

auto BuildAccessesForInst = [this, Stmt,
Expand Down Expand Up @@ -3673,7 +3673,7 @@ void ScopBuilder::buildScop(Region &R, AssumptionCache &AC) {
// created somewhere.
const InvariantLoadsSetTy &RIL = scop->getRequiredInvariantLoads();
for (BasicBlock *BB : scop->getRegion().blocks()) {
if (isErrorBlock(*BB, scop->getRegion(), LI, DT))
if (SD.isErrorBlock(*BB, scop->getRegion()))
continue;

for (Instruction &Inst : *BB) {
Expand Down

0 comments on commit 58e4e71

Please sign in to comment.