Skip to content

Commit 2e8ad49

Browse files
authored
[SandboxVec] Add pass to create Regions from metadata. Generalize SandboxVec pass pipelines. (#112288)
My previous attempt (#111904) hacked creation of Regions from metadata into the bottom-up vectorizer. I got some feedback that it should be its own pass. So now we have two SandboxIR function passes (`BottomUpVec` and `RegionsFromMetadata`) that are interchangeable, and we could have other SandboxIR function passes doing other kinds of transforms, so this commit revamps pipeline creation and parsing. First, `sandboxir::PassManager::setPassPipeline` now accepts pass arguments in angle brackets. Pass arguments are arbitrary strings that must be parsed by each pass, the only requirement is that nested angle bracket pairs must be balanced, to allow for nested pipelines with more arguments. For example: ``` bottom-up-vec<region-pass-1,region-pass-2<arg>,region-pass-3> ``` This has complicated the parser a little bit (the loop over pipeline characters now contains a small state machine), and we now have some new test cases to exercise the new features. The main SandboxVectorizerPass now contains a customizable pipeline of SandboxIR function passes, defined by the `sbvec-passes` flag. Region passes for the bottom-up vectorizer pass are now in pass arguments (like in the example above). Because we have now several classes that can build sub-pass pipelines, I've moved the logic that interacts with PassRegistry.def into its own files (PassBuilder.{h,cpp} so it can be easily reused. Finally, I've added a `RegionsFromMetadata` function pass, which will allow us to run region passes in isolation from lit tests without relying on the bottom-up vectorizer, and a new lit test that does exactly this. Note that the new pipeline parser now allows empty pipelines. This is useful for testing. For example, if we use ``` -sbvec-passes="bottom-up-vec<>" ``` SandboxVectorizer converts LLVM IR to SandboxIR and runs the bottom-up vectorizer, but no region passes afterwards. ``` -sbvec-passes="" ``` SandboxVectorizer converts LLVM IR to SandboxIR and runs no passes on it. This is useful to exercise SandboxIR conversion on its own.
1 parent f6c2322 commit 2e8ad49

File tree

17 files changed

+409
-79
lines changed

17 files changed

+409
-79
lines changed

llvm/include/llvm/SandboxIR/Pass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Pass {
4343
LLVM_DUMP_METHOD virtual void dump() const;
4444
#endif
4545
/// Similar to print() but adds a newline. Used for testing.
46-
void printPipeline(raw_ostream &OS) const { OS << Name << "\n"; }
46+
virtual void printPipeline(raw_ostream &OS) const { OS << Name << "\n"; }
4747
};
4848

4949
/// A pass that runs on a sandbox::Function.

llvm/include/llvm/SandboxIR/PassManager.h

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,20 @@ class Value;
3232
/// Base class.
3333
template <typename ParentPass, typename ContainedPass>
3434
class PassManager : public ParentPass {
35+
public:
36+
// CreatePassFunc(StringRef PassName, StringRef PassArgs).
37+
using CreatePassFunc =
38+
std::function<std::unique_ptr<ContainedPass>(StringRef, StringRef)>;
39+
3540
protected:
3641
/// The list of passes that this pass manager will run.
3742
SmallVector<std::unique_ptr<ContainedPass>> Passes;
3843

3944
PassManager(StringRef Name) : ParentPass(Name) {}
45+
PassManager(StringRef Name, StringRef Pipeline, CreatePassFunc CreatePass)
46+
: ParentPass(Name) {
47+
setPassPipeline(Pipeline, CreatePass);
48+
}
4049
PassManager(const PassManager &) = delete;
4150
PassManager(PassManager &&) = default;
4251
virtual ~PassManager() = default;
@@ -49,41 +58,125 @@ class PassManager : public ParentPass {
4958
Passes.push_back(std::move(Pass));
5059
}
5160

52-
using CreatePassFunc =
53-
std::function<std::unique_ptr<ContainedPass>(StringRef)>;
54-
5561
/// Parses \p Pipeline as a comma-separated sequence of pass names and sets
5662
/// the pass pipeline, using \p CreatePass to instantiate passes by name.
5763
///
58-
/// After calling this function, the PassManager contains only the specified
59-
/// pipeline, any previously added passes are cleared.
64+
/// Passes can have arguments, for example:
65+
/// "pass1<arg1,arg2>,pass2,pass3<arg3,arg4>"
66+
///
67+
/// The arguments between angle brackets are treated as a mostly opaque string
68+
/// and each pass is responsible for parsing its arguments. The exception to
69+
/// this are nested angle brackets, which must match pair-wise to allow
70+
/// arguments to contain nested pipelines, like:
71+
///
72+
/// "pass1<subpass1,subpass2<arg1,arg2>,subpass3>"
73+
///
74+
/// An empty args string is treated the same as no args, so "pass" and
75+
/// "pass<>" are equivalent.
6076
void setPassPipeline(StringRef Pipeline, CreatePassFunc CreatePass) {
6177
static constexpr const char EndToken = '\0';
78+
static constexpr const char BeginArgsToken = '<';
79+
static constexpr const char EndArgsToken = '>';
6280
static constexpr const char PassDelimToken = ',';
6381

6482
assert(Passes.empty() &&
6583
"setPassPipeline called on a non-empty sandboxir::PassManager");
84+
85+
// Accept an empty pipeline as a special case. This can be useful, for
86+
// example, to test conversion to SandboxIR without running any passes on
87+
// it.
88+
if (Pipeline.empty())
89+
return;
90+
6691
// Add EndToken to the end to ease parsing.
6792
std::string PipelineStr = std::string(Pipeline) + EndToken;
68-
int FlagBeginIdx = 0;
69-
70-
for (auto [Idx, C] : enumerate(PipelineStr)) {
71-
// Keep moving Idx until we find the end of the pass name.
72-
bool FoundDelim = C == EndToken || C == PassDelimToken;
73-
if (!FoundDelim)
74-
continue;
75-
unsigned Sz = Idx - FlagBeginIdx;
76-
std::string PassName(&PipelineStr[FlagBeginIdx], Sz);
77-
FlagBeginIdx = Idx + 1;
93+
Pipeline = StringRef(PipelineStr);
7894

95+
auto AddPass = [this, CreatePass](StringRef PassName, StringRef PassArgs) {
96+
if (PassName.empty()) {
97+
errs() << "Found empty pass name.\n";
98+
exit(1);
99+
}
79100
// Get the pass that corresponds to PassName and add it to the pass
80101
// manager.
81-
auto Pass = CreatePass(PassName);
102+
auto Pass = CreatePass(PassName, PassArgs);
82103
if (Pass == nullptr) {
83104
errs() << "Pass '" << PassName << "' not registered!\n";
84105
exit(1);
85106
}
86107
addPass(std::move(Pass));
108+
};
109+
110+
enum class State {
111+
ScanName, // reading a pass name
112+
ScanArgs, // reading a list of args
113+
ArgsEnded, // read the last '>' in an args list, must read delimiter next
114+
} CurrentState = State::ScanName;
115+
int PassBeginIdx = 0;
116+
int ArgsBeginIdx;
117+
StringRef PassName;
118+
int NestedArgs = 0;
119+
for (auto [Idx, C] : enumerate(Pipeline)) {
120+
switch (CurrentState) {
121+
case State::ScanName:
122+
if (C == BeginArgsToken) {
123+
// Save pass name for later and begin scanning args.
124+
PassName = Pipeline.slice(PassBeginIdx, Idx);
125+
ArgsBeginIdx = Idx + 1;
126+
++NestedArgs;
127+
CurrentState = State::ScanArgs;
128+
break;
129+
}
130+
if (C == EndArgsToken) {
131+
errs() << "Unexpected '>' in pass pipeline.\n";
132+
exit(1);
133+
}
134+
if (C == EndToken || C == PassDelimToken) {
135+
// Delimiter found, add the pass (with empty args), stay in the
136+
// ScanName state.
137+
AddPass(Pipeline.slice(PassBeginIdx, Idx), StringRef());
138+
PassBeginIdx = Idx + 1;
139+
}
140+
break;
141+
case State::ScanArgs:
142+
// While scanning args, we only care about making sure nesting of angle
143+
// brackets is correct.
144+
if (C == BeginArgsToken) {
145+
++NestedArgs;
146+
break;
147+
}
148+
if (C == EndArgsToken) {
149+
--NestedArgs;
150+
if (NestedArgs == 0) {
151+
// Done scanning args.
152+
AddPass(PassName, Pipeline.slice(ArgsBeginIdx, Idx));
153+
CurrentState = State::ArgsEnded;
154+
} else if (NestedArgs < 0) {
155+
errs() << "Unexpected '>' in pass pipeline.\n";
156+
exit(1);
157+
}
158+
break;
159+
}
160+
if (C == EndToken) {
161+
errs() << "Missing '>' in pass pipeline. End-of-string reached while "
162+
"reading arguments for pass '"
163+
<< PassName << "'.\n";
164+
exit(1);
165+
}
166+
break;
167+
case State::ArgsEnded:
168+
// Once we're done scanning args, only a delimiter is valid. This avoids
169+
// accepting strings like "foo<args><more-args>" or "foo<args>bar".
170+
if (C == EndToken || C == PassDelimToken) {
171+
PassBeginIdx = Idx + 1;
172+
CurrentState = State::ScanName;
173+
} else {
174+
errs() << "Expected delimiter or end-of-string after pass "
175+
"arguments.\n";
176+
exit(1);
177+
}
178+
break;
179+
}
87180
}
88181
}
89182

@@ -101,7 +194,7 @@ class PassManager : public ParentPass {
101194
}
102195
#endif
103196
/// Similar to print() but prints one pass per line. Used for testing.
104-
void printPipeline(raw_ostream &OS) const {
197+
void printPipeline(raw_ostream &OS) const override {
105198
OS << this->getName() << "\n";
106199
for (const auto &PassPtr : Passes)
107200
PassPtr->printPipeline(OS);
@@ -112,12 +205,18 @@ class FunctionPassManager final
112205
: public PassManager<FunctionPass, FunctionPass> {
113206
public:
114207
FunctionPassManager(StringRef Name) : PassManager(Name) {}
208+
FunctionPassManager(StringRef Name, StringRef Pipeline,
209+
CreatePassFunc CreatePass)
210+
: PassManager(Name, Pipeline, CreatePass) {}
115211
bool runOnFunction(Function &F) final;
116212
};
117213

118214
class RegionPassManager final : public PassManager<RegionPass, RegionPass> {
119215
public:
120216
RegionPassManager(StringRef Name) : PassManager(Name) {}
217+
RegionPassManager(StringRef Name, StringRef Pipeline,
218+
CreatePassFunc CreatePass)
219+
: PassManager(Name, Pipeline, CreatePass) {}
121220
bool runOnRegion(Region &R) final;
122221
};
123222

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_BOTTOMUPVEC_H
1414

1515
#include "llvm/ADT/ArrayRef.h"
16+
#include "llvm/ADT/StringRef.h"
1617
#include "llvm/SandboxIR/Constant.h"
1718
#include "llvm/SandboxIR/Pass.h"
1819
#include "llvm/SandboxIR/PassManager.h"
20+
#include "llvm/Support/raw_ostream.h"
1921
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h"
2022

2123
namespace llvm::sandboxir {
2224

23-
class RegionPassManager;
24-
2525
class BottomUpVec final : public FunctionPass {
2626
bool Change = false;
2727
LegalityAnalysis Legality;
@@ -32,8 +32,12 @@ class BottomUpVec final : public FunctionPass {
3232
RegionPassManager RPM;
3333

3434
public:
35-
BottomUpVec();
35+
BottomUpVec(StringRef Pipeline);
3636
bool runOnFunction(Function &F) final;
37+
void printPipeline(raw_ostream &OS) const final {
38+
OS << getName() << "\n";
39+
RPM.printPipeline(OS);
40+
}
3741
};
3842

3943
} // namespace llvm::sandboxir
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNT_H
2+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNT_H
3+
4+
#include "llvm/SandboxIR/Pass.h"
5+
#include "llvm/SandboxIR/Region.h"
6+
7+
namespace llvm::sandboxir {
8+
9+
/// A Region pass that prints the instruction count for the region to stdout.
10+
/// Used to test -sbvec-passes while we don't have any actual optimization
11+
/// passes.
12+
class PrintInstructionCount final : public RegionPass {
13+
public:
14+
PrintInstructionCount() : RegionPass("null") {}
15+
bool runOnRegion(Region &R) final {
16+
outs() << "InstructionCount: " << std::distance(R.begin(), R.end()) << "\n";
17+
return false;
18+
}
19+
};
20+
21+
} // namespace llvm::sandboxir
22+
23+
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNTPASS_H
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- RegionsFromMetadata.h ------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// A SandboxIR function pass that builds regions from IR metadata and then runs
10+
// a pipeline of region passes on them. This is useful to test region passes in
11+
// isolation without relying on the output of the bottom-up vectorizer.
12+
//
13+
14+
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H
15+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H
16+
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/SandboxIR/Pass.h"
19+
#include "llvm/SandboxIR/PassManager.h"
20+
21+
namespace llvm::sandboxir {
22+
23+
class RegionsFromMetadata final : public FunctionPass {
24+
// The PM containing the pipeline of region passes.
25+
RegionPassManager RPM;
26+
27+
public:
28+
RegionsFromMetadata(StringRef Pipeline);
29+
bool runOnFunction(Function &F) final;
30+
void printPipeline(raw_ostream &OS) const final {
31+
OS << getName() << "\n";
32+
RPM.printPipeline(OS);
33+
}
34+
};
35+
36+
} // namespace llvm::sandboxir
37+
38+
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include <memory>
1212

1313
#include "llvm/IR/PassManager.h"
14-
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.h"
14+
#include "llvm/SandboxIR/PassManager.h"
1515

1616
namespace llvm {
1717

@@ -20,8 +20,8 @@ class TargetTransformInfo;
2020
class SandboxVectorizerPass : public PassInfoMixin<SandboxVectorizerPass> {
2121
TargetTransformInfo *TTI = nullptr;
2222

23-
// The main vectorizer pass.
24-
sandboxir::BottomUpVec BottomUpVecPass;
23+
// A pipeline of SandboxIR function passes run by the vectorizer.
24+
sandboxir::FunctionPassManager FPM;
2525

2626
bool runImpl(Function &F);
2727

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- SandboxVectorizerPassBuilder.h ---------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Utility functions so passes with sub-pipelines can create SandboxVectorizer
10+
// passes without replicating the same logic in each pass.
11+
//
12+
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H
13+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H
14+
15+
#include "llvm/ADT/StringRef.h"
16+
#include "llvm/SandboxIR/Pass.h"
17+
18+
#include <memory>
19+
20+
namespace llvm::sandboxir {
21+
22+
class SandboxVectorizerPassBuilder {
23+
public:
24+
static std::unique_ptr<FunctionPass> createFunctionPass(StringRef Name,
25+
StringRef Args);
26+
static std::unique_ptr<RegionPass> createRegionPass(StringRef Name,
27+
StringRef Args);
28+
};
29+
30+
} // namespace llvm::sandboxir
31+
32+
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H

llvm/lib/Transforms/Vectorize/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ add_llvm_component_library(LLVMVectorize
66
SandboxVectorizer/DependencyGraph.cpp
77
SandboxVectorizer/Interval.cpp
88
SandboxVectorizer/Passes/BottomUpVec.cpp
9+
SandboxVectorizer/Passes/RegionsFromMetadata.cpp
910
SandboxVectorizer/SandboxVectorizer.cpp
11+
SandboxVectorizer/SandboxVectorizerPassBuilder.cpp
1012
SandboxVectorizer/SeedCollector.cpp
1113
SLPVectorizer.cpp
1214
Vectorize.cpp

0 commit comments

Comments
 (0)