Add a new IR optimization or fusion pass to MindSpore's compiler pipeline. Use when implementing a graph-level optimization, fusing multiple ops into one, eliminating redundant ops, or adding a backend actor fusion. Triggers on phrases like "add fusion pass", "graph optimization", "IR pass", "optimize graph", "eliminate op", "fuse ops", "add pass", "compiler optimization", "AnfVisitor", "OptimizerCaller".
MindSpore has two types of passes depending on when they run:
Most new optimizations are frontend IR passes.
File: mindspore/ccsrc/frontend/optimizer/irpass/{your_pass}.h
#pragma once
#include "mindspore/ccsrc/frontend/optimizer/anf_visitor.h"
#include "mindspore/ccsrc/frontend/optimizer/optimizer_caller.h"
#include "ir/primitive.h"
#include "ops/core_ops.h"
namespace mindspore::opt::irpass {
// Pattern: TargetOp(CastOp(x), ...) -> TargetOp(x, ...) if types match
class YourFusionPass : public AnfVisitor {
public:
AnfNodePtr operator()(const OptimizerPtr &optimizer, const AnfNodePtr &node) override;
void Visit(const CNodePtr &cnode) override;
void Visit(const AnfNodePtr &node) override;
private:
void Reset() {
x_ = nullptr;
is_match_ = false;
}
AnfNodePtr x_{nullptr};
bool is_match_{false};
};
} // namespace mindspore::opt::irpass
File: mindspore/ccsrc/frontend/optimizer/irpass/{your_pass}.cc
#include "frontend/optimizer/irpass/{your_pass}.h"
#include "frontend/optimizer/irpass/symbol_resolver.h"
#include "ir/func_graph.h"
#include "pipeline/jit/ps/pass.h"
namespace mindspore::opt::irpass {
AnfNodePtr YourFusionPass::operator()(const OptimizerPtr &optimizer, const AnfNodePtr &node) {
Reset();
// Match pattern: look for the target op
AnfVisitor::Match(prim::kPrimYourTargetOp, {IsCNode, IsNode})(node);
if (!is_match_ || x_ == nullptr) return nullptr; // no match, return nullptr
auto fg = node->func_graph();
if (fg == nullptr) return nullptr;
// Build the rewritten node:
auto new_node = fg->NewCNode({NewValueNode(prim::kPrimReplacementOp), x_});
new_node->set_abstract(node->abstract()); // preserve type info
return new_node;
}
void YourFusionPass::Visit(const CNodePtr &cnode) {
// Called for each CNode child when matching pattern
// Check if this is the inner op we want to fuse
if (!IsPrimitiveCNode(cnode, prim::kPrimCast)) return;
auto input = cnode->input(1); // first real input (index 0 = the primitive)
// ... check types, set x_ if pattern matches
x_ = input;
is_match_ = true;
}
void YourFusionPass::Visit(const AnfNodePtr &node) {
// Called for non-CNode children
}
} // namespace mindspore::opt::irpass
File: mindspore/ccsrc/frontend/optimizer/irpass.h — Add member declaration:
class OptimizeIRPassLib {
public:
// ... existing members ...
SubstitutionPtr your_fusion_pass_;
};
File: mindspore/ccsrc/frontend/optimizer/irpass.cc — Add in constructor:
OptimizeIRPassLib::OptimizeIRPassLib() {
// ... existing ...
your_fusion_pass_ = MakeSubstitution(
std::make_shared<YourFusionPass>(),
"your_fusion_pass",
prim::kPrimYourTargetOp // only run on nodes with this primitive
);
}
The third argument to MakeSubstitution is the pattern predicate — it filters which nodes this pass is applied to. Options: