Skip to content

Commit e8b01e9

Browse files
committed
[SandboxVec] Add pass to create Regions from metadata. Generalize SandboxVec pass pipelines.
My previous attempt (llvm#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 3484ed9 commit e8b01e9

File tree

17 files changed

+369
-75
lines changed

17 files changed

+369
-75
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: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,40 +50,130 @@ class PassManager : public ParentPass {
5050
}
5151

5252
using CreatePassFunc =
53-
std::function<std::unique_ptr<ContainedPass>(StringRef)>;
53+
std::function<std::unique_ptr<ContainedPass>(StringRef, StringRef)>;
5454

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

6476
assert(Passes.empty() &&
6577
"setPassPipeline called on a non-empty sandboxir::PassManager");
66-
// Add EndToken to the end to ease parsing.
67-
std::string PipelineStr = std::string(Pipeline) + EndToken;
68-
int FlagBeginIdx = 0;
6978

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;
79+
// Accept an empty pipeline as a special case. This can be useful, for
80+
// example, to test conversion to SandboxIR without running any passes on
81+
// it.
82+
if (Pipeline.empty())
83+
return;
7884

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

@@ -101,7 +191,7 @@ class PassManager : public ParentPass {
101191
}
102192
#endif
103193
/// Similar to print() but prints one pass per line. Used for testing.
104-
void printPipeline(raw_ostream &OS) const {
194+
void printPipeline(raw_ostream &OS) const override {
105195
OS << this->getName() << "\n";
106196
for (const auto &PassPtr : Passes)
107197
PassPtr->printPipeline(OS);

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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
};
31+
32+
} // namespace llvm::sandboxir
33+
34+
#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

llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.cpp

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,17 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.h"
10+
1011
#include "llvm/ADT/SmallVector.h"
1112
#include "llvm/SandboxIR/Function.h"
1213
#include "llvm/SandboxIR/Instruction.h"
13-
#include "llvm/Support/CommandLine.h"
14-
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Passes/NullPass.h"
14+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizerPassBuilder.h"
1515

1616
namespace llvm::sandboxir {
1717

18-
static cl::opt<bool>
19-
PrintPassPipeline("sbvec-print-pass-pipeline", cl::init(false), cl::Hidden,
20-
cl::desc("Prints the pass pipeline and returns."));
21-
22-
/// A magic string for the default pass pipeline.
23-
static const char *DefaultPipelineMagicStr = "*";
24-
25-
static cl::opt<std::string> UserDefinedPassPipeline(
26-
"sbvec-passes", cl::init(DefaultPipelineMagicStr), cl::Hidden,
27-
cl::desc("Comma-separated list of vectorizer passes. If not set "
28-
"we run the predefined pipeline."));
29-
30-
static std::unique_ptr<RegionPass> createRegionPass(StringRef Name) {
31-
#define REGION_PASS(NAME, CREATE_PASS) \
32-
if (Name == NAME) \
33-
return std::make_unique<decltype(CREATE_PASS)>(CREATE_PASS);
34-
#include "PassRegistry.def"
35-
return nullptr;
36-
}
37-
38-
BottomUpVec::BottomUpVec() : FunctionPass("bottom-up-vec"), RPM("rpm") {
39-
// Create a pipeline to be run on each Region created by BottomUpVec.
40-
if (UserDefinedPassPipeline == DefaultPipelineMagicStr) {
41-
// TODO: Add default passes to RPM.
42-
} else {
43-
// Create the user-defined pipeline.
44-
RPM.setPassPipeline(UserDefinedPassPipeline, createRegionPass);
45-
}
18+
BottomUpVec::BottomUpVec(StringRef Pipeline)
19+
: FunctionPass("bottom-up-vec"), RPM("rpm") {
20+
RPM.setPassPipeline(Pipeline, SandboxVectorizerPassBuilder::createRegionPass);
4621
}
4722

4823
// TODO: This is a temporary function that returns some seeds.
@@ -82,11 +57,6 @@ void BottomUpVec::vectorizeRec(ArrayRef<Value *> Bndl) {
8257
void BottomUpVec::tryVectorize(ArrayRef<Value *> Bndl) { vectorizeRec(Bndl); }
8358

8459
bool BottomUpVec::runOnFunction(Function &F) {
85-
if (PrintPassPipeline) {
86-
RPM.printPipeline(outs());
87-
return false;
88-
}
89-
9060
Change = false;
9161
// TODO: Start from innermost BBs first
9262
for (auto &BB : F) {

llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/PassRegistry.def

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,19 @@
1414
// NOTE: NO INCLUDE GUARD DESIRED!
1515

1616
#ifndef REGION_PASS
17-
#define REGION_PASS(NAME, CREATE_PASS)
17+
#define REGION_PASS(NAME, CLASS_NAME)
1818
#endif
1919

20-
REGION_PASS("null", NullPass())
20+
REGION_PASS("null", ::llvm::sandboxir::NullPass)
21+
REGION_PASS("print-instruction-count", ::llvm::sandboxir::PrintInstructionCount)
2122

2223
#undef REGION_PASS
24+
25+
#ifndef FUNCTION_PASS_WITH_PARAMS
26+
#define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS_NAME)
27+
#endif
28+
29+
FUNCTION_PASS_WITH_PARAMS("bottom-up-vec", ::llvm::sandboxir::BottomUpVec)
30+
FUNCTION_PASS_WITH_PARAMS("regions-from-metadata", ::llvm::sandboxir::RegionsFromMetadata)
31+
32+
#undef FUNCTION_PASS_WITH_PARAMS

0 commit comments

Comments
 (0)