Files
gps/tools/local/circt/include/llvm/SandboxIR/PassManager.h
T
2026-07-12 11:01:51 +03:00

299 lines
10 KiB
C++

//===- 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 <memory>
#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 <typename ParentPass, typename ContainedPass>
class PassManager : public ParentPass {
public:
// CreatePassFunc(StringRef PassName, StringRef PassArgs, StringRef AuxArg)
using CreatePassFunc = std::function<std::unique_ptr<ContainedPass>(
StringRef, StringRef, StringRef)>;
protected:
/// The list of passes that this pass manager will run.
SmallVector<std::unique_ptr<ContainedPass>> 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<ContainedPass> 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<arg1,arg2>,pass2,pass3<arg3,arg4>"
/// - 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)<arg1,arg2>,pass2,pass3(bar)<arg3,arg4>"
/// 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<subpass1,subpass2<arg1,arg2>,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<args><more-args>" or "foo<args>bar".
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<FunctionPass, FunctionPass> {
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<RegionPass, RegionPass> {
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