Bound ISL operations during DeLICM scalar collapsing (#203073)
The IslMaxOperationsGuard in DeLICM previously covered only the zone analysis performed in computeZone(). The subsequent greedyCollapse() -> collapseScalarsToStore() path calls computeScalarReachingOverwrite(), which performs an unbounded ISL lexmin(). Share a single[already present], dormant IslMaxOperationsGuard across both regions and arm it narrowly around each operation intensive computation via IslQuotaScope, so the same operations budget covers both computeZone() and the scalar collapsing. When the quota is exceeded during scalar collapsing, bail out cleanly: increment DeLICMOutOfQuota and emit an "OutOfQuota" analysis remark. Fixes: https://github.com/llvm/llvm-project/issues/202045
This commit is contained in:
parent
83582cfcdb
commit
59d7709ee9
2 changed files with 118 additions and 6 deletions
|
|
@ -40,7 +40,7 @@ cl::opt<int>
|
|||
DelicmMaxOps("polly-delicm-max-ops",
|
||||
cl::desc("Maximum number of isl operations to invest for "
|
||||
"lifetime analysis; 0=no limit"),
|
||||
cl::init(1000000), cl::cat(PollyCategory));
|
||||
cl::init(1500000), cl::cat(PollyCategory));
|
||||
|
||||
cl::opt<bool> DelicmOverapproximateWrites(
|
||||
"polly-delicm-overapproximate-writes",
|
||||
|
|
@ -545,6 +545,13 @@ private:
|
|||
/// The number of PHIs mapped to some array element.
|
||||
int NumberOfMappedPHIScalars = 0;
|
||||
|
||||
/// Shared ISL operations budget guarding the expensive zone analysis
|
||||
/// (computeZone) and the scalar-to-store collapsing (greedyCollapse). It is
|
||||
/// constructed dormant (AutoEnter=false) and armed narrowly around each
|
||||
/// dangerous region via IslQuotaScope, so the same budget covers both
|
||||
/// regions without ever nesting two armed scopes.
|
||||
IslMaxOperationsGuard MaxOpGuard;
|
||||
|
||||
/// Determine whether two knowledges are conflicting with each other.
|
||||
///
|
||||
/// @see Knowledge::isConflicting
|
||||
|
|
@ -1024,9 +1031,35 @@ private:
|
|||
auto TargetAccRel = getAccessRelationFor(TargetStoreMA);
|
||||
|
||||
// { Zone[] -> DomTarget[] }
|
||||
// For each point in time, find the next target store instance.
|
||||
auto Target =
|
||||
computeScalarReachingOverwrite(Schedule, TargetDom, false, true);
|
||||
// For each point in time, find the next target store instance. This can be
|
||||
// expensive for SCoPs with many modular/quasi-affine constraints, so bound
|
||||
// it with the shared ISL operations budget.
|
||||
isl::map Target;
|
||||
{
|
||||
IslQuotaScope MaxOpScope = MaxOpGuard.enter();
|
||||
Target = computeScalarReachingOverwrite(Schedule, TargetDom, false, true);
|
||||
|
||||
if (MaxOpScope.hasQuotaExceeded()) {
|
||||
DeLICMOutOfQuota++;
|
||||
assert(
|
||||
isl_ctx_last_error(IslCtx.get()) == isl_error_quota &&
|
||||
"The only reason that these things have not been computed should "
|
||||
"be if the max-operations limit hit");
|
||||
POLLY_DEBUG(
|
||||
dbgs() << "collapseScalarsToStore exceeded max_operations\n");
|
||||
DebugLoc Begin, End;
|
||||
getDebugLocations(getBBPairForRegion(&S->getRegion()), Begin, End);
|
||||
OptimizationRemarkAnalysis R(DEBUG_TYPE, "OutOfQuota", Begin,
|
||||
S->getEntry());
|
||||
R << "maximal number of operations exceeded during "
|
||||
"collapseScalarsToStore";
|
||||
S->getFunction().getContext().diagnose(R);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Target.is_null())
|
||||
return false;
|
||||
}
|
||||
|
||||
// { Zone[] -> Element[] }
|
||||
// Use the target store's write location as a suggestion to map scalars to.
|
||||
|
|
@ -1187,7 +1220,9 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
DeLICMImpl(Scop *S, LoopInfo *LI) : ZoneAlgorithm("polly-delicm", S, LI) {}
|
||||
DeLICMImpl(Scop *S, LoopInfo *LI)
|
||||
: ZoneAlgorithm("polly-delicm", S, LI),
|
||||
MaxOpGuard(IslCtx.get(), DelicmMaxOps, /*AutoEnter=*/false) {}
|
||||
|
||||
/// Calculate the lifetime (definition to last use) of every array element.
|
||||
///
|
||||
|
|
@ -1200,7 +1235,7 @@ public:
|
|||
isl::union_map EltKnown, EltWritten;
|
||||
|
||||
{
|
||||
IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), DelicmMaxOps);
|
||||
IslQuotaScope MaxOpScope = MaxOpGuard.enter();
|
||||
|
||||
computeCommon();
|
||||
|
||||
|
|
@ -1239,6 +1274,7 @@ public:
|
|||
/// the first processed element claims it.
|
||||
void greedyCollapse() {
|
||||
bool Modified = false;
|
||||
bool MaxOpQuotaExceeded = false;
|
||||
|
||||
for (auto &Stmt : *S) {
|
||||
for (auto *MA : Stmt) {
|
||||
|
|
@ -1334,7 +1370,13 @@ public:
|
|||
POLLY_DEBUG(dbgs() << "Analyzing target access " << MA << "\n");
|
||||
if (collapseScalarsToStore(MA))
|
||||
Modified = true;
|
||||
else if (MaxOpGuard.hasQuotaExceeded()) {
|
||||
MaxOpQuotaExceeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (MaxOpQuotaExceeded)
|
||||
break;
|
||||
}
|
||||
|
||||
if (Modified)
|
||||
|
|
|
|||
70
polly/test/DeLICM/outofquota-greedycollapse.ll
Normal file
70
polly/test/DeLICM/outofquota-greedycollapse.ll
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
; RUN: opt %loadNPMPolly '-passes=polly-custom<delicm>' -polly-process-unprofitable -pass-remarks-analysis=polly-delicm -disable-output < %s 2>&1 | FileCheck %s
|
||||
|
||||
; This test exercises the ISL operations guard in the DeLICM phase when
|
||||
; collapsing scalars to a store. The structure of the SCoP below has a few
|
||||
; important characteristics:
|
||||
; a) A large loop iteration space.
|
||||
; b) A long if-else ladder such that, of the two branches exiting each
|
||||
; conditional block, one is always a predecessor of a common basic block.
|
||||
; c) The domain of the common BB is the union of all domains reaching it,
|
||||
; which forces certain ISL operations to handle a high-dimensional,
|
||||
; extremely large search space.
|
||||
; Thus it is necessary to bound these operations and bail out instead of
|
||||
; hanging indefinitely.
|
||||
; With the shared IslMaxOperationsGuard[budget defined by -polly-delicm-max-ops]
|
||||
; now armed during collapseScalarsToStore() as well, the analysis aborts
|
||||
; once the operations budget is exhausted, emitting an "OutOfQuota" remark
|
||||
; and leaving the SCoP unmodified.
|
||||
|
||||
; CHECK: maximal number of operations exceeded during collapseScalarsToStore
|
||||
|
||||
define void @ham(ptr %arg) {
|
||||
bb:
|
||||
br label %bb1
|
||||
|
||||
bb1: ; preds = %bb19, %bb
|
||||
%phi = phi i64 [ 1, %bb ], [ %add, %bb19 ]
|
||||
%trunc = trunc i64 %phi to i32
|
||||
%and = and i32 %trunc, 1
|
||||
%icmp = icmp eq i32 %and, 0
|
||||
br i1 %icmp, label %bb4, label %bb2
|
||||
|
||||
bb2: ; preds = %bb16, %bb13, %bb10, %bb7, %bb4, %bb1
|
||||
%phi3 = phi i8 [ 1, %bb1 ], [ 0, %bb4 ], [ 1, %bb13 ], [ 0, %bb16 ], [ 0, %bb10 ], [ 1, %bb7 ]
|
||||
%getelementptr = getelementptr i8, ptr %arg, i64 %phi
|
||||
store i8 %phi3, ptr %getelementptr, align 1
|
||||
br label %bb19
|
||||
|
||||
bb4: ; preds = %bb1
|
||||
%and5 = and i64 %phi, 254
|
||||
%icmp6 = icmp eq i64 %and5, 0
|
||||
br i1 %icmp6, label %bb7, label %bb2
|
||||
|
||||
bb7: ; preds = %bb4
|
||||
%and8 = and i32 %trunc, 256
|
||||
%icmp9 = icmp eq i32 %and8, 0
|
||||
br i1 %icmp9, label %bb10, label %bb2
|
||||
|
||||
bb10: ; preds = %bb7
|
||||
%and11 = and i64 %phi, 3584
|
||||
%icmp12 = icmp eq i64 %and11, 0
|
||||
br i1 %icmp12, label %bb13, label %bb2
|
||||
|
||||
bb13: ; preds = %bb10
|
||||
%and14 = and i32 %trunc, 8192
|
||||
%icmp15 = icmp eq i32 %and14, 0
|
||||
br i1 %icmp15, label %bb16, label %bb2
|
||||
|
||||
bb16: ; preds = %bb13
|
||||
%and17 = and i64 %phi, 49152
|
||||
%icmp18 = icmp eq i64 %and17, 0
|
||||
br i1 %icmp18, label %bb19, label %bb2
|
||||
|
||||
bb19: ; preds = %bb16, %bb2
|
||||
%add = add i64 %phi, 1
|
||||
%icmp20 = icmp eq i64 %add, 65536
|
||||
br i1 %icmp20, label %bb21, label %bb1
|
||||
|
||||
bb21: ; preds = %bb19
|
||||
ret void
|
||||
}
|
||||
Loading…
Reference in a new issue