-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir][inliner] Refactor MLIR inliner pass and utils. #84059
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
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//===- Inliner.h - Inliner pass utilities -----------------------*- 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 header file declares utility structures for the inliner pass. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef MLIR_TRANSFORMS_INLINER_H | ||
#define MLIR_TRANSFORMS_INLINER_H | ||
|
||
#include "mlir/Analysis/CallGraph.h" | ||
#include "mlir/Interfaces/CallInterfaces.h" | ||
#include "mlir/Pass/AnalysisManager.h" | ||
#include "mlir/Pass/PassManager.h" | ||
#include "mlir/Support/LogicalResult.h" | ||
#include "llvm/ADT/StringMap.h" | ||
|
||
namespace mlir { | ||
class OpPassManager; | ||
class Operation; | ||
|
||
class InlinerConfig { | ||
public: | ||
using DefaultPipelineTy = std::function<void(OpPassManager &)>; | ||
using OpPipelinesTy = llvm::StringMap<OpPassManager>; | ||
|
||
InlinerConfig() = default; | ||
InlinerConfig(DefaultPipelineTy defaultPipeline, | ||
unsigned maxInliningIterations) | ||
: defaultPipeline(std::move(defaultPipeline)), | ||
maxInliningIterations(maxInliningIterations) {} | ||
|
||
const DefaultPipelineTy &getDefaultPipeline() const { | ||
return defaultPipeline; | ||
} | ||
const OpPipelinesTy &getOpPipelines() const { return opPipelines; } | ||
unsigned getMaxInliningIterations() const { return maxInliningIterations; } | ||
void setDefaultPipeline(DefaultPipelineTy pipeline) { | ||
defaultPipeline = std::move(pipeline); | ||
} | ||
void setOpPipelines(OpPipelinesTy pipelines) { | ||
opPipelines = std::move(pipelines); | ||
} | ||
void setMaxInliningIterations(unsigned max) { maxInliningIterations = max; } | ||
|
||
private: | ||
/// An optional function that constructs an optimization pipeline for | ||
/// a given operation. This optimization pipeline is applied | ||
/// only to those callable operations that do not have dedicated | ||
/// optimization pipeline in opPipelines (based on the operation name). | ||
DefaultPipelineTy defaultPipeline; | ||
/// A map of operation names to pass pipelines to use when optimizing | ||
/// callable operations of these types. This provides a specialized pipeline | ||
/// instead of the one produced by defaultPipeline. | ||
OpPipelinesTy opPipelines; | ||
/// For SCC-based inlining algorithms, specifies maximum number of iterations | ||
/// when inlining within an SCC. | ||
unsigned maxInliningIterations{0}; | ||
}; | ||
|
||
/// This is an implementation of the inliner | ||
/// that operates bottom up over the Strongly Connected Components(SCCs) | ||
/// of the CallGraph. This enables a more incremental propagation | ||
/// of inlining decisions from the leafs to the roots of the callgraph. | ||
class Inliner { | ||
public: | ||
using RunPipelineHelperTy = std::function<LogicalResult( | ||
Pass &pass, OpPassManager &pipeline, Operation *op)>; | ||
|
||
Inliner(Operation *op, CallGraph &cg, Pass &pass, AnalysisManager am, | ||
RunPipelineHelperTy runPipelineHelper, const InlinerConfig &config) | ||
: op(op), cg(cg), pass(pass), am(am), | ||
runPipelineHelper(std::move(runPipelineHelper)), config(config) {} | ||
Inliner(Inliner &) = delete; | ||
void operator=(const Inliner &) = delete; | ||
|
||
/// Perform inlining on a OpTrait::SymbolTable operation. | ||
LogicalResult doInlining(); | ||
|
||
/// This struct represents a resolved call to a given callgraph node. Given | ||
/// that the call does not actually contain a direct reference to the | ||
/// Region(CallGraphNode) that it is dispatching to, we need to resolve them | ||
/// explicitly. | ||
struct ResolvedCall { | ||
ResolvedCall(CallOpInterface call, CallGraphNode *sourceNode, | ||
CallGraphNode *targetNode) | ||
: call(call), sourceNode(sourceNode), targetNode(targetNode) {} | ||
CallOpInterface call; | ||
CallGraphNode *sourceNode, *targetNode; | ||
}; | ||
|
||
protected: | ||
/// An OpTrait::SymbolTable operation to run the inlining on. | ||
Operation *op; | ||
/// A CallGraph analysis for the given operation. | ||
CallGraph &cg; | ||
/// A reference to the pass using this inliner. | ||
Pass &pass; | ||
/// Analysis manager for the given operation instance. | ||
AnalysisManager am; | ||
/// A callback for running a nested pass pipeline on the operation | ||
/// contained within the main operation. | ||
const RunPipelineHelperTy runPipelineHelper; | ||
/// The inliner configuration parameters. | ||
const InlinerConfig &config; | ||
|
||
private: | ||
/// Forward declaration of the class providing the actual implementation. | ||
class Impl; | ||
|
||
public: | ||
}; | ||
} // namespace mlir | ||
|
||
#endif // MLIR_TRANSFORMS_INLINER_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
//===- InlinerPass.cpp - Pass to inline function calls --------------------===// | ||
// | ||
// 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 implements a basic inlining algorithm that operates bottom up over | ||
// the Strongly Connect Components(SCCs) of the CallGraph. This enables a more | ||
// incremental propagation of inlining decisions from the leafs to the roots of | ||
// the callgraph. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "mlir/Transforms/Passes.h" | ||
|
||
#include "mlir/Analysis/CallGraph.h" | ||
#include "mlir/Pass/PassManager.h" | ||
#include "mlir/Transforms/Inliner.h" | ||
|
||
namespace mlir { | ||
#define GEN_PASS_DEF_INLINER | ||
#include "mlir/Transforms/Passes.h.inc" | ||
} // namespace mlir | ||
|
||
using namespace mlir; | ||
|
||
/// This function implements the inliner optimization pipeline. | ||
static void defaultInlinerOptPipeline(OpPassManager &pm) { | ||
pm.addPass(createCanonicalizerPass()); | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// InlinerPass | ||
//===----------------------------------------------------------------------===// | ||
|
||
namespace { | ||
class InlinerPass : public impl::InlinerBase<InlinerPass> { | ||
public: | ||
InlinerPass(); | ||
InlinerPass(const InlinerPass &) = default; | ||
InlinerPass(std::function<void(OpPassManager &)> defaultPipeline); | ||
InlinerPass(std::function<void(OpPassManager &)> defaultPipeline, | ||
llvm::StringMap<OpPassManager> opPipelines); | ||
void runOnOperation() override; | ||
|
||
/// A callback provided to the inliner driver to execute | ||
/// the specified pass pipeline on the given operation | ||
/// within the context of the current inliner pass, | ||
/// which is passed as the first argument. | ||
/// runPipeline API is protected within the Pass class, | ||
/// so this helper is required to call it from the foreign | ||
/// inliner driver. | ||
static LogicalResult runPipelineHelper(Pass &pass, OpPassManager &pipeline, | ||
Operation *op) { | ||
return mlir::cast<InlinerPass>(pass).runPipeline(pipeline, op); | ||
} | ||
|
||
private: | ||
/// Attempt to initialize the options of this pass from the given string. | ||
/// Derived classes may override this method to hook into the point at which | ||
/// options are initialized, but should generally always invoke this base | ||
/// class variant. | ||
LogicalResult initializeOptions(StringRef options) override; | ||
|
||
/// Inliner configuration parameters created from the pass options. | ||
InlinerConfig config; | ||
}; | ||
} // namespace | ||
|
||
InlinerPass::InlinerPass() : InlinerPass(defaultInlinerOptPipeline) {} | ||
|
||
InlinerPass::InlinerPass( | ||
std::function<void(OpPassManager &)> defaultPipelineArg) | ||
: InlinerPass(std::move(defaultPipelineArg), | ||
llvm::StringMap<OpPassManager>{}) {} | ||
|
||
InlinerPass::InlinerPass(std::function<void(OpPassManager &)> defaultPipeline, | ||
llvm::StringMap<OpPassManager> opPipelines) | ||
: config(std::move(defaultPipeline), maxInliningIterations) { | ||
if (opPipelines.empty()) | ||
return; | ||
|
||
// Update the option for the op specific optimization pipelines. | ||
for (auto &it : opPipelines) | ||
opPipelineList.addValue(it.second); | ||
config.setOpPipelines(std::move(opPipelines)); | ||
} | ||
|
||
void InlinerPass::runOnOperation() { | ||
CallGraph &cg = getAnalysis<CallGraph>(); | ||
|
||
// The inliner should only be run on operations that define a symbol table, | ||
// as the callgraph will need to resolve references. | ||
Operation *op = getOperation(); | ||
if (!op->hasTrait<OpTrait::SymbolTable>()) { | ||
op->emitOpError() << " was scheduled to run under the inliner, but does " | ||
"not define a symbol table"; | ||
return signalPassFailure(); | ||
} | ||
|
||
// Get an instance of the inliner. | ||
Inliner inliner(op, cg, *this, getAnalysisManager(), runPipelineHelper, | ||
config); | ||
|
||
// Run the inlining. | ||
if (failed(inliner.doInlining())) | ||
signalPassFailure(); | ||
return; | ||
} | ||
|
||
LogicalResult InlinerPass::initializeOptions(StringRef options) { | ||
if (failed(Pass::initializeOptions(options))) | ||
return failure(); | ||
|
||
// Initialize the pipeline builder for operations without the dedicated | ||
// optimization pipeline in opPipelineList to use the option string. | ||
// TODO: Use a generic pass manager for the pre-inline pipeline, and remove | ||
// this. | ||
if (!defaultPipelineStr.empty()) { | ||
std::string defaultPipelineCopy = defaultPipelineStr; | ||
config.setDefaultPipeline([=](OpPassManager &pm) { | ||
(void)parsePassPipeline(defaultPipelineCopy, pm); | ||
}); | ||
} else if (defaultPipelineStr.getNumOccurrences()) { | ||
config.setDefaultPipeline(nullptr); | ||
} | ||
|
||
// Initialize the op specific pass pipelines. | ||
llvm::StringMap<OpPassManager> pipelines; | ||
for (OpPassManager pipeline : opPipelineList) | ||
if (!pipeline.empty()) | ||
pipelines.try_emplace(pipeline.getOpAnchorName(), pipeline); | ||
config.setOpPipelines(std::move(pipelines)); | ||
|
||
config.setMaxInliningIterations(maxInliningIterations); | ||
|
||
return success(); | ||
} | ||
|
||
std::unique_ptr<Pass> mlir::createInlinerPass() { | ||
return std::make_unique<InlinerPass>(); | ||
} | ||
std::unique_ptr<Pass> | ||
mlir::createInlinerPass(llvm::StringMap<OpPassManager> opPipelines) { | ||
return std::make_unique<InlinerPass>(defaultInlinerOptPipeline, | ||
std::move(opPipelines)); | ||
} | ||
std::unique_ptr<Pass> mlir::createInlinerPass( | ||
llvm::StringMap<OpPassManager> opPipelines, | ||
std::function<void(OpPassManager &)> defaultPipelineBuilder) { | ||
return std::make_unique<InlinerPass>(std::move(defaultPipelineBuilder), | ||
std::move(opPipelines)); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I see why the original name was "defaultPipeline", it's because the user can also specify another pipeline per-operation.
I would likely keep the original name then and not change the option here.
These options should be documented carefully in the InlinerConfig as well.
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.
Okay, I will rename it back.
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.
This should be done now.