[ForwardOpTree] Allow out-of-quota in examination part of forwardTree.

Computing the reaching definition in forwardTree() can take a long time
if the coefficients are large. When the forwarding is
carried-out (doIt==true), forwardTree() must execute entirely or not at
all to get a consistent output, which means we cannot just allow
out-of-quota errors to happen in the middle of the processing.

We introduce the class IslQuotaScope which allows to opt-in code that is
conformant and has been tested with out-of-quota events. In case of
ForwardOpTree, out-of-quota is allowed during the operand tree
examination, but not during the transformation. The same forwardTree()
recursion is used for examination and execution, meaning that the
reaching definition has already been computed in the examination tree
walk and cached for reuse in the transformation tree walk.

This should fix the time-out of grtestutils.ll of the asop buildbot. If
the compilation still takes too long, we can reduce the max-operations
allows for -polly-optree.

Differential Revision: https://reviews.llvm.org/D37984

llvm-svn: 313690
This commit is contained in:
Michael Kruse 2017-09-19 22:53:20 +00:00
parent 8930f383fc
commit 89972e21f8
2 changed files with 122 additions and 32 deletions

View File

@ -287,6 +287,71 @@ operator<<(llvm::DiagnosticInfoOptimizationBase &OS,
return OS; return OS;
} }
/// Scope guard for code that allows arbitrary isl function to return an error
/// if the max-operations quota exceeds.
///
/// This allows to opt-in code sections that have known long executions times.
/// code not in a hot path can continue to assume that no unexpected error
/// occurs.
///
/// This is typically used inside a nested IslMaxOperationsGuard scope. The
/// IslMaxOperationsGuard defines the number of allowed base operations for some
/// code, IslQuotaScope defines where it is allowed to return an error result.
class IslQuotaScope {
isl_ctx *IslCtx;
int OldOnError;
public:
IslQuotaScope() : IslCtx(nullptr) {}
IslQuotaScope(const IslQuotaScope &) = delete;
IslQuotaScope(IslQuotaScope &&Other)
: IslCtx(Other.IslCtx), OldOnError(Other.OldOnError) {
Other.IslCtx = nullptr;
}
const IslQuotaScope &operator=(IslQuotaScope &&Other) {
std::swap(this->IslCtx, Other.IslCtx);
std::swap(this->OldOnError, Other.OldOnError);
return *this;
}
/// Enter a quota-aware scope.
///
/// Should not be used directly. Use IslMaxOperationsGuard::enter() instead.
explicit IslQuotaScope(isl_ctx *IslCtx, unsigned long LocalMaxOps)
: IslCtx(IslCtx) {
assert(IslCtx);
assert(isl_ctx_get_max_operations(IslCtx) == 0 && "Incorrect nesting");
if (LocalMaxOps == 0) {
this->IslCtx = nullptr;
return;
}
OldOnError = isl_options_get_on_error(IslCtx);
isl_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE);
isl_ctx_reset_error(IslCtx);
isl_ctx_set_max_operations(IslCtx, LocalMaxOps);
}
~IslQuotaScope() {
if (!IslCtx)
return;
assert(isl_ctx_get_max_operations(IslCtx) > 0 && "Incorrect nesting");
assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE &&
"Incorrect nesting");
isl_ctx_set_max_operations(IslCtx, 0);
isl_options_set_on_error(IslCtx, OldOnError);
}
/// Return whether the current quota has exceeded.
bool hasQuotaExceeded() const {
if (!IslCtx)
return false;
return isl_ctx_last_error(IslCtx) == isl_error_quota;
}
};
/// Scoped limit of ISL operations. /// Scoped limit of ISL operations.
/// ///
/// Limits the number of ISL operations during the lifetime of this object. The /// Limits the number of ISL operations during the lifetime of this object. The
@ -307,8 +372,11 @@ private:
/// scope. /// scope.
isl_ctx *IslCtx; isl_ctx *IslCtx;
/// Old OnError setting; to reset to when the scope ends. /// Maximum number of operations for the scope.
int OldOnError; unsigned long LocalMaxOps;
/// When AutoEnter is enabled, holds the IslQuotaScope object.
IslQuotaScope TopLevelScope;
public: public:
/// Enter a max operations scope. /// Enter a max operations scope.
@ -316,8 +384,14 @@ public:
/// @param IslCtx The ISL context to set the operations limit for. /// @param IslCtx The ISL context to set the operations limit for.
/// @param LocalMaxOps Maximum number of operations allowed in the /// @param LocalMaxOps Maximum number of operations allowed in the
/// scope. If set to zero, no operations limit is enforced. /// scope. If set to zero, no operations limit is enforced.
IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps) /// @param AutoEnter If true, automatically enters an IslQuotaScope such
: IslCtx(IslCtx) { /// that isl operations may return quota errors
/// immediately. If false, only starts the operations
/// counter, but isl does not return quota errors before
/// calling enter().
IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps,
bool AutoEnter = true)
: IslCtx(IslCtx), LocalMaxOps(LocalMaxOps) {
assert(IslCtx); assert(IslCtx);
assert(isl_ctx_get_max_operations(IslCtx) == 0 && assert(isl_ctx_get_max_operations(IslCtx) == 0 &&
"Nested max operations not supported"); "Nested max operations not supported");
@ -328,26 +402,26 @@ public:
return; return;
} }
// Save previous state.
OldOnError = isl_options_get_on_error(IslCtx);
// Activate the new setting.
isl_ctx_set_max_operations(IslCtx, LocalMaxOps);
isl_ctx_reset_operations(IslCtx); isl_ctx_reset_operations(IslCtx);
isl_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE); TopLevelScope = enter(AutoEnter);
} }
/// Leave the max operations scope. /// Enter a scope that can handle out-of-quota errors.
~IslMaxOperationsGuard() { ///
/// @param AllowReturnNull Whether the scoped code can handle out-of-quota
/// errors. If false, returns a dummy scope object that
/// does nothing.
IslQuotaScope enter(bool AllowReturnNull = true) {
return AllowReturnNull && IslCtx ? IslQuotaScope(IslCtx, LocalMaxOps)
: IslQuotaScope();
}
/// Return whether the current quota has exceeded.
bool hasQuotaExceeded() const {
if (!IslCtx) if (!IslCtx)
return; return false;
assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE && return isl_ctx_last_error(IslCtx) == isl_error_quota;
"Unexpected change of the on_error setting");
// Return to the previous error setting.
isl_ctx_set_max_operations(IslCtx, 0);
isl_options_set_on_error(IslCtx, OldOnError);
} }
}; };

View File

@ -131,6 +131,9 @@ enum ForwardingDecision {
/// statements. The simplification pass can clean these up. /// statements. The simplification pass can clean these up.
class ForwardOpTreeImpl : ZoneAlgorithm { class ForwardOpTreeImpl : ZoneAlgorithm {
private: private:
/// Scope guard to limit the number of isl operations for this pass.
IslMaxOperationsGuard &MaxOpGuard;
/// How many instructions have been copied to other statements. /// How many instructions have been copied to other statements.
int NumInstructionsCopied = 0; int NumInstructionsCopied = 0;
@ -248,8 +251,8 @@ private:
} }
public: public:
ForwardOpTreeImpl(Scop *S, LoopInfo *LI) ForwardOpTreeImpl(Scop *S, LoopInfo *LI, IslMaxOperationsGuard &MaxOpGuard)
: ZoneAlgorithm("polly-optree", S, LI) {} : ZoneAlgorithm("polly-optree", S, LI), MaxOpGuard(MaxOpGuard) {}
/// Compute the zones of known array element contents. /// Compute the zones of known array element contents.
/// ///
@ -260,9 +263,8 @@ public:
// Check that nothing strange occurs. // Check that nothing strange occurs.
collectCompatibleElts(); collectCompatibleElts();
isl_ctx_reset_error(IslCtx.get());
{ {
IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), MaxOps); IslQuotaScope QuotaScope = MaxOpGuard.enter();
computeCommon(); computeCommon();
Known = computeKnown(true, true); Known = computeKnown(true, true);
@ -273,7 +275,6 @@ public:
if (!Known || !Translator) { if (!Known || !Translator) {
assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota); assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota);
KnownOutOfQuota++;
Known = nullptr; Known = nullptr;
Translator = nullptr; Translator = nullptr;
DEBUG(dbgs() << "Known analysis exceeded max_operations\n"); DEBUG(dbgs() << "Known analysis exceeded max_operations\n");
@ -402,7 +403,8 @@ public:
Loop *DefLoop, isl::map DefToTarget, Loop *DefLoop, isl::map DefToTarget,
bool DoIt) { bool DoIt) {
// Cannot do anything without successful known analysis. // Cannot do anything without successful known analysis.
if (Known.is_null()) if (Known.is_null() || Translator.is_null() || UseToTarget.is_null() ||
DefToTarget.is_null() || MaxOpGuard.hasQuotaExceeded())
return FD_NotApplicable; return FD_NotApplicable;
LoadInst *LI = dyn_cast<LoadInst>(Inst); LoadInst *LI = dyn_cast<LoadInst>(Inst);
@ -445,6 +447,8 @@ public:
llvm_unreachable("Shouldn't return this"); llvm_unreachable("Shouldn't return this");
} }
IslQuotaScope QuotaScope = MaxOpGuard.enter(!DoIt);
// { DomainDef[] -> ValInst[] } // { DomainDef[] -> ValInst[] }
isl::map ExpectedVal = makeValInst(Inst, UseStmt, UseLoop); isl::map ExpectedVal = makeValInst(Inst, UseStmt, UseLoop);
@ -699,6 +703,8 @@ public:
DefLoop = LI->getLoopFor(Inst->getParent()); DefLoop = LI->getLoopFor(Inst->getParent());
if (DefToTarget.is_null() && !Known.is_null()) { if (DefToTarget.is_null() && !Known.is_null()) {
IslQuotaScope QuotaScope = MaxOpGuard.enter(!DoIt);
// { UseDomain[] -> DefDomain[] } // { UseDomain[] -> DefDomain[] }
isl::map UseToDef = computeUseToDefFlowDependency(UseStmt, DefStmt); isl::map UseToDef = computeUseToDefFlowDependency(UseStmt, DefStmt);
@ -846,16 +852,26 @@ public:
releaseMemory(); releaseMemory();
LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
Impl = llvm::make_unique<ForwardOpTreeImpl>(&S, &LI);
if (AnalyzeKnown) { {
DEBUG(dbgs() << "Prepare forwarders...\n"); IslMaxOperationsGuard MaxOpGuard(S.getIslCtx(), MaxOps, false);
Impl->computeKnownValues(); Impl = llvm::make_unique<ForwardOpTreeImpl>(&S, &LI, MaxOpGuard);
if (AnalyzeKnown) {
DEBUG(dbgs() << "Prepare forwarders...\n");
Impl->computeKnownValues();
}
DEBUG(dbgs() << "Forwarding operand trees...\n");
Impl->forwardOperandTrees();
if (MaxOpGuard.hasQuotaExceeded()) {
DEBUG(dbgs() << "Not all operations completed because of "
"max_operations exceeded\n");
KnownOutOfQuota++;
}
} }
DEBUG(dbgs() << "Forwarding operand trees...\n");
Impl->forwardOperandTrees();
DEBUG(dbgs() << "\nFinal Scop:\n"); DEBUG(dbgs() << "\nFinal Scop:\n");
DEBUG(dbgs() << S); DEBUG(dbgs() << S);