//===------ 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 &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(F); DominatorTree &DT = FAM.getResult(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(); PA.preserve(); FAM.invalidate(F, PA); ModifiedIR = true; } } // Can't do anything without detection if (!Opts.isPhaseEnabled(PassPhase::Detection)) return false; AAResults &AA = FAM.getResult(F); ScalarEvolution &SE = FAM.getResult(F); OptimizationRemarkEmitter &ORE = FAM.getResult(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(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 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 Worklist; for (auto &[R, S] : Info) if (S) Worklist.insert(R); TargetTransformInfo &TTI = FAM.getResult(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 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(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(Phase) <= static_cast(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( formatv("'{0}' requires 'detect' to be enabled", getPhaseName(P)) .str(), inconvertibleErrorCode()); if (static_cast(P) < static_cast(PassPhase::ScopInfo)) continue; if (!isPhaseEnabled(PassPhase::ScopInfo)) return make_error( formatv("'{0}' requires 'scops' to be enabled", getPhaseName(P)) .str(), inconvertibleErrorCode()); if (dependsOnDependenceInfo(P) && !isPhaseEnabled(PassPhase::Dependences)) return make_error( formatv("'{0}' requires 'deps' to be enabled", getPhaseName(P)).str(), inconvertibleErrorCode()); } if (isPhaseEnabled(PassPhase::CodeGen) && !isPhaseEnabled(PassPhase::AstGen)) return make_error("'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(); }