forked from OSchip/llvm-project
[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:
parent
8930f383fc
commit
89972e21f8
|
@ -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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue