-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[SandboxVec] Add pass to create Regions from metadata. Generalize SandboxVec pass pipelines. #112288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SandboxVec] Add pass to create Regions from metadata. Generalize SandboxVec pass pipelines. #112288
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,40 +50,130 @@ class PassManager : public ParentPass { | |
| } | ||
|
|
||
| using CreatePassFunc = | ||
| std::function<std::unique_ptr<ContainedPass>(StringRef)>; | ||
| std::function<std::unique_ptr<ContainedPass>(StringRef, StringRef)>; | ||
|
||
|
|
||
| /// Parses \p Pipeline as a comma-separated sequence of pass names and sets | ||
| /// the pass pipeline, using \p CreatePass to instantiate passes by name. | ||
| /// | ||
| /// After calling this function, the PassManager contains only the specified | ||
| /// pipeline, any previously added passes are cleared. | ||
| /// Passes can have arguments, for example: | ||
| /// "pass1<arg1,arg2>,pass2,pass3<arg3,arg4>" | ||
| /// | ||
| /// The arguments between angle brackets are treated as a mostly opaque string | ||
| /// and each pass is responsible for parsing its arguments. The exception to | ||
| /// this are nested angle brackets, which must match pair-wise to allow | ||
| /// arguments to contain nested pipelines, like: | ||
| /// | ||
| /// "pass1<subpass1,subpass2<arg1,arg2>,subpass3>" | ||
| /// | ||
| /// An empty args string is treated the same as no args, so "pass" and | ||
| /// "pass<>" are equivalent. | ||
| void setPassPipeline(StringRef Pipeline, CreatePassFunc CreatePass) { | ||
| static constexpr const char EndToken = '\0'; | ||
| static constexpr const char BeginArgsToken = '<'; | ||
| static constexpr const char EndArgsToken = '>'; | ||
| static constexpr const char PassDelimToken = ','; | ||
|
|
||
| assert(Passes.empty() && | ||
| "setPassPipeline called on a non-empty sandboxir::PassManager"); | ||
| // Add EndToken to the end to ease parsing. | ||
| std::string PipelineStr = std::string(Pipeline) + EndToken; | ||
| int FlagBeginIdx = 0; | ||
|
|
||
| for (auto [Idx, C] : enumerate(PipelineStr)) { | ||
| // Keep moving Idx until we find the end of the pass name. | ||
| bool FoundDelim = C == EndToken || C == PassDelimToken; | ||
| if (!FoundDelim) | ||
| continue; | ||
| unsigned Sz = Idx - FlagBeginIdx; | ||
| std::string PassName(&PipelineStr[FlagBeginIdx], Sz); | ||
| FlagBeginIdx = Idx + 1; | ||
| // 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); | ||
|
|
||
| enum { | ||
|
||
| ScanName, // reading a pass name | ||
| ScanArgs, // reading a list of args | ||
| ArgsEnded, // read the last '>' in an args list, must read delimiter next | ||
| } State; | ||
| State = ScanName; | ||
| int PassBeginIdx = 0; | ||
| int ArgsBeginIdx; | ||
| StringRef PassName; | ||
|
||
| StringRef PassArgs; | ||
|
||
| int NestedArgs = 0; | ||
|
|
||
| auto AddPass = [this, CreatePass](StringRef PassName, StringRef PassArgs) { | ||
| 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); | ||
| auto Pass = CreatePass(PassName, PassArgs); | ||
| if (Pass == nullptr) { | ||
| errs() << "Pass '" << PassName << "' not registered!\n"; | ||
| exit(1); | ||
| } | ||
| addPass(std::move(Pass)); | ||
| }; | ||
| for (auto [Idx, C] : enumerate(Pipeline)) { | ||
| switch (State) { | ||
| case ScanName: | ||
| if (C == BeginArgsToken) { | ||
| // Save pass name for later and begin scanning args. | ||
| PassName = Pipeline.slice(PassBeginIdx, Idx); | ||
| ArgsBeginIdx = Idx + 1; | ||
| ++NestedArgs; | ||
| State = ScanArgs; | ||
| break; | ||
| } | ||
| if (C == EndArgsToken) { | ||
| errs() << "Unexpected '>' 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()); | ||
| PassBeginIdx = Idx + 1; | ||
| } | ||
| break; | ||
| case 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. | ||
| PassArgs = Pipeline.slice(ArgsBeginIdx, Idx); | ||
| AddPass(PassName, PassArgs); | ||
| State = ArgsEnded; | ||
| } else if (NestedArgs < 0) { | ||
| errs() << "Unbalanced '>' 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 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; | ||
| State = ScanName; | ||
| } else { | ||
| errs() << "Expected delimiter or end-of-string after pass " | ||
| "arguments.\n"; | ||
| exit(1); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -101,7 +191,7 @@ class PassManager : public ParentPass { | |
| } | ||
| #endif | ||
| /// Similar to print() but prints one pass per line. Used for testing. | ||
| void printPipeline(raw_ostream &OS) const { | ||
| void printPipeline(raw_ostream &OS) const override { | ||
| OS << this->getName() << "\n"; | ||
| for (const auto &PassPtr : Passes) | ||
| PassPtr->printPipeline(OS); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNT_H | ||
| #define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNT_H | ||
|
|
||
| #include "llvm/SandboxIR/Pass.h" | ||
| #include "llvm/SandboxIR/Region.h" | ||
|
|
||
| namespace llvm::sandboxir { | ||
|
|
||
| /// A Region pass that prints the instruction count for the region to stdout. | ||
| /// Used to test -sbvec-passes while we don't have any actual optimization | ||
| /// passes. | ||
| class PrintInstructionCount final : public RegionPass { | ||
| public: | ||
| PrintInstructionCount() : RegionPass("null") {} | ||
| bool runOnRegion(Region &R) final { | ||
| outs() << "InstructionCount: " << std::distance(R.begin(), R.end()) << "\n"; | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| } // namespace llvm::sandboxir | ||
|
|
||
| #endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_PRINTINSTRUCTIONCOUNTPASS_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| //===- RegionsFromMetadata.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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // A SandboxIR function pass that builds regions from IR metadata and then runs | ||
| // a pipeline of region passes on them. This is useful to test region passes in | ||
| // isolation without relying on the output of the bottom-up vectorizer. | ||
| // | ||
|
|
||
| #ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H | ||
| #define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H | ||
|
|
||
| #include "llvm/ADT/StringRef.h" | ||
| #include "llvm/SandboxIR/Pass.h" | ||
| #include "llvm/SandboxIR/PassManager.h" | ||
|
|
||
| namespace llvm::sandboxir { | ||
|
|
||
| class RegionsFromMetadata final : public FunctionPass { | ||
| // The PM containing the pipeline of region passes. | ||
| RegionPassManager RPM; | ||
|
|
||
| public: | ||
| RegionsFromMetadata(StringRef Pipeline); | ||
| bool runOnFunction(Function &F) final; | ||
| }; | ||
|
|
||
| } // namespace llvm::sandboxir | ||
|
|
||
| #endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_REGIONSFROMMETADATA_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| //===- SandboxVectorizerPassBuilder.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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Utility functions so passes with sub-pipelines can create SandboxVectorizer | ||
| // passes without replicating the same logic in each pass. | ||
| // | ||
| #ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H | ||
| #define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H | ||
|
|
||
| #include "llvm/ADT/StringRef.h" | ||
| #include "llvm/SandboxIR/Pass.h" | ||
|
|
||
| #include <memory> | ||
|
|
||
| namespace llvm::sandboxir { | ||
|
|
||
| class SandboxVectorizerPassBuilder { | ||
| public: | ||
| static std::unique_ptr<FunctionPass> createFunctionPass(StringRef Name, | ||
| StringRef Args); | ||
| static std::unique_ptr<RegionPass> createRegionPass(StringRef Name, | ||
| StringRef Args); | ||
| }; | ||
|
|
||
| } // namespace llvm::sandboxir | ||
|
|
||
| #endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZERPASSBUILDER_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,9 +14,19 @@ | |
| // NOTE: NO INCLUDE GUARD DESIRED! | ||
|
|
||
| #ifndef REGION_PASS | ||
| #define REGION_PASS(NAME, CREATE_PASS) | ||
| #define REGION_PASS(NAME, CLASS_NAME) | ||
| #endif | ||
|
|
||
| REGION_PASS("null", NullPass()) | ||
| REGION_PASS("null", ::llvm::sandboxir::NullPass) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to specify the namespace
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't NEED to, but with the vectorizer having code in both the Now that I've introduced the SandboxVectorizerPassBuilder helper it's less of a concern because that's probably the only place that will need to include the .def file. I think it doesn't hurt, but if you'd prefer not having the qualifier I can remove it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't feel strongly about it, but if it works fine without the qualifiers I would prefer them that way because it's just easier to read. |
||
| REGION_PASS("print-instruction-count", ::llvm::sandboxir::PrintInstructionCount) | ||
|
|
||
| #undef REGION_PASS | ||
|
|
||
| #ifndef FUNCTION_PASS_WITH_PARAMS | ||
| #define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS_NAME) | ||
| #endif | ||
|
|
||
| FUNCTION_PASS_WITH_PARAMS("bottom-up-vec", ::llvm::sandboxir::BottomUpVec) | ||
| FUNCTION_PASS_WITH_PARAMS("regions-from-metadata", ::llvm::sandboxir::RegionsFromMetadata) | ||
|
|
||
| #undef FUNCTION_PASS_WITH_PARAMS | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be guarde by
#ifndef NDEBUG?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the code that implements the
-sbvec-print-pass-pipelineflag. And it wasn't guarded before. Do we want the flag to only work in debug builds? If so, should we guard the flag as well? (also the tests that rely on printing the pass pipeline will become unsupported in release builds) I think those questions are out of scope for this PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it's because of the tests, makes sense.