Skip to content

[SandboxVectorizer] Use sbvec-passes flag to create a pipeline of Region passes after BottomUpVec. #111223

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

Merged
merged 8 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 46 additions & 36 deletions llvm/include/llvm/SandboxIR/PassManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#ifndef LLVM_SANDBOXIR_PASSMANAGER_H
#define LLVM_SANDBOXIR_PASSMANAGER_H

#include <memory>

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/SandboxIR/Pass.h"
Expand All @@ -32,25 +34,65 @@ template <typename ParentPass, typename ContainedPass>
class PassManager : public ParentPass {
protected:
/// The list of passes that this pass manager will run.
SmallVector<ContainedPass *> Passes;
SmallVector<std::unique_ptr<ContainedPass>> Passes;

PassManager(StringRef Name) : ParentPass(Name) {}
PassManager(const PassManager &) = delete;
PassManager(PassManager &&) = default;
virtual ~PassManager() = default;
PassManager &operator=(const PassManager &) = delete;

public:
/// Adds \p Pass to the pass pipeline.
void addPass(ContainedPass *Pass) {
void addPass(std::unique_ptr<ContainedPass> Pass) {
// TODO: Check that Pass's class type works with this PassManager type.
Passes.push_back(Pass);
Passes.push_back(std::move(Pass));
}

using CreatePassFunc =
std::function<std::unique_ptr<ContainedPass>(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.
void setPassPipeline(StringRef Pipeline, CreatePassFunc CreatePass) {
static constexpr const char EndToken = '\0';
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;

// Get the pass that corresponds to PassName and add it to the pass
// manager.
auto Pass = CreatePass(PassName);
if (Pass == nullptr) {
errs() << "Pass '" << PassName << "' not registered!\n";
exit(1);
}
addPass(std::move(Pass));
}
}

#ifndef NDEBUG
void print(raw_ostream &OS) const override {
OS << this->getName();
OS << "(";
// TODO: This should call Pass->print(OS) because Pass may be a PM.
interleave(Passes, OS, [&OS](auto *Pass) { OS << Pass->getName(); }, ",");
interleave(Passes, OS, [&OS](auto &Pass) { OS << Pass->getName(); }, ",");
OS << ")";
}
LLVM_DUMP_METHOD void dump() const override {
Expand Down Expand Up @@ -79,38 +121,6 @@ class RegionPassManager final : public PassManager<RegionPass, RegionPass> {
bool runOnRegion(Region &R) final;
};

/// Owns the passes and provides an API to get a pass by its name.
class PassRegistry {
SmallVector<std::unique_ptr<Pass>, 8> Passes;
DenseMap<StringRef, Pass *> NameToPassMap;

public:
static constexpr const char PassDelimToken = ',';
PassRegistry() = default;
/// Registers \p PassPtr and takes ownership.
Pass &registerPass(std::unique_ptr<Pass> &&PassPtr) {
auto &PassRef = *PassPtr.get();
NameToPassMap[PassRef.getName()] = &PassRef;
Passes.push_back(std::move(PassPtr));
return PassRef;
}
/// \Returns the pass with name \p Name, or null if not registered.
Pass *getPassByName(StringRef Name) const {
auto It = NameToPassMap.find(Name);
return It != NameToPassMap.end() ? It->second : nullptr;
}
/// Creates a pass pipeline and returns the first pass manager.
FunctionPassManager &parseAndCreatePassPipeline(StringRef Pipeline);

#ifndef NDEBUG
void print(raw_ostream &OS) const {
for (const auto &PassPtr : Passes)
OS << PassPtr->getName() << "\n";
}
LLVM_DUMP_METHOD void dump() const;
#endif
};

} // namespace llvm::sandboxir

#endif // LLVM_SANDBOXIR_PASSMANAGER_H
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,24 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/SandboxIR/Constant.h"
#include "llvm/SandboxIR/Pass.h"
#include "llvm/SandboxIR/PassManager.h"
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h"

namespace llvm::sandboxir {

class RegionPassManager;

class BottomUpVec final : public FunctionPass {
bool Change = false;
LegalityAnalysis Legality;
void vectorizeRec(ArrayRef<Value *> Bndl);
void tryVectorize(ArrayRef<Value *> Seeds);

// The PM containing the pipeline of region passes.
RegionPassManager RPM;

public:
BottomUpVec() : FunctionPass("bottom-up-vec") {}
BottomUpVec();
bool runOnFunction(Function &F) final;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_NULLPASS_H
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_NULLPASS_H

#include "llvm/SandboxIR/Pass.h"

namespace llvm::sandboxir {

class Region;

/// A Region pass that does nothing, for use as a placeholder in tests.
class NullPass final : public RegionPass {
public:
NullPass() : RegionPass("null") {}
bool runOnRegion(Region &R) final { return false; }
};

} // namespace llvm::sandboxir

#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_PASSES_NULLPASS_H
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZER_H
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SANDBOXVECTORIZER_H

#include <memory>

#include "llvm/IR/PassManager.h"
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BottomUpVec.h"

namespace llvm {

Expand All @@ -17,10 +20,13 @@ class TargetTransformInfo;
class SandboxVectorizerPass : public PassInfoMixin<SandboxVectorizerPass> {
TargetTransformInfo *TTI = nullptr;

public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
// The main vectorizer pass.
sandboxir::BottomUpVec BottomUpVecPass;

bool runImpl(Function &F);

public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};

} // namespace llvm
Expand Down
44 changes: 4 additions & 40 deletions llvm/lib/SandboxIR/PassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

#include "llvm/SandboxIR/PassManager.h"

using namespace llvm::sandboxir;
namespace llvm::sandboxir {

bool FunctionPassManager::runOnFunction(Function &F) {
bool Change = false;
for (FunctionPass *Pass : Passes) {
for (auto &Pass : Passes) {
Change |= Pass->runOnFunction(F);
// TODO: run the verifier.
}
Expand All @@ -22,48 +22,12 @@ bool FunctionPassManager::runOnFunction(Function &F) {

bool RegionPassManager::runOnRegion(Region &R) {
bool Change = false;
for (RegionPass *Pass : Passes) {
for (auto &Pass : Passes) {
Change |= Pass->runOnRegion(R);
// TODO: run the verifier.
}
// TODO: Check ChangeAll against hashes before/after.
return Change;
}

FunctionPassManager &
PassRegistry::parseAndCreatePassPipeline(StringRef Pipeline) {
static constexpr const char EndToken = '\0';
// Add EndToken to the end to ease parsing.
std::string PipelineStr = std::string(Pipeline) + EndToken;
int FlagBeginIdx = 0;
// Start with a FunctionPassManager.
auto &InitialPM = static_cast<FunctionPassManager &>(
registerPass(std::make_unique<FunctionPassManager>("init-fpm")));

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;

// Get the pass that corresponds to PassName and add it to the pass manager.
auto *Pass = getPassByName(PassName);
if (Pass == nullptr) {
errs() << "Pass '" << PassName << "' not registered!\n";
exit(1);
}
// TODO: This is safe for now, but would require proper upcasting once we
// add more Pass sub-classes.
InitialPM.addPass(static_cast<FunctionPass *>(Pass));
}
return InitialPM;
}
#ifndef NDEBUG
void PassRegistry::dump() const {
print(dbgs());
dbgs() << "\n";
}
#endif // NDEBUG
} // namespace llvm::sandboxir
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,41 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/SandboxIR/Function.h"
#include "llvm/SandboxIR/Instruction.h"

using namespace llvm::sandboxir;
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Passes/NullPass.h"

namespace llvm::sandboxir {

static cl::opt<bool>
PrintPassPipeline("sbvec-print-pass-pipeline", cl::init(false), cl::Hidden,
cl::desc("Prints the pass pipeline and returns."));

/// A magic string for the default pass pipeline.
static const char *DefaultPipelineMagicStr = "*";

static cl::opt<std::string> UserDefinedPassPipeline(
"sbvec-passes", cl::init(DefaultPipelineMagicStr), cl::Hidden,
cl::desc("Comma-separated list of vectorizer passes. If not set "
"we run the predefined pipeline."));

static std::unique_ptr<RegionPass> createRegionPass(StringRef Name) {
#define REGION_PASS(NAME, CREATE_PASS) \
if (Name == NAME) \
return std::make_unique<decltype(CREATE_PASS)>(CREATE_PASS);
#include "PassRegistry.def"
return nullptr;
}

BottomUpVec::BottomUpVec() : FunctionPass("bottom-up-vec"), RPM("rpm") {
// Create a pipeline to be run on each Region created by BottomUpVec.
if (UserDefinedPassPipeline == DefaultPipelineMagicStr) {
// TODO: Add default passes to RPM.
} else {
// Create the user-defined pipeline.
RPM.setPassPipeline(UserDefinedPassPipeline, createRegionPass);
}
}

// TODO: This is a temporary function that returns some seeds.
// Replace this with SeedCollector's function when it lands.
static llvm::SmallVector<Value *, 4> collectSeeds(BasicBlock &BB) {
Expand All @@ -34,8 +65,6 @@ static SmallVector<Value *, 4> getOperand(ArrayRef<Value *> Bndl,
return Operands;
}

} // namespace llvm::sandboxir

void BottomUpVec::vectorizeRec(ArrayRef<Value *> Bndl) {
auto LegalityRes = Legality.canVectorize(Bndl);
switch (LegalityRes.getSubclassID()) {
Expand All @@ -53,14 +82,23 @@ void BottomUpVec::vectorizeRec(ArrayRef<Value *> Bndl) {
void BottomUpVec::tryVectorize(ArrayRef<Value *> Bndl) { vectorizeRec(Bndl); }

bool BottomUpVec::runOnFunction(Function &F) {
if (PrintPassPipeline) {
RPM.printPipeline(outs());
return false;
}

Change = false;
// TODO: Start from innermost BBs first
for (auto &BB : F) {
// TODO: Replace with proper SeedCollector function.
auto Seeds = collectSeeds(BB);
// TODO: Slice Seeds into smaller chunks.
// TODO: If vectorization succeeds, run the RegionPassManager on the
// resulting region.
if (Seeds.size() >= 2)
tryVectorize(Seeds);
}
return Change;
}

} // namespace llvm::sandboxir
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===- PassRegistry.def - Registry of passes --------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file is used as the registry of sub-passes that are part of the
// SandboxVectorizer pass.
//
//===----------------------------------------------------------------------===//

// NOTE: NO INCLUDE GUARD DESIRED!

#ifndef REGION_PASS
#define REGION_PASS(NAME, CREATE_PASS)
#endif

REGION_PASS("null", NullPass())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need some cleanup code at the end like:

#ifdef REGION_PASS
#undef REGION_PASS
#endif

to avoid getting a warning of redefining REGION_PASS if we ever decide to include the .def file more than once

Copy link
Collaborator Author

@slackito slackito Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added #undef REGION_PASS. By the time we get to the end the macro is always defined, either to something useful by the user or to an empty macro above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why use NullPass() and not just the class name NullPass in REGION_PASS("null", NullPass()). Isn't the class name easier to work with?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just followed the pattern for the existing FUNCTION_PASS in llvm/lib/Passes/PassRegistry.def. I have no preference one way or the other.


#undef REGION_PASS
Loading
Loading