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

Michael Kruse via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 19 15:53:21 PDT 2017


Author: meinersbur
Date: Tue Sep 19 15:53:20 2017
New Revision: 313690

URL: http://llvm.org/viewvc/llvm-project?rev=313690&view=rev
Log:
[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

Modified:
    polly/trunk/include/polly/Support/GICHelper.h
    polly/trunk/lib/Transform/ForwardOpTree.cpp

Modified: polly/trunk/include/polly/Support/GICHelper.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/Support/GICHelper.h?rev=313690&r1=313689&r2=313690&view=diff
==============================================================================
--- polly/trunk/include/polly/Support/GICHelper.h (original)
+++ polly/trunk/include/polly/Support/GICHelper.h Tue Sep 19 15:53:20 2017
@@ -287,6 +287,71 @@ operator<<(llvm::DiagnosticInfoOptimizat
   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.
 ///
 /// Limits the number of ISL operations during the lifetime of this object. The
@@ -307,8 +372,11 @@ private:
   /// scope.
   isl_ctx *IslCtx;
 
-  /// Old OnError setting; to reset to when the scope ends.
-  int OldOnError;
+  /// Maximum number of operations for the scope.
+  unsigned long LocalMaxOps;
+
+  /// When AutoEnter is enabled, holds the IslQuotaScope object.
+  IslQuotaScope TopLevelScope;
 
 public:
   /// Enter a max operations scope.
@@ -316,8 +384,14 @@ public:
   /// @param IslCtx      The ISL context to set the operations limit for.
   /// @param LocalMaxOps Maximum number of operations allowed in the
   ///                    scope. If set to zero, no operations limit is enforced.
-  IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps)
-      : IslCtx(IslCtx) {
+  /// @param AutoEnter   If true, automatically enters an IslQuotaScope such
+  ///                    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(isl_ctx_get_max_operations(IslCtx) == 0 &&
            "Nested max operations not supported");
@@ -328,26 +402,26 @@ public:
       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_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE);
+    TopLevelScope = enter(AutoEnter);
   }
 
-  /// Leave the max operations scope.
-  ~IslMaxOperationsGuard() {
-    if (!IslCtx)
-      return;
+  /// Enter a scope that can handle out-of-quota errors.
+  ///
+  /// @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();
+  }
 
-    assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE &&
-           "Unexpected change of the on_error setting");
+  /// Return whether the current quota has exceeded.
+  bool hasQuotaExceeded() const {
+    if (!IslCtx)
+      return false;
 
-    // Return to the previous error setting.
-    isl_ctx_set_max_operations(IslCtx, 0);
-    isl_options_set_on_error(IslCtx, OldOnError);
+    return isl_ctx_last_error(IslCtx) == isl_error_quota;
   }
 };
 

Modified: polly/trunk/lib/Transform/ForwardOpTree.cpp
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Transform/ForwardOpTree.cpp?rev=313690&r1=313689&r2=313690&view=diff
==============================================================================
--- polly/trunk/lib/Transform/ForwardOpTree.cpp (original)
+++ polly/trunk/lib/Transform/ForwardOpTree.cpp Tue Sep 19 15:53:20 2017
@@ -131,6 +131,9 @@ enum ForwardingDecision {
 /// statements. The simplification pass can clean these up.
 class ForwardOpTreeImpl : ZoneAlgorithm {
 private:
+  /// Scope guard to limit the number of isl operations for this pass.
+  IslMaxOperationsGuard &MaxOpGuard;
+
   /// How many instructions have been copied to other statements.
   int NumInstructionsCopied = 0;
 
@@ -248,8 +251,8 @@ private:
   }
 
 public:
-  ForwardOpTreeImpl(Scop *S, LoopInfo *LI)
-      : ZoneAlgorithm("polly-optree", S, LI) {}
+  ForwardOpTreeImpl(Scop *S, LoopInfo *LI, IslMaxOperationsGuard &MaxOpGuard)
+      : ZoneAlgorithm("polly-optree", S, LI), MaxOpGuard(MaxOpGuard) {}
 
   /// Compute the zones of known array element contents.
   ///
@@ -260,9 +263,8 @@ public:
     // Check that nothing strange occurs.
     collectCompatibleElts();
 
-    isl_ctx_reset_error(IslCtx.get());
     {
-      IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), MaxOps);
+      IslQuotaScope QuotaScope = MaxOpGuard.enter();
 
       computeCommon();
       Known = computeKnown(true, true);
@@ -273,7 +275,6 @@ public:
 
     if (!Known || !Translator) {
       assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota);
-      KnownOutOfQuota++;
       Known = nullptr;
       Translator = nullptr;
       DEBUG(dbgs() << "Known analysis exceeded max_operations\n");
@@ -402,7 +403,8 @@ public:
                                       Loop *DefLoop, isl::map DefToTarget,
                                       bool DoIt) {
     // 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;
 
     LoadInst *LI = dyn_cast<LoadInst>(Inst);
@@ -445,6 +447,8 @@ public:
       llvm_unreachable("Shouldn't return this");
     }
 
+    IslQuotaScope QuotaScope = MaxOpGuard.enter(!DoIt);
+
     // { DomainDef[] -> ValInst[] }
     isl::map ExpectedVal = makeValInst(Inst, UseStmt, UseLoop);
 
@@ -699,6 +703,8 @@ public:
       DefLoop = LI->getLoopFor(Inst->getParent());
 
       if (DefToTarget.is_null() && !Known.is_null()) {
+        IslQuotaScope QuotaScope = MaxOpGuard.enter(!DoIt);
+
         // { UseDomain[] -> DefDomain[] }
         isl::map UseToDef = computeUseToDefFlowDependency(UseStmt, DefStmt);
 
@@ -846,15 +852,25 @@ public:
     releaseMemory();
 
     LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
-    Impl = llvm::make_unique<ForwardOpTreeImpl>(&S, &LI);
 
-    if (AnalyzeKnown) {
-      DEBUG(dbgs() << "Prepare forwarders...\n");
-      Impl->computeKnownValues();
-    }
+    {
+      IslMaxOperationsGuard MaxOpGuard(S.getIslCtx(), MaxOps, false);
+      Impl = llvm::make_unique<ForwardOpTreeImpl>(&S, &LI, MaxOpGuard);
 
-    DEBUG(dbgs() << "Forwarding operand trees...\n");
-    Impl->forwardOperandTrees();
+      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() << "\nFinal Scop:\n");
     DEBUG(dbgs() << S);




More information about the llvm-commits mailing list