PR #125442 replaces the pass-based Polly architecture with a monolithic pass consisting of phases. Reasons listed in https://github.com/llvm/llvm-project/pull/125442. With this change, the SCoP-passes became redundant problematic versions of the same functionality and are removed.
438 lines
14 KiB
C++
438 lines
14 KiB
C++
//===------ PhaseManager.cpp ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "polly/Pass/PhaseManager.h"
|
|
#include "polly/CodeGen/CodeGeneration.h"
|
|
#include "polly/CodeGen/IslAst.h"
|
|
#include "polly/CodePreparation.h"
|
|
#include "polly/DeLICM.h"
|
|
#include "polly/DeadCodeElimination.h"
|
|
#include "polly/DependenceInfo.h"
|
|
#include "polly/FlattenSchedule.h"
|
|
#include "polly/ForwardOpTree.h"
|
|
#include "polly/JSONExporter.h"
|
|
#include "polly/MaximalStaticExpansion.h"
|
|
#include "polly/PruneUnprofitable.h"
|
|
#include "polly/ScheduleOptimizer.h"
|
|
#include "polly/ScopDetection.h"
|
|
#include "polly/ScopDetectionDiagnostic.h"
|
|
#include "polly/ScopGraphPrinter.h"
|
|
#include "polly/ScopInfo.h"
|
|
#include "polly/Simplify.h"
|
|
#include "polly/Support/PollyDebug.h"
|
|
#include "llvm/ADT/PriorityWorklist.h"
|
|
#include "llvm/Analysis/AssumptionCache.h"
|
|
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#define DEBUG_TYPE "polly-pass"
|
|
|
|
using namespace polly;
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
/// Recurse through all subregions and all regions and add them to RQ.
|
|
static void addRegionIntoQueue(Region &R, SmallVector<Region *> &RQ) {
|
|
RQ.push_back(&R);
|
|
for (const auto &E : R)
|
|
addRegionIntoQueue(*E, RQ);
|
|
}
|
|
|
|
/// The phase pipeline of Polly to be embedded into another pass manager than
|
|
/// runs passes on functions.
|
|
///
|
|
/// Polly holds state besides LLVM-IR (RegionInfo and ScopInfo) between phases
|
|
/// that LLVM pass managers do not consider when scheduling analyses and passes.
|
|
/// That is, the ScopInfo must persist between phases that a pass manager must
|
|
/// not invalidate to recompute later.
|
|
class PhaseManager {
|
|
private:
|
|
Function &F;
|
|
FunctionAnalysisManager &FAM;
|
|
PollyPassOptions Opts;
|
|
|
|
public:
|
|
PhaseManager(Function &F, FunctionAnalysisManager &FAM, PollyPassOptions Opts)
|
|
: F(F), FAM(FAM), Opts(std::move(Opts)) {}
|
|
|
|
/// Execute Polly's phases as indicated by the options.
|
|
bool run() {
|
|
// Get analyses from the function pass manager.
|
|
// These must be preserved during all phases so that if processing one SCoP
|
|
// has finished, the next SCoP can still use them. Recomputing is not an
|
|
// option because ScopDetection stores references to the old results.
|
|
// TODO: CodePreparation doesn't actually need these analysis, it just keeps
|
|
// them up-to-date. If they are not computed yet, can also compute after the
|
|
// prepare phase.
|
|
LoopInfo &LI = FAM.getResult<LoopAnalysis>(F);
|
|
DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
|
|
bool ModifiedIR = false;
|
|
|
|
// Phase: prepare
|
|
// TODO: Setting ModifiedIR will invalidate any analysis, even if DT, LI are
|
|
// preserved.
|
|
if (Opts.isPhaseEnabled(PassPhase::Prepare)) {
|
|
if (runCodePreparation(F, &DT, &LI, nullptr)) {
|
|
PreservedAnalyses PA;
|
|
PA.preserve<DominatorTreeAnalysis>();
|
|
PA.preserve<LoopAnalysis>();
|
|
FAM.invalidate(F, PA);
|
|
ModifiedIR = true;
|
|
}
|
|
}
|
|
|
|
// Can't do anything without detection
|
|
if (!Opts.isPhaseEnabled(PassPhase::Detection))
|
|
return false;
|
|
|
|
AAResults &AA = FAM.getResult<AAManager>(F);
|
|
ScalarEvolution &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
|
|
OptimizationRemarkEmitter &ORE =
|
|
FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
|
|
|
|
// ScopDetection is modifying RegionInfo, do not cache it, nor use a cached
|
|
// version.
|
|
RegionInfo RI = RegionInfoAnalysis().run(F, FAM);
|
|
|
|
// Phase: detection
|
|
ScopDetection SD(DT, SE, LI, RI, AA, ORE);
|
|
SD.detect(F);
|
|
if (Opts.isPhaseEnabled(PassPhase::PrintDetect)) {
|
|
outs() << "Detected Scops in Function " << F.getName() << "\n";
|
|
for (const Region *R : SD.ValidRegions)
|
|
outs() << "Valid Region for Scop: " << R->getNameStr() << '\n';
|
|
outs() << "\n";
|
|
}
|
|
|
|
if (Opts.isPhaseEnabled(PassPhase::DotScops))
|
|
printGraphForFunction(F, &SD, "scops", false);
|
|
if (Opts.isPhaseEnabled(PassPhase::DotScopsOnly))
|
|
printGraphForFunction(F, &SD, "scopsonly", true);
|
|
|
|
auto ViewScops = [&](const char *Name, bool IsSimply) {
|
|
if (Opts.ViewFilter.empty() && !F.getName().count(Opts.ViewFilter))
|
|
return;
|
|
|
|
if (Opts.ViewAll || std::distance(SD.begin(), SD.end()) > 0)
|
|
viewGraphForFunction(F, &SD, Name, IsSimply);
|
|
};
|
|
if (Opts.isPhaseEnabled(PassPhase::ViewScops))
|
|
ViewScops("scops", false);
|
|
if (Opts.isPhaseEnabled(PassPhase::ViewScopsOnly))
|
|
ViewScops("scopsonly", true);
|
|
|
|
// Phase: scops
|
|
AssumptionCache &AC = FAM.getResult<AssumptionAnalysis>(F);
|
|
const DataLayout &DL = F.getParent()->getDataLayout();
|
|
ScopInfo Info(DL, SD, SE, LI, AA, DT, AC, ORE);
|
|
if (Opts.isPhaseEnabled(PassPhase::PrintScopInfo)) {
|
|
if (Region *TLR = RI.getTopLevelRegion()) {
|
|
SmallVector<Region *> Regions;
|
|
addRegionIntoQueue(*TLR, Regions);
|
|
|
|
// reverse iteration because the regression tests expect it.
|
|
for (Region *R : reverse(Regions)) {
|
|
Scop *S = Info.getScop(R);
|
|
outs() << "Printing analysis 'Polly - Create polyhedral "
|
|
"description of Scops' for region: '"
|
|
<< R->getNameStr() << "' in function '" << F.getName()
|
|
<< "':\n";
|
|
if (S)
|
|
outs() << *S;
|
|
else
|
|
outs() << "Invalid Scop!\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
SmallPriorityWorklist<Region *, 4> Worklist;
|
|
for (auto &[R, S] : Info)
|
|
if (S)
|
|
Worklist.insert(R);
|
|
|
|
TargetTransformInfo &TTI = FAM.getResult<TargetIRAnalysis>(F);
|
|
while (!Worklist.empty()) {
|
|
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.
|
|
POLLY_DEBUG(dbgs() << "SCoP in Region '" << *R << "' disappeared");
|
|
continue;
|
|
}
|
|
|
|
if (!SD.isMaxRegionInScop(*R, /*Verify=*/false))
|
|
continue;
|
|
|
|
// Phase: flatten
|
|
if (Opts.isPhaseEnabled(PassPhase::Flatten))
|
|
runFlattenSchedulePass(*S);
|
|
|
|
// Phase: deps
|
|
// Actual analysis runs on-demand, so it does not matter whether the phase
|
|
// is actually enabled, but use this location to print dependencies.
|
|
DependenceAnalysis::Result DA = runDependenceAnalysis(*S);
|
|
if (Opts.isPhaseEnabled(PassPhase::PrintDependences)) {
|
|
assert(Opts.isPhaseEnabled(PassPhase::Dependences));
|
|
const Dependences &D = DA.getDependences(Opts.PrintDepsAnalysisLevel);
|
|
D.print(outs());
|
|
}
|
|
|
|
// Phase: import-jscop
|
|
if (Opts.isPhaseEnabled(PassPhase::ImportJScop))
|
|
runImportJSON(*S, DA);
|
|
|
|
// Phase: simplify-0
|
|
bool ModifiedSinceSimplify = true;
|
|
if (Opts.isPhaseEnabled(PassPhase::Simplify0)) {
|
|
runSimplify(*S, 0);
|
|
ModifiedSinceSimplify = false;
|
|
}
|
|
|
|
// Phase: optree
|
|
if (Opts.isPhaseEnabled(PassPhase::Optree)) {
|
|
bool ModifiedByOptree = runForwardOpTree(*S);
|
|
ModifiedSinceSimplify |= ModifiedByOptree;
|
|
}
|
|
|
|
// Phase: delicm
|
|
if (Opts.isPhaseEnabled(PassPhase::DeLICM)) {
|
|
bool ModifiedByDelicm = runDeLICM(*S);
|
|
ModifiedSinceSimplify |= ModifiedByDelicm;
|
|
}
|
|
|
|
// Phase: simplify-1
|
|
// If we have already run simplify-0, do not re-run it if the SCoP has not
|
|
// changed since then.
|
|
if (ModifiedSinceSimplify && Opts.isPhaseEnabled(PassPhase::Simplify1)) {
|
|
runSimplify(*S, 1);
|
|
ModifiedSinceSimplify = false;
|
|
}
|
|
|
|
// Phase: dce
|
|
if (Opts.isPhaseEnabled(PassPhase::DeadCodeElimination))
|
|
runDeadCodeElim(*S, DA);
|
|
|
|
// Phase: mse
|
|
if (Opts.isPhaseEnabled(PassPhase::MaximumStaticExtension))
|
|
runMaximalStaticExpansion(*S, DA);
|
|
|
|
// Phase: prune
|
|
if (Opts.isPhaseEnabled(PassPhase::PruneUnprofitable))
|
|
runPruneUnprofitable(*S);
|
|
|
|
// Phase: opt-isl
|
|
if (Opts.isPhaseEnabled(PassPhase::Optimization))
|
|
runIslScheduleOptimizer(*S, &TTI, DA);
|
|
|
|
// Phase: import-jscop
|
|
if (Opts.isPhaseEnabled(PassPhase::ExportJScop))
|
|
runExportJSON(*S);
|
|
|
|
// Phase: ast
|
|
// Cannot run codegen unless ast is enabled
|
|
if (!Opts.isPhaseEnabled(PassPhase::AstGen))
|
|
continue;
|
|
std::unique_ptr<IslAstInfo> IslAst = runIslAstGen(*S, DA);
|
|
|
|
// Phase: codegen
|
|
if (!Opts.isPhaseEnabled(PassPhase::CodeGen))
|
|
continue;
|
|
bool ModifiedByCodeGen = runCodeGeneration(*S, RI, *IslAst);
|
|
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();
|
|
}
|
|
}
|
|
|
|
return ModifiedIR;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
StringRef polly::getPhaseName(PassPhase Phase) {
|
|
switch (Phase) {
|
|
case PassPhase::Prepare:
|
|
return "prepare";
|
|
case PassPhase::Detection:
|
|
return "detect";
|
|
case PassPhase::PrintDetect:
|
|
return "print-detect";
|
|
case PassPhase::DotScops:
|
|
return "dot-scops";
|
|
case PassPhase::DotScopsOnly:
|
|
return "dot-scops-only";
|
|
case PassPhase::ViewScops:
|
|
return "view-scops";
|
|
case PassPhase::ViewScopsOnly:
|
|
return "view-scops-only";
|
|
case PassPhase::ScopInfo:
|
|
return "scops";
|
|
case PassPhase::PrintScopInfo:
|
|
return "print-scops";
|
|
case PassPhase::Flatten:
|
|
return "flatten";
|
|
case PassPhase::Dependences:
|
|
return "deps";
|
|
case PassPhase::PrintDependences:
|
|
return "print-deps";
|
|
case PassPhase::ImportJScop:
|
|
return "import-jscop";
|
|
case PassPhase::Simplify0:
|
|
return "simplify-0";
|
|
case PassPhase::Optree:
|
|
return "optree";
|
|
case PassPhase::DeLICM:
|
|
return "delicm";
|
|
case PassPhase::Simplify1:
|
|
return "simplify-1";
|
|
case PassPhase::DeadCodeElimination:
|
|
return "dce";
|
|
case PassPhase::MaximumStaticExtension:
|
|
return "mse";
|
|
case PassPhase::PruneUnprofitable:
|
|
return "prune";
|
|
case PassPhase::Optimization:
|
|
return "opt-isl"; // "opt" would conflict with the llvm executable
|
|
case PassPhase::ExportJScop:
|
|
return "export-jscop";
|
|
case PassPhase::AstGen:
|
|
return "ast";
|
|
case PassPhase::CodeGen:
|
|
return "codegen";
|
|
default:
|
|
llvm_unreachable("Unexpected phase");
|
|
}
|
|
}
|
|
|
|
PassPhase polly::parsePhase(StringRef Name) {
|
|
return StringSwitch<PassPhase>(Name)
|
|
.Case("prepare", PassPhase::Prepare)
|
|
.Case("detect", PassPhase::Detection)
|
|
.Case("print-detect", PassPhase::PrintDetect)
|
|
.Case("dot-scops", PassPhase::DotScops)
|
|
.Case("dot-scops-only", PassPhase::DotScopsOnly)
|
|
.Case("view-scops", PassPhase::ViewScops)
|
|
.Case("view-scops-only", PassPhase::ViewScopsOnly)
|
|
.Case("scops", PassPhase::ScopInfo)
|
|
.Case("print-scops", PassPhase::PrintScopInfo)
|
|
.Case("flatten", PassPhase::Flatten)
|
|
.Case("deps", PassPhase::Dependences)
|
|
.Case("print-deps", PassPhase::PrintDependences)
|
|
.Case("import-jscop", PassPhase::ImportJScop)
|
|
.Case("simplify-0", PassPhase::Simplify0)
|
|
.Case("optree", PassPhase::Optree)
|
|
.Case("delicm", PassPhase::DeLICM)
|
|
.Case("simplify-1", PassPhase::Simplify1)
|
|
.Case("dce", PassPhase::DeadCodeElimination)
|
|
.Case("mse", PassPhase::MaximumStaticExtension)
|
|
.Case("prune", PassPhase::PruneUnprofitable)
|
|
.Case("opt-isl", PassPhase::Optimization)
|
|
.Case("export-jscop", PassPhase::ExportJScop)
|
|
.Case("ast", PassPhase::AstGen)
|
|
.Case("codegen", PassPhase::CodeGen)
|
|
.Default(PassPhase::None);
|
|
}
|
|
|
|
bool polly::dependsOnDependenceInfo(PassPhase Phase) {
|
|
// Nothing before dep phase can depend on it
|
|
if (static_cast<size_t>(Phase) <= static_cast<size_t>(PassPhase::Dependences))
|
|
return false;
|
|
|
|
switch (Phase) {
|
|
case PassPhase::Simplify0:
|
|
case PassPhase::Optree:
|
|
case PassPhase::DeLICM:
|
|
case PassPhase::Simplify1:
|
|
case PassPhase::PruneUnprofitable:
|
|
case PassPhase::ImportJScop:
|
|
case PassPhase::ExportJScop:
|
|
case PassPhase::AstGen: // transitively through codegen
|
|
case PassPhase::CodeGen:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void PollyPassOptions::enableEnd2End() {
|
|
setPhaseEnabled(PassPhase::Detection);
|
|
setPhaseEnabled(PassPhase::ScopInfo);
|
|
setPhaseEnabled(PassPhase::Dependences);
|
|
setPhaseEnabled(PassPhase::AstGen);
|
|
setPhaseEnabled(PassPhase::CodeGen);
|
|
}
|
|
|
|
void PollyPassOptions::enableDefaultOpts() {
|
|
setPhaseEnabled(PassPhase::Prepare);
|
|
setPhaseEnabled(PassPhase::Simplify0);
|
|
setPhaseEnabled(PassPhase::Optree);
|
|
setPhaseEnabled(PassPhase::DeLICM);
|
|
setPhaseEnabled(PassPhase::Simplify1);
|
|
setPhaseEnabled(PassPhase::PruneUnprofitable);
|
|
setPhaseEnabled(PassPhase::Optimization);
|
|
}
|
|
|
|
void PollyPassOptions::disableAfter(PassPhase Phase) {
|
|
assert(Phase != PassPhase::None);
|
|
for (PassPhase P : enum_seq_inclusive(Phase, PassPhase::PassPhaseLast)) {
|
|
if (P == Phase)
|
|
continue;
|
|
setPhaseEnabled(P, false);
|
|
}
|
|
}
|
|
|
|
Error PollyPassOptions::checkConsistency() const {
|
|
for (PassPhase P : enum_seq_inclusive(PassPhase::PassPhaseFirst,
|
|
PassPhase::PassPhaseLast)) {
|
|
if (!isPhaseEnabled(P))
|
|
continue;
|
|
|
|
// Prepare and Detection have no requirements
|
|
if (P == PassPhase::Prepare || P == PassPhase::Detection)
|
|
continue;
|
|
|
|
if (!isPhaseEnabled(PassPhase::Detection))
|
|
return make_error<StringError>(
|
|
formatv("'{0}' requires 'detect' to be enabled", getPhaseName(P))
|
|
.str(),
|
|
inconvertibleErrorCode());
|
|
|
|
if (static_cast<size_t>(P) < static_cast<size_t>(PassPhase::ScopInfo))
|
|
continue;
|
|
|
|
if (!isPhaseEnabled(PassPhase::ScopInfo))
|
|
return make_error<StringError>(
|
|
formatv("'{0}' requires 'scops' to be enabled", getPhaseName(P))
|
|
.str(),
|
|
inconvertibleErrorCode());
|
|
|
|
if (dependsOnDependenceInfo(P) && !isPhaseEnabled(PassPhase::Dependences))
|
|
return make_error<StringError>(
|
|
formatv("'{0}' requires 'deps' to be enabled", getPhaseName(P)).str(),
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if (isPhaseEnabled(PassPhase::CodeGen) && !isPhaseEnabled(PassPhase::AstGen))
|
|
return make_error<StringError>("'codegen' requires 'ast' to be enabled",
|
|
inconvertibleErrorCode());
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
bool polly::runPollyPass(Function &F, FunctionAnalysisManager &FAM,
|
|
PollyPassOptions Opts) {
|
|
return PhaseManager(F, FAM, std::move(Opts)).run();
|
|
}
|