forked from huawei/mindspore2022
454 lines
14 KiB
C++
454 lines
14 KiB
C++
/**
|
|
* This is the C++ adaptation and derivative work of Myia (https://github.com/mila-iqia/myia/).
|
|
*
|
|
* Copyright 2019-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 "vm/vmimpl.h"
|
|
|
|
#include <algorithm>
|
|
#include <exception>
|
|
#include <utility>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <set>
|
|
|
|
#include "ir/tensor.h"
|
|
#include "frontend/operator/ops.h"
|
|
#include "ir/manager.h"
|
|
#include "utils/func_graph_cloner.h"
|
|
#include "utils/primitive_py.h"
|
|
#include "utils/convert_utils.h"
|
|
#include "utils/primitive_utils.h"
|
|
#include "debug/draw.h"
|
|
|
|
namespace mindspore {
|
|
namespace compile {
|
|
|
|
// Indicate a call to a new frame.
|
|
struct CallWrap : public Base {
|
|
explicit CallWrap(const VMFramePtr &vm_frame) : frame(vm_frame) {}
|
|
VMFramePtr frame{nullptr};
|
|
};
|
|
using CallWrapPtr = std::shared_ptr<CallWrap>;
|
|
|
|
// Indicates a return with its value.
|
|
struct ReturnWrap : public Base {
|
|
explicit ReturnWrap(const BaseRef &r_value) : value(r_value) {}
|
|
BaseRef value{BaseRef()};
|
|
};
|
|
using ReturnWrapPtr = std::shared_ptr<ReturnWrap>;
|
|
|
|
VMFrame::VMFrame(const AnfNodePtrList &nodes, const AnfNodePtrToBaseRefMap &values,
|
|
const AnfNodePtrToBaseRefMap &closure)
|
|
: values_(values), todo_(nodes), closure_(closure) {
|
|
std::reverse(std::begin(todo_), std::end(todo_));
|
|
}
|
|
|
|
const BaseRef VMFrame::operator[](const AnfNodePtr &node) {
|
|
MS_EXCEPTION_IF_NULL(node);
|
|
auto ret = values_.find(node);
|
|
if (ret != values_.end()) {
|
|
return ret->second;
|
|
}
|
|
|
|
ret = closure_.find(node);
|
|
if (ret != closure_.end()) {
|
|
return ret->second;
|
|
}
|
|
|
|
if (node->isa<ValueNode>()) {
|
|
return GetValueNode(node);
|
|
}
|
|
|
|
MS_LOG(EXCEPTION) << "ValueError " << node->type_name();
|
|
}
|
|
|
|
Closure::Closure(const FuncGraphPtr &graph, const AnfNodePtrToBaseRefMap &values)
|
|
: func_graph_(graph), values_(values) {}
|
|
|
|
BaseRef Closure::operator()(const VectorRef &args) {
|
|
MS_LOG(DEBUG) << "start closure";
|
|
return vm_->Evaluate(func_graph_, args, values_);
|
|
}
|
|
|
|
Partial::Partial(const BaseRef &fn, const VectorRef &args, const VMPtr &vm) : fn_(fn), args_(args), vm_(vm) {}
|
|
|
|
BaseRef Partial::operator()(const VectorRef &nodes) {
|
|
VectorRef arglist;
|
|
(void)arglist.insert(arglist.end(), args_.begin(), args_.end());
|
|
(void)arglist.insert(arglist.end(), nodes.begin(), nodes.end());
|
|
return vm_->Call(fn_, arglist);
|
|
}
|
|
|
|
SetRef VM::ComputeFvs(const FuncGraphPtr &graph) {
|
|
MS_EXCEPTION_IF_NULL(graph);
|
|
SetRef rval;
|
|
for (auto &fkv : graph->free_variables_total()) {
|
|
if (utils::isa<FuncGraphPtr>(fkv.first)) {
|
|
// Add all value_nodes of g that refer to a fv graph
|
|
auto g = utils::cast<FuncGraphPtr>(fkv.first);
|
|
for (auto &ctkv : g->value_nodes()) {
|
|
auto ct = ctkv.first;
|
|
if (GetValueNode(ct) == g) {
|
|
(void)rval.insert(ct);
|
|
}
|
|
}
|
|
} else {
|
|
// Add a normal fv
|
|
(void)rval.insert(fkv.first);
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
void VM::AcquireGraph(const FuncGraphPtr &graph) {
|
|
// Already acquired
|
|
if (vars_.find(graph) != vars_.end()) {
|
|
return;
|
|
}
|
|
// Add g to manager
|
|
manager_->AddFuncGraph(graph);
|
|
// Compute fvs for all acquired graph
|
|
auto graphs = graph->manager()->func_graphs();
|
|
for (auto g = graphs.begin(); g != graphs.end(); ++g) {
|
|
vars_[*g] = ComputeFvs(*g);
|
|
}
|
|
}
|
|
|
|
VectorRef VM::ExportSequence(const VectorRef &seq) {
|
|
std::vector<BaseRef> ret;
|
|
(void)std::transform(std::begin(seq), std::end(seq), std::back_inserter(ret),
|
|
[&, this](const BaseRef &x) -> BaseRef { return Export(x); });
|
|
return VectorRef(ret);
|
|
}
|
|
|
|
ClosurePtr VM::ExportClosure(const ClosurePtr &clos) {
|
|
MS_EXCEPTION_IF_NULL(clos);
|
|
clos->set_vm(shared_from_this());
|
|
return clos;
|
|
}
|
|
|
|
// transform graph to executable closure
|
|
ClosurePtr VM::ExportGraph(const FuncGraphPtr &g) {
|
|
auto c = std::make_shared<Closure>(g, AnfNodePtrToBaseRefMap());
|
|
MS_EXCEPTION_IF_NULL(c);
|
|
c->set_vm(shared_from_this());
|
|
return c;
|
|
}
|
|
|
|
BaseRef VM::ExportObj(const BaseRef &obj) const { return obj; }
|
|
|
|
BaseRef VM::Export(const BaseRef &value) {
|
|
if (utils::isa<ValuePtr>(value) && utils::cast<ValuePtr>(value)->isa<FuncGraph>()) {
|
|
return ExportGraph(utils::cast<ValuePtr>(value)->cast<FuncGraphPtr>());
|
|
}
|
|
|
|
if (utils::isa<ValuePtr>(value) && utils::cast<ValuePtr>(value)->isa<Primitive>()) {
|
|
return ExportPrimitive(utils::cast<ValuePtr>(value)->cast<PrimitivePtr>());
|
|
}
|
|
|
|
if (utils::isa<FuncGraphPtr>(value)) {
|
|
return ExportGraph(utils::cast<FuncGraphPtr>(value));
|
|
}
|
|
|
|
if (utils::isa<ClosurePtr>(value)) {
|
|
return ExportClosure(utils::cast<ClosurePtr>(value));
|
|
}
|
|
|
|
if (utils::isa<PrimitivePtr>(value)) {
|
|
return ExportPrimitive(utils::cast<PrimitivePtr>(value));
|
|
}
|
|
|
|
if (utils::isa<VectorRef>(value)) {
|
|
return ExportSequence(utils::cast<VectorRef>(value));
|
|
}
|
|
|
|
return ExportObj(value);
|
|
}
|
|
|
|
// Run a graph.
|
|
// This will evaluate the passed-in graph and return the resulting value.
|
|
BaseRef VM::Evaluate(const FuncGraphPtr &graph, const VectorRef &args, const AnfNodePtrToBaseRefMap &closure) {
|
|
AcquireGraph(graph);
|
|
MS_LOG(DEBUG) << "evalue arg size: " << args.size();
|
|
if (args.size() != graph->parameters().size()) {
|
|
MS_LOG(EXCEPTION) << "Call with wrong number of arguments, expect " << graph->parameters().size() << ", but got "
|
|
<< args.size();
|
|
}
|
|
|
|
// toposort graph nodes, the order will be reversed by frame so that the dependent be computed first
|
|
auto nodes = TopoSort(graph->get_return(), SuccVm(graph));
|
|
// mapping parameters to args
|
|
AnfNodePtrToBaseRefMap values;
|
|
for (size_t i = 0; i < args.size(); i++) {
|
|
values[graph->parameters()[i]] = args[i];
|
|
}
|
|
// create top frame with params initialized
|
|
VMFramePtrList frames{std::make_shared<VMFrame>(nodes, values, closure)};
|
|
// execute frames starting from top frame
|
|
while (!frames.empty()) {
|
|
auto frame = frames[frames.size() - 1];
|
|
auto todo = frame->todo();
|
|
while (!todo.empty()) {
|
|
auto except = HandleNode(todo[todo.size() - 1], frame);
|
|
if (utils::isa<CallWrapPtr>(except)) {
|
|
if (todo.size() == 2) {
|
|
// The last element is always a return, replace the ret with call frame
|
|
frames[frames.size() - 1] = utils::cast<CallWrapPtr>(except)->frame;
|
|
} else {
|
|
frames.push_back(utils::cast<CallWrapPtr>(except)->frame);
|
|
}
|
|
break;
|
|
}
|
|
if (utils::isa<ReturnWrapPtr>(except)) {
|
|
(void)frames.erase(frames.begin() + (static_cast<ssize_t>(frames.size()) - 1));
|
|
if (frames.size() > 0) {
|
|
auto top = frames[frames.size() - 1];
|
|
auto td = top->todo();
|
|
// set value for top frame's last evaluated node
|
|
if (td.empty()) {
|
|
MS_LOG(EXCEPTION) << "The td is empty";
|
|
}
|
|
top->values()[td[td.size() - 1]] = utils::cast<ReturnWrapPtr>(except)->value;
|
|
(void)td.erase(td.begin() + (static_cast<ssize_t>(td.size()) - 1));
|
|
} else {
|
|
return Export(utils::cast<ReturnWrapPtr>(except)->value);
|
|
}
|
|
break;
|
|
}
|
|
(void)todo.erase(todo.begin() + (static_cast<ssize_t>(todo.size()) - 1));
|
|
}
|
|
}
|
|
MS_LOG(EXCEPTION) << "VM Evaluate error";
|
|
}
|
|
|
|
SuccFunc VM::SuccVm(const FuncGraphPtr &graph) {
|
|
auto fn = [&, this](const AnfNodePtr &node) -> AnfNodePtrList {
|
|
MS_EXCEPTION_IF_NULL(node);
|
|
AnfNodePtrList ret;
|
|
|
|
// Follow node.incoming
|
|
if (node->isa<CNode>()) {
|
|
auto &inputs = node->cast<CNodePtr>()->inputs();
|
|
for (auto &i : inputs) {
|
|
if (i->func_graph() == node->func_graph() ||
|
|
(IsValueNode<FuncGraph>(i) && GetValueNode<FuncGraphPtr>(i)->parent() == graph)) {
|
|
ret.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// for subgraph input, add their fvs as succ nodes
|
|
if (IsValueNode<FuncGraph>(node) && GetValueNode<FuncGraphPtr>(node)->parent() == graph) {
|
|
auto fvs = utils::cast<SetRef>(vars_[GetValueNode<FuncGraphPtr>(node)]);
|
|
(void)std::transform(fvs.begin(), fvs.end(), std::back_inserter(ret),
|
|
[](const BaseRef &value) -> AnfNodePtr { return utils::cast<AnfNodePtr>(value); });
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
return fn;
|
|
}
|
|
|
|
BaseRef VM::Call(const BaseRef &fn, const VectorRef &args) {
|
|
if (utils::isa<PrimitivePtr>(fn)) {
|
|
return RunOperation(utils::cast<PrimitivePtr>(fn), args);
|
|
}
|
|
|
|
if (utils::isa<FuncGraphPtr>(fn)) {
|
|
return Evaluate(utils::cast<FuncGraphPtr>(fn), args);
|
|
}
|
|
|
|
if (utils::isa<ClosurePtr>(fn)) {
|
|
auto clos = utils::cast<ClosurePtr>(fn);
|
|
return Evaluate(clos->func_graph(), args, clos->values());
|
|
}
|
|
|
|
MS_LOG(EXCEPTION) << "Can't call fn";
|
|
}
|
|
|
|
// make call frame for graph
|
|
BaseRef VM::_Call(const BaseRef &graph, const VectorRef &args) {
|
|
AnfNodePtrToBaseRefMap clos;
|
|
auto func_graph = graph;
|
|
if (utils::isa<ClosurePtr>(func_graph)) {
|
|
clos = utils::cast<ClosurePtr>(func_graph)->values();
|
|
func_graph = utils::cast<ClosurePtr>(func_graph)->func_graph();
|
|
}
|
|
if (utils::isa<ValuePtr>(func_graph)) {
|
|
func_graph = utils::cast<ValuePtr>(func_graph)->cast<FuncGraphPtr>();
|
|
}
|
|
|
|
if (!utils::isa<FuncGraphPtr>(func_graph)) {
|
|
MS_LOG(EXCEPTION) << "Graph type error";
|
|
}
|
|
|
|
auto graphPtr = utils::cast<FuncGraphPtr>(func_graph);
|
|
|
|
if (vars_.find(graphPtr) == vars_.end()) {
|
|
AcquireGraph(graphPtr);
|
|
}
|
|
|
|
if (args.size() != graphPtr->parameters().size()) {
|
|
MS_LOG(EXCEPTION) << "Call with wrong number of arguments, expect " << graphPtr->parameters().size() << ", but got "
|
|
<< args.size();
|
|
}
|
|
|
|
auto nodes = TopoSort(graphPtr->get_return(), SuccVm(graphPtr));
|
|
AnfNodePtrToBaseRefMap values;
|
|
for (size_t i = 0; i < args.size(); i++) {
|
|
values[graphPtr->parameters()[i]] = args[i];
|
|
}
|
|
|
|
return std::make_shared<CallWrap>(std::make_shared<VMFrame>(nodes, values, clos));
|
|
}
|
|
|
|
// make closure out of graph with fv values from frame
|
|
ClosurePtr VM::MakeClosure(const FuncGraphPtr &graph, const VMFramePtr &frame) {
|
|
MS_EXCEPTION_IF_NULL(frame);
|
|
AnfNodePtrToBaseRefMap clos;
|
|
|
|
for (auto &v : utils::cast<SetRef>(vars_[graph])) {
|
|
auto anf = utils::cast<AnfNodePtr>(v);
|
|
clos[anf] = (*frame)[anf];
|
|
}
|
|
|
|
return std::make_shared<Closure>(graph, clos);
|
|
}
|
|
|
|
BaseRef VM::DispatchCall(const AnfNodePtr &node, const VMFramePtr &frame, const BaseRef &fn, const VectorRef &args) {
|
|
if (utils::isa<ValuePtr>(fn) && utils::cast<ValuePtr>(fn)->isa<Primitive>()) {
|
|
auto fnval = utils::cast<ValuePtr>(fn)->cast<PrimitivePtr>();
|
|
MS_LOG(DEBUG) << "DispatchCall prim:" << fnval->name() << ", node:" << node->DebugString(true);
|
|
if (args.empty()) {
|
|
MS_LOG(EXCEPTION) << "args is empty";
|
|
}
|
|
if (fnval == prim::kPrimReturn) {
|
|
MS_LOG(DEBUG) << "return args:" << args.size();
|
|
return std::make_shared<ReturnWrap>(args[0]);
|
|
}
|
|
|
|
if (fnval == prim::kPrimMakeTuple) {
|
|
frame->values()[node] = args;
|
|
return BaseRef();
|
|
}
|
|
|
|
if (fnval == prim::kPrimPartial) {
|
|
VectorRef partial_args(args.begin() + 1, args.end());
|
|
frame->values()[node] = (std::make_shared<Partial>(args[0], partial_args, shared_from_this()));
|
|
return BaseRef();
|
|
}
|
|
|
|
// call prim implementation
|
|
frame->values()[node] = RunOperation(fnval, args);
|
|
return BaseRef();
|
|
}
|
|
|
|
// partial args logic
|
|
if (utils::isa<PartialPtr>(fn)) {
|
|
auto fnPtr = utils::cast<PartialPtr>(fn);
|
|
|
|
VectorRef arglist;
|
|
(void)arglist.insert(arglist.end(), fnPtr->args().begin(), fnPtr->args().end());
|
|
(void)arglist.insert(arglist.end(), args.begin(), args.end());
|
|
|
|
auto ret = DispatchCall(node, frame, fnPtr->fn(), arglist);
|
|
if (utils::isa<CallWrapPtr>(ret) || utils::isa<ReturnWrapPtr>(ret)) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// create frame for graph and closure
|
|
if ((utils::isa<ValuePtr>(fn) && utils::cast<ValuePtr>(fn)->isa<FuncGraph>()) || utils::isa<ClosurePtr>(fn)) {
|
|
auto ret = _Call(fn, args);
|
|
if (utils::isa<CallWrapPtr>(ret) || utils::isa<ReturnWrapPtr>(ret)) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
MS_LOG(EXCEPTION) << "Invalid fn to call";
|
|
}
|
|
|
|
BaseRef VM::HandleNode(const AnfNodePtr &node, const VMFramePtr &frame) {
|
|
MS_EXCEPTION_IF_NULL(node);
|
|
if (node->isa<Parameter>()) {
|
|
// pass
|
|
return BaseRef();
|
|
}
|
|
|
|
if (node->isa<ValueNode>()) {
|
|
// We only visit valuenode graphs
|
|
if (!IsValueNode<FuncGraph>(node)) {
|
|
MS_LOG(EXCEPTION) << "We only visit valuenode graphs ";
|
|
}
|
|
auto g = GetValueNode<FuncGraphPtr>(node);
|
|
|
|
// if g is a graph with fvs, we need to make a closure for it
|
|
auto iterG = vars_.find(g);
|
|
if (iterG != vars_.end() && utils::cast<SetRef>(iterG->second).size() != 0) {
|
|
frame->values()[node] = MakeClosure(g, frame);
|
|
}
|
|
|
|
return BaseRef();
|
|
}
|
|
|
|
if (node->isa<CNode>()) {
|
|
std::vector<BaseRef> fnArgs;
|
|
auto &inputs = node->cast<CNodePtr>()->inputs();
|
|
// set args' values in frame
|
|
(void)std::transform(std::begin(inputs), std::end(inputs), std::back_inserter(fnArgs),
|
|
[&](const AnfNodePtr &inp) -> BaseRef { return (*frame)[inp]; });
|
|
if (fnArgs.empty()) {
|
|
MS_LOG(EXCEPTION) << "function arguments is empty";
|
|
} else {
|
|
auto args = VectorRef(fnArgs.begin() + 1, fnArgs.end());
|
|
auto except = DispatchCall(node, frame, fnArgs[0], args);
|
|
return except;
|
|
}
|
|
}
|
|
|
|
MS_LOG(EXCEPTION) << "Unknown node type";
|
|
}
|
|
|
|
VectorRef VM::RunGraph(const FuncGraphPtr &g, const VectorRef &args) {
|
|
this->manager_ = Manage(g);
|
|
|
|
auto fn = utils::cast<ClosurePtr>(Export(g));
|
|
auto result = (*fn)(args);
|
|
|
|
if (utils::isa<VectorRef>(result)) {
|
|
return utils::cast<VectorRef>(result);
|
|
} else {
|
|
VectorRef ret({result});
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
BaseRef RunOperation(const PrimitivePtr &prim, const VectorRef &args) {
|
|
MS_LOG(DEBUG) << "operation start " << prim->name();
|
|
MS_EXCEPTION_IF_NULL(prim);
|
|
auto result = prim->RunComputeFunction(args);
|
|
if (result.is_null()) {
|
|
return RunComputeFunction(prim, args);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace compile
|
|
} // namespace mindspore
|