[Polly][ScopInfo] Lazy Scop instantiation (#179535)

The old NPM was using ScopInfo pass introduced in
https://reviews.llvm.org/D20962, which in contrast to the LPM was using
ScopInfoRegionPass. ScopInfo was instantiating all Scop objects
immediately. After codegenning, all Scop objects need to be recomputed
anyway, making this approach wastful. The PhaseManager inherited this
behaviour from the NPM, leading to some concerns.

Replace the instantiate-all behavior of ScopInfo with an on-demand
instantiation. SCoPs now must be iterated using ScopDetection instead
using ScopInfo, but only some unsed legacy NPM passes (now removed) were
doing that anyway.
This commit is contained in:
Michael Kruse 2026-02-16 23:28:51 +01:00 committed by GitHub
parent 6d91695530
commit d5607ad55c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 118 deletions

View File

@ -2670,17 +2670,10 @@ public:
raw_ostream &operator<<(raw_ostream &OS, const Scop &scop);
class ScopInfo {
public:
using RegionToScopMapTy = MapVector<Region *, std::unique_ptr<Scop>>;
using reverse_iterator = RegionToScopMapTy::reverse_iterator;
using const_reverse_iterator = RegionToScopMapTy::const_reverse_iterator;
using iterator = RegionToScopMapTy::iterator;
using const_iterator = RegionToScopMapTy::const_iterator;
private:
/// A map of Region to its Scop object containing
/// Polly IR of static control part.
RegionToScopMapTy RegionToScopMap;
llvm::SmallDenseMap<const Region *, std::unique_ptr<Scop>> RegionToScopMap;
const DataLayout &DL;
ScopDetection &SD;
ScalarEvolution &SE;
@ -2701,47 +2694,12 @@ public:
/// the scop object. If the given region is a subregion, return a
/// nullptr. Top level region containing the entry block of a function
/// is not considered in the scop creation.
Scop *getScop(Region *R) const {
auto MapIt = RegionToScopMap.find(R);
if (MapIt != RegionToScopMap.end())
return MapIt->second.get();
return nullptr;
}
Scop *getScop(const Region *R);
/// Recompute the Scop-Information for a function.
///
/// This invalidates any iterators.
void recompute();
/// Handle invalidation explicitly
bool invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv);
iterator begin() { return RegionToScopMap.begin(); }
iterator end() { return RegionToScopMap.end(); }
const_iterator begin() const { return RegionToScopMap.begin(); }
const_iterator end() const { return RegionToScopMap.end(); }
reverse_iterator rbegin() { return RegionToScopMap.rbegin(); }
reverse_iterator rend() { return RegionToScopMap.rend(); }
const_reverse_iterator rbegin() const { return RegionToScopMap.rbegin(); }
const_reverse_iterator rend() const { return RegionToScopMap.rend(); }
bool empty() const { return RegionToScopMap.empty(); }
};
struct ScopInfoAnalysis : AnalysisInfoMixin<ScopInfoAnalysis> {
static AnalysisKey Key;
using Result = ScopInfo;
Result run(Function &, FunctionAnalysisManager &);
};
struct ScopInfoPrinterPass final : PassInfoMixin<ScopInfoPrinterPass> {
ScopInfoPrinterPass(raw_ostream &OS) : Stream(OS) {}
PreservedAnalyses run(Function &, FunctionAnalysisManager &);
raw_ostream &Stream;
void invalidate();
};
} // end namespace polly

View File

@ -2579,73 +2579,30 @@ void updateLoopCountStatistic(ScopDetection::LoopStats Stats,
ScopInfo::ScopInfo(const DataLayout &DL, ScopDetection &SD, ScalarEvolution &SE,
LoopInfo &LI, AliasAnalysis &AA, DominatorTree &DT,
AssumptionCache &AC, OptimizationRemarkEmitter &ORE)
: DL(DL), SD(SD), SE(SE), LI(LI), AA(AA), DT(DT), AC(AC), ORE(ORE) {
recompute();
}
: DL(DL), SD(SD), SE(SE), LI(LI), AA(AA), DT(DT), AC(AC), ORE(ORE) {}
void ScopInfo::recompute() {
RegionToScopMap.clear();
/// Create polyhedral description of scops for all the valid regions of a
/// function.
for (auto &It : SD) {
Region *R = const_cast<Region *>(It);
if (!SD.isMaxRegionInScop(*R))
continue;
Scop *ScopInfo::getScop(const Region *R) {
auto &&[It, Inserted] = RegionToScopMap.try_emplace(R);
if (Inserted && SD.isMaxRegionInScop(*R)) {
ScopBuilder SB(const_cast<Region *>(R), AC, AA, DL, DT, LI, SD, SE, ORE);
It->second = SB.getScop();
Scop *S = It->second.get();
ScopBuilder SB(R, AC, AA, DL, DT, LI, SD, SE, ORE);
std::unique_ptr<Scop> S = SB.getScop();
if (!S)
continue;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
ScopDetection::LoopStats Stats =
ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
updateLoopCountStatistic(Stats, S->getStatistics());
if (S) {
ScopDetection::LoopStats Stats =
ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
updateLoopCountStatistic(Stats, S->getStatistics());
}
#endif
bool Inserted = RegionToScopMap.insert({R, std::move(S)}).second;
assert(Inserted && "Building Scop for the same region twice!");
(void)Inserted;
return S;
}
return It->second.get();
}
bool ScopInfo::invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv) {
// Check whether the analysis, all analyses on functions have been preserved
// or anything we're holding references to is being invalidated
auto PAC = PA.getChecker<ScopInfoAnalysis>();
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()) ||
Inv.invalidate<ScopAnalysis>(F, PA) ||
Inv.invalidate<ScalarEvolutionAnalysis>(F, PA) ||
Inv.invalidate<LoopAnalysis>(F, PA) ||
Inv.invalidate<AAManager>(F, PA) ||
Inv.invalidate<DominatorTreeAnalysis>(F, PA) ||
Inv.invalidate<AssumptionAnalysis>(F, PA);
}
AnalysisKey ScopInfoAnalysis::Key;
ScopInfoAnalysis::Result ScopInfoAnalysis::run(Function &F,
FunctionAnalysisManager &FAM) {
auto &SD = FAM.getResult<ScopAnalysis>(F);
auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
auto &LI = FAM.getResult<LoopAnalysis>(F);
auto &AA = FAM.getResult<AAManager>(F);
auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
auto &AC = FAM.getResult<AssumptionAnalysis>(F);
auto &DL = F.getParent()->getDataLayout();
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
return {DL, SD, SE, LI, AA, DT, AC, ORE};
}
PreservedAnalyses ScopInfoPrinterPass::run(Function &F,
FunctionAnalysisManager &FAM) {
auto &SI = FAM.getResult<ScopInfoAnalysis>(F);
// Since the legacy PM processes Scops in bottom up, we print them in reverse
// order here to keep the output persistent
for (auto &It : reverse(SI)) {
if (It.second)
It.second->print(Stream, PollyPrintInstructions);
else
Stream << "Invalid Scop!\n";
}
return PreservedAnalyses::all();
void ScopInfo::invalidate() {
// Recompute all SCoPs on-demand
RegionToScopMap.clear();
}

View File

@ -156,18 +156,18 @@ public:
}
}
SmallPriorityWorklist<Region *, 4> Worklist;
for (auto &[R, S] : Info)
if (S)
Worklist.insert(R);
SmallPriorityWorklist<const Region *, 4> Worklist;
for (const Region *R : SD)
Worklist.insert(R);
TargetTransformInfo &TTI = FAM.getResult<TargetIRAnalysis>(F);
while (!Worklist.empty()) {
Region *R = Worklist.pop_back_val();
const Region *R = Worklist.pop_back_val();
Scop *S = Info.getScop(R);
if (!S) {
// This can happen if codegenning of a previous SCoP made this region
// not-a-SCoP anymore.
// This can happen if the region is not maximal, is not determined a
// valid SCoP by ScopBuilder, or codegenning of a previous SCoP made
// this region not-a-SCoP anymore.
POLLY_DEBUG(dbgs() << "SCoP in Region '" << *R << "' disappeared");
continue;
}
@ -253,10 +253,10 @@ public:
if (ModifiedByCodeGen) {
ModifiedIR = true;
// For all regions, create new polly::Scop objects because the old ones
// refere to invalidated LLVM-IR.
// FIXME: Adds all SCoPs again to statistics
Info.recompute();
// Discard old polly::Scop objects because they may refer to invalidated
// LLVM-IR instructions and SCEV expressions. ScopInfo will recreate
// them on demand.
Info.invalidate();
}
}