forked from huawei/mindspore2022
239 lines
7.6 KiB
C++
239 lines
7.6 KiB
C++
/**
|
|
* Copyright 2020 Huawei Technologies Co., Ltd
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "frontend/optimizer/py_pass.h"
|
|
#include <unordered_set>
|
|
#include <deque>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "ir/func_graph.h"
|
|
#include "ir/manager.h"
|
|
#include "pipeline/jit/parse/parse_base.h"
|
|
#include "pipeline/jit/resource.h"
|
|
|
|
namespace mindspore {
|
|
namespace opt {
|
|
namespace python_pass {
|
|
namespace internal {
|
|
std::string GetNodeRepr(AnfNodePtr node) {
|
|
if (node != nullptr) {
|
|
if (node->isa<CNode>()) {
|
|
std::string repr = "(";
|
|
auto const &inputs = node->cast<CNodePtr>()->inputs();
|
|
for (auto &input : inputs) {
|
|
repr += " ";
|
|
repr += GetNodeRepr(input);
|
|
repr += " ";
|
|
}
|
|
repr += ")";
|
|
return repr;
|
|
}
|
|
if (node->isa<ValueNode>()) {
|
|
return GetValueNode(node)->ToString();
|
|
}
|
|
return node->ToString();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void ResolveFuncGraph_(const FuncGraphPtr &fg) {
|
|
auto manager = Manage(fg, false);
|
|
auto use_sig = parse::python_adapter::UseSignatureInResolve();
|
|
parse::python_adapter::set_use_signature_in_resolve(false);
|
|
parse::ResolveAll(manager);
|
|
parse::python_adapter::set_use_signature_in_resolve(use_sig);
|
|
}
|
|
|
|
bool Match(const AnfNodePtr &pattern, const AnfNodePtr &node, const NodeEquivPtr &equiv_ptr) {
|
|
if (node == nullptr) {
|
|
return false;
|
|
}
|
|
MS_EXCEPTION_IF_NULL(pattern);
|
|
if (pattern->isa<ValueNode>()) {
|
|
if (!node->isa<ValueNode>()) {
|
|
return false;
|
|
}
|
|
if (GetNodeRepr(pattern) == GetNodeRepr(node)) {
|
|
// add to equiv_ptr
|
|
equiv_ptr->insert(std::make_pair(GetValueNode(pattern)->ToString(), node));
|
|
return true;
|
|
}
|
|
return false;
|
|
} else if (pattern->isa<Parameter>()) {
|
|
MS_LOG(DEBUG) << pattern->ToString() + "\n";
|
|
// add to equiv_ptr
|
|
equiv_ptr->insert(std::make_pair(pattern->ToString(), node));
|
|
return true;
|
|
} else if (pattern->isa<CNode>()) {
|
|
// match every single sub ANode
|
|
if (!node->isa<CNode>()) {
|
|
return false;
|
|
}
|
|
auto pattern_inputs = pattern->cast<CNodePtr>()->inputs();
|
|
auto node_inputs = node->cast<CNodePtr>()->inputs();
|
|
if (pattern_inputs.size() != node_inputs.size()) {
|
|
return false;
|
|
}
|
|
for (auto p_item = pattern_inputs.begin(), node_item = node_inputs.begin(); p_item != pattern_inputs.end();
|
|
p_item++, node_item++) {
|
|
auto res = Match(*p_item, *node_item, equiv_ptr);
|
|
if (!res) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
MS_LOG(EXCEPTION) << "Unexpected condition, (" + pattern->ToString() + " , " + node->ToString() + ")\n";
|
|
}
|
|
|
|
AnfNodePtr BuildTarget(const FuncGraphPtr &func_graph, const AnfNodePtr cur_raw_dst_node_,
|
|
const NodeEquivPtr &equiv_ptr) {
|
|
if (cur_raw_dst_node_->isa<Parameter>()) {
|
|
auto sub_pair = equiv_ptr->find(cur_raw_dst_node_->ToString());
|
|
if (sub_pair != equiv_ptr->end()) {
|
|
return sub_pair->second;
|
|
}
|
|
MS_LOG(EXCEPTION) << "cur_raw_dst_node_ : " + internal::GetNodeRepr(cur_raw_dst_node_) + "\n";
|
|
} else if (cur_raw_dst_node_->isa<ValueNode>()) {
|
|
// check primitive ValueNode
|
|
auto sub_pair = equiv_ptr->find(cur_raw_dst_node_->cast<ValueNodePtr>()->value()->ToString());
|
|
if (sub_pair != equiv_ptr->end()) {
|
|
return sub_pair->second;
|
|
}
|
|
return cur_raw_dst_node_;
|
|
} else if (cur_raw_dst_node_->isa<CNode>()) {
|
|
std::vector<AnfNodePtr> new_inputs;
|
|
auto inputs = cur_raw_dst_node_->cast<CNodePtr>()->inputs();
|
|
for (auto sub_node = inputs.begin(); sub_node != inputs.end(); sub_node++) {
|
|
auto subed = internal::BuildTarget(func_graph, *sub_node, equiv_ptr);
|
|
new_inputs.push_back(subed);
|
|
}
|
|
return func_graph->NewCNode(new_inputs);
|
|
}
|
|
MS_LOG(EXCEPTION) << "Unexpected node type, got : " + internal::GetNodeRepr(cur_raw_dst_node_);
|
|
}
|
|
|
|
bool isTraversable(const AnfNodePtr &node) {
|
|
if (node == nullptr) {
|
|
return false;
|
|
}
|
|
if (node->isa<CNode>() || node->isa<Parameter>()) {
|
|
return true;
|
|
}
|
|
if (IsValueNode<FuncGraph>(node) || IsValueNode<RefKey>(node)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
} // namespace internal
|
|
|
|
void PythonPass::Build(const py::function &src, const py::function &dst) {
|
|
// 1. get FuncGraph from py::function
|
|
auto src_fg_ = parse::ParsePythonCode(src);
|
|
auto dst_fg_ = parse::ParsePythonCode(dst);
|
|
if (src_fg_ == nullptr || dst_fg_ == nullptr) {
|
|
MS_LOG(EXCEPTION) << "Failed to parse python code.\n";
|
|
}
|
|
// 2. Resolve
|
|
internal::ResolveFuncGraph_(src_fg_);
|
|
internal::ResolveFuncGraph_(dst_fg_);
|
|
// 3. from FuncGraphPtr to ValueNode
|
|
src_node_ = src_fg_->output();
|
|
dst_node_ = dst_fg_->output();
|
|
}
|
|
|
|
PythonPass::PythonPass(const std::string &name, const py::function &src, const py::function &dst, bool run_only_once,
|
|
bool multigraph)
|
|
: name_(name), run_only_once_(run_only_once), multigraph_(multigraph) {
|
|
Build(src, dst);
|
|
}
|
|
|
|
AnfNodePtr PythonPass::Run(const FuncGraphPtr &func_graph, const AnfNodePtr &node) {
|
|
auto equiv_ptr = std::make_shared<NodeEquiv>();
|
|
bool is_a_match = internal::Match(src_node_, node, equiv_ptr);
|
|
if (is_a_match) {
|
|
auto new_node = internal::BuildTarget(func_graph, dst_node_, equiv_ptr);
|
|
MS_LOG(DEBUG) << "To be replaced node: " + internal::GetNodeRepr(new_node) + "\n";
|
|
return new_node;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool PythonPass::Run(const FuncGraphPtr &func_graph) {
|
|
MS_EXCEPTION_IF_NULL(func_graph);
|
|
FuncGraphManagerPtr manager = func_graph->manager();
|
|
MS_EXCEPTION_IF_NULL(manager);
|
|
manager->AddFuncGraph(func_graph);
|
|
auto seen = NewSeenGeneration();
|
|
// 1024 is for the initial capacity of deque
|
|
std::deque<AnfNodePtr> todo(1024);
|
|
todo.push_back(func_graph->output());
|
|
bool changes = false;
|
|
|
|
auto &all_nodes = manager->all_nodes();
|
|
while (!todo.empty()) {
|
|
AnfNodePtr node = todo.front();
|
|
todo.pop_front();
|
|
|
|
// check whether this node has been matched.
|
|
if (node == nullptr || node->seen_ == seen || !internal::isTraversable(node) || !all_nodes.contains(node)) {
|
|
continue;
|
|
}
|
|
node->seen_ = seen;
|
|
|
|
// select nodes that this transform can be applied.
|
|
AnfNodePtr new_node = Run(func_graph, node);
|
|
bool change = (new_node != nullptr);
|
|
if (new_node != nullptr && new_node != node) {
|
|
(void)manager->Replace(node, new_node);
|
|
} else if (new_node == nullptr) {
|
|
new_node = node;
|
|
}
|
|
if (run_only_once_) {
|
|
return change;
|
|
}
|
|
|
|
// find success, and add them to todo list
|
|
if (IsValueNode<FuncGraph>(node)) {
|
|
todo.push_back(GetValueNode<FuncGraphPtr>(node)->output());
|
|
}
|
|
|
|
if (node->isa<CNode>()) {
|
|
auto &inputs = node->cast<CNodePtr>()->inputs();
|
|
(void)std::copy(inputs.begin(), inputs.end(), std::back_inserter(todo));
|
|
}
|
|
|
|
auto &node_users = manager->node_users();
|
|
if (change && node_users.find(node) != node_users.end()) {
|
|
for (auto &use : node_users[node]) {
|
|
auto use_node = use.first;
|
|
if (use_node == nullptr) {
|
|
continue;
|
|
}
|
|
todo.push_back(use_node);
|
|
if (use_node->seen_ == seen) {
|
|
use_node->seen_--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return changes;
|
|
}
|
|
} // namespace python_pass
|
|
} // namespace opt
|
|
} // namespace mindspore
|