/** * Copyright 2019-2021 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 "debug/anf_ir_utils.h" #include #include #include #include #include #include #include "ir/graph_utils.h" #include "utils/symbolic.h" #include "ir/meta_func_graph.h" #include "ir/param_info.h" #include "pybind_api/ir/tensor_py.h" #include "pipeline/jit/parse/python_adapter.h" #include "pipeline/jit/parse/resolve.h" #include "frontend/operator/composite/composite.h" #include "frontend/operator/composite/map.h" #include "utils/ordered_map.h" #include "utils/ordered_set.h" #include "utils/utils.h" #include "utils/shape_utils.h" #include "debug/trace.h" #include "utils/label.h" #include "utils/ms_context.h" #include "frontend/operator/ops.h" #include "pipeline/jit/base.h" #include "debug/common.h" using mindspore::tensor::TensorPy; namespace mindspore { // ============================================= MindSpore IR Exporter ============================================= std::string AnfExporter::GetNodeType(const AnfNodePtr &nd) { abstract::ShapePtr shape = nd->Shape() == nullptr ? nullptr : dyn_cast(nd->Shape()); TypePtr type = dyn_cast(nd->Type()); std::ostringstream oss; if ((nullptr != shape) && (nullptr != type)) { oss << type->DumpText() << shape->DumpText(); } else if (nullptr != type) { oss << type->DumpText(); } else { oss << "Undefined"; } return oss.str(); } int AnfExporter::GetParamIndex(const FuncGraphPtr &func_graph, const AnfNodePtr ¶m, bool throw_excp) { if (func_graph == nullptr || param == nullptr) { return -1; } FuncGraphPtr fg = func_graph; while (fg != nullptr) { if (exported.find(fg) == exported.end()) { if (!check_integrity_) { break; } MS_LOG(EXCEPTION) << "Can not find func graph '" << fg->DumpText() << "." << fg->debug_info()->get_id() << "'"; } auto param_map = exported[fg]; if (param_map.find(param) != param_map.end()) { return param_map[param]; } fg = fg->parent(); } if (throw_excp) { MS_LOG(EXCEPTION) << "Can not find index for param '" << param->DumpText() << "' for func graph '" << func_graph->DumpText() << "." << func_graph->debug_info()->get_id() << "'"; } return -1; } // try to find index of parameter for SymbolicKeyInstance from all exported graphs // NOTICE: Suppose name of all parameters in SymbolicKeyInstance are different int AnfExporter::GetParamIndexFromExported(const AnfNodePtr ¶m) { if (param == nullptr) { return -1; } int ret = -1; for (const auto &item : exported) { auto pram_iter = item.second.find(param); if (pram_iter != item.second.end()) { return pram_iter->second; } } return ret; } std::string AnfExporter::GetValueNodeText(const FuncGraphPtr &fg, const ValueNodePtr &node) { MS_EXCEPTION_IF_NULL(node); return GetValueText(fg, node->value()); } std::string AnfExporter::GetMultitypeFuncGraphText(const prim::MultitypeFuncGraphPtr &mt_func_graph) { auto py_funcs = mt_func_graph->GetPyFunctions(); if (py_funcs.empty()) { return ""; } std::ostringstream oss; oss << "{"; bool is_first = true; for (const auto &py_func : py_funcs) { if (is_first) { is_first = false; } else { oss << ", "; } oss << "("; for (size_t i = 0; i < py_func.first.size(); ++i) { if (i > 0) { oss << ", "; } oss << py_func.first[i]->DumpText(); } oss << ")"; } oss << "}"; return oss.str(); } inline bool Skip(const MetaFuncGraphPtr &meta_func_graph) { return meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa() || meta_func_graph->isa(); } /* inherit relation of MetaFuncGraph * * MetaGraph * ├── MultitypeGraph * ├── HyperMap * │ └── HyperMapPy * ├── Map * │ └── MapPy * ├── Tail * ├── MakeTupleGradient * ├── MakeListGradient * ├── GradOperation * └── TupleAdd */ std::string AnfExporter::GetMetaFuncGraphText(const MetaFuncGraphPtr &meta_func_graph) { if (meta_func_graph == nullptr) { return ""; } std::ostringstream oss; oss << meta_func_graph->type_name() << "::" << meta_func_graph->name(); if (meta_func_graph->isa()) { prim::MultitypeFuncGraphPtr mt_func_graph = meta_func_graph->cast(); oss << GetMultitypeFuncGraphText(mt_func_graph); } else if (meta_func_graph ->isa()) { // this statement must before 'meta_graph->isa()' auto hyper_map = meta_func_graph->cast(); if (hyper_map->GetFnLeaf() != nullptr) { oss << "{fn_leaf=" << GetMetaFuncGraphText(hyper_map->GetFnLeaf()) << "}"; } } else if (meta_func_graph->isa()) { auto hyper_map = meta_func_graph->cast(); if (hyper_map->GetFnLeaf() != nullptr) { oss << "{fn_leaf=" << GetMetaFuncGraphText(hyper_map->GetFnLeaf()) << "}"; } } else if (meta_func_graph->isa()) { // this statement must before 'meta_graph->isa()' auto map = meta_func_graph->cast(); if (map->GetFnLeaf() != nullptr) { oss << "{fn_leaf=" << GetMetaFuncGraphText(map->GetFnLeaf()) << "}"; } } else if (meta_func_graph->isa()) { auto map = meta_func_graph->cast(); if (map->GetFnLeaf() != nullptr) { oss << "{fn_leaf=" << GetMetaFuncGraphText(map->GetFnLeaf()) << "}"; } } else if (meta_func_graph->isa()) { prim::GradOperationPtr grad_op = meta_func_graph->cast(); oss << "{get_all=" << grad_op->get_all_ << ", get_by_list=" << grad_op->get_by_list_ << ", sens_param=" << grad_op->sens_param_ << "}"; } else if (Skip(meta_func_graph)) { // do nothing } else { MS_LOG(EXCEPTION) << "Unknown MetaFuncGraph type " << meta_func_graph->type_name(); } return oss.str(); } std::string AnfExporter::GetPrimitiveText(const PrimitivePtr &prim) { std::ostringstream oss; if (prim == nullptr) { return oss.str(); } oss << prim->type_name() << "::" << prim->name(); // output primitive type oss << "{prim_type=" << static_cast(prim->prim_type()) << "}"; // output primitive attributes oss << prim->GetAttrsText(); if (prim->isa()) { auto do_signature = dyn_cast(prim); auto &func = do_signature->function(); if (func->isa()) { auto sig_prim = dyn_cast(func); oss << sig_prim->GetAttrsText(); } } return oss.str(); } std::string AnfExporter::GetNameSpaceText(const parse::NameSpacePtr &ns) { std::ostringstream oss; if (ns == nullptr) { return oss.str(); } // dump related module information in Namespace oss << ns->type_name() << "::" << ns->module(); return oss.str(); } std::string AnfExporter::GetSymbolicKeyInstanceText(const FuncGraphPtr &func_graph, const SymbolicKeyInstancePtr &sym_inst) { MS_EXCEPTION_IF_NULL(func_graph); MS_EXCEPTION_IF_NULL(sym_inst); AnfNodePtr sym_node = sym_inst->node(); MS_EXCEPTION_IF_NULL(sym_node); std::ostringstream oss; if (sym_node->isa()) { int idx = GetParamIndex(func_graph, sym_node, false); // if can not find SymbolicKeyInstance related parameter from ancestors, // try to find from all exported graphs if (idx < 0) { idx = GetParamIndexFromExported(sym_node); } if (idx < 0) { ParameterPtr p = dyn_cast(sym_node); if (p == nullptr) { MS_LOG(EXCEPTION) << "Sym_inst's node could not cast to parameter"; } MS_LOG(WARNING) << "Can not find SymbolicKeyInstance: " << p->name(); } oss << "SymInst(%para" << idx << ")"; } else { MS_LOG(WARNING) << "SymbolicKeyInstance does not embed a parameter: " << sym_node->ToString(); oss << "SymInst(cnode_" << sym_node->ToString() << ")"; } return oss.str(); } std::string AnfExporter::GetSequenceText(const FuncGraphPtr &func_graph, const ValuePtr &value) { std::ostringstream oss; // output ValueList, ValueTuple ValueSequeuePtr seq = dyn_cast(value); MS_EXCEPTION_IF_NULL(seq); MS_EXCEPTION_IF_NULL(value); bool is_tuple = value->isa(); oss << (is_tuple ? "(" : "["); bool first_flag = true; for (auto elem : seq->value()) { if (first_flag) { first_flag = false; } else { oss << ", "; } oss << GetValueText(func_graph, elem); } oss << (is_tuple ? ")" : "]"); return oss.str(); } std::string AnfExporter::GetDictText(const FuncGraphPtr &func_graph, const ValuePtr &value) { std::ostringstream oss; ValueDictionaryPtr dict = value->cast(); oss << "{"; bool first_flag = true; for (const auto &elem : dict->value()) { if (first_flag) { first_flag = false; } else { oss << ", "; } oss << "\"" << elem.first << "\": " << GetValueText(func_graph, elem.second); } oss << "}"; return oss.str(); } std::string AnfExporter::GetOtherValueText(const FuncGraphPtr &, const ValuePtr &value) { std::ostringstream oss; if (check_integrity_) { MS_LOG(EXCEPTION) << "Need to process type: " << value->type_name() << ", dump text: " << value->DumpText(); } oss << value->type_name() << "[" << value->DumpText() << "]"; return oss.str(); } std::string AnfExporter::GetValueText(const FuncGraphPtr &func_graph, const ValuePtr &value) { std::ostringstream oss; bool is_null_ptr = (func_graph == nullptr || value == nullptr); if (is_null_ptr) { return oss.str(); } if (value->isa()) { oss << GetPrimitiveText(value->cast()); } else if (value->isa()) { MetaFuncGraphPtr meta_func_graph = value->cast(); oss << GetMetaFuncGraphText(meta_func_graph); } else if (value->isa()) { oss << GetSymbolicKeyInstanceText(func_graph, value->cast()); } else if (value->isa()) { oss << value->DumpText(); } else if (value->isa() || value->isa()) { oss << value->DumpText(); } else if (value->isa()) { oss << value->DumpText(); } else if (value->isa() || value->isa() || value->isa()) { oss << value->DumpText(); } else if (value->isa()) { oss << GetSequenceText(func_graph, value); } else if (value->isa()) { oss << GetDictText(func_graph, value); } else if (value->isa()) { ValueSlicePtr slice = value->cast(); oss << slice->DumpText(); } else if (value->isa()) { oss << value->DumpText(); } else if (value->isa()) { oss << GetNameSpaceText(value->cast()); } else if (value->isa()) { oss << value->type_name(); } else if (value->isa()) { KeywordArgPtr keyword_arg = value->cast(); oss << keyword_arg->DumpText(); } else { return GetOtherValueText(func_graph, value); } return oss.str(); } // this function is used to output node in CNode's inputs std::string AnfExporter::GetAnfNodeText(const FuncGraphPtr &func_graph, const AnfNodePtr &node, const std::map &apply_map) { std::ostringstream oss; if (func_graph == nullptr || node == nullptr) { return oss.str(); } if (node->isa()) { auto iter = apply_map.find(node); if (iter == apply_map.end()) { MS_LOG(EXCEPTION) << "Can not find node '" << node->DumpText() << "' in apply_map"; } oss << "%" << iter->second; } else if (node->isa()) { // Parameter maybe a free variable, so check it in its own funcgraph. oss << "%para" << GetParamIndex(node->func_graph(), node, check_integrity_); } else if (IsValueNode(node)) { FuncGraphPtr fg = GetValueNode(node); oss << fg->type_name() << "::fg_" << fg->debug_info()->get_id(); if (!func_graph_set.contains(fg) && exported.find(fg) == exported.end() && export_used_) { func_graph_set.add(fg); } } else if (node->isa()) { oss << GetValueNodeText(func_graph, node->cast()); } else { MS_LOG(EXCEPTION) << "Unknown node '" << node->DumpText() << "'"; } return oss.str(); } void AnfExporter::OutputParameters(std::ofstream &ofs, const std::vector ¶meters, OrderedMap *param_map) { bool first_flag = true; for (const AnfNodePtr ¶m : parameters) { if (first_flag) { first_flag = false; ofs << " "; } else { ofs << " , "; } (*param_map)[param] = param_index; std::string type_info = GetNodeType(param); // output parameter and type if (type_info == "Undefined") { ofs << "%para" << param_index; } else { ofs << "%para" << param_index << " : " << type_info; } // output comment ofs << " # " << param->DumpText() << "\n"; param_index += 1; } } void AnfExporter::OutputStatementComment(std::ofstream &ofs, const CNodePtr &node) { if (node == nullptr) { return; } // output type of each input argument auto &inputs = node->inputs(); if (inputs.size() > 1) { ofs << " #("; for (size_t i = 1; i < inputs.size(); ++i) { if (i != 1) { ofs << ", "; } AnfNodePtr arg = inputs[i]; ofs << GetNodeType(arg); } ofs << ")"; } // output other comment, map the graph name to original representation(containing unicode character) std::ostringstream comment; comment << " #"; bool has_comment = false; for (size_t i = 0; i < inputs.size(); ++i) { AnfNodePtr arg = inputs[i]; if (!IsValueNode(arg)) { continue; } if (!has_comment) { has_comment = true; } else { comment << ","; } FuncGraphPtr fg = GetValueNode(arg); std::string func_graph_id = fg->debug_info()->get_id(); comment << " fg_" << func_graph_id << "=" << fg->ToString() << "." << func_graph_id; } if (has_comment) { ofs << comment.str(); } ofs << " #scope: " << node->scope()->name(); } void AnfExporter::OutputCNodes(std::ofstream &ofs, const std::vector &nodes, const FuncGraphPtr &func_graph) { if (func_graph == nullptr) { return; } int idx = 1; std::map apply_map; for (const AnfNodePtr &node : nodes) { MS_EXCEPTION_IF_NULL(node); if (!node->isa()) { continue; } auto iter = tagged_cnodes_.find(node); if (iter != tagged_cnodes_.end()) { ofs << "\n#------------------------> " << iter->second << "\n"; } auto cnode = node->cast(); auto &inputs = cnode->inputs(); std::string op_text = GetAnfNodeText(func_graph, inputs[0], apply_map); // non-return node if (node != func_graph->get_return()) { int apply_idx = idx++; apply_map[node] = apply_idx; std::string type_info = GetNodeType(node); if (type_info == "Undefined") { ofs << " %" << apply_idx << " = " << op_text << "("; } else { ofs << " %" << apply_idx << " : " << type_info << " = " << op_text << "("; } } else { ofs << " " << op_text << "("; } for (size_t i = 1; i < inputs.size(); ++i) { if (i != 1) { ofs << ", "; } AnfNodePtr arg = inputs[i]; ofs << GetAnfNodeText(func_graph, arg, apply_map); } ofs << ")"; // output comment OutputStatementComment(ofs, cnode); ofs << "\n"; if (label_manage::GetGlobalTraceLabelType() == label_manage::TraceLabelType::kWithUniqueId) { ofs << trace::GetDebugInfo(cnode->debug_info(), " # ", kSourceLineTipDiscard) << "#" << label_manage::Label(cnode->debug_info()) << "\n"; } else { ofs << trace::GetDebugInfo(cnode->debug_info(), " # ", kSourceLineTipDiscard) << "#" << cnode->ToString() << "\n"; } } } void AnfExporter::OutputOrderList(std::ofstream &ofs, const FuncGraphPtr &func_graph) { auto &order_list = func_graph->order_list(); if (order_list.empty()) { return; } constexpr int width = 4; ofs << "# order:\n"; int i = 1; for (auto &node : order_list) { ofs << '#' << std::setw(width) << i << ": " << node->DebugString() << '\n'; ++i; } } void AnfExporter::ExportOneFuncGraph(std::ofstream &ofs, const FuncGraphPtr &func_graph) { if (func_graph == nullptr) { return; } std::vector nodes = TopoSort(func_graph->get_return(), SuccIncoming, AlwaysInclude); std::vector parameters = func_graph->parameters(); OrderedMap param_map; if (*(func_graph->switch_input())) { ofs << "switch_input: " << *(func_graph->switch_input()) << "\n"; } if (*(func_graph->switch_layer_input())) { ofs << "switch_layer_input: " << *(func_graph->switch_layer_input()) << "\n"; } ofs << "# [No." << (exported.size() + 1) << "] " << func_graph->DumpText() << "." << func_graph->debug_info()->get_id() << "\n"; if (label_manage::GetGlobalTraceLabelType() == label_manage::TraceLabelType::kWithUniqueId) { ofs << trace::GetDebugInfo(func_graph->debug_info(), "# ", kSourceLineTipDiscard) << "#" << label_manage::Label(func_graph->debug_info()) << "\n"; } else { ofs << trace::GetDebugInfo(func_graph->debug_info(), "# ", kSourceLineTipDiscard) << "\n"; } ofs << "funcgraph fg_" << func_graph->debug_info()->get_id(); // output name of parent of graph if exists if (func_graph->parent() != nullptr) { ofs << "[fg_" << func_graph->parent()->debug_info()->get_id() << "]"; } ofs << "(\n"; OutputParameters(ofs, parameters, ¶m_map); exported[func_graph] = param_map; ofs << (!parameters.empty() ? " " : "") << ") {\n"; OutputCNodes(ofs, nodes, func_graph); ofs << "}\n"; OutputOrderList(ofs, func_graph); } void AnfExporter::ExportFuncGraph(const std::string &filename, const FuncGraphPtr &func_graph) { if (func_graph == nullptr) { return; } std::ofstream ofs(filename); if (!ofs.is_open()) { MS_LOG(ERROR) << "Open file '" << filename << "' failed!"; return; } param_index = 1; func_graph_set.add(func_graph); while (!func_graph_set.empty()) { FuncGraphPtr fg = *func_graph_set.begin(); ExportOneFuncGraph(ofs, fg); ofs << "\n\n"; (void)func_graph_set.erase(fg); } ofs << "# num of total function graphs: " << exported.size(); ofs.close(); } void AnfExporter::ExportFuncGraph(const std::string &filename, const std::vector &graphs) { if (graphs.empty()) { return; } std::ofstream ofs(filename); if (!ofs.is_open()) { MS_LOG(ERROR) << "Open file '" << filename << "' failed!"; return; } param_index = 1; for (const auto &tagged_graph : graphs) { tagged_cnodes_ = tagged_graph.second; ExportOneFuncGraph(ofs, tagged_graph.first); tagged_cnodes_.clear(); ofs << "\n\n"; } ofs << "# num of total function graphs: " << graphs.size(); ofs.close(); } #ifdef ENABLE_DUMP_IR void ExportIR(const std::string &filename, const FuncGraphPtr &func_graph) { if (func_graph == nullptr) { return; } auto real_filename = pipeline::GetSaveGraphsPathName(Common::AddId(filename, ".dat")); AnfExporter exporter; ChangeFileMode(real_filename, S_IRWXU); exporter.ExportFuncGraph(real_filename, func_graph); // set file mode to read only by user ChangeFileMode(real_filename, S_IRUSR); } void ExportIR(const std::string &filename, const std::vector &graphs) { auto real_filename = pipeline::GetSaveGraphsPathName(Common::AddId(filename, ".dat")); AnfExporter exporter("", false); ChangeFileMode(real_filename, S_IRWXU); exporter.ExportFuncGraph(real_filename, graphs); // set file mode to read only by user ChangeFileMode(real_filename, S_IRUSR); } #else void ExportIR(const std::string &, const std::string &, const FuncGraphPtr &) { static bool already_printed = false; if (already_printed) { return; } already_printed = true; MS_LOG(WARNING) << "The functionality of dumping function graph IR is disabled, " << "please recompile source to enable it. See help of building script."; } void ExportIR(const std::string &filename, const std::vector &graphs) { static bool already_printed = false; if (already_printed) { return; } already_printed = true; MS_LOG(WARNING) << "The functionality of dumping function graph IR is disabled, " << "please recompile source to enable it. See help of building script."; } #endif } // namespace mindspore