//===- PassManager.h --------------------------------------------*- 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 // //===----------------------------------------------------------------------===// // // Registers and executes the Sandbox IR passes. // // The pass manager contains an ordered sequence of passes that it runs in // order. The passes are owned by the PassRegistry, not by the PassManager. // // Note that in this design a pass manager is also a pass. So a pass manager // runs when it is it's turn to run in its parent pass-manager pass pipeline. // #ifndef LLVM_SANDBOXIR_PASSMANAGER_H #define LLVM_SANDBOXIR_PASSMANAGER_H #include "llvm/Support/Compiler.h" #include #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/SandboxIR/Pass.h" #include "llvm/Support/Debug.h" namespace llvm::sandboxir { class Value; /// Base class. template class PassManager : public ParentPass { public: // CreatePassFunc(StringRef PassName, StringRef PassArgs, StringRef AuxArg) using CreatePassFunc = std::function( StringRef, StringRef, StringRef)>; protected: /// The list of passes that this pass manager will run. SmallVector> Passes; PassManager(StringRef Name) : ParentPass(Name) {} PassManager(StringRef Name, StringRef Pipeline, CreatePassFunc CreatePass) : ParentPass(Name) { setPassPipeline(Pipeline, CreatePass); } PassManager(const PassManager &) = delete; PassManager(PassManager &&) = default; ~PassManager() override = default; PassManager &operator=(const PassManager &) = delete; public: /// Adds \p Pass to the pass pipeline. void addPass(std::unique_ptr Pass) { // TODO: Check that Pass's class type works with this PassManager type. Passes.push_back(std::move(Pass)); } static constexpr char EndToken = '\0'; static constexpr char BeginArgsToken = '<'; static constexpr char EndArgsToken = '>'; static constexpr char BeginAuxArgsToken = '('; static constexpr char EndAuxArgsToken = ')'; static constexpr char PassDelimToken = ','; /// Parses \p Pipeline as a comma-separated sequence of pass names and sets /// the pass pipeline, using \p CreatePass to instantiate passes by name. /// /// Passes can have two types of arguments: /// - The standard pass arguments within < >, which are commonly used for /// passing a comma-separated list of pass names, for example: /// "pass1,pass2,pass3" /// - An auxiliary pass argument within ( ), which is used as a secondary /// argument, for example: /// "pass1(foo),pass2,pass3(bar)" /// Note that both types of arguments can be combined, like: /// "pass1(foo),pass2,pass3(bar)" /// The reason why there is more than one type of pass argument, is to make /// it easier for passes to parse the argument contents, and make the pass /// pipeline more consistent and easier to read, than relying on some /// formatting that the passes might expect. /// /// The arguments between both bracket types are treated as a mostly opaque /// string and each pass is responsible for parsing its arguments. The /// exception to this are nested brackets, which must match pair-wise to /// allow arguments to contain nested pipelines or nested aux arguments, like: /// /// "pass1,subpass3>" /// or /// "pass1(arg1(nestedarg))" /// /// An empty args string is treated the same as no args, so "pass" and /// "pass<>" are equivalent. /// void setPassPipeline(StringRef Pipeline, CreatePassFunc CreatePass) { assert(Passes.empty() && "setPassPipeline called on a non-empty sandboxir::PassManager"); // Accept an empty pipeline as a special case. This can be useful, for // example, to test conversion to SandboxIR without running any passes on // it. if (Pipeline.empty()) return; // Add EndToken to the end to ease parsing. std::string PipelineStr = std::string(Pipeline) + EndToken; Pipeline = StringRef(PipelineStr); auto AddPass = [this, CreatePass](StringRef PassName, StringRef PassArgs, StringRef AuxArg) { if (PassName.empty()) { errs() << "Found empty pass name.\n"; exit(1); } // Get the pass that corresponds to PassName and add it to the pass // manager. auto Pass = CreatePass(PassName, PassArgs, AuxArg); if (Pass == nullptr) { errs() << "Pass '" << PassName << "' not registered!\n"; exit(1); } addPass(std::move(Pass)); }; enum class State { ScanName, // reading a pass name ScanArgs, // reading a list of args ArgsEnded, // read the last '>' in an args list, must read delimiter next ScanAuxArgs, // reading the auxiliary argument AuxArgsEnded, // read the last ')' in aux args list } CurrentState = State::ScanName; int PassBeginIdx = 0; int ArgsBeginIdx; int AuxArgsBeginIdx = 0; StringRef PassName; StringRef AuxArg; int NestedArgs = 0; int NestedAuxArgs = 0; for (auto [Idx, C] : enumerate(Pipeline)) { switch (CurrentState) { case State::ScanName: if (C == BeginArgsToken) { // Save pass name for later and begin scanning args. PassName = Pipeline.slice(PassBeginIdx, Idx); ArgsBeginIdx = Idx + 1; ++NestedArgs; CurrentState = State::ScanArgs; break; } if (C == BeginAuxArgsToken) { // Save pass name for later and begin scanning args. PassName = Pipeline.slice(PassBeginIdx, Idx); AuxArgsBeginIdx = Idx + 1; ++NestedAuxArgs; CurrentState = State::ScanAuxArgs; AuxArg = StringRef(); break; } if (C == EndArgsToken || C == EndAuxArgsToken) { errs() << "Unexpected '" << C << "' in pass pipeline.\n"; exit(1); } if (C == EndToken || C == PassDelimToken) { // Delimiter found, add the pass (with empty args), stay in the // ScanName state. AddPass(Pipeline.slice(PassBeginIdx, Idx), StringRef(), AuxArg); PassBeginIdx = Idx + 1; } break; case State::ScanArgs: // While scanning args, we only care about making sure nesting of angle // brackets is correct. if (C == BeginArgsToken) { ++NestedArgs; break; } if (C == EndArgsToken) { --NestedArgs; if (NestedArgs == 0) { // Done scanning args. AddPass(PassName, Pipeline.slice(ArgsBeginIdx, Idx), AuxArg); CurrentState = State::ArgsEnded; } else if (NestedArgs < 0) { errs() << "Unexpected '>' in pass pipeline.\n"; exit(1); } break; } if (C == EndToken) { errs() << "Missing '>' in pass pipeline. End-of-string reached while " "reading arguments for pass '" << PassName << "'.\n"; exit(1); } break; case State::ArgsEnded: // Once we're done scanning args, only a delimiter is valid. This avoids // accepting strings like "foo" or "foobar". if (C == EndToken || C == PassDelimToken) { PassBeginIdx = Idx + 1; AuxArg = StringRef(); CurrentState = State::ScanName; } else { errs() << "Expected delimiter or end-of-string after pass " "arguments.\n"; exit(1); } break; case State::ScanAuxArgs: if (C == BeginAuxArgsToken) { ++NestedAuxArgs; break; } if (C == EndAuxArgsToken) { --NestedAuxArgs; if (NestedAuxArgs == 0) { AuxArg = Pipeline.slice(AuxArgsBeginIdx, Idx); CurrentState = State::AuxArgsEnded; } else if (NestedAuxArgs < 0) { errs() << "Unexpected '" << EndAuxArgsToken << "' in pass pipeline.\n"; exit(1); } break; } if (C == EndToken) { errs() << "Missing '" << EndAuxArgsToken << "' in pass pipeline. End-of-string reached while " "reading arguments for pass '" << PassName << "'.\n"; exit(1); } break; case State::AuxArgsEnded: if (C == EndToken || C == PassDelimToken) { AddPass(PassName, StringRef(), AuxArg); CurrentState = State::ScanArgs; } else if (C == BeginArgsToken) { ++NestedArgs; ArgsBeginIdx = Idx + 1; CurrentState = State::ScanArgs; } else { errs() << "Expected delimiter, begin-of-args or end-of-string after " "pass aux argument.\n"; exit(1); } break; } } } #ifndef NDEBUG void print(raw_ostream &OS) const override { OS << this->getName(); OS << BeginArgsToken; std::string Delim(1, PassDelimToken); interleave(Passes, OS, [&OS](auto &Pass) { Pass->print(OS); }, Delim); OS << EndArgsToken; } LLVM_DUMP_METHOD void dump() const override { print(dbgs()); dbgs() << "\n"; } #endif /// Similar to print() but prints one pass per line. Used for testing. void printPipeline(raw_ostream &OS) const override { OS << this->getName() << "\n"; for (const auto &PassPtr : Passes) PassPtr->printPipeline(OS); } }; class LLVM_ABI FunctionPassManager final : public PassManager { public: FunctionPassManager(StringRef Name) : PassManager(Name) {} FunctionPassManager(StringRef Name, StringRef Pipeline, CreatePassFunc CreatePass) : PassManager(Name, Pipeline, CreatePass) {} bool runOnFunction(Function &F, const Analyses &A) final; }; class LLVM_ABI RegionPassManager final : public PassManager { public: RegionPassManager(StringRef Name) : PassManager(Name) {} RegionPassManager(StringRef Name, StringRef Pipeline, CreatePassFunc CreatePass) : PassManager(Name, Pipeline, CreatePass) {} bool runOnRegion(Region &R, const Analyses &A) final; }; } // namespace llvm::sandboxir #endif // LLVM_SANDBOXIR_PASSMANAGER_H